shithub: pokecrystal

Download patch

ref: 1d6fa83902fb34113389436cc3fac349b839f7db
parent: 38726204c8b8a481f378166d57011efb7c64737b
author: Bryan Bishop <[email protected]>
date: Sat May 19 11:31:35 EDT 2012

finish up the classes related to TrainerGroupHeader

--- a/extras/trainers.py
+++ b/extras/trainers.py
@@ -17,7 +17,7 @@
 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 @@
 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 @@
     """
 
     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 @@
         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 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 @@
         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]