You've already forked pokecrystal-board
mirror of
https://gitlab.com/xCrystal/pokecrystal-board.git
synced 2025-09-08 08:13:02 -07:00
move trainer parsing classes into crystal.py
This commit is contained in:
@@ -3434,6 +3434,306 @@ class TrainerFragmentParam(PointerLabelParam):
|
|||||||
|
|
||||||
from trainers import *
|
from trainers import *
|
||||||
|
|
||||||
|
# for fixing trainer_group_names
|
||||||
|
import re
|
||||||
|
|
||||||
|
trainer_group_pointer_table_address = 0x39999
|
||||||
|
trainer_group_pointer_table_address_gs = 0x3993E
|
||||||
|
|
||||||
|
class TrainerGroupTable:
|
||||||
|
""" A list of pointers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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)
|
||||||
|
self.size = None
|
||||||
|
self.last_address = None
|
||||||
|
self.dependencies = None
|
||||||
|
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 != []:
|
||||||
|
return self.dependencies
|
||||||
|
dependencies = [self.headers]
|
||||||
|
for header in self.headers:
|
||||||
|
dependencies += header.get_dependencies(recompute=recompute, global_dependencies=global_dependencies)
|
||||||
|
return dependencies
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
size = 0
|
||||||
|
for (key, kvalue) in trainer_group_names.items():
|
||||||
|
# calculate the location of this trainer group header from its pointer
|
||||||
|
pointer_bytes_location = kvalue["pointer_address"]
|
||||||
|
parsed_address = calculate_pointer_from_bytes_at(pointer_bytes_location, bank=self.bank)
|
||||||
|
trainer_group_names[key]["parsed_address"] = parsed_address
|
||||||
|
|
||||||
|
# parse the trainer group header at this location
|
||||||
|
name = kvalue["name"]
|
||||||
|
trainer_group_header = TrainerGroupHeader(address=parsed_address, group_id=key, group_name=name)
|
||||||
|
trainer_group_names[key]["header"] = trainer_group_header
|
||||||
|
self.headers.append(trainer_group_header)
|
||||||
|
|
||||||
|
# keep track of the size of this pointer table
|
||||||
|
size += 2
|
||||||
|
self.size = size
|
||||||
|
self.last_address = self.address + self.size
|
||||||
|
|
||||||
|
def to_asm(self):
|
||||||
|
output = "".join([str("dw "+get_label_for(header.address)+"\n") for header in self.headers])
|
||||||
|
return output
|
||||||
|
|
||||||
|
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.
|
||||||
|
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, group_id=None, group_name=None):
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
# 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
|
||||||
|
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):
|
||||||
|
output = "db \""+self.name+"\"\n"
|
||||||
|
output += "; data type\n"
|
||||||
|
output += "db $%.2x\n"%(self.data_byte)
|
||||||
|
output += self.party_mons.to_asm()
|
||||||
|
return output
|
||||||
|
|
||||||
|
# TODO: MoveParam should map to an actual attack
|
||||||
|
MoveParam = SingleByteParam
|
||||||
|
|
||||||
|
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.args = {}
|
||||||
|
self.params = {}
|
||||||
|
self.parse()
|
||||||
|
|
||||||
|
# pick up the $FF at the end
|
||||||
|
self.size += 1
|
||||||
|
self.last_address += 1
|
||||||
|
|
||||||
|
# this is exactly Command.parse
|
||||||
|
def parse(self):
|
||||||
|
#id, size (inclusive), param_types
|
||||||
|
#param_type = {"name": each[1], "class": each[0]}
|
||||||
|
if not self.override_byte_check:
|
||||||
|
current_address = self.address+1
|
||||||
|
else:
|
||||||
|
current_address = self.address
|
||||||
|
byte = ord(rom[self.address])
|
||||||
|
if not self.override_byte_check and (not byte == self.id):
|
||||||
|
raise Exception, "byte ("+hex(byte)+") != self.id ("+hex(self.id)+")"
|
||||||
|
i = 0
|
||||||
|
for (key, param_type) in self.param_types.items():
|
||||||
|
name = param_type["name"]
|
||||||
|
klass = param_type["class"]
|
||||||
|
#make an instance of this class, like SingleByteParam()
|
||||||
|
#or ItemLabelByte.. by making an instance, obj.parse() is called
|
||||||
|
obj = klass(address=current_address, name=name, parent=self, **dict([(k,v) for (k, v) in self.args.items() if k not in ["parent"]]))
|
||||||
|
#save this for later
|
||||||
|
self.params[i] = obj
|
||||||
|
#increment our counters
|
||||||
|
current_address += obj.size
|
||||||
|
i += 1
|
||||||
|
self.last_address = current_address
|
||||||
|
return True
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
def find_trainer_ids_from_scripts():
|
def find_trainer_ids_from_scripts():
|
||||||
""" Looks through all scripts to find trainer group numbers and trainer numbers.
|
""" Looks through all scripts to find trainer group numbers and trainer numbers.
|
||||||
|
|
||||||
@@ -7623,7 +7923,7 @@ def run_main():
|
|||||||
find_trainer_ids_from_scripts()
|
find_trainer_ids_from_scripts()
|
||||||
|
|
||||||
# and parse the main TrainerGroupTable once we know the max number of trainers
|
# and parse the main TrainerGroupTable once we know the max number of trainers
|
||||||
#gtable = TrainerGroupTable()
|
trainer_group_header = TrainerGroupTable()
|
||||||
|
|
||||||
#just a helpful alias
|
#just a helpful alias
|
||||||
main=run_main
|
main=run_main
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
trainer_group_pointer_table_address = 0x39999
|
trainer_group_pointer_table_address = 0x39999
|
||||||
trianer_group_pointer_table_address_gs = 0x3993E
|
trainer_group_pointer_table_address_gs = 0x3993E
|
||||||
|
|
||||||
# TODO: check if "é", ".", "♂", "♀" are okay in the output
|
# TODO: check if "é", ".", "♂", "♀" are okay in the output
|
||||||
trainer_group_names = {
|
trainer_group_names = {
|
||||||
@@ -102,268 +102,3 @@ def remove_parentheticals_from_trainer_group_names():
|
|||||||
|
|
||||||
# remove [Blue] from each trainer group name
|
# remove [Blue] from each trainer group name
|
||||||
remove_parentheticals_from_trainer_group_names()
|
remove_parentheticals_from_trainer_group_names()
|
||||||
|
|
||||||
class TrainerGroupTable:
|
|
||||||
""" A list of pointers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
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)
|
|
||||||
self.size = None
|
|
||||||
self.last_address = None
|
|
||||||
self.dependencies = None
|
|
||||||
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 != []:
|
|
||||||
return self.dependencies
|
|
||||||
dependencies = [self.headers]
|
|
||||||
for header in self.headers:
|
|
||||||
dependencies += header.get_dependencies(recompute=recompute, global_dependencies=global_dependencies)
|
|
||||||
return dependencies
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
size = 0
|
|
||||||
for (key, kvalue) in trainer_group_names.items():
|
|
||||||
# calculate the location of this trainer group header from its pointer
|
|
||||||
pointer_bytes_location = kvalue["pointer_address"]
|
|
||||||
parsed_address = calculate_pointer_from_bytes_at(pointer_bytes_location, bank=self.bank)
|
|
||||||
trainer_group_names[key]["parsed_address"] = parsed_address
|
|
||||||
|
|
||||||
# parse the trainer group header at this location
|
|
||||||
name = kvalue["name"]
|
|
||||||
trainer_group_header = TrainerGroupHeader(address=parsed_address, group_id=key, group_name=name)
|
|
||||||
trainer_group_names[key]["header"] = trainer_group_header
|
|
||||||
self.headers.append(trainer_group_header)
|
|
||||||
|
|
||||||
# keep track of the size of this pointer table
|
|
||||||
size += 2
|
|
||||||
self.size = size
|
|
||||||
self.last_address = self.address + self.size
|
|
||||||
|
|
||||||
def to_asm(self):
|
|
||||||
output = "".join([str("dw "+get_label_for(header.address)+"\n") for header in self.headers])
|
|
||||||
return output
|
|
||||||
|
|
||||||
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.
|
|
||||||
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, group_id=None, group_name=None):
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
# 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
|
|
||||||
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):
|
|
||||||
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]
|
|
||||||
|
Reference in New Issue
Block a user