diff options
author | Bryan Bishop <kanzure@gmail.com> | 2012-05-19 15:31:35 -0500 |
---|---|---|
committer | Bryan Bishop <kanzure@gmail.com> | 2012-05-19 15:31:35 -0500 |
commit | 8315dd436c6a3335071bd2f15d0e813231acd1e2 (patch) | |
tree | 7869983a495e54fdc9b226c7d9411f8b4dffc1aa /trainers.py | |
parent | 9c1eaca2841cd7a4052192f79cf3871e55330ad4 (diff) |
finish up the classes related to TrainerGroupHeader
original-commit-id: 1d6fa83902fb34113389436cc3fac349b839f7db
Diffstat (limited to 'trainers.py')
-rw-r--r-- | trainers.py | 196 |
1 files changed, 189 insertions, 7 deletions
diff --git a/trainers.py b/trainers.py index 6cb4c95..2257213 100644 --- a/trainers.py +++ b/trainers.py @@ -17,7 +17,7 @@ trainer_group_names = { 0x06: {"name": "Leader [Jasmine]"}, 0x07: {"name": "Leader [Chuck]"}, 0x08: {"name": "Leader [Clair]"}, -0x09: {"name": "Silver1"}, +0x09: {"name": "Rival1"}, 0x0A: {"name": "Pokémon Prof."}, 0x0B: {"name": "Elite Four [Will]"}, 0x0C: {"name": "PKMN Trainer [Cal]"}, @@ -50,7 +50,7 @@ trainer_group_names = { 0x27: {"name": "SwimmerF"}, 0x28: {"name": "Sailor"}, 0x29: {"name": "Super Nerd"}, -0x2A: {"name": "Silver2"}, +0x2A: {"name": "Rival2"}, 0x2B: {"name": "Guitarist"}, 0x2C: {"name": "Hiker"}, 0x2D: {"name": "Biker"}, @@ -108,6 +108,7 @@ class TrainerGroupTable: """ def __init__(self): + assert 0x43 in trainer_group_maximums.keys(), "TrainerGroupTable should onyl be created after all the trainers have been found" self.address = trainer_group_pointer_table_address self.bank = calculate_bank(trainer_group_pointer_table_address) self.label = Label(name="TrainerGroupPointerTable", address=self.address, object=self) @@ -117,6 +118,9 @@ class TrainerGroupTable: self.headers = [] self.parse() + # TODO: add this to script_parse_table + #script_parse_table[address : self.last_address] = self + def get_dependencies(self, recompute=False, global_dependencies=set()): global_dependencies.update(self.headers) if recompute == True and self.dependencies != None and self.dependencies != []: @@ -151,6 +155,8 @@ class TrainerGroupTable: class TrainerGroupHeader: """ + A trainer group header is a repeating list of individual trainer headers. + <Trainer Name> <0x50> <Data type> <Pokémon Data>+ <0xFF> Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers. @@ -163,25 +169,201 @@ class TrainerGroupHeader: assert address!=None, "TrainerGroupHeader requires an address" assert group_id!=None, "TrainerGroupHeader requires a group_id" assert group_name!=None, "TrainerGroupHeader requires a group_name" + self.address = address self.group_id = group_id self.group_name = group_name self.dependencies = None + self.individual_trainer_headers = [] self.label = Label(name=group_name+"TrainerGroupHeader", address=self.address, object=self) self.parse() + # TODO: add this to script_parse_table + #script_parse_table[address : self.last_address] = self + def get_dependencies(self, recompute=False, global_dependencies=set()): """ TrainerGroupHeader has no dependencies. """ - return [] + # TODO: possibly include self.individual_trainer_headers + if recompute or self.dependencies == None: + self.dependencies = [] + return self.dependencies def parse(self): + """ + how do i know when there's no more data for this header? + do a global analysis of the rom and figure out the max ids + this wont work for rom hacks of course + see find_trainer_ids_from_scripts + """ size = 0 + current_address = self.address + + # create an IndividualTrainerHeader for each id in range(min id, max id + 1) + min_id = min(trainer_group_maximums[self.group_id]) + max_id = max(trainer_group_maximums[self.group_id]) + + for trainer_id in range(min_id, max_id+1): + trainer_header = TrainerHeader(address=current_address, trainer_group_id=self.group_id, trainer_id=trainer_id, parent=self) + current_address += trainer_header.size + size += trainer_header.size + + self.last_address = current_address + self.size = size + + def to_asm(self): raise NotImplementedError - # how do i know when there's no more data for this header? - # do a global analysis of the rom and figure out the max ids i guess - # this wont work for rom hacks of course + output += " + +class TrainerHeader: + """ + <Trainer Name> <0x50> <Data type> <Pokémon Data>+ <0xFF> + + Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers. + Data type <0x01>: Pokémon Data is <Level> <Pokémon> <Move1> <Move2> <Move3> <Move4>. Used often for Gym Leaders. + Data type <0x02>: Pokémon Data is <Level> <Pokémon> <Held Item>. Used mainly by Pokéfans. + Data type <0x03>: Pokémon Data is <Level> <Pokémon> <Held Item> <Move1> <Move2> <Move3> <Move4>. Used by a few Cooltrainers. + """ + + def __init__(self, address=None, trainer_group_id=None, trainer_id=None, parent=None): + self.parent = parent + self.address = address + self.trainer_group_id = trainer_group_id + self.trainer_id = trainer_id + self.dependencies = [] + self.size = None + self.last_address = None + self.parse() + self.label = Label(name=self.make_name(), address=self.address, object=self) + # this shouldn't be added to script_parse_table because + # TrainerGroupHeader covers its address range + + def make_name(self): + """ Must occur after parse() is called. + Constructs a name based on self.parent.group_name and self.name. + """ + return self.parent.group_name + "_" + self.name + + def get_dependencies(self, recompute=False, global_dependencies=set()): + if recompute or self.dependencies == None: + self.dependencies = [] + return self.dependencies + + def parse(self): + address = self.address + + # figure out how many bytes until 0x50 "@" + jump = how_many_until(chr(0x50), address) + + # parse the "@" into the name + self.name = parse_text_at(address, jump+1) + + # where is the next byte? + current_address = address + jump + 1 + + # figure out the pokemon data type + self.data_type = ord(rom[current_address]) + + current_address += 1 + + # figure out which partymon parser to use for this trainer header + party_mon_parser = None + for monparser in trainer_party_mon_parsers: + if monparser.id == self.data_type: + party_mon_parser = monparser + break + + if party_mon_parser == None: + raise Exception, "no trainer party mon parser found to parse data type " + hex(self.data_type) + + self.party_mons = party_mon_parser(address=current_address, group_id=self.trainer_group_id, trainer_id=self.trainer_id, parent=self) + + # let's have everything in trainer_party_mon_parsers handle the last $FF + self.size = self.party_mons.size + 1 + len(self.name) + self.last_address = self.party_mons.last_address def to_asm(self): - pass + output = "db \""+self.name+"\"\n" + output += "; data type\n" + output += "db $%.2x\n"%(self.data_byte) + output += self.party_mons.to_asm() + return output + +class TrainerPartyMonParser: + """ Just a generic trainer party mon parser. + Don't use this directly. Only use the child classes. + """ + id = None + dependencies = None + params = [] + param_types = None + + # could go either way on this one.. TrainerGroupHeader.parse would need to be changed + # so as to not increase current_address by one after reading "data_type" + override_byte_check = True + + def __init__(self, address=None, group_id=None, trainer_id=None, parent=None): + self.address = address + self.group_id = group_id + self.trainer_id = trainer_id + self.parent = parent + self.parse() + + # pick up the $FF at the end + self.size += 1 + self.last_address += 1 + + parse = Command.parse + + def to_asm(self): + output = "; " + ", ".join([param_type["name"] for param_type in self.param_types]) + "\n" + output += "db " + ", ".join([param.to_asm() for (name, param) in self.params.items()]) + output += "\n" + output += "db $FF ; end trainer party mons" + return output + +class TrainerPartyMonParser0(TrainerPartyMonParser): + """ Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers. """ + id = 0 + size = 2 + 1 + param_types = { + 0: {"name": "level", "class": DecimalParam}, + 1: {"name": "species", "class": PokemonParam}, + } +class TrainerPartyMonParser1(TrainerPartyMonParser): + """ Data type <0x01>: Pokémon Data is <Level> <Pokémon> <Move1> <Move2> <Move3> <Move4>. Used often for Gym Leaders.""" + id = 1 + size = 6 + 1 + param_types = { + 0: {"name": "level", "class": DecimalParam}, + 1: {"name": "species", "class": PokemonParam}, + 2: {"name": "move1", "class": MoveParam}, + 3: {"name": "move2", "class": MoveParam}, + 4: {"name": "move3", "class": MoveParam}, + 5: {"name": "move4", "class": MoveParam}, + } +class TrainerPartyMonParser2(TrainerPartyMonParser): + """ Data type <0x02>: Pokémon Data is <Level> <Pokémon> <Held Item>. Used mainly by Pokéfans. """ + id = 2 + size = 3 + 1 + param_types = { + 0: {"name": "level", "class": DecimalParam}, + 1: {"name": "species", "class": PokemonParam}, + 2: {"name": "item", "class": ItemLabelByte}, + } +class TrainerPartyMonParser3(TrainerPartyMonParser): + """ Data type <0x03>: Pokémon Data is <Level> <Pokémon> <Held Item> <Move1> <Move2> <Move3> <Move4>. + Used by a few Cooltrainers. """ + id = 3 + size = 7 + 1 + param_types = { + 0: {"name": "level", "class": DecimalParam}, + 1: {"name": "species", "class": PokemonParam}, + 2: {"name": "item", "class": ItemLabelByte}, + 3: {"name": "move1", "class": MoveParam}, + 4: {"name": "move2", "class": MoveParam}, + 5: {"name": "move3", "class": MoveParam}, + 6: {"name": "move4", "class": MoveParam}, + } +trainer_party_mon_parsers = [TrainerPartyMonParser0, TrainerPartyMonParser1, TrainerPartyMonParser2, TrainerPartyMonParser3] |