Advertisement
tipsypastels

Untitled

Sep 5th, 2020
2,579
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 16.17 KB | None | 0 0
  1. require 'active_support/all'
  2.  
  3. class Object
  4.   def not_nil!
  5.     self
  6.   end
  7. end
  8.  
  9. class NilClass
  10.   def not_nil!
  11.     raise "Called #not_nil! on nil"
  12.   end
  13. end
  14.  
  15. def parse_supplementary_file(file)
  16.   File.read(file).split(/\n/).map { _1.split(/,/)[1..2] }.to_h
  17. end
  18.  
  19. ABILITIES = parse_supplementary_file('abilities.pbs')
  20. MOVES = parse_supplementary_file('moves.pbs')
  21. ITEMS = parse_supplementary_file('items.pbs')
  22.  
  23. SHAPES = {
  24.   1 => 'Head',
  25.   7 => 'HeadLegs',
  26.   3 => 'Fins',
  27.   14 => 'Insectoid',
  28.   8 => 'Quadruped',
  29.   13 => 'MultiWings',
  30.   11 => 'MultiBody',
  31.   10 => 'Tentacles',
  32.   5 => 'HeadBase',
  33.   6 => 'BipedalTail',
  34.   12 => 'BipedalNoTail',
  35.   9 => 'SingleWings',
  36.   2 => 'Serpentine',
  37.   4 => 'HeadArms',
  38.   0 => 'Varies',
  39. }
  40.  
  41. RATIOS = {
  42.   'Female50Percent' => { female: 0.5, male: 0.5 },
  43.   'Female25Percent' => { female: 0.25, male: 0.75 },
  44.   'FemaleOneEighth' => { female: 0.125, male: 0.875 },
  45.   'Genderless' => { genderless: 1 },
  46.   'Female75Percent' => { female: 0.75, male: 0.25 },
  47.   'AlwaysFemale' => { female: 1 },
  48.   'AlwaysMale' => { male: 1 },
  49. }
  50.  
  51. def id2gen(id)
  52.   case id
  53.   when 1..151
  54.    1
  55.   when 152..251
  56.    2
  57.   when 252..386
  58.    3
  59.   when 387..493
  60.    4
  61.   when 494..649
  62.    5
  63.   when 650..721
  64.    6
  65.   when 722..809
  66.    7
  67.   when 810..893
  68.    8
  69.   end
  70. end
  71.  
  72. def initial_hash(line)
  73.   value = line.gsub(/\[|\]/, '')
  74.  
  75.   if value.match?(/^\d+$/)
  76.     num = value.to_i
  77.     { 'number' => num, 'generation' => id2gen(num) }
  78.   else
  79.     { 'baseForm' => value.sub(/\-\d+$/, '').downcase }
  80.   end
  81. end
  82.  
  83. BASE_STATS = %w[health attack defense speed specialAttack specialDefense]
  84.  
  85. def to_stats_hash(str)
  86.   BASE_STATS.zip(str.split(/,/).map(&:to_i)).to_h
  87. end
  88.  
  89. def to_learnset(str)
  90.   str.split(/,/).in_groups_of(2).inject([]) do |moves, (level_str, move_id)|
  91.     level = level_str.to_i
  92.     move = MOVES.fetch(move_id)
  93.  
  94.     if moves.last && moves.last[0] == level
  95.       moves[0...-1] + [[moves.last[0], Array(moves.last[1]) + [move]]]
  96.     else
  97.       moves + [[level, move]]
  98.     end
  99.   end.to_h
  100. end
  101.  
  102. EVO_METHODS = {
  103.   Level: -> value {
  104.     { type: 'level', level: value.to_i }
  105.   },
  106.   Item: -> value {
  107.     { type: 'item', item: ITEMS.fetch(value) }
  108.   },
  109.   Happiness: -> {
  110.     { type: 'happiness' }
  111.   },
  112.   TradeItem: -> value {
  113.     { type: 'tradeItem', item: ITEMS.fetch(value) }
  114.   },
  115.   Trade: -> {
  116.     { type: 'trade' }
  117.   },
  118.   HasMove: -> value {
  119.     { type: 'knowsMove', move: MOVES.fetch(value) }
  120.   },
  121.   HappinessMoveType: -> value {
  122.     { type: 'knowsMoveTypeWhileHappy', moveType: value.capitalize }
  123.   },
  124.   HappinessDay: -> {
  125.     { type: 'happinessAtDay' }
  126.   },
  127.   HappinessNight: -> {
  128.     { type: 'happinessAtNight' }
  129.   },
  130.   DayHoldItem: -> value {
  131.     { type: 'holdingItemAtDay', item: ITEMS.fetch(value) }
  132.   },
  133.   NightHoldItem: -> value {
  134.     { type: 'holdingItemAtNight', item: ITEMS.fetch(value) }
  135.   },
  136.   AttackGreater: -> value {
  137.     { type: 'attackGreaterAtLevel', level: value.to_i }
  138.   },
  139.   DefenseGreater: -> value {
  140.     { type: 'defenseGreaterAtLevel', level: value.to_i }
  141.   },
  142.   AtkDefEqual: -> value {
  143.     { type: 'attackDefenseEqualAtLevel', level: value.to_i }
  144.   },
  145.   Silcoon: -> value {
  146.     { type: 'silcoonPersonalityAtLevel', level: value.to_i }
  147.   },
  148.   Cascoon: -> value {
  149.     { type: 'cascoonPersonalityAtLevel', level: value.to_i }
  150.   },
  151.   ItemMale: -> value {
  152.     { type: 'itemWhenMale', item: ITEMS.fetch(value) }
  153.   },
  154.   ItemFemale: -> value {
  155.     { type: 'itemWhenFemale', item: ITEMS.fetch(value) }
  156.   },
  157.   Ninjask: -> value {
  158.     { type: 'ninjask', level: value.to_i }
  159.   },
  160.   Shedinja: -> value {
  161.     { type: 'shedinja', level: value.to_i }
  162.   },
  163.   Location: -> value {
  164.     { type: 'specialLocationAtLevel', level: value.to_i }
  165.   },
  166.   TookDamage: -> value {
  167.     # pbs file says 2, not sure if it's wrong or is represented differently
  168.     { type: 'specialLocationTookDamage', damage: 49 }
  169.   },
  170.   LandCritical: -> value {
  171.     { type: 'landedCriticalHit' }
  172.   },
  173.   Beauty: -> value {
  174.     { type: 'beauty', beauty: value.to_i }
  175.   },
  176.   LevelFemale: -> value {
  177.     { type: 'levelWhenFemale', level: value.to_i }
  178.   },
  179.   LevelMale: -> value {
  180.     { type: 'levelWhenMale', level: value.to_i }
  181.   },
  182.   HasInParty: -> value {
  183.     { type: 'hasInParty', pokemon: value.downcase }
  184.   },
  185.   TradeSpecies: -> value {
  186.     { type: 'tradeFor', pokemon: value.downcase }
  187.   },
  188.   LevelDarkInParty: -> value {
  189.     { type: 'darkInPartyWhileAtLevel', level: value.to_i }
  190.   },
  191.   LevelDay: -> value {
  192.     { type: 'levelAtDay', level: value.to_i }
  193.   },
  194.   LevelNight: -> value {
  195.     { type: 'levelAtNight', level: value.to_i }
  196.   },
  197.   LevelRain: -> value {
  198.     { type: 'levelInRain', level: value.to_i }
  199.   },
  200.   PotItem: -> {
  201.     { type: 'potItem' }
  202.   },
  203.   HoldSweetItem: -> {
  204.     { type: 'holdingSweetItem' }
  205.   },
  206. }.with_indifferent_access
  207.  
  208. def to_evos(str)
  209.   tokens = str.split(/,/).reject(&:empty?)
  210.   i = 0
  211.   output = []
  212.   current_mon = nil
  213.  
  214.   while i < tokens.size
  215.     token = tokens[i]
  216.  
  217.     if current_mon.nil?
  218.       current_mon = token.downcase
  219.       i += 1
  220.     else
  221.       callback = EVO_METHODS.fetch(token)
  222.      
  223.       result =
  224.         if callback.arity == 1
  225.           value = tokens[i + 1]
  226.           i += 2
  227.           callback.call(value)
  228.         else
  229.           i += 1
  230.           callback.call
  231.         end
  232.  
  233.       output << [current_mon, result]
  234.       current_mon = nil
  235.     end
  236.   end
  237.  
  238.   output
  239. end
  240.  
  241. IGNORED_KEYS = %w[
  242.   regionalNumbers
  243.   battlerPlayerY battlerEnemyY battlerAltitude
  244.   wildItemUncommon wildItemCommon wildItemRare
  245.   incense megaStone
  246. ]
  247.  
  248. def parse_main_file(file)
  249.   mons = []
  250.  
  251.   File.open(file, 'r:UTF-8').readlines.each do |line|
  252.     line.strip!
  253.  
  254.     next if line.match?(/#/)
  255.     next mons.push(initial_hash(line)) if line.match?(/^\[/)
  256.  
  257.     mon = mons.last
  258.     key, value = line.split('=')
  259.  
  260.     next unless value # empty field
  261.    
  262.     key = key.strip.camelize(:lower)
  263.     value = value.strip
  264.  
  265.     case key
  266.     when 'internalName'
  267.       mon['slug'] = value.downcase
  268.     when 'type1'
  269.       mon['types'] = [value.capitalize]
  270.     when 'type2'
  271.       mon['types'] << value.capitalize
  272.     when 'baseStats'
  273.       mon['baseStats'] = to_stats_hash(value)
  274.     when 'genderRate'
  275.       mon['genderRatio'] = RATIOS.fetch(value)
  276.     when 'shape'
  277.       mon['shape'] = SHAPES.fetch(value.to_i)
  278.     when 'baseEXP'
  279.       mon['baseExp'] = value.to_i
  280.     when 'effortPoints'
  281.       mon['evYield'] = to_stats_hash(value)
  282.     when 'rareness'
  283.       mon['catchRate'] = value.to_i
  284.     when 'happiness'
  285.       mon['happiness'] = value.to_i
  286.     when 'height', 'weight'
  287.       mon[key] = value.to_f
  288.     when 'compatibility'
  289.       mon['eggGroups'] = value.split(/,/)
  290.     when 'pokedex'
  291.       mon['dexEntry'] = value
  292.     when 'stepsToHatch'
  293.       mon['hatchSteps'] = value.to_i
  294.     when 'hiddenAbility'
  295.       # greninja has a second HA listed in this file for some reason
  296.       # i'm hardcoding it because i want any future mons that also have
  297.       # that to raise until i've explicitly double checked them
  298.       if mon['number'] == 658
  299.         mon['hiddenAbility'] = ABILITIES.fetch(value.split(/,/).first)
  300.       else
  301.         mon['hiddenAbility'] = ABILITIES.fetch(value)
  302.       end
  303.     when 'abilities'
  304.       mon['abilities'] = value.split(/,/).collect { ABILITIES.fetch(_1) }
  305.     when 'eggMoves'
  306.       mon['eggMoves'] = value.split(/,/).collect { MOVES.fetch(_1) }
  307.     when 'moves'
  308.       mon['moves'] = to_learnset(value)
  309.     when 'evolutions'
  310.       mon['evolutions'] = to_evos(value)
  311.     when *IGNORED_KEYS
  312.       # ignore
  313.     else
  314.       mon[key] = value
  315.     end
  316.   end
  317.  
  318.   mons
  319. end
  320.  
  321. ALTFORM_HANDLERS = {
  322.   _mega: -> mon {
  323.     # for some reason this is missing
  324.     mon['formName'] += ' Y' if mon['formName'] == 'Mega Charizard'
  325.  
  326.     mon['slug'] +=
  327.       case mon['formName']
  328.       when / Y$/
  329.         '-mega-y'
  330.       when / X$/
  331.         '-mega-x'
  332.       else
  333.         '-mega'
  334.       end
  335.  
  336.     mon['formName'].sub!(/\b#{mon['name']}\b/, '').strip!
  337.     mon['formName'].sub!(/\s+/, ' ')
  338.   },
  339.   _alola: -> mon {
  340.     mon['slug'] += '-alola'
  341.     mon['formName'].delete_suffix!(" #{mon['name']}")
  342.   },
  343.   _galar: -> mon {
  344.     if mon['formName'] =~ /Zen Mode/
  345.       mon['slug'] += '-galarzen'
  346.     else
  347.       mon['slug'] += '-galar'
  348.     end
  349.  
  350.     mon['formName'].delete_suffix!(" #{mon['name']}")
  351.   },
  352.   deoxys: -> mon {
  353.     mon['slug'] = "Deoxys #{mon['formName'].delete_suffix(' Forme')}".parameterize
  354.   },
  355.   unown: -> mon {
  356.     mon['name'] = 'Unown'
  357.     mon['formName'] = mon['formName'][0]
  358.  
  359.     case mon['formName']
  360.     when '!'
  361.       mon['slug'] = 'unown-exclamation'
  362.     when '?'
  363.       mon['slug'] = 'unown-question'
  364.     else
  365.       mon['slug'] = "Unown #{mon['formName']}".parameterize
  366.     end
  367.   },
  368.   pichu: -> mon {
  369.     mon['formName'] = 'Spiky-Eared'
  370.     mon['slug'] = 'pichu-spikyeared'
  371.   },
  372.   castform: -> mon {
  373.     mon['formName'].delete_suffix!(' Form')
  374.     mon['slug'] = "Castform #{mon['formName']}".parameterize
  375.   },
  376.   kyogre: -> mon {
  377.     mon['formName'] = 'Primal'
  378.     mon['slug'] = 'kyogre-primal'
  379.   },
  380.   groudon: -> mon {
  381.     mon['formName'] = 'Primal'
  382.     mon['slug'] = 'groudon-primal'
  383.   },
  384.   burmy: -> mon {
  385.     mon['formName'].delete_suffix!(' Cloak')
  386.     mon['slug'] = "Burmy #{mon['formName']}".parameterize
  387.   },
  388.   wormadam: -> mon {
  389.     mon['formName'].delete_suffix!(' Cloak')
  390.     mon['slug'] = "Wormadam #{mon['formName']}".parameterize
  391.   },
  392.   cherrim: -> mon {
  393.     mon['formName'] = 'Sunshine'
  394.     mon['slug'] = 'cherrim-sunshine'
  395.   },
  396.   shellos: -> mon {
  397.     mon['formName'] = 'East Sea'
  398.     mon['slug'] = 'shellos-east'
  399.   },
  400.   gastrodon: -> mon {
  401.     mon['formName'] = 'East Sea'
  402.     mon['slug'] = 'gastrodon-east'
  403.   },
  404.   rotom: -> mon {
  405.     mon['formName'].delete_suffix!(' Rotom')
  406.     mon['slug'] = "Rotom #{mon['formName']}".parameterize
  407.   },
  408.   giratina: -> mon {
  409.     mon['slug'] = 'giratina-origin'
  410.   },
  411.   shaymin: -> mon {
  412.     mon['slug'] = 'shaymin-sky'
  413.   },
  414.   arceus: -> mon {
  415.     mon['formName'].delete_suffix!(' Type')
  416.     mon['slug'] = "Arceus #{mon['formName']}".parameterize
  417.   },
  418.   basculin: -> mon {
  419.     mon['slug'] = 'basculin-bluestriped'
  420.   },
  421.   darmanitan: -> mon {
  422.     # galar form has already been parsed, doesnt hit this func
  423.     mon['slug'] = 'darmanitan-zen'
  424.   },
  425.   deerling: -> mon {
  426.     mon['formName'].delete_suffix!(' Form')
  427.     mon['slug'] = "Deerling #{mon['formName']}".parameterize
  428.   },
  429.   sawsbuck: -> mon {
  430.     mon['formName'].delete_suffix!(' Form')
  431.     mon['slug'] = "Sawsbuck #{mon['formName']}".parameterize
  432.   },
  433.   tornadus: -> mon {
  434.     mon['slug'] = 'tornadus-therian'
  435.   },
  436.   thundurus: -> mon {
  437.     mon['slug'] = 'thundurus-therian'
  438.   },
  439.   landorus: -> mon {
  440.     mon['slug'] = 'landorus-therian'
  441.   },
  442.   kyurem: -> mon {
  443.     mon['formName'].delete_suffix!(' Kyurem')
  444.     mon['slug'] = "Kyurem #{mon['formName']}".parameterize
  445.   },
  446.   keldeo: -> mon {
  447.     mon['slug'] = 'keldeo-resolute'
  448.   },
  449.   meloetta: -> mon {
  450.     mon['slug'] = 'meloetta-pirouette'
  451.   },
  452.   genesect: -> mon {
  453.     mon['slug'] = "Genesect #{mon['formName'].delete_suffix(' Drive')}".parameterize
  454.   },
  455.   greninja: -> mon {
  456.     mon['slug'] = 'greninja-ash'
  457.   },
  458.   vivillon: -> mon {
  459.     mon['formName'].delete_suffix!(' Pattern')
  460.     mon['slug'] = "Vivillon #{mon['formName'].sub(' ', '')}".parameterize
  461.   },
  462.   flabebe: -> mon {
  463.     mon['formName'].delete_suffix!(' Flower')
  464.     mon['slug'] = "Flabebe #{mon['formName']}".parameterize
  465.   },
  466.   floette: -> mon {
  467.     mon['formName'].delete_suffix!(' Flower')
  468.     mon['slug'] = "Floette #{mon['formName']}".parameterize
  469.   },
  470.   florges: -> mon {
  471.     mon['formName'].delete_suffix!(' Flower')
  472.     mon['slug'] = "Florges #{mon['formName']}".parameterize
  473.   },
  474.   furfrou: -> mon {
  475.     mon['formName'].delete_suffix!(' Trim')
  476.     mon['slug'] = "Furfrou #{mon['formName']}".parameterize
  477.   },
  478.   meowstic: -> mon {
  479.     mon['slug'] = 'meowstic-female'
  480.   },
  481.   aegislash: -> mon {
  482.     mon['slug'] = 'aegislash-blade'
  483.   },
  484.   pumpkaboo: -> mon {
  485.     mon['formName'].delete_suffix!(' Size')
  486.     mon['slug'] = "Pumpkaboo #{mon['formName']}".parameterize
  487.   },
  488.   gourgeist: -> mon {
  489.     mon['formName'].delete_suffix!(' Size')
  490.     mon['slug'] = "Gourgeist #{mon['formName']}".parameterize
  491.   },
  492.   zygarde: -> mon {
  493.     if mon['formName'] =~ /Complete/
  494.       mon['slug'] = 'zygarde-complete'
  495.     else
  496.       mon['slug'] = 'zygarde-10'
  497.     end
  498.   },
  499.   hoopa: -> mon {
  500.     mon['slug'] = 'hoopa-unbound'
  501.   },
  502.   oricorio: -> mon {
  503.     mon['formName'].delete_suffix!(' Style')
  504.     mon['slug'] = "Oricorio #{mon['formName'].sub(/[\-\']/, '')}".parameterize
  505.   },
  506.   lycanroc: -> mon {
  507.     mon['formName'].delete_suffix!(' Form')
  508.     mon['slug'] = "Lycanroc #{mon['formName']}".parameterize
  509.   },
  510.   wishiwashi: -> mon {
  511.     mon['formName'].delete_suffix!(' Form')
  512.     mon['slug'] = "Wishiwashi #{mon['formName']}".parameterize
  513.   },
  514.   silvally: -> mon {
  515.     mon['formName'].delete_suffix!(' Type')
  516.     mon['slug'] = "Silvally #{mon['formName']}".parameterize
  517.   },
  518.   minior: -> mon {
  519.     mon['formName'] = 'Indigo Core' if mon['formName'] == 'Gray Core'
  520.     mon['formName'].delete_suffix!(' Core')
  521.     mon['slug'] = "Minior #{mon['formName']}".parameterize
  522.   },
  523.   mimikyu: -> mon {
  524.     mon['formName'].delete_suffix!(' Form')
  525.     mon['slug'] = "Mimikyu #{mon['formName']}".parameterize
  526.   },
  527.   necrozma: -> mon {
  528.     mon['formName'].delete_suffix!(' Necrozma')
  529.     mon['slug'] = "Necrozma #{mon['formName'].tr(' ', '')}".parameterize
  530.   },
  531.   cramorant: -> mon {
  532.     mon['formName'].delete_suffix!(' Form')
  533.     mon['slug'] = "Cramorant #{mon['formName']}".parameterize
  534.   },
  535.   toxtricity: -> mon {
  536.     mon['formName'].delete_suffix!(' Form')
  537.     mon['slug'] = "Toxtricity #{mon['formName'].tr(' ', '')}".parameterize
  538.   },
  539.   sinistea: -> mon {
  540.     mon['formName'] = 'Antique'
  541.     mon['slug'] = 'sinistea-antique'
  542.   },
  543.   polteageist: -> mon {
  544.     mon['formName'] = 'Antique'
  545.     mon['slug'] = 'polteageist-antique'
  546.   },
  547.   alcremie: -> mon {
  548.     if mon['formName'] =~ /Strawberry/ # base case
  549.       mon['slug'] = "Alcremie #{mon['formName'].delete_prefix('Strawberry').tr(' ', '')}".parameterize
  550.     else
  551.       # put the item name at the end for files
  552.       item, *flavor = mon['formName'].split(/\s+/)
  553.       mon['slug'] = "Alcremie #{flavor.join(' ')} #{item}".parameterize
  554.     end
  555.   },
  556.   eiscue: -> mon {
  557.     mon['slug'] = 'eiscue-noice'
  558.   },
  559.   indeedee: -> mon {
  560.     mon['slug'] = 'indeedee-female'
  561.   },
  562.   morpeko: -> mon {
  563.     mon['formName'] = 'Hangry'
  564.     mon['slug'] = 'morpeko-hangry'
  565.   },
  566.   zacian: -> mon {
  567.     mon['slug'] = 'zacian-crowned'
  568.   },
  569.   zamazenta: -> mon {
  570.     mon['slug'] = 'zamazenta-crowned'
  571.   },
  572.   urshifu: -> mon {
  573.     # this will need to change once you get gmax
  574.     mon['slug'] = 'urshifu-rapidstrike'
  575.   },
  576. }.with_indifferent_access
  577.  
  578. $pumpkin_base_form_cache = {}
  579.  
  580. mons = parse_main_file('input.pbs') + parse_main_file('altforms.pbs')
  581.  
  582. # one of the miniors is missing, add it
  583. mons.push(
  584.   mons.detect { _1['formName'] == 'Red Core' }
  585.       .dup
  586.       .tap { _1['formName'] = 'Orange Core' }
  587. )
  588.  
  589. mons = mons.map { |mon|
  590.   # fix nidoran inconsistencies
  591.   mon['slug'] = 'nidoran-female' if mon['slug'] == 'nidoranfe'
  592.   mon['slug'] = 'nidoran' if mon['slug'] == 'nidoranma'
  593.  
  594.   next mon unless mon['baseForm']
  595.  
  596.   # some mons have "forms" just for the purposes of giving them
  597.   # additional data in essentials code, which we don't care about,
  598.   # so we skip them entirely
  599.   next if mon.keys == ['baseForm']
  600.  
  601.   base_mon = mons.detect { _1['slug'] == mon['baseForm'] }.not_nil!
  602.   mon.reverse_merge!(base_mon)
  603.  
  604.   ALTFORM_HANDLERS[:_mega].call(mon)      if mon['formName'] =~ /^Mega /
  605.   ALTFORM_HANDLERS[:_galar].call(mon)     if mon['formName'] =~ /^Galarian /
  606.   ALTFORM_HANDLERS[:_alola].call(mon)     if mon['formName'] =~ /^Alolan /
  607.   ALTFORM_HANDLERS[mon['slug']].call(mon) if ALTFORM_HANDLERS[mon['slug']]
  608.   mon
  609. }.compact
  610.  
  611. mons = mons.map { |mon| mon.sort_by { |key| key }.to_h }
  612. mons = mons.sort_by { |mon| [mon['number'], mon['name'].size] }
  613.  
  614. # slugs = mons.collect { _1['slug'] }
  615. # dupes = slugs.select { slugs.count(_1) > 1 }
  616. # puts "#{dupes.size} unresolved conflicts"
  617. # p dupes
  618.  
  619. File.write('./output.json', JSON.pretty_generate(mons))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement