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',
|
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
|
# ja-Hrkt: hiragana/katakana
|
||||||
# zh-Hans: simplified
|
# zh-Hans: simplified
|
||||||
# zh-Hant: traditional
|
# 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(
|
pokemon_struct = Struct(
|
||||||
'stat_hp' / Int8ul,
|
'stat_hp' / Int8ul,
|
||||||
'stat_atk' / Int8ul,
|
'stat_atk' / Int8ul,
|
||||||
|
@ -337,7 +364,7 @@ pokemon_struct = Struct(
|
||||||
'gender_rate' / Int8ul,
|
'gender_rate' / Int8ul,
|
||||||
'steps_to_hatch' / Int8ul,
|
'steps_to_hatch' / Int8ul,
|
||||||
'base_happiness' / 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_group1' / Int8ul,
|
||||||
'egg_group2' / Int8ul,
|
'egg_group2' / Int8ul,
|
||||||
'ability1' / Int8ul,
|
'ability1' / Int8ul,
|
||||||
|
@ -402,9 +429,9 @@ level_up_moves_struct = GreedyRange(
|
||||||
)
|
)
|
||||||
|
|
||||||
move_struct = Struct(
|
move_struct = Struct(
|
||||||
'type' / Enum(Int8ul, **{v:k for (k, v) in TYPES.items()}),
|
'type' / VeekunEnum(Int8ul, TYPES),
|
||||||
'category' / Int8ul,
|
'category' / Int8ul,
|
||||||
'damage_class' / Int8ul,
|
'damage_class' / VeekunEnum(Int8ul, DAMAGE_CLASSES),
|
||||||
'power' / Int8ul,
|
'power' / Int8ul,
|
||||||
'accuracy' / Int8ul,
|
'accuracy' / Int8ul,
|
||||||
'pp' / Int8ul,
|
'pp' / Int8ul,
|
||||||
|
@ -420,17 +447,22 @@ move_struct = Struct(
|
||||||
'effect' / Int16ul,
|
'effect' / Int16ul,
|
||||||
'recoil' / Int8sl,
|
'recoil' / Int8sl,
|
||||||
'healing' / Int8ul,
|
'healing' / Int8ul,
|
||||||
'range' / Int8ul, # ok
|
'range' / VeekunEnum(Int8ul, MOVE_RANGES),
|
||||||
'stat_change' / Bitwise(Array(6, BitsInteger(4))),
|
'stat_change' / Bitwise(Array(6, BitsInteger(4))),
|
||||||
'stat_amount' / Bitwise(Array(6, BitsInteger(4))),
|
'stat_amount' / Bitwise(Array(6, BitsInteger(4))),
|
||||||
'stat_chance' / Bitwise(Array(6, BitsInteger(4))),
|
'stat_chance' / Bitwise(Array(6, BitsInteger(4))),
|
||||||
'padding0' / Int8ul, # ok
|
# FIXME sumo only; padding in oras i think
|
||||||
'padding1' / Int8ul, # ok
|
'z_move_id' / Int16ul, # ok
|
||||||
'flags' / Int16ul,
|
'flags' / Int16ul,
|
||||||
'padding2' / Int8ul, # ok
|
'padding2' / Int8ul, # ok
|
||||||
'extra' / Int8ul,
|
'extra' / Int8ul,
|
||||||
# FIXME unsure whether this exists in ORAS; should use a length limiter in the parent
|
# 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',
|
move_container_struct = FocusedSeq('records',
|
||||||
Const(b'WD'), # waza... descriptions?
|
Const(b'WD'), # waza... descriptions?
|
||||||
|
@ -1469,23 +1501,84 @@ def extract_data(root, out):
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# Move stats
|
# Move stats
|
||||||
|
|
||||||
|
all_moves = OrderedDict()
|
||||||
#with read_garc(root / 'rom/a/1/8/9') as garc: # ORAS
|
#with read_garc(root / 'rom/a/1/8/9') as garc: # ORAS
|
||||||
with read_garc(root / 'rom/a/0/1/1') as garc: # SUMO
|
with read_garc(root / 'rom/a/0/1/1') as garc: # SUMO
|
||||||
# Only one subfile
|
# Only one subfile
|
||||||
# TODO assert this wherever i do it
|
# TODO assert only one file wherever i do this
|
||||||
data = garc[0][0].read()
|
records = move_container_struct.parse_stream(garc[0][0])
|
||||||
print(Struct('magic' / Bytes(2), 'count' / Int16ul, 'pointers' / Array(16, Int32ul)).parse(data))
|
|
||||||
print(move_struct.sizeof())
|
|
||||||
records = move_container_struct.parse(data)
|
|
||||||
for i, record in enumerate(records):
|
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??
|
# 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(
|
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}")
|
||||||
i,
|
|
||||||
texts['en']['move-names'][i],
|
ident = identifiers['move'][i]
|
||||||
m=record,
|
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
|
# Egg moves
|
||||||
with read_garc(root / 'rom/a/0/1/2') as garc: # SUMO
|
with read_garc(root / 'rom/a/0/1/2') as garc: # SUMO
|
||||||
|
@ -1742,7 +1835,6 @@ def extract_data(root, out):
|
||||||
machineset.append(moveident)
|
machineset.append(moveident)
|
||||||
|
|
||||||
with (out / 'pokemon.yaml').open('w') as f:
|
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))
|
f.write(Camel([schema.POKEDEX_TYPES]).dump(all_pokémon))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -249,13 +249,45 @@ Pokemon = Pokémon
|
||||||
MoveEffect = _ForwardDeclaration()
|
MoveEffect = _ForwardDeclaration()
|
||||||
|
|
||||||
class Move(VersionedLocus):
|
class Move(VersionedLocus):
|
||||||
|
game_index = _Value(int)
|
||||||
|
|
||||||
name = _Localized(str)
|
name = _Localized(str)
|
||||||
type = _Value(Type)
|
type = _Value(Type)
|
||||||
power = _Value(int)
|
power = _Value(int)
|
||||||
pp = _Value(int)
|
pp = _Value(int)
|
||||||
accuracy = _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 = _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
|
return data
|
||||||
|
|
||||||
@POKEDEX_TYPES.loader('pokemon', version=None)
|
def _make_locus_loader(cls):
|
||||||
def _load_locus(data, version):
|
def load_locus(data, version):
|
||||||
cls = Pokémon
|
# TODO wrap with a writer thing?
|
||||||
# TODO wrap with a writer thing?
|
obj = cls()
|
||||||
obj = cls()
|
for key, value in data.items():
|
||||||
for key, value in data.items():
|
key = key.replace('-', '_')
|
||||||
key = key.replace('-', '_')
|
assert hasattr(cls, key)
|
||||||
assert hasattr(cls, key)
|
setattr(obj, key, value)
|
||||||
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.dumper(Ability, 'ability', version=None, inherit=True)(_dump_locus)
|
||||||
|
POKEDEX_TYPES.loader('ability', version=None)(_make_locus_loader(Ability))
|
||||||
|
|
||||||
@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.dumper(Item, 'item', version=None, inherit=True)(_dump_locus)
|
POKEDEX_TYPES.dumper(Item, 'item', version=None, inherit=True)(_dump_locus)
|
||||||
|
POKEDEX_TYPES.loader('item', version=None)(_make_locus_loader(Item))
|
||||||
|
|
||||||
@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
|
|
||||||
|
|
||||||
|
|
||||||
def load_repository():
|
def load_repository():
|
||||||
|
|
Loading…
Reference in a new issue