diff --git a/pokedex/extract/oras.py b/pokedex/extract/oras.py index 91f5e5b..1561378 100644 --- a/pokedex/extract/oras.py +++ b/pokedex/extract/oras.py @@ -375,6 +375,15 @@ pokemon_mega_evolutions_struct = Filter(this.number != 0, Range( ) )) +# FIXME need a thing for "pad to 8 junk entries"? +pokemon_evolutions_struct = Filter(this.method != 0, Array(8, Struct( + 'method' / Int16ul, + 'param' / Int16ul, + 'into_species' / Int16ul, + 'form' / Int8sl, + 'level' / Int8sl, +))) + egg_moves_struct = Struct( 'moveids' / PrefixedArray(Int16ul, Int16ul), ) @@ -1392,6 +1401,7 @@ def extract_data(root, out): pokémon.weight = record.weight pokémon.moves = {} + pokémon.evolutions = [] # TODO transform to an OD somehow probably pokemon_data.append(record) @@ -1432,7 +1442,6 @@ def extract_data(root, out): texts['en']['move-names'][i], m=record, )) - return # Egg moves with read_garc(root / 'rom/a/0/1/2') as garc: # SUMO @@ -1444,7 +1453,6 @@ def extract_data(root, out): if not data: continue container = egg_moves_struct.parse(data) - print(i, ident, container.first_form_id, container.moveids) # FIXME: 961 pokémon, 1063 named forms, but 1048 egg movesets. # what? they get completely out of order after 802 and i don't # know how to fix this. didn't magical write some code...? @@ -1485,16 +1493,181 @@ def extract_data(root, out): # Evolution #with read_garc(root / 'rom/a/1/9/2') as garc: # ORAS with read_garc(root / 'rom/a/0/1/4') as garc: # SUMO? - for subfile in garc: - evolution = subfile[0].read() - print(repr(evolution)) + for i, subfile in enumerate(garc): + identifier = identifiers['pokémon'][i] + for raw_evo in pokemon_evolutions_struct.parse_stream(subfile[0]): + # FIXME how do i include form in here? especially when it + # could be -1?? i guess that's a null or optional key or + # something... do i even have separate identifiers for pokémon + # vs forms yet? really gotta figure that junk out. i don't + # even remember if all the unowns count as separate forms in + # sumo + # TODO should make this its own schema sub-node, probably, so + # its structure is actually documented + evo = OrderedDict() + evo['into'] = identifiers['pokémon'][raw_evo.into_species] + + if raw_evo.method == 1: + evo['trade'] = 'ev.level-up' + evo['minimum-friendship'] = 220 + elif raw_evo.method == 2: + evo['trade'] = 'ev.level-up' + # FIXME is this an enum? also really it's morning OR day + evo['time-of-day'] = 'day' + elif raw_evo.method == 3: + evo['trade'] = 'ev.level-up' + # FIXME is this an enum? + evo['time-of-day'] = 'night' + elif raw_evo.method == 4: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + elif raw_evo.method == 5: + evo['trigger'] = 'ev.trade' + elif raw_evo.method == 6: + evo['trigger'] = 'ev.trade' + evo['held-item'] = identifiers['item'][raw_evo.param] + elif raw_evo.method == 7: + evo['trigger'] = 'ev.trade' + evo['traded-with'] = identifiers['pokémon'][raw_evo.param] + elif raw_evo.method == 8: + evo['trigger'] = 'ev.use-item' + evo['trigger-item'] = identifiers['item'][raw_evo.param] + elif raw_evo.method == 9: + evo['trigger'] = 'ev.level-up' + evo['higher-physical-stat'] = 'defense' + evo['minimum-level'] = raw_evo.level + elif raw_evo.method == 10: + evo['trigger'] = 'ev.level-up' + # FIXME hm, attack and defense are both names of stats, but + # this isn't? i am bothered + evo['higher-physical-stat'] = 'equal' + evo['minimum-level'] = raw_evo.level + elif raw_evo.method == 11: + evo['trigger'] = 'ev.level-up' + evo['higher-physical-stat'] = 'attack' + evo['minimum-level'] = raw_evo.level + elif raw_evo.method == 12: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + # FIXME this is wurmple -> silcoon + elif raw_evo.method == 13: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + # FIXME this is wurmple -> cascoon + elif raw_evo.method == 14: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + # FIXME this is ninjask; where does shedinja come in? i + # think it used to be in the data but it doesn't seem to be + # any more + elif raw_evo.method == 16: + evo['trigger'] = 'ev.level-up' + evo['minimum-beauty'] = raw_evo.param + elif raw_evo.method == 17: + evo['trigger'] = 'ev.use-item' + evo['trigger-item'] = identifiers['item'][raw_evo.param] + evo['gender'] = 'male' + elif raw_evo.method == 18: + evo['trigger'] = 'ev.use-item' + evo['trigger-item'] = identifiers['item'][raw_evo.param] + evo['gender'] = 'female' + elif raw_evo.method == 19: + evo['trigger'] = 'ev.level-up' + evo['time-of-day'] = 'day' + evo['held-item'] = identifiers['item'][raw_evo.param] + elif raw_evo.method == 20: + evo['trigger'] = 'ev.level-up' + evo['held-item'] = identifiers['item'][raw_evo.param] + elif raw_evo.method == 21: + evo['trigger'] = 'ev.level-up' + evo['known-move'] = identifiers['move'][raw_evo.param] + elif raw_evo.method == 22: + evo['trigger'] = 'ev.level-up' + evo['party-member'] = identifiers['pokémon'][raw_evo.param] + elif raw_evo.method == 23: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + evo['gender'] = 'male' + elif raw_evo.method == 24: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + evo['gender'] = 'female' + elif raw_evo.method == 25: + evo['trigger'] = 'ev.level-up' + # FIXME this is magnetic field, which means i need to know + # the identifier of the appropriate place in this game! + # (for sumo it's vast poni canyon; oras, new mauville) + # OR... is that a property of the places?? + elif raw_evo.method == 26: + evo['trigger'] = 'ev.level-up' + # FIXME this is level up near mossy stone + elif raw_evo.method == 27: + evo['trigger'] = 'ev.level-up' + # FIXME this is level up near icy stone + elif raw_evo.method == 28: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + evo['upside-down'] = True + elif raw_evo.method == 29: + evo['trigger'] = 'ev.level-up' + # FIXME i would prefer if this used the actual value rather + # than the number of hearts, but + evo['minimum-affection'] = 2 + evo['known-move-type'] = TYPES[raw_evo.param] + elif raw_evo.method == 30: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + evo['party-member-type'] = TYPES[raw_evo.param] + elif raw_evo.method == 31: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + evo['overworld-weather'] = 'rain' + elif raw_evo.method == 32: + evo['trigger'] = 'ev.level-up' + evo['time-of-day'] = 'day' + evo['minimum-level'] = raw_evo.level + elif raw_evo.method == 33: + evo['trigger'] = 'ev.level-up' + evo['time-of-day'] = 'night' + evo['minimum-level'] = raw_evo.level + elif raw_evo.method == 34: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + # FIXME this is a little confusing. it's espurr's second + # evolution, but neither of them have either params or forms? + elif raw_evo.method == 36: + evo['trigger'] = 'ev.level-up' + evo['minimum-level'] = raw_evo.level + # FIXME should probably have a list somewhere of these; see + # https://bulbapedia.bulbagarden.net/wiki/User:SnorlaxMonster/Game_of_origin + # XXX should this actually be a field, or should we just + # omit in all but the applicable game? + evo['game'] = {30: 'sun', 31: 'moon'}[raw_evo.param] + # FIXME this is cosmoem; again, 30 for sun, 31 for moon + elif raw_evo.method == 37: + evo['trigger'] = 'ev.level-up' + evo['time-of-day'] = 'day' + evo['minimum-level'] = raw_evo.level + evo['game'] = {30: 'sun', 31: 'moon'}[raw_evo.param] + elif raw_evo.method == 38: + evo['trigger'] = 'ev.level-up' + evo['time-of-day'] = 'night' + evo['minimum-level'] = raw_evo.level + evo['game'] = {30: 'sun', 31: 'moon'}[raw_evo.param] + elif raw_evo.method == 39: + evo['trigger'] = 'ev.level-up' + # FIXME this is crabrawler, which only evolves when leveled + # up at mount lanakila; need an identifier for that area + else: + raise ValueError(f"Unrecognized evolution method: {raw_evo.method}") + + all_pokémon[identifier].evolutions.append(evo) # Mega evolution - #with read_garc(root / 'rom/a/1/9/3') as garc: # ORAS - #with read_garc(root / 'rom/a/0/1/5') as garc: # SUMO? - # for subfile in garc: - # evolution = subfile[0].read() - # print(repr(evolution)) + # TODO + # already parsed a/0/1/5 as mega_evolutions... but... that only lists + # items? what lists the actual megas? OH is the number a form number?? + # wow i really need a list of species -> forms eh # TODO what is a/1/9/4 (ORAS) or a/0/1/6 (SUMO)? 8 files of 404 bytes each # Baby Pokémon #with read_garc(root / 'rom/a/1/9/6') as garc: # ORAS @@ -1503,13 +1676,6 @@ def extract_data(root, out): # baby_pokemon = subfile[0].read() # print(repr(baby_pokemon)) - # Item stats - # TODO - #with read_garc(root / 'rom/a/1/9/7') as garc: # ORAS - with read_garc(root / 'rom/a/0/1/9') as garc: # ORAS - for subfile in garc: - item_stats = subfile[0].read() - # Tutor moves (from the personal structs) for i, datum in enumerate(pokemon_data): ident = identifiers['pokémon'][i]