mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
Dump most of the interesting bits about moves
This commit is contained in:
parent
215366968b
commit
3d296ff7b4
2 changed files with 160 additions and 57 deletions
|
@ -70,6 +70,30 @@ TYPES = {
|
|||
17: 't.fairy',
|
||||
}
|
||||
|
||||
DAMAGE_CLASSES = {
|
||||
0: 'dc.status',
|
||||
1: 'dc.physical',
|
||||
2: 'dc.special',
|
||||
}
|
||||
|
||||
MOVE_RANGES = {
|
||||
13: 'mr.specific-move',
|
||||
3: 'mr.selected-pokemon-me-first',
|
||||
2: 'mr.ally',
|
||||
6: 'mr.users-field',
|
||||
1: 'mr.user-or-ally',
|
||||
11: 'mr.opponents-field',
|
||||
7: 'mr.user',
|
||||
9: 'mr.random-opponent',
|
||||
4: 'mr.all-other-pokemon',
|
||||
0: 'mr.selected-pokemon',
|
||||
5: 'mr.all-opponents',
|
||||
10: 'mr.entire-field',
|
||||
12: 'mr.user-and-allies',
|
||||
8: 'mr.all-pokemon',
|
||||
}
|
||||
|
||||
|
||||
# ja-Hrkt: hiragana/katakana
|
||||
# zh-Hans: simplified
|
||||
# zh-Hant: traditional
|
||||
|
@ -319,6 +343,9 @@ FORM_NAMES = {
|
|||
}
|
||||
|
||||
|
||||
def VeekunEnum(subcon, enumdict):
|
||||
return Enum(subcon, **{v: k for (k, v) in enumdict.items()})
|
||||
|
||||
pokemon_struct = Struct(
|
||||
'stat_hp' / Int8ul,
|
||||
'stat_atk' / Int8ul,
|
||||
|
@ -337,7 +364,7 @@ pokemon_struct = Struct(
|
|||
'gender_rate' / Int8ul,
|
||||
'steps_to_hatch' / Int8ul,
|
||||
'base_happiness' / Int8ul,
|
||||
'growth_rate' / Enum(Int8ul, **{v: k for (k, v) in GROWTH_RATES.items()}),
|
||||
'growth_rate' / VeekunEnum(Int8ul, GROWTH_RATES),
|
||||
'egg_group1' / Int8ul,
|
||||
'egg_group2' / Int8ul,
|
||||
'ability1' / Int8ul,
|
||||
|
@ -402,9 +429,9 @@ level_up_moves_struct = GreedyRange(
|
|||
)
|
||||
|
||||
move_struct = Struct(
|
||||
'type' / Enum(Int8ul, **{v:k for (k, v) in TYPES.items()}),
|
||||
'type' / VeekunEnum(Int8ul, TYPES),
|
||||
'category' / Int8ul,
|
||||
'damage_class' / Int8ul,
|
||||
'damage_class' / VeekunEnum(Int8ul, DAMAGE_CLASSES),
|
||||
'power' / Int8ul,
|
||||
'accuracy' / Int8ul,
|
||||
'pp' / Int8ul,
|
||||
|
@ -420,17 +447,22 @@ move_struct = Struct(
|
|||
'effect' / Int16ul,
|
||||
'recoil' / Int8sl,
|
||||
'healing' / Int8ul,
|
||||
'range' / Int8ul, # ok
|
||||
'range' / VeekunEnum(Int8ul, MOVE_RANGES),
|
||||
'stat_change' / Bitwise(Array(6, BitsInteger(4))),
|
||||
'stat_amount' / Bitwise(Array(6, BitsInteger(4))),
|
||||
'stat_chance' / Bitwise(Array(6, BitsInteger(4))),
|
||||
'padding0' / Int8ul, # ok
|
||||
'padding1' / Int8ul, # ok
|
||||
# FIXME sumo only; padding in oras i think
|
||||
'z_move_id' / Int16ul, # ok
|
||||
'flags' / Int16ul,
|
||||
'padding2' / Int8ul, # ok
|
||||
'extra' / Int8ul,
|
||||
# FIXME unsure whether this exists in ORAS; should use a length limiter in the parent
|
||||
'extra2' / Int32ul,
|
||||
'extra2' / Int8ul,
|
||||
'extra3' / Int8ul,
|
||||
# a single flag, 1 = dance move
|
||||
'extra4' / Int8ul,
|
||||
# all zeroes
|
||||
Padding(1),
|
||||
)
|
||||
move_container_struct = FocusedSeq('records',
|
||||
Const(b'WD'), # waza... descriptions?
|
||||
|
@ -1469,23 +1501,84 @@ def extract_data(root, out):
|
|||
|
||||
# -------------------------------------------------------------------------
|
||||
# Move stats
|
||||
|
||||
all_moves = OrderedDict()
|
||||
#with read_garc(root / 'rom/a/1/8/9') as garc: # ORAS
|
||||
with read_garc(root / 'rom/a/0/1/1') as garc: # SUMO
|
||||
# Only one subfile
|
||||
# TODO assert this wherever i do it
|
||||
data = garc[0][0].read()
|
||||
print(Struct('magic' / Bytes(2), 'count' / Int16ul, 'pointers' / Array(16, Int32ul)).parse(data))
|
||||
print(move_struct.sizeof())
|
||||
records = move_container_struct.parse(data)
|
||||
# TODO assert only one file wherever i do this
|
||||
records = move_container_struct.parse_stream(garc[0][0])
|
||||
for i, record in enumerate(records):
|
||||
#print(texts['en']['move-names'][i])
|
||||
#print(record)
|
||||
# TODO with the release of oras all moves have contest types and effects again! where are they??
|
||||
print("{:3d} {:30s} | {m.type:10s} {m.category:3d} / {m.power:3d} {m.pp:2d} {m.accuracy:3d} / {m.priority:2d} {m.range:2d} {m.damage_class:1d} / {m.effect:3d} {m.caused_effect:3d} {m.effect_chance:3d} -- {m.status:3d} {m.min_turns:3d} {m.max_turns:3d} {m.crit_rate:3d} {m.flinch_chance:3d} {m.recoil:4d} {m.healing:3d} / {m.stat_change!r} {m.stat_amount!r} {m.stat_chance!r} ~ {m.padding0:3d} {m.padding1:3d} {m.flags:04x} {m.padding2:3d} {m.extra:3d} {m.extra2:10d}".format(
|
||||
i,
|
||||
texts['en']['move-names'][i],
|
||||
m=record,
|
||||
))
|
||||
print(f"{i:3d} {texts['en']['move-names'][i]:30s} | {record.type:10s} {record.category:3d} / {record.priority:2d} {record.range:20s} {record.damage_class:12s} / {record.effect:3d} {record.caused_effect:3d} {record.effect_chance:3d} -- {record.min_max_hits:3d}, {record.status:3d} {record.min_turns:3d} {record.max_turns:3d} {record.crit_rate:3d} {record.flinch_chance:3d} {record.recoil:4d} {record.healing:3d} ~ {identifiers['move'][record.z_move_id]:30s} {record.flags:04x} {record.padding2:3d} {record.extra:3d} {record.extra2:08b} {record.extra2 >> 3:3d}+{record.extra2 & 7:<3d} {record.extra3:3d} {record.extra4:3d}")
|
||||
|
||||
ident = identifiers['move'][i]
|
||||
move = all_moves[ident] = schema.Move()
|
||||
move.game_index = i
|
||||
|
||||
move.name = collect_text(texts, 'move-names', i)
|
||||
move.type = record.type
|
||||
# TODO does this need munging somehow?
|
||||
move.power = record.power
|
||||
move.pp = record.pp
|
||||
# TODO does this need munging somehow?
|
||||
move.accuracy = record.accuracy
|
||||
move.priority = record.priority
|
||||
|
||||
move.damage_class = record.damage_class
|
||||
move.range = record.range
|
||||
|
||||
move.effect = record.effect
|
||||
move.effect_chance = record.effect_chance
|
||||
# FIXME need to identify whether THIS move is a z-move?
|
||||
if record.z_move_id:
|
||||
move.z_move = identifiers['move'][record.z_move_id]
|
||||
else:
|
||||
# TODO what does it mean if the z-move is missing?? (how do z
|
||||
# moves work for status moves anyway?)
|
||||
pass
|
||||
|
||||
move.min_hits = record.min_max_hits & 0x0f
|
||||
move.max_hits = record.min_max_hits >> 4
|
||||
# -1: tri attack? telekinesis, smack down, thousand arrows
|
||||
# 1: paralysis
|
||||
# 2: sleep
|
||||
# 3: frozen
|
||||
# 4: burn
|
||||
# 5: poison
|
||||
# 6: confusion
|
||||
# 7: infatuation
|
||||
# 8: trapped? multi-turn move?
|
||||
# 9: nightmare
|
||||
# 12: tormented
|
||||
# 13: disabled
|
||||
# 14: drowsy (yawn)
|
||||
# 15: heal blocked
|
||||
# 17: foresight + odor sleuth + miracle eye (identified?)
|
||||
# 18: seeded
|
||||
# 19: embargoed
|
||||
# 20: perish song
|
||||
# 21: ingrain?
|
||||
# 24: throat chop?? (silenced?)
|
||||
move.category = record.category
|
||||
move.ailment = record.caused_effect
|
||||
# FIXME what is record.status???
|
||||
# FIXME where is this
|
||||
#ailment_chance = _Value(int)
|
||||
# FIXME this is nonsense, it should be per-stat??
|
||||
#move.stat_chance = _Value(int)
|
||||
move.min_turns = record.min_turns
|
||||
move.max_turns = record.max_turns
|
||||
# FIXME split drain out from healing
|
||||
#drain = _Value(int)
|
||||
move.healing = record.healing
|
||||
move.crit_rate = record.crit_rate
|
||||
move.flinch_chance = record.flinch_chance
|
||||
|
||||
with (out / 'moves.yaml').open('w') as f:
|
||||
f.write(Camel([schema.POKEDEX_TYPES]).dump(all_moves))
|
||||
|
||||
return
|
||||
|
||||
# Egg moves
|
||||
with read_garc(root / 'rom/a/0/1/2') as garc: # SUMO
|
||||
|
@ -1742,7 +1835,6 @@ def extract_data(root, out):
|
|||
machineset.append(moveident)
|
||||
|
||||
with (out / 'pokemon.yaml').open('w') as f:
|
||||
#dump_to_yaml(all_pokémon, f)
|
||||
f.write(Camel([schema.POKEDEX_TYPES]).dump(all_pokémon))
|
||||
|
||||
|
||||
|
|
|
@ -249,13 +249,45 @@ Pokemon = Pokémon
|
|||
MoveEffect = _ForwardDeclaration()
|
||||
|
||||
class Move(VersionedLocus):
|
||||
game_index = _Value(int)
|
||||
|
||||
name = _Localized(str)
|
||||
type = _Value(Type)
|
||||
power = _Value(int)
|
||||
pp = _Value(int)
|
||||
accuracy = _Value(int)
|
||||
priority = _Value(int)
|
||||
|
||||
# FIXME should be enums
|
||||
damage_class = _Value(str)
|
||||
range = _Value(str)
|
||||
|
||||
# FIXME oh goodness this is a self-referential one
|
||||
z_move = _Value(str)
|
||||
|
||||
# FIXME this should be an enum really too
|
||||
effect = _Value(MoveEffect)
|
||||
|
||||
effect_chance = _Value(int)
|
||||
|
||||
# NOTE: In the old schema, this stuff is in a separate table, since it's
|
||||
# not quite 100% reliable; in particular the lack of a value doesn't mean
|
||||
# that the effect cannot happen via code. Also, consider Tri Attack, whose
|
||||
# inflicted ailments aren't listed here at all. :(
|
||||
# TODO i wonder if these should be left out of the yaml if blank
|
||||
max_hits = _Value(int)
|
||||
# FIXME these should be enums
|
||||
category = _Value(int)
|
||||
ailment = _Value(int)
|
||||
min_hits = _Value(int)
|
||||
stat_chance = _Value(int)
|
||||
min_turns = _Value(int)
|
||||
max_turns = _Value(int)
|
||||
drain = _Value(int)
|
||||
healing = _Value(int)
|
||||
crit_rate = _Value(int)
|
||||
ailment_chance = _Value(int)
|
||||
flinch_chance = _Value(int)
|
||||
|
||||
|
||||
|
||||
|
@ -342,49 +374,28 @@ def _dump_locus(locus):
|
|||
|
||||
return data
|
||||
|
||||
@POKEDEX_TYPES.loader('pokemon', version=None)
|
||||
def _load_locus(data, version):
|
||||
cls = Pokémon
|
||||
# TODO wrap with a writer thing?
|
||||
obj = cls()
|
||||
for key, value in data.items():
|
||||
key = key.replace('-', '_')
|
||||
assert hasattr(cls, key)
|
||||
setattr(obj, key, value)
|
||||
def _make_locus_loader(cls):
|
||||
def load_locus(data, version):
|
||||
# TODO wrap with a writer thing?
|
||||
obj = cls()
|
||||
for key, value in data.items():
|
||||
key = key.replace('-', '_')
|
||||
assert hasattr(cls, key)
|
||||
setattr(obj, key, value)
|
||||
|
||||
return obj
|
||||
return obj
|
||||
return load_locus
|
||||
|
||||
POKEDEX_TYPES.loader('pokemon', version=None)(_make_locus_loader(Pokémon))
|
||||
|
||||
POKEDEX_TYPES.dumper(Move, 'move', version=None, inherit=True)(_dump_locus)
|
||||
POKEDEX_TYPES.loader('move', version=None)(_make_locus_loader(Move))
|
||||
|
||||
POKEDEX_TYPES.dumper(Ability, 'ability', version=None, inherit=True)(_dump_locus)
|
||||
|
||||
|
||||
@POKEDEX_TYPES.loader('ability', version=None)
|
||||
def _load_locus(data, version):
|
||||
cls = Ability
|
||||
# TODO wrap with a writer thing?
|
||||
obj = cls()
|
||||
for key, value in data.items():
|
||||
key = key.replace('-', '_')
|
||||
assert hasattr(cls, key)
|
||||
setattr(obj, key, value)
|
||||
|
||||
return obj
|
||||
|
||||
POKEDEX_TYPES.loader('ability', version=None)(_make_locus_loader(Ability))
|
||||
|
||||
POKEDEX_TYPES.dumper(Item, 'item', version=None, inherit=True)(_dump_locus)
|
||||
|
||||
|
||||
@POKEDEX_TYPES.loader('item', version=None)
|
||||
def _load_locus(data, version):
|
||||
cls = Item
|
||||
# TODO wrap with a writer thing?
|
||||
obj = cls()
|
||||
for key, value in data.items():
|
||||
key = key.replace('-', '_')
|
||||
assert hasattr(cls, key)
|
||||
setattr(obj, key, value)
|
||||
|
||||
return obj
|
||||
POKEDEX_TYPES.loader('item', version=None)(_make_locus_loader(Item))
|
||||
|
||||
|
||||
def load_repository():
|
||||
|
|
Loading…
Reference in a new issue