Make great strides towards an actual game-aware API

Huzzah!

Also, I decided to namespace identifiers.  For now.  We'll see how it
goes.
This commit is contained in:
Eevee (Lexy Munroe) 2016-08-14 00:15:30 -07:00
parent aa32cf0b53
commit 969d671c48
2 changed files with 470 additions and 380 deletions

View file

@ -17,6 +17,7 @@ from pathlib import Path
import re
import sys
from camel import Camel
from classtools import reify
from construct import *
@ -92,364 +93,364 @@ GAME_RELEASE_MD5SUM_INDEX = {
GROWTH_RATES = {
0: 'medium',
3: 'medium-slow',
4: 'fast',
5: 'slow',
0: 'growth-rate.medium',
3: 'growth-rate.medium-slow',
4: 'growth-rate.fast',
5: 'growth-rate.slow',
}
EVOLUTION_TRIGGERS = {
1: 'level-up',
2: 'use-item',
3: 'trade',
1: 'evolution-trigger.level-up',
2: 'evolution-trigger.use-item',
3: 'evolution-trigger.trade',
}
# TODO these are loci, not enums, so hardcoding all their identifiers here
# makes me nonspecifically uncomfortable
POKEMON_IDENTIFIERS = {
1: 'bulbasaur',
2: 'ivysaur',
3: 'venusaur',
4: 'charmander',
5: 'charmeleon',
6: 'charizard',
7: 'squirtle',
8: 'wartortle',
9: 'blastoise',
10: 'caterpie',
11: 'metapod',
12: 'butterfree',
13: 'weedle',
14: 'kakuna',
15: 'beedrill',
16: 'pidgey',
17: 'pidgeotto',
18: 'pidgeot',
19: 'rattata',
20: 'raticate',
21: 'spearow',
22: 'fearow',
23: 'ekans',
24: 'arbok',
25: 'pikachu',
26: 'raichu',
27: 'sandshrew',
28: 'sandslash',
29: 'nidoran-f',
30: 'nidorina',
31: 'nidoqueen',
32: 'nidoran-m',
33: 'nidorino',
34: 'nidoking',
35: 'clefairy',
36: 'clefable',
37: 'vulpix',
38: 'ninetales',
39: 'jigglypuff',
40: 'wigglytuff',
41: 'zubat',
42: 'golbat',
43: 'oddish',
44: 'gloom',
45: 'vileplume',
46: 'paras',
47: 'parasect',
48: 'venonat',
49: 'venomoth',
50: 'diglett',
51: 'dugtrio',
52: 'meowth',
53: 'persian',
54: 'psyduck',
55: 'golduck',
56: 'mankey',
57: 'primeape',
58: 'growlithe',
59: 'arcanine',
60: 'poliwag',
61: 'poliwhirl',
62: 'poliwrath',
63: 'abra',
64: 'kadabra',
65: 'alakazam',
66: 'machop',
67: 'machoke',
68: 'machamp',
69: 'bellsprout',
70: 'weepinbell',
71: 'victreebel',
72: 'tentacool',
73: 'tentacruel',
74: 'geodude',
75: 'graveler',
76: 'golem',
77: 'ponyta',
78: 'rapidash',
79: 'slowpoke',
80: 'slowbro',
81: 'magnemite',
82: 'magneton',
83: 'farfetchd',
84: 'doduo',
85: 'dodrio',
86: 'seel',
87: 'dewgong',
88: 'grimer',
89: 'muk',
90: 'shellder',
91: 'cloyster',
92: 'gastly',
93: 'haunter',
94: 'gengar',
95: 'onix',
96: 'drowzee',
97: 'hypno',
98: 'krabby',
99: 'kingler',
100: 'voltorb',
101: 'electrode',
102: 'exeggcute',
103: 'exeggutor',
104: 'cubone',
105: 'marowak',
106: 'hitmonlee',
107: 'hitmonchan',
108: 'lickitung',
109: 'koffing',
110: 'weezing',
111: 'rhyhorn',
112: 'rhydon',
113: 'chansey',
114: 'tangela',
115: 'kangaskhan',
116: 'horsea',
117: 'seadra',
118: 'goldeen',
119: 'seaking',
120: 'staryu',
121: 'starmie',
122: 'mr-mime',
123: 'scyther',
124: 'jynx',
125: 'electabuzz',
126: 'magmar',
127: 'pinsir',
128: 'tauros',
129: 'magikarp',
130: 'gyarados',
131: 'lapras',
132: 'ditto',
133: 'eevee',
134: 'vaporeon',
135: 'jolteon',
136: 'flareon',
137: 'porygon',
138: 'omanyte',
139: 'omastar',
140: 'kabuto',
141: 'kabutops',
142: 'aerodactyl',
143: 'snorlax',
144: 'articuno',
145: 'zapdos',
146: 'moltres',
147: 'dratini',
148: 'dragonair',
149: 'dragonite',
150: 'mewtwo',
151: 'mew',
1: 'pokemon.bulbasaur',
2: 'pokemon.ivysaur',
3: 'pokemon.venusaur',
4: 'pokemon.charmander',
5: 'pokemon.charmeleon',
6: 'pokemon.charizard',
7: 'pokemon.squirtle',
8: 'pokemon.wartortle',
9: 'pokemon.blastoise',
10: 'pokemon.caterpie',
11: 'pokemon.metapod',
12: 'pokemon.butterfree',
13: 'pokemon.weedle',
14: 'pokemon.kakuna',
15: 'pokemon.beedrill',
16: 'pokemon.pidgey',
17: 'pokemon.pidgeotto',
18: 'pokemon.pidgeot',
19: 'pokemon.rattata',
20: 'pokemon.raticate',
21: 'pokemon.spearow',
22: 'pokemon.fearow',
23: 'pokemon.ekans',
24: 'pokemon.arbok',
25: 'pokemon.pikachu',
26: 'pokemon.raichu',
27: 'pokemon.sandshrew',
28: 'pokemon.sandslash',
29: 'pokemon.nidoran-f',
30: 'pokemon.nidorina',
31: 'pokemon.nidoqueen',
32: 'pokemon.nidoran-m',
33: 'pokemon.nidorino',
34: 'pokemon.nidoking',
35: 'pokemon.clefairy',
36: 'pokemon.clefable',
37: 'pokemon.vulpix',
38: 'pokemon.ninetales',
39: 'pokemon.jigglypuff',
40: 'pokemon.wigglytuff',
41: 'pokemon.zubat',
42: 'pokemon.golbat',
43: 'pokemon.oddish',
44: 'pokemon.gloom',
45: 'pokemon.vileplume',
46: 'pokemon.paras',
47: 'pokemon.parasect',
48: 'pokemon.venonat',
49: 'pokemon.venomoth',
50: 'pokemon.diglett',
51: 'pokemon.dugtrio',
52: 'pokemon.meowth',
53: 'pokemon.persian',
54: 'pokemon.psyduck',
55: 'pokemon.golduck',
56: 'pokemon.mankey',
57: 'pokemon.primeape',
58: 'pokemon.growlithe',
59: 'pokemon.arcanine',
60: 'pokemon.poliwag',
61: 'pokemon.poliwhirl',
62: 'pokemon.poliwrath',
63: 'pokemon.abra',
64: 'pokemon.kadabra',
65: 'pokemon.alakazam',
66: 'pokemon.machop',
67: 'pokemon.machoke',
68: 'pokemon.machamp',
69: 'pokemon.bellsprout',
70: 'pokemon.weepinbell',
71: 'pokemon.victreebel',
72: 'pokemon.tentacool',
73: 'pokemon.tentacruel',
74: 'pokemon.geodude',
75: 'pokemon.graveler',
76: 'pokemon.golem',
77: 'pokemon.ponyta',
78: 'pokemon.rapidash',
79: 'pokemon.slowpoke',
80: 'pokemon.slowbro',
81: 'pokemon.magnemite',
82: 'pokemon.magneton',
83: 'pokemon.farfetchd',
84: 'pokemon.doduo',
85: 'pokemon.dodrio',
86: 'pokemon.seel',
87: 'pokemon.dewgong',
88: 'pokemon.grimer',
89: 'pokemon.muk',
90: 'pokemon.shellder',
91: 'pokemon.cloyster',
92: 'pokemon.gastly',
93: 'pokemon.haunter',
94: 'pokemon.gengar',
95: 'pokemon.onix',
96: 'pokemon.drowzee',
97: 'pokemon.hypno',
98: 'pokemon.krabby',
99: 'pokemon.kingler',
100: 'pokemon.voltorb',
101: 'pokemon.electrode',
102: 'pokemon.exeggcute',
103: 'pokemon.exeggutor',
104: 'pokemon.cubone',
105: 'pokemon.marowak',
106: 'pokemon.hitmonlee',
107: 'pokemon.hitmonchan',
108: 'pokemon.lickitung',
109: 'pokemon.koffing',
110: 'pokemon.weezing',
111: 'pokemon.rhyhorn',
112: 'pokemon.rhydon',
113: 'pokemon.chansey',
114: 'pokemon.tangela',
115: 'pokemon.kangaskhan',
116: 'pokemon.horsea',
117: 'pokemon.seadra',
118: 'pokemon.goldeen',
119: 'pokemon.seaking',
120: 'pokemon.staryu',
121: 'pokemon.starmie',
122: 'pokemon.mr-mime',
123: 'pokemon.scyther',
124: 'pokemon.jynx',
125: 'pokemon.electabuzz',
126: 'pokemon.magmar',
127: 'pokemon.pinsir',
128: 'pokemon.tauros',
129: 'pokemon.magikarp',
130: 'pokemon.gyarados',
131: 'pokemon.lapras',
132: 'pokemon.ditto',
133: 'pokemon.eevee',
134: 'pokemon.vaporeon',
135: 'pokemon.jolteon',
136: 'pokemon.flareon',
137: 'pokemon.porygon',
138: 'pokemon.omanyte',
139: 'pokemon.omastar',
140: 'pokemon.kabuto',
141: 'pokemon.kabutops',
142: 'pokemon.aerodactyl',
143: 'pokemon.snorlax',
144: 'pokemon.articuno',
145: 'pokemon.zapdos',
146: 'pokemon.moltres',
147: 'pokemon.dratini',
148: 'pokemon.dragonair',
149: 'pokemon.dragonite',
150: 'pokemon.mewtwo',
151: 'pokemon.mew',
}
TYPE_IDENTIFIERS = {
0: 'normal',
1: 'fighting',
2: 'flying',
3: 'poison',
4: 'ground',
5: 'rock',
#6: 'bird',
7: 'bug',
8: 'ghost',
9: 'steel',
20: 'fire',
21: 'water',
22: 'grass',
23: 'electric',
24: 'psychic',
25: 'ice',
26: 'dragon',
27: 'dark',
0: 'type.normal',
1: 'type.fighting',
2: 'type.flying',
3: 'type.poison',
4: 'type.ground',
5: 'type.rock',
#6: 'type.bird',
7: 'type.bug',
8: 'type.ghost',
9: 'type.steel',
20: 'type.fire',
21: 'type.water',
22: 'type.grass',
23: 'type.electric',
24: 'type.psychic',
25: 'type.ice',
26: 'type.dragon',
27: 'type.dark',
}
MOVE_IDENTIFIERS = {
# TODO stupid hack for initial moveset
0: '--',
1: 'pound',
2: 'karate-chop',
3: 'double-slap',
4: 'comet-punch',
5: 'mega-punch',
6: 'pay-day',
7: 'fire-punch',
8: 'ice-punch',
9: 'thunder-punch',
10: 'scratch',
11: 'vice-grip',
12: 'guillotine',
13: 'razor-wind',
14: 'swords-dance',
15: 'cut',
16: 'gust',
17: 'wing-attack',
18: 'whirlwind',
19: 'fly',
20: 'bind',
21: 'slam',
22: 'vine-whip',
23: 'stomp',
24: 'double-kick',
25: 'mega-kick',
26: 'jump-kick',
27: 'rolling-kick',
28: 'sand-attack',
29: 'headbutt',
30: 'horn-attack',
31: 'fury-attack',
32: 'horn-drill',
33: 'tackle',
34: 'body-slam',
35: 'wrap',
36: 'take-down',
37: 'thrash',
38: 'double-edge',
39: 'tail-whip',
40: 'poison-sting',
41: 'twineedle',
42: 'pin-missile',
43: 'leer',
44: 'bite',
45: 'growl',
46: 'roar',
47: 'sing',
48: 'supersonic',
49: 'sonic-boom',
50: 'disable',
51: 'acid',
52: 'ember',
53: 'flamethrower',
54: 'mist',
55: 'water-gun',
56: 'hydro-pump',
57: 'surf',
58: 'ice-beam',
59: 'blizzard',
60: 'psybeam',
61: 'bubble-beam',
62: 'aurora-beam',
63: 'hyper-beam',
64: 'peck',
65: 'drill-peck',
66: 'submission',
67: 'low-kick',
68: 'counter',
69: 'seismic-toss',
70: 'strength',
71: 'absorb',
72: 'mega-drain',
73: 'leech-seed',
74: 'growth',
75: 'razor-leaf',
76: 'solar-beam',
77: 'poison-powder',
78: 'stun-spore',
79: 'sleep-powder',
80: 'petal-dance',
81: 'string-shot',
82: 'dragon-rage',
83: 'fire-spin',
84: 'thunder-shock',
85: 'thunderbolt',
86: 'thunder-wave',
87: 'thunder',
88: 'rock-throw',
89: 'earthquake',
90: 'fissure',
91: 'dig',
92: 'toxic',
93: 'confusion',
94: 'psychic',
95: 'hypnosis',
96: 'meditate',
97: 'agility',
98: 'quick-attack',
99: 'rage',
100: 'teleport',
101: 'night-shade',
102: 'mimic',
103: 'screech',
104: 'double-team',
105: 'recover',
106: 'harden',
107: 'minimize',
108: 'smokescreen',
109: 'confuse-ray',
110: 'withdraw',
111: 'defense-curl',
112: 'barrier',
113: 'light-screen',
114: 'haze',
115: 'reflect',
116: 'focus-energy',
117: 'bide',
118: 'metronome',
119: 'mirror-move',
120: 'self-destruct',
121: 'egg-bomb',
122: 'lick',
123: 'smog',
124: 'sludge',
125: 'bone-club',
126: 'fire-blast',
127: 'waterfall',
128: 'clamp',
129: 'swift',
130: 'skull-bash',
131: 'spike-cannon',
132: 'constrict',
133: 'amnesia',
134: 'kinesis',
135: 'soft-boiled',
136: 'high-jump-kick',
137: 'glare',
138: 'dream-eater',
139: 'poison-gas',
140: 'barrage',
141: 'leech-life',
142: 'lovely-kiss',
143: 'sky-attack',
144: 'transform',
145: 'bubble',
146: 'dizzy-punch',
147: 'spore',
148: 'flash',
149: 'psywave',
150: 'splash',
151: 'acid-armor',
152: 'crabhammer',
153: 'explosion',
154: 'fury-swipes',
155: 'bonemerang',
156: 'rest',
157: 'rock-slide',
158: 'hyper-fang',
159: 'sharpen',
160: 'conversion',
161: 'tri-attack',
162: 'super-fang',
163: 'slash',
164: 'substitute',
165: 'struggle',
1: 'move.pound',
2: 'move.karate-chop',
3: 'move.double-slap',
4: 'move.comet-punch',
5: 'move.mega-punch',
6: 'move.pay-day',
7: 'move.fire-punch',
8: 'move.ice-punch',
9: 'move.thunder-punch',
10: 'move.scratch',
11: 'move.vice-grip',
12: 'move.guillotine',
13: 'move.razor-wind',
14: 'move.swords-dance',
15: 'move.cut',
16: 'move.gust',
17: 'move.wing-attack',
18: 'move.whirlwind',
19: 'move.fly',
20: 'move.bind',
21: 'move.slam',
22: 'move.vine-whip',
23: 'move.stomp',
24: 'move.double-kick',
25: 'move.mega-kick',
26: 'move.jump-kick',
27: 'move.rolling-kick',
28: 'move.sand-attack',
29: 'move.headbutt',
30: 'move.horn-attack',
31: 'move.fury-attack',
32: 'move.horn-drill',
33: 'move.tackle',
34: 'move.body-slam',
35: 'move.wrap',
36: 'move.take-down',
37: 'move.thrash',
38: 'move.double-edge',
39: 'move.tail-whip',
40: 'move.poison-sting',
41: 'move.twineedle',
42: 'move.pin-missile',
43: 'move.leer',
44: 'move.bite',
45: 'move.growl',
46: 'move.roar',
47: 'move.sing',
48: 'move.supersonic',
49: 'move.sonic-boom',
50: 'move.disable',
51: 'move.acid',
52: 'move.ember',
53: 'move.flamethrower',
54: 'move.mist',
55: 'move.water-gun',
56: 'move.hydro-pump',
57: 'move.surf',
58: 'move.ice-beam',
59: 'move.blizzard',
60: 'move.psybeam',
61: 'move.bubble-beam',
62: 'move.aurora-beam',
63: 'move.hyper-beam',
64: 'move.peck',
65: 'move.drill-peck',
66: 'move.submission',
67: 'move.low-kick',
68: 'move.counter',
69: 'move.seismic-toss',
70: 'move.strength',
71: 'move.absorb',
72: 'move.mega-drain',
73: 'move.leech-seed',
74: 'move.growth',
75: 'move.razor-leaf',
76: 'move.solar-beam',
77: 'move.poison-powder',
78: 'move.stun-spore',
79: 'move.sleep-powder',
80: 'move.petal-dance',
81: 'move.string-shot',
82: 'move.dragon-rage',
83: 'move.fire-spin',
84: 'move.thunder-shock',
85: 'move.thunderbolt',
86: 'move.thunder-wave',
87: 'move.thunder',
88: 'move.rock-throw',
89: 'move.earthquake',
90: 'move.fissure',
91: 'move.dig',
92: 'move.toxic',
93: 'move.confusion',
94: 'move.psychic',
95: 'move.hypnosis',
96: 'move.meditate',
97: 'move.agility',
98: 'move.quick-attack',
99: 'move.rage',
100: 'move.teleport',
101: 'move.night-shade',
102: 'move.mimic',
103: 'move.screech',
104: 'move.double-team',
105: 'move.recover',
106: 'move.harden',
107: 'move.minimize',
108: 'move.smokescreen',
109: 'move.confuse-ray',
110: 'move.withdraw',
111: 'move.defense-curl',
112: 'move.barrier',
113: 'move.light-screen',
114: 'move.haze',
115: 'move.reflect',
116: 'move.focus-energy',
117: 'move.bide',
118: 'move.metronome',
119: 'move.mirror-move',
120: 'move.self-destruct',
121: 'move.egg-bomb',
122: 'move.lick',
123: 'move.smog',
124: 'move.sludge',
125: 'move.bone-club',
126: 'move.fire-blast',
127: 'move.waterfall',
128: 'move.clamp',
129: 'move.swift',
130: 'move.skull-bash',
131: 'move.spike-cannon',
132: 'move.constrict',
133: 'move.amnesia',
134: 'move.kinesis',
135: 'move.soft-boiled',
136: 'move.high-jump-kick',
137: 'move.glare',
138: 'move.dream-eater',
139: 'move.poison-gas',
140: 'move.barrage',
141: 'move.leech-life',
142: 'move.lovely-kiss',
143: 'move.sky-attack',
144: 'move.transform',
145: 'move.bubble',
146: 'move.dizzy-punch',
147: 'move.spore',
148: 'move.flash',
149: 'move.psywave',
150: 'move.splash',
151: 'move.acid-armor',
152: 'move.crabhammer',
153: 'move.explosion',
154: 'move.fury-swipes',
155: 'move.bonemerang',
156: 'move.rest',
157: 'move.rock-slide',
158: 'move.hyper-fang',
159: 'move.sharpen',
160: 'move.conversion',
161: 'move.tri-attack',
162: 'move.super-fang',
163: 'move.slash',
164: 'move.substitute',
165: 'move.struggle',
}
@ -1178,11 +1179,11 @@ evos_moves_struct = Struct(
Embedded(Switch(
'evo_arguments',
lambda ctx: ctx.evo_trigger, {
'level-up': Struct(
'evolution-trigger.level-up': Struct(
'---',
ULInt8('evo_level'),
),
'use-item': Struct(
'evolution-trigger.use-item': Struct(
'---',
# TODO item enum too wow!
ULInt8('evo_item'),
@ -1190,7 +1191,7 @@ evos_moves_struct = Struct(
ULInt8('evo_level'),
),
# TODO ??? always seems to be 1 here too
'trade': Struct(
'evolution-trigger.trade': Struct(
'---',
ULInt8('evo_level'),
),
@ -1280,8 +1281,14 @@ class RBYCart:
Return a dict of raw file offsets. The keys are the names used in the
pokered project.
"""
# The base stats are always in the same place in RBY, and only slightly
# off in RG. Not sure why! But it hopefully means recompilation
# doesn't affect them.
addresses = {
# These seem to always be the same. Not sure why!
# These ones have, thusfar, defied automatic detection, as they're
# just part of a big old block of data — so I can't just look for
# code nearby.
# TODO these are for rby; fix for rg, and maybe y?
'BaseStats': unbank('0E:43DE'),
'MewBaseStats': unbank('01:425B'),
}
@ -1624,6 +1631,7 @@ class RBYCart:
def pokemon_records(self):
"""List of pokemon_structs."""
self.stream.seek(self.addrs['BaseStats'])
print(self.stream.read(100).hex())
records = Array(self.NUM_POKEMON - 1, pokemon_struct).parse_stream(self.stream)
# Mew's data is, awkwardly, stored separately
self.stream.seek(self.addrs['MewBaseStats'])
@ -1713,13 +1721,16 @@ class WriterWrapper:
return getattr(self.locus, key)
def main():
def main(root):
# TODO does this need to take arguments? or like, sprite mode i guess
carts = []
for filename in sys.argv[1:]:
cart = RBYCart(Path(filename))
carts.append(cart)
root /= carts[0].game
root.mkdir(exist_ok=True)
#loader = RBYLoader(*carts)
pokemons = OrderedDict([
(POKEMON_IDENTIFIERS[id + 1], schema.Pokemon())
@ -1788,11 +1799,10 @@ def main():
evolutions.append(evo)
writer.evolutions = evolutions
from camel import Camel
print(Camel([schema.POKEDEX_TYPES]).dump(pokemons))
with (root / 'pokemon.yaml').open('w') as f:
f.write(Camel([schema.POKEDEX_TYPES]).dump(pokemons))
if __name__ == '__main__':
main()
# TODO yeah fix this up
main(Path('pokedex/data'))

View file

@ -3,21 +3,40 @@
from collections import defaultdict
from collections import OrderedDict
from pprint import pprint
import types
import camel
class _Attribute:
name = None
_creation_order = 0
def __init__(self):
self._creation_order = _Attribute._creation_order
_Attribute._creation_order += 1
def __get__(self, inst, owner):
# TODO this is intended for the glom object, not a slice
return self.Glommed(self, inst)
def __set_name__(self, cls, name):
self.name = name
class Glommed:
def __init__(self, prop, obj):
self.prop = prop
self.obj = obj
def __repr__(self):
return "<{} of {!r}.{}: {!r}>".format(
type(self).__qualname__,
self.obj,
self.prop.name,
{game: getattr(slice, self.prop.name) for game, slice in self.obj._slices.items()},
)
# TODO classtools, key sort by _creation_order
@ -55,20 +74,48 @@ class _ForwardDeclaration:
pass
class Slice:
is_slice = True
def __init__(self):
pass
class LocusMeta(type):
def __init__(cls, name, bases, attrs):
for key, attr in attrs.items():
if hasattr(attr, '__set_name__'):
attr.__set_name__(cls, key)
super().__init__(name, bases, attrs)
# TODO uhh yeah figure this out. possibly related to attrs
cls.index = {}
# TODO need default values
# This is purely a backport of Python 3.6 functionality, and is taken from
# PEP 487. Once the minimum version supported is 3.6, this metaclass can
# go away entirely.
if not hasattr(object, '__init_subclass__'):
def __new__(cls, *args, **kwargs):
if len(args) != 3:
return super().__new__(cls, *args)
name, bases, ns = args
init = ns.get('__init_subclass__')
if isinstance(init, types.FunctionType):
ns['__init_subclass__'] = classmethod(init)
else:
init = None
self = super().__new__(cls, name, bases, ns)
for k, v in self.__dict__.items():
func = getattr(v, '__set_name__', None)
if func is not None:
func(self, k)
sup = super(self, self)
if hasattr(sup, '__init_subclass__'):
sup.__init_subclass__(**kwargs)
return self
class Locus(metaclass=LocusMeta):
_attributes = {}
def __init_subclass__(cls, **kwargs):
# super().__init_subclass__(**kwargs)
cls._attributes = cls._attributes.copy()
for key, value in cls.__dict__.items():
if isinstance(value, _Attribute):
cls._attributes[key] = value
def __init__(self, **kwargs):
cls = type(self)
@ -78,6 +125,25 @@ class Locus(metaclass=LocusMeta):
setattr(self, key, value)
def __repr__(self):
return "<{}: {}>".format(
type(self).__qualname__,
self.identifier,
)
class VersionedLocus(Locus):
def __init_subclass__(cls, **kwargs):
super(VersionedLocus, cls).__init_subclass__(**kwargs)
if not issubclass(cls, Slice):
class Sliced(cls, Slice):
base_class = cls
# TODO this is a circular reference; do i care?
cls.Sliced = Sliced
cls._slices = {}
# TODO seems to me that each of these, regardless of whether they have any
# additional data attached or not, are restricted to a fixed extra-game-ular
@ -91,10 +157,9 @@ MoveSet = _ForwardDeclaration()
Pokedex = _ForwardDeclaration()
class Pokemon(Locus):
# TODO version, language. but those are kind of meta-fields; do they need treating specially?
class Pokémon(VersionedLocus):
# TODO version, language. but those are kind of meta-fields; do they need
# treating specially?
# TODO in old games, names are unique per game; in later games, they differ
# per language. what do i do about that?
name = _Value(str)
@ -125,19 +190,34 @@ class Pokemon(Locus):
# TODO should this be written in hex, maybe?
game_index = _Value(int)
Pokemon = Pokémon
class Repository:
def __init__(self):
# type -> identifier -> list of objects
self.objects = defaultdict(lambda: defaultdict(set))
# type -> identifier -> object
self.objects = defaultdict(lambda: {})
# type -> property -> value -> list of objects
self.index = defaultdict(lambda: defaultdict(lambda: defaultdict(set)))
def add(self, obj):
# TODO this should be declared by the type itself, obviously
cls = type(obj)
self.objects[cls][obj.identifier].add(obj)
self.index[cls][cls.name][obj.name].add(obj)
# TODO both branches here should check for duplicates
if isinstance(obj, Slice):
cls = cls.base_class
if obj.identifier not in self.objects[cls]:
glom = cls()
glom.identifier = obj.identifier
self.objects[cls][obj.identifier] = glom
else:
glom = self.objects[cls][obj.identifier]
# TODO this... feels special-cased, but i guess, it is?
glom._slices[obj.game] = obj
else:
self.objects[cls][obj.identifier] = obj
# TODO this is more complex now that names are multi-language
#self.index[cls][cls.name][obj.name].add(obj)
def fetch(self, cls, identifier):
# TODO wrap in a... multi-thing
@ -161,7 +241,8 @@ def _dump_locus(locus):
@POKEDEX_TYPES.loader('pokemon', version=None)
def _load_locus(data, version):
cls = Pokemon
cls = Pokemon.Sliced
# TODO wrap with a writer thing?
obj = cls()
for key, value in data.items():
key = key.replace('-', '_')
@ -176,26 +257,24 @@ def _temp_main():
# just testing for now
cam = camel.Camel([POKEDEX_TYPES])
PATH = 'pokedex/data/gen1/red/en/pokemon.yaml'
PATH = 'pokedex/data/ww-red/pokemon.yaml'
with open(PATH) as f:
all_pokemon = cam.load(f.read())
for identifier, pokemon in all_pokemon.items():
# TODO i don't reeeally like this, but configuring a camel to do it
# is a little unwieldy
pokemon.version = 'red'
pokemon.language = 'en'
pokemon.game = 'ww-red'
# TODO this in particular seems extremely clumsy, but identifiers ARE fundamentally keys...
pokemon.identifier = identifier
repository.add(pokemon)
PATH = 'pokedex/data/gen1/red/fr/pokemon.yaml'
PATH = 'pokedex/data/ww-blue/pokemon.yaml'
with open(PATH) as f:
all_pokemon = cam.load(f.read())
for identifier, pokemon in all_pokemon.items():
# TODO i don't reeeally like this, but configuring a camel to do it
# is a little unwieldy
pokemon.version = 'red'
pokemon.language = 'fr'
pokemon.game = 'ww-blue'
# TODO this in particular seems extremely clumsy, but identifiers ARE fundamentally keys...
pokemon.identifier = identifier
@ -206,11 +285,12 @@ def _temp_main():
# - but what about the vast majority of properties that are the same in every language and only vary by version?
# - what about later games, where only some properties vary by language? in the extreme case, xy/oras are single games!
eevee = repository.fetch(Pokemon, 'eevee')
# TODO should this prepend the prefix automatically... eh
eevee = repository.fetch(Pokemon, 'pokemon.eevee')
pprint(eevee)
return
eevee = Pokemon['eevee']
# TODO i feel like this should work: eevee = repository.Pokemon['eevee']
print(eevee.name)
print(eevee.types)
# TODO alright so we need to figure out the "index" part, and how you