├── .gitignore ├── poketypes ├── testdata.sav ├── __init__.py ├── pokemon │ ├── status.py │ ├── species.py │ ├── __init__.py │ ├── moves.py │ ├── types.py │ ├── basestats.py │ └── experience.py ├── team.py ├── gamesave.py ├── basic.py ├── encoding.py ├── item.py ├── pokedex.py └── tests.py ├── SerialGameLinkProxy ├── GameLink.h ├── SerialGameLinkProxy.ino └── GameLink.cpp ├── LICENSE ├── README.md ├── pokecreator.py └── pokeduino.py /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .*.swo 3 | *.o 4 | *.pyc 5 | *~ 6 | testdata.sav 7 | *.sqlite3 8 | -------------------------------------------------------------------------------- /poketypes/testdata.sav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tzwenn/PokeDuino/HEAD/poketypes/testdata.sav -------------------------------------------------------------------------------- /poketypes/__init__.py: -------------------------------------------------------------------------------- 1 | from .pokemon import * 2 | from .gamesave import * 3 | from .team import * 4 | from . import pokedex 5 | 6 | def loadGame(fileName): 7 | with open(fileName, "rb") as f: 8 | return GameSave.fromBytes(f.read()) 9 | -------------------------------------------------------------------------------- /SerialGameLinkProxy/GameLink.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace GameLink { 6 | 7 | typedef uint8_t(*ReceivedByteCallback)(uint8_t data); 8 | 9 | void setup(ReceivedByteCallback callback); 10 | unsigned long microsSinceLastReceive(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /SerialGameLinkProxy/SerialGameLinkProxy.ino: -------------------------------------------------------------------------------- 1 | #include "GameLink.h" 2 | 3 | const long BAUDRATE = 115200; 4 | 5 | uint8_t SerialGameLinkProxy(uint8_t serial_out) 6 | { 7 | static volatile int serial_in = 0; 8 | serial_in = Serial.read(); 9 | Serial.write(serial_out); 10 | return serial_in; 11 | } 12 | 13 | void setup() 14 | { 15 | Serial.begin(BAUDRATE); 16 | GameLink::setup(SerialGameLinkProxy); 17 | } 18 | 19 | void loop() 20 | { 21 | ;; 22 | } 23 | -------------------------------------------------------------------------------- /poketypes/pokemon/status.py: -------------------------------------------------------------------------------- 1 | import enum 2 | import operator 3 | import functools 4 | 5 | class Status(enum.Enum): 6 | Asleep = 0x04 7 | Poisoned = 0x08 8 | Burned = 0x10 9 | Frozen = 0x20 10 | Paralyzed = 0x40 11 | 12 | class StatusField(object): 13 | 14 | def __init__(self, data): 15 | self.data = set(field for field in Status if data & field.value) 16 | 17 | def add(self, s): 18 | if not isinstance(s, Status): 19 | raise TypeError("Only %s enum allowed as content" % Status.__class__.__name__) 20 | else: 21 | self.data.add(s) 22 | 23 | def __iter__(self): 24 | return iter(self.data) 25 | 26 | def __int__(self): 27 | return self.value 28 | 29 | def __str__(self): 30 | return "StatusField(%s)" % ", ".join(map(str, self)) 31 | 32 | @property 33 | def value(self): 34 | return functools.reduce(lambda curr, elem: curr | elem.value, self.data, 0) 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016, Sven Köhler 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /SerialGameLinkProxy/GameLink.cpp: -------------------------------------------------------------------------------- 1 | #include "GameLink.h" 2 | #include 3 | 4 | /* 5 | Name ArduinoPin Color 6 | --------------------------------------------- 7 | GND GND Blue 8 | SOut 6 Orange (Red@GB) 9 | SIn 3 Red (Orange@GB) 10 | SClk 2 Green 11 | 12 | Note: SOut and SIn are crossed. 13 | Colors refer to what is connected at arduino end. 14 | */ 15 | 16 | namespace GameLink { 17 | 18 | static const int timeout_us = 120; 19 | 20 | static volatile uint8_t _in; 21 | static volatile uint8_t _out; 22 | static volatile unsigned long _lastReceive = 0; 23 | 24 | static ReceivedByteCallback _callback; 25 | 26 | enum PIN { 27 | clk = 2, 28 | in = 3, 29 | out = 6 30 | }; 31 | 32 | static void externalClockTick() 33 | { 34 | static volatile int _currentBit = 0; 35 | 36 | if (_lastReceive > 0 && microsSinceLastReceive() > timeout_us) { 37 | _in = 0; 38 | _currentBit = 0; 39 | } 40 | 41 | _in <<= 1; 42 | _in |= (digitalRead(PIN::in) == HIGH); 43 | 44 | if (++_currentBit >= 8) { 45 | _out = _callback(_in); 46 | _in = 0; 47 | _currentBit = 0; 48 | } 49 | 50 | _lastReceive = micros(); 51 | while (digitalRead(PIN::clk) != HIGH); 52 | 53 | digitalWrite(PIN::out, _out & 0x80 ? HIGH : LOW); 54 | _out <<= 1; 55 | } 56 | 57 | /////////////////////////////////////////////////////////////////////// 58 | 59 | void setup(ReceivedByteCallback callback) 60 | { 61 | _in = _out = 0; 62 | _callback = callback; 63 | 64 | pinMode(PIN::clk, INPUT); 65 | pinMode(PIN::in, INPUT); 66 | pinMode(PIN::out, OUTPUT); 67 | 68 | digitalWrite(PIN::in, LOW); 69 | attachInterrupt(digitalPinToInterrupt(PIN::clk), externalClockTick, RISING); 70 | } 71 | 72 | unsigned long microsSinceLastReceive() 73 | { 74 | return micros() - _lastReceive; 75 | } 76 | 77 | }; // namespace GameLink 78 | 79 | -------------------------------------------------------------------------------- /poketypes/team.py: -------------------------------------------------------------------------------- 1 | from .basic import * 2 | from .pokemon import Pokemon 3 | 4 | __all__ = ["Team"] 5 | 6 | class Team(PokeStructure): 7 | 8 | TERMINATOR = 0xFF 9 | 10 | _fields_ = [ 11 | ("entries_used", ctypes.c_uint8), 12 | ("species_list", Pokearray(7)), 13 | ("pokemon_list", Pokemon * 6), 14 | ("ot_names", Pokestring(11) * 6), 15 | ("pokemon_names", Pokestring(11) * 6) 16 | ] 17 | 18 | def nickname_at(self, index): 19 | """Returns nickname of pokemon at given index, or None if it hasn't any""" 20 | name = self.pokemon_names[index].toString() 21 | # FIXME: Properly handle localization 22 | if name != self.pokemon_list[index].species.name.upper(): 23 | return name 24 | return None # No nickname 25 | 26 | def __iter__(self): 27 | return (self[i] for i in range(self.entries_used)) 28 | 29 | def add(self, pokemon, ot_name, nickname=None): 30 | if self.entries_used >= 6: 31 | raise IndexError("PokemonList cannot hold more than 6 pokemon") 32 | self.entries_used = max(0, self.entries_used + 1) 33 | index = self.entries_used - 1 34 | self.species_list[index] = pokemon.species.value 35 | self.species_list[index + 1] = self.TERMINATOR 36 | self.pokemon_list[index] = pokemon 37 | self.ot_names[index] = Pokestring(11).fromString(ot_name) 38 | # FIXME: Properly handle localization 39 | self.pokemon_names[index] = Pokestring(11).fromString( 40 | nickname if nickname is not None else pokemon.species.name.upper(), 41 | fillUp=True) 42 | 43 | def __getitem__(self, index): 44 | if index >= self.entries_used: 45 | raise IndexError("list index out of range") 46 | return self.pokemon_list[index], self.ot_names[index].toString(), self.nickname_at(index) 47 | 48 | def __init__(self, pokemon_ot_nickname_list=[]): 49 | super().__init__() 50 | self.species_list[0] = self.TERMINATOR 51 | for entry in pokemon_ot_nickname_list: 52 | self.add(*entry) 53 | 54 | def __repr__(self): 55 | return "%s.%s(%s)" % (__name__, self.__class__.__name__, repr(list(self))) 56 | -------------------------------------------------------------------------------- /poketypes/gamesave.py: -------------------------------------------------------------------------------- 1 | from .basic import * 2 | from .pokemon import Pokemon 3 | from .team import Team 4 | from .item import ItemList 5 | 6 | __all__ = ["GameSave"] 7 | 8 | PokemonBox = Pokearray(1122) 9 | 10 | class GameSaveGenI(PokeStructure): 11 | 12 | """Pokemon game save structure as Generation I according to 13 | http://bulbapedia.bulbagarden.net/wiki/Save_data_structure_in_Generation_I#Pok.C3.A9mon_lists""" 14 | 15 | _fields_ = [ 16 | PaddingBytes(0x2598), 17 | ("player_name", Pokestring(11)), 18 | ("pokedex_owned", Pokearray(19)), 19 | ("pokedex_seen", Pokearray(19)), 20 | ("pocket_item_list", ItemList(20)), 21 | ("money", Pokearray(3)), 22 | ("rival_name", Pokestring(11)), 23 | ("options", ctypes.c_uint8), 24 | ("badges", ctypes.c_uint8), 25 | PaddingBytes(2), 26 | ("player_id", ctypes.c_uint16), 27 | PaddingBytes(277), 28 | ("pikachu_friendship", ctypes.c_uint8), 29 | PaddingBytes(201), 30 | ("pc_item_list", ItemList(50)), 31 | ("current_pc_box", ctypes.c_uint8), 32 | PaddingBytes(3), 33 | ("casino_coins", ctypes.c_uint16), 34 | PaddingBytes(1180), 35 | ("time_played", ctypes.c_uint32), 36 | PaddingBytes(570), 37 | ("team", Team), 38 | ("current_box", PokemonBox), 39 | PaddingBytes(1), 40 | ("checksum", ctypes.c_uint8), 41 | PaddingBytes(2780), 42 | ("pc_box_1", PokemonBox), 43 | ("pc_box_2", PokemonBox), 44 | ("pc_box_3", PokemonBox), 45 | ("pc_box_4", PokemonBox), 46 | ("pc_box_5", PokemonBox), 47 | ("pc_box_6", PokemonBox), 48 | PaddingBytes(1460), 49 | ("pc_box_7", PokemonBox), 50 | ("pc_box_8", PokemonBox), 51 | ("pc_box_9", PokemonBox), 52 | ("pc_box_10", PokemonBox), 53 | ("pc_box_11", PokemonBox), 54 | ("pc_box_12", PokemonBox), 55 | PaddingBytes(1460) 56 | ] 57 | 58 | def _calcChecksum(self): 59 | sum = 0 60 | for b in PokeStructure.bytes(self)[0x2598:0x3522 + 1]: 61 | sum += b 62 | sum &= 0xFF 63 | return ~sum & 0xFF 64 | 65 | def bytes(self): 66 | self.checksum = self._calcChecksum() 67 | return PokeStructure.bytes(self) 68 | 69 | def save(self, fileName): 70 | open(fileName, "wb").write(self.bytes()) 71 | 72 | GameSave = GameSaveGenI 73 | 74 | -------------------------------------------------------------------------------- /poketypes/basic.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | from . import encoding 4 | 5 | class PokeMetaStructure(type(ctypes.BigEndianStructure)): 6 | 7 | def __new__(metacls, name, bases, dct): 8 | cls = super().__new__(metacls, name, bases, dct) 9 | for member, adapter_type in cls._adapters_: 10 | cls.buildProperty(member, adapter_type) 11 | return cls 12 | 13 | def buildProperty(cls, member, adapter_type): 14 | 15 | def get(self): 16 | return adapter_type(getattr(self, member)) 17 | 18 | def set(self, value): 19 | if isinstance(value, adapter_type): 20 | setattr(self, member, value.value) 21 | else: 22 | setattr(self, member, value) 23 | if member.startswith("_"): 24 | property_name = member[1:] 25 | else: 26 | property_name = member + "_adapter" 27 | setattr(cls, property_name, 28 | property(fget=get, fset=set, doc="%s adapter to member %s" % (adapter_type.__name__, member))) 29 | 30 | class PokeStructure(ctypes.BigEndianStructure, metaclass=PokeMetaStructure): 31 | 32 | _pack_ = 1 33 | _adapters_ = [] 34 | 35 | @classmethod 36 | def fromBytes(cls, data): 37 | return cls.from_buffer_copy(data) 38 | 39 | def bytes(self): 40 | return ctypes.string_at(ctypes.byref(self), ctypes.sizeof(self)) 41 | 42 | def bytecount(self): 43 | return ctypes.sizeof(self) 44 | 45 | def Pokearray(length): 46 | 47 | # okay, I learned. 48 | # It's not possible to use a custom base class 49 | # in a ctypes.Structure field. Forget about it 50 | 51 | @classmethod 52 | def fromBytes(cls, data): 53 | return cls(*data) 54 | 55 | def asBytes(self): 56 | return bytes(iter(self)) 57 | 58 | t = ctypes.c_uint8 * length 59 | t.fromBytes = fromBytes 60 | t.bytes = asBytes 61 | return t 62 | 63 | def Pokestring(length): 64 | 65 | @classmethod 66 | def fromString(cls, data, fillUp=False): 67 | encoded = encoding.encode(data) + encoding.ENDCHAR 68 | if fillUp: 69 | encoded = encoded.ljust(length, encoding.ENDCHAR) 70 | return cls(*encoded[:length]) 71 | 72 | def toString(self): 73 | encoded = self.bytes().partition(encoding.ENDCHAR)[0] 74 | return encoding.decode(encoded) 75 | 76 | t = Pokearray(length) 77 | t.fromString = fromString 78 | t.toString = toString 79 | return t 80 | 81 | def PaddingBytes(length): 82 | return ("padding", Pokearray(length)) 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PokeDuino 2 | 3 | Generate a Generation I Pokemon on your computer and transfer it to a GameBoy using an Arduino as Game Link relay. 4 | 5 | ## Setup 6 | 7 | Cut and strip a Game Link Cable open. Solder the inner wires onto jumper cables that you can connect to your Arduino. 8 | 9 | ___________ 10 | | 6 * 2 | 11 | \_5__3__*_/ (at cable) 12 | 13 | 14 | | Cable Pin | Name | Color | Arduino Pin | 15 | |-----------|---------------|--------|--------------| 16 | | 2 | Serial Out | Orange | 6 | 17 | | 3 | Serial In | Red | 3 | 18 | | 5 | Serial Clock | Green | 2 | 19 | | 6 | GND | Blue | GND | 20 | 21 | ***Note that*** the ```Out``` and ```In``` wires are crossed from one end to the other, so it's recommended to completely open one socket and follow the color scheme there. 22 | 23 | Clone the repository, upload ```SerialGameLinkProxy.ino``` to the Arduino 24 | and install [pyserial](https://pypi.python.org/pypi/pyserial): 25 | 26 | ``` 27 | $ pip3 install pyserial 28 | ``` 29 | 30 | ## Usage 31 | 32 | You can use the ```pokecreator.py``` to generate a [PokemonDataStructure](http://bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_data_structure_in_Generation_I) that can be piped into ```pokeduino.py``` and transferred to the GameBoy. Any Pokemon received during that trade will be likewise outputted to stdout and can be later restored. 33 | 34 | ``` 35 | $ ./pokecreator.py --species Mew --level 7 --moves Pound PayDay | ./pokeduino.py > received_pokemon.dat 36 | ``` 37 | 38 | See ```--help``` for a comprehensive list of specifiable fields and trade options. 39 | 40 | #### Note for non-English games 41 | 42 | Nicknames and species names are a mess. 43 | 44 | When you use a non-English game, you'll need to set the Pokemon's nickname to match your uppercase native species translation. Otherwise the Pokemon will stick with its English species name as nickname. 45 | 46 | Specify the nickname (and also possible original trainer info) at ```pokeduino.py```, since they are not integral part of the Pokemon data structure itself. 47 | 48 | ``` 49 | # German Example 50 | $ ./pokecreator.py -s Bulbasaur | ./pokeduino.py -n BISASAM > received_pokemon.dat 51 | ``` 52 | 53 | ## See also 54 | 55 | * Adan Scotney's pokemon [trade protocol specification](http://www.adanscotney.com/2014/01/spoofing-pokemon-trades-with-stellaris.html) and [implementation](https://bitbucket.org/adanscotney/gameboy-spoof) 56 | * Bulbapedia's great list of [in-game data structures](http://bulbapedia.bulbagarden.net/wiki/Save_data_structure_in_Generation_I) 57 | * Esteban Fuentealba's [trade spoofer](https://github.com/EstebanFuentealba/Arduino-Spoofing-Gameboy-Pokemon-Trades) running ***on*** Arduino 58 | 59 | -------------------------------------------------------------------------------- /poketypes/encoding.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Shorthands and decoded strings 4 | NULL = "\0" 5 | JUNK = "" 6 | TLC = "⌜" # top left corner icon 7 | TRC = "⌝" 8 | BLC = "⌞" 9 | BRC = "⌟" 10 | SPACE = " " 11 | PK = "PK" 12 | MN = "MN" 13 | POKEDOLLAR = "$" 14 | 15 | # Encoded constants 16 | ENDCHAR = b'\x50' 17 | 18 | 19 | class holdover(object): 20 | 21 | def __init__(self, s): 22 | self.s = s 23 | 24 | def __str__(self): 25 | return self.s 26 | 27 | def __repr__(self): 28 | return "holdover(%s)" % self.s 29 | 30 | _ = holdover 31 | 32 | def holdovers(*args): 33 | return list(map(holdover, args)) 34 | 35 | 36 | control_characters = [""] * 24 37 | 38 | char_set = [ 39 | # -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F 40 | NULL, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, # 0- 41 | JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, # 1- 42 | JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, # 2- 43 | JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, # 3- 44 | JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK] + control_characters + \ 45 | holdovers( # 5- 46 | 'A', 'B' , 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'V', 'S', 'L', 'M', ':', 'ぃ', 'ぅ', # 6- 47 | '‘', '’', '“', '”', '・', '…', 'ぁ', 'ぇ', 'ぉ', TLC, '=', TRC, '||', BLC, BRC)+[SPACE, # 7- 48 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', # 8- 49 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '(', ')', ':', ';', '[', ']', # 9- 50 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', # A- 51 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'é', '\'d', '\'l', '\'s', '\'t', '\'v', # B- 52 | JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, # C- 53 | JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, # D- 54 | '\'', PK, MN, '-', '\'r', '\'m', '?', '!', '.',_('ァ'),_('ゥ'),_('ェ'),'▷', '▶', '▼', '♂', # E- 55 | POKEDOLLAR, '×', '.', '/', ',', '♀', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] # F- 56 | 57 | def decode(data): 58 | return "".join(str(char_set[b]) for b in data) 59 | 60 | def encode(s): 61 | return bytes(char_set.index(c) for c in s) 62 | 63 | -------------------------------------------------------------------------------- /poketypes/item.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | from .basic import * 4 | 5 | class Index(enum.Enum): 6 | Nothing, \ 7 | MasterBall, \ 8 | UltraBall, \ 9 | GreatBall, \ 10 | PokeBall, \ 11 | TownMap, \ 12 | Bicycle, \ 13 | unobtainable0x7, \ 14 | SafariBall, \ 15 | Pokedex, \ 16 | MoonStone, \ 17 | Antidote, \ 18 | BurnHeal, \ 19 | IceHeal, \ 20 | Awakening, \ 21 | ParlyzHeal, \ 22 | FullRestore, \ 23 | MaxPotion, \ 24 | HyperPotion, \ 25 | SuperPotion, \ 26 | Potion, \ 27 | BoulderBadge, \ 28 | CascadeBadge, \ 29 | ThunderBadge, \ 30 | RainbowBadge, \ 31 | SoulBadge, \ 32 | MarshBadge, \ 33 | VolcanoBadge, \ 34 | EarthBadge, \ 35 | EscapeRope, \ 36 | Repel, \ 37 | OldAmber, \ 38 | FireStone, \ 39 | ThunderStone, \ 40 | WaterStone, \ 41 | HPUp, \ 42 | Protein, \ 43 | Iron, \ 44 | Carbos, \ 45 | Calcium, \ 46 | RareCandy, \ 47 | DomeFossil, \ 48 | HelixFossil, \ 49 | SecretKey, \ 50 | unobtainable0x2c, \ 51 | BikeVoucher, \ 52 | XAccuracy, \ 53 | LeafStone, \ 54 | CardKey, \ 55 | Nugget, \ 56 | glicht_PPUp, \ 57 | PokeDoll, \ 58 | FullHeal, \ 59 | Revive, \ 60 | MaxRevive, \ 61 | GuardSpec, \ 62 | SuperRepel, \ 63 | MaxRepel, \ 64 | DireHit, \ 65 | Coin, \ 66 | FreshWater, \ 67 | SodaPop, \ 68 | Lemonade, \ 69 | SS_Ticket, \ 70 | GoldTeeth, \ 71 | XAttack, \ 72 | XDefend, \ 73 | XSpeed, \ 74 | XSpecial, \ 75 | CoinCase, \ 76 | OaksParcel, \ 77 | Itemfinder, \ 78 | SilphScope, \ 79 | PokeFlute, \ 80 | LiftKey, \ 81 | ExpAll, \ 82 | OldRod, \ 83 | GoodRod, \ 84 | SuperRod, \ 85 | PPUp, \ 86 | Ether, \ 87 | MaxEther, \ 88 | Elixer, \ 89 | MaxElixer = range(84) 90 | 91 | HM01, \ 92 | HM02, \ 93 | HM03, \ 94 | HM04, \ 95 | HM05, \ 96 | TM01, \ 97 | TM02, \ 98 | TM03, \ 99 | TM04, \ 100 | TM05, \ 101 | TM06, \ 102 | TM07, \ 103 | TM08, \ 104 | TM09, \ 105 | TM10, \ 106 | TM11, \ 107 | TM12, \ 108 | TM13, \ 109 | TM14, \ 110 | TM15, \ 111 | TM16, \ 112 | TM17, \ 113 | TM18, \ 114 | TM19, \ 115 | TM20, \ 116 | TM21, \ 117 | TM22, \ 118 | TM23, \ 119 | TM24, \ 120 | TM25, \ 121 | TM26, \ 122 | TM27, \ 123 | TM28, \ 124 | TM29, \ 125 | TM30, \ 126 | TM31, \ 127 | TM32, \ 128 | TM33, \ 129 | TM34, \ 130 | TM35, \ 131 | TM36, \ 132 | TM37, \ 133 | TM38, \ 134 | TM39, \ 135 | TM40, \ 136 | TM41, \ 137 | TM42, \ 138 | TM43, \ 139 | TM44, \ 140 | TM45, \ 141 | TM46, \ 142 | TM47, \ 143 | TM48, \ 144 | TM49, \ 145 | TM50, \ 146 | TM51, \ 147 | TM52, \ 148 | TM53, \ 149 | TM54, \ 150 | TM55 = range(196, 256) 151 | 152 | class Item(PokeStructure): 153 | 154 | _fields_ = [ 155 | ("_index", ctypes.c_uint8), 156 | ("count", ctypes.c_uint8) 157 | ] 158 | 159 | _adapters_ = [ 160 | ("_index", Index) 161 | ] 162 | 163 | def __init__(self, index, count): 164 | self.index = index 165 | self.count = count 166 | 167 | def ItemList(length): 168 | 169 | class ItemListObject(PokeStructure): 170 | _fields_ = [ 171 | ("count", ctypes.c_uint8), 172 | ("entries", Item * length), 173 | ("terminator", ctypes.c_uint8) 174 | ] 175 | 176 | TERMINATOR = 0xFF 177 | 178 | def addItem(index, count=1): 179 | if self.count == length: 180 | raise IndexError("Item list can only store %d elements" % length) 181 | self.entries[count] = Item(index, count) 182 | self.count += 1 183 | self.entries[count].terminator = self.TERMINATOR 184 | 185 | return ItemListObject 186 | 187 | -------------------------------------------------------------------------------- /poketypes/pokedex.py: -------------------------------------------------------------------------------- 1 | from .pokemon.species import Species 2 | 3 | national = [ 4 | Species.MissingNo_00, 5 | Species.Bulbasaur, 6 | Species.Ivysaur, 7 | Species.Venusaur, 8 | Species.Charmander, 9 | Species.Charmeleon, 10 | Species.Charizard, 11 | Species.Squirtle, 12 | Species.Wartortle, 13 | Species.Blastoise, 14 | Species.Caterpie, 15 | Species.Metapod, 16 | Species.Butterfree, 17 | Species.Weedle, 18 | Species.Kakuna, 19 | Species.Beedrill, 20 | Species.Pidgey, 21 | Species.Pidgeotto, 22 | Species.Pidgeot, 23 | Species.Rattata, 24 | Species.Raticate, 25 | Species.Spearow, 26 | Species.Fearow, 27 | Species.Ekans, 28 | Species.Arbok, 29 | Species.Pikachu, 30 | Species.Raichu, 31 | Species.Sandshrew, 32 | Species.Sandslash, 33 | Species.Nidoran_f, 34 | Species.Nidorina, 35 | Species.Nidoqueen, 36 | Species.Nidoran_m, 37 | Species.Nidorino, 38 | Species.Nidoking, 39 | Species.Clefairy, 40 | Species.Clefable, 41 | Species.Vulpix, 42 | Species.Ninetales, 43 | Species.Jigglypuff, 44 | Species.Wigglytuff, 45 | Species.Zubat, 46 | Species.Golbat, 47 | Species.Oddish, 48 | Species.Gloom, 49 | Species.Vileplume, 50 | Species.Paras, 51 | Species.Parasect, 52 | Species.Venonat, 53 | Species.Venomoth, 54 | Species.Diglett, 55 | Species.Dugtrio, 56 | Species.Meowth, 57 | Species.Persian, 58 | Species.Psyduck, 59 | Species.Golduck, 60 | Species.Mankey, 61 | Species.Primeape, 62 | Species.Growlithe, 63 | Species.Arcanine, 64 | Species.Poliwag, 65 | Species.Poliwhirl, 66 | Species.Poliwrath, 67 | Species.Abra, 68 | Species.Kadabra, 69 | Species.Alakazam, 70 | Species.Machop, 71 | Species.Machoke, 72 | Species.Machamp, 73 | Species.Bellsprout, 74 | Species.Weepinbell, 75 | Species.Victreebel, 76 | Species.Tentacool, 77 | Species.Tentacruel, 78 | Species.Geodude, 79 | Species.Graveler, 80 | Species.Golem, 81 | Species.Ponyta, 82 | Species.Rapidash, 83 | Species.Slowpoke, 84 | Species.Slowbro, 85 | Species.Magnemite, 86 | Species.Magneton, 87 | Species.Farfetchd, 88 | Species.Doduo, 89 | Species.Dodrio, 90 | Species.Seel, 91 | Species.Dewgong, 92 | Species.Grimer, 93 | Species.Muk, 94 | Species.Shellder, 95 | Species.Cloyster, 96 | Species.Gastly, 97 | Species.Haunter, 98 | Species.Gengar, 99 | Species.Onix, 100 | Species.Drowzee, 101 | Species.Hypno, 102 | Species.Krabby, 103 | Species.Kingler, 104 | Species.Voltorb, 105 | Species.Electrode, 106 | Species.Exeggcute, 107 | Species.Exeggutor, 108 | Species.Cubone, 109 | Species.Marowak, 110 | Species.Hitmonlee, 111 | Species.Hitmonchan, 112 | Species.Lickitung, 113 | Species.Koffing, 114 | Species.Weezing, 115 | Species.Rhyhorn, 116 | Species.Rhydon, 117 | Species.Chansey, 118 | Species.Tangela, 119 | Species.Kangaskhan, 120 | Species.Horsea, 121 | Species.Seadra, 122 | Species.Goldeen, 123 | Species.Seaking, 124 | Species.Staryu, 125 | Species.Starmie, 126 | Species.Mr_Mime, 127 | Species.Scyther, 128 | Species.Jynx, 129 | Species.Electabuzz, 130 | Species.Magmar, 131 | Species.Pinsir, 132 | Species.Tauros, 133 | Species.Magikarp, 134 | Species.Gyarados, 135 | Species.Lapras, 136 | Species.Ditto, 137 | Species.Eevee, 138 | Species.Vaporeon, 139 | Species.Jolteon, 140 | Species.Flareon, 141 | Species.Porygon, 142 | Species.Omanyte, 143 | Species.Omastar, 144 | Species.Kabuto, 145 | Species.Kabutops, 146 | Species.Aerodactyl, 147 | Species.Snorlax, 148 | Species.Articuno, 149 | Species.Zapdos, 150 | Species.Moltres, 151 | Species.Dratini, 152 | Species.Dragonair, 153 | Species.Dragonite, 154 | Species.Mewtwo, 155 | Species.Mew 156 | ] 157 | -------------------------------------------------------------------------------- /poketypes/tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | 4 | from . import encoding, loadGame, item 5 | from .gamesave import GameSave 6 | from .basic import PokeStructure 7 | from .pokemon import Pokemon, Species, experience 8 | 9 | class TestEncoding(unittest.TestCase): 10 | 11 | def test_encode(self): 12 | self.assertEqual(encoding.encode("Test"), b"\x93\xa4\xb2\xb3") 13 | 14 | def test_decode(self): 15 | self.assertEqual(encoding.decode(b"\x93\xa4\xb2\xb3"), "Test") 16 | 17 | def test_encode_decode(self): 18 | import string 19 | alphanum = string.ascii_letters + string.digits 20 | self.assertEqual(encoding.decode(encoding.encode(alphanum)), alphanum) 21 | 22 | class TestPokeStructure(unittest.TestCase): 23 | 24 | def setUp(self): 25 | 26 | import enum, ctypes 27 | 28 | class Fruit(enum.Enum): 29 | Apple, Banana, Citron = range(3) 30 | 31 | class TestStruct(PokeStructure): 32 | 33 | _fields_ = [ 34 | ("_fruit", ctypes.c_uint8), 35 | ] 36 | 37 | _adapters_ = [ 38 | ("_fruit", Fruit) 39 | ] 40 | 41 | self.Fruit = Fruit 42 | self.TestStruct = TestStruct 43 | 44 | def test_enum_property(self): 45 | obj = self.TestStruct() 46 | self.assertIsInstance(obj.fruit, self.Fruit) 47 | 48 | obj.fruit = self.Fruit.Apple 49 | self.assertEqual(obj.fruit, self.Fruit.Apple) 50 | self.assertEqual(obj._fruit, self.Fruit.Apple.value) 51 | 52 | obj.fruit = self.Fruit.Banana.value 53 | self.assertEqual(obj.fruit.name, "Banana") 54 | 55 | def test_item_enum(self): 56 | pokeball = item.Item(item.Index.PokeBall.value, 1) 57 | self.assertEqual(pokeball.index, item.Index.PokeBall) 58 | 59 | class TestPokemon(unittest.TestCase): 60 | 61 | def test_xp_to_level_conversion(self): 62 | for xpClass in experience.ExperienceClass: 63 | for level in range(2, 101): 64 | self.assertEqual(level, experience.level_for_exp[xpClass](experience.exp_for_level[xpClass](level))) 65 | 66 | class TestSpecies(unittest.TestCase): 67 | 68 | def test_normal_name(self): 69 | self.assertEqual(Species.Mew.name, "Mew") 70 | self.assertEqual(Species["Mew"], Species.Mew) 71 | 72 | def test_special_name(self): 73 | self.assertEqual(Species.Mr_Mime.name, "Mr. Mime") 74 | self.assertEqual(Species["Mr. Mime"], Species.Mr_Mime) 75 | 76 | def test_stats(self): 77 | """According to example 1 from http://bulbapedia.bulbagarden.net/wiki/Individual_values#Usage""" 78 | mewtwo = Pokemon() 79 | mewtwo.species = Species.Mewtwo 80 | mewtwo.level = 70 81 | mewtwo.iv = 0b1110010110000110 82 | mewtwo.sanitize() 83 | self.assertEqual(mewtwo.max_hp, 234) 84 | self.assertEqual(mewtwo.attack, 178) 85 | self.assertEqual(mewtwo.defense, 138) 86 | self.assertEqual(mewtwo.speed, 198) 87 | self.assertEqual(mewtwo.special, 229) 88 | 89 | class TestGameSave(unittest.TestCase): 90 | 91 | batterySaveFileName = os.path.join(os.path.dirname(__file__), "testdata.sav") 92 | 93 | def setUp(self): 94 | self.save = loadGame(self.batterySaveFileName) 95 | 96 | def test_player_name(self): 97 | self.assertEqual(self.save.player_name.toString(), "Player") 98 | 99 | def test_rival_name(self): 100 | self.assertEqual(self.save.rival_name.toString(), "Rival") 101 | 102 | def test_calc_checksum(self): 103 | self.assertEqual(self.save._calcChecksum(), self.save.checksum) 104 | 105 | def test_item_list(self): 106 | firstItem = self.save.pocket_item_list.entries[0] 107 | self.assertEqual(firstItem.index, item.Index.Potion) 108 | 109 | def test_second_pokemon_in_team(self): 110 | pokemon, ot, nickname = self.save.team[1] 111 | self.assertEqual(pokemon.species.value, self.save.team.species_list[1]) 112 | self.assertEqual(pokemon.species, Species.Rattata) 113 | self.assertEqual(ot, "Trainer") 114 | self.assertEqual(nickname, "SQUEAK") 115 | 116 | if __name__ == "__main__": 117 | unittest.main() 118 | 119 | -------------------------------------------------------------------------------- /pokecreator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import argparse 5 | import random 6 | 7 | import poketypes 8 | 9 | _s = lambda s: bytes(s, "ascii") 10 | 11 | output_format = { 12 | "binary": lambda data: data, 13 | "hex": lambda data: _s("".join("%02x" % c for c in data) + "\n"), 14 | "c_array": lambda data: _s(", ".join("0x%02X" % c for c in data) + "\n"), 15 | "python": lambda data: _s(repr(poketypes.Pokemon.fromBytes(data)) + "\n") 16 | } 17 | 18 | class FindSpeciesInPokedex(argparse.Action): 19 | 20 | def __call__(self, parser, namespace, values, option_string=None): 21 | species = poketypes.pokedex.national[values] 22 | setattr(namespace, "species", species) 23 | 24 | class FindSpeciesByName(argparse.Action): 25 | 26 | def __call__(self, parser, namespace, values, option_string=None): 27 | species = poketypes.pokemon.Species[values] 28 | setattr(namespace, "species", species) 29 | 30 | class StoreMove(argparse.Action): 31 | 32 | def __call__(self, parser, namespace, values, option_string=None): 33 | if len(values) > 4: 34 | raise argparse.ArgumentTypeError("Max 4 moves allowed") 35 | moves = [poketypes.pokemon.Move[name] for name in values] 36 | moves += [poketypes.pokemon.Move.NONE] * (4 - len(moves)) 37 | setattr(namespace, "moves", moves) 38 | 39 | class StorePPUps(argparse.Action): 40 | def __call__(self, parser, namespace, values, option_string=None): 41 | if len(values) > 4: 42 | raise argparse.ArgumentTypeError("PP-ups for max 4 moves allowed") 43 | setattr(namespace, "pp_ups", values + [0] * (4 - len(values))) 44 | 45 | 46 | def parseArguments(): 47 | parser = argparse.ArgumentParser( 48 | description="Generate a pokemon that can be send e.g. to the PokeDuino.", 49 | ) 50 | 51 | group = parser.add_mutually_exclusive_group(required=True) 52 | group.add_argument("--species", "-s", type=str, 53 | action=FindSpeciesByName, 54 | help="Species as a string.") 55 | group.add_argument("--pokedex", "-p", type=int, 56 | action=FindSpeciesInPokedex, 57 | help="Index number ") 58 | parser.add_argument("--level", "-l", type=int, 59 | default=5) 60 | parser.add_argument("--original-trainer", "-o", type=int, default=0) 61 | 62 | parser.add_argument("--moves", "-m", type=str, 63 | default=[poketypes.pokemon.Move.NONE] * 4, 64 | nargs='+', 65 | action=StoreMove) 66 | parser.add_argument("--pp-ups", "-pu", type=int, 67 | choices=range(4), 68 | nargs="+", 69 | default=[0] * 4, 70 | action=StorePPUps) 71 | 72 | parser.add_argument("--health-ev", "-hv", type=int, default=0) 73 | parser.add_argument("--attack-ev", "-av", type=int, default=0) 74 | parser.add_argument("--defense-ev", "-dv", type=int, default=0) 75 | parser.add_argument("--speed-ev", "-sv", type=int, default=0) 76 | parser.add_argument("--special-ev", "-cv", type=int, default=0) 77 | 78 | parser.add_argument("--individual-values", "-iv", type=lambda s: 0xffff if s == "max" else int(s, 16), 79 | default=int(random.random() * 0x10000), 80 | help="Hex representation of the pokemon's 16bit individual values vector or \"max\". Random value if not specified." 81 | ) 82 | 83 | parser.add_argument("--output-format", "-f", 84 | choices=output_format.keys(), 85 | default="binary" 86 | ) 87 | try: 88 | return parser.parse_args() 89 | except argparse.ArgumentTypeError as err: 90 | parser.error(err) 91 | 92 | def createPokemon(args): 93 | pokemon = poketypes.Pokemon( 94 | species=args.species, 95 | level=args.level, 96 | hp_ev=args.health_ev, 97 | attack_ev=args.attack_ev, 98 | defense_ev=args.defense_ev, 99 | speed_ev=args.speed_ev, 100 | special_ev=args.special_ev, 101 | iv=args.individual_values, 102 | original_trainer=args.original_trainer 103 | ) 104 | pokemon.move1, pokemon.move2, pokemon.move3, pokemon.move4 = args.moves 105 | pokemon.move1_pp.up, pokemon.move2_pp.up, pokemon.move3_pp.up, pokemon.move4_pp.up = args.pp_ups 106 | pokemon.heal() 107 | return pokemon 108 | 109 | if __name__ == "__main__": 110 | args = parseArguments() 111 | pokemon = createPokemon(args) 112 | sys.stdout.buffer.write(output_format[args.output_format](pokemon.bytes())) 113 | -------------------------------------------------------------------------------- /poketypes/pokemon/species.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import enum 4 | from types import DynamicClassAttribute 5 | 6 | __all__ = ["Species"] 7 | 8 | _special_names = None 9 | _special_names_rev = None 10 | 11 | def special_names(): 12 | global _special_names 13 | 14 | if _special_names is None: 15 | _special_names = { 16 | "Nidoran♀": Species.Nidoran_f, 17 | "Nidoran♂": Species.Nidoran_m, 18 | "Farfetch'd": Species.Farfetchd, 19 | "Mr. Mime": Species.Mr_Mime 20 | } 21 | return _special_names 22 | 23 | def special_names_rev(): 24 | global _special_names_rev 25 | if _special_names_rev is None: 26 | _special_names_rev = {v: k for k, v in special_names().items()} 27 | return _special_names_rev 28 | 29 | class SpeciesMeta(type(enum.Enum)): 30 | 31 | def __getitem__(cls, name): 32 | try: 33 | return special_names()[name] 34 | except KeyError: 35 | return super().__getitem__(name) 36 | 37 | class Species(enum.Enum, metaclass=SpeciesMeta): 38 | MissingNo_00, \ 39 | Rhydon, \ 40 | Kangaskhan, \ 41 | Nidoran_m, \ 42 | Clefairy, \ 43 | Spearow, \ 44 | Voltorb, \ 45 | Nidoking, \ 46 | Slowbro, \ 47 | Ivysaur, \ 48 | Exeggutor, \ 49 | Lickitung, \ 50 | Exeggcute, \ 51 | Grimer, \ 52 | Gengar, \ 53 | Nidoran_f, \ 54 | Nidoqueen, \ 55 | Cubone, \ 56 | Rhyhorn, \ 57 | Lapras, \ 58 | Arcanine, \ 59 | Mew, \ 60 | Gyarados, \ 61 | Shellder, \ 62 | Tentacool, \ 63 | Gastly, \ 64 | Scyther, \ 65 | Staryu, \ 66 | Blastoise, \ 67 | Pinsir, \ 68 | Tangela, \ 69 | MissingNo_1F, \ 70 | MissingNo_20, \ 71 | Growlithe, \ 72 | Onix, \ 73 | Fearow, \ 74 | Pidgey, \ 75 | Slowpoke, \ 76 | Kadabra, \ 77 | Graveler, \ 78 | Chansey, \ 79 | Machoke, \ 80 | Mr_Mime, \ 81 | Hitmonlee, \ 82 | Hitmonchan, \ 83 | Arbok, \ 84 | Parasect, \ 85 | Psyduck, \ 86 | Drowzee, \ 87 | Golem, \ 88 | MissingNo_32, \ 89 | Magmar, \ 90 | MissingNo_34, \ 91 | Electabuzz, \ 92 | Magneton, \ 93 | Koffing, \ 94 | MissingNo_38, \ 95 | Mankey, \ 96 | Seel, \ 97 | Diglett, \ 98 | Tauros, \ 99 | MissingNo_3D, \ 100 | MissingNo_3E, \ 101 | MissingNo_3F, \ 102 | Farfetchd, \ 103 | Venonat, \ 104 | Dragonite, \ 105 | MissingNo_43, \ 106 | MissingNo_44, \ 107 | MissingNo_45, \ 108 | Doduo, \ 109 | Poliwag, \ 110 | Jynx, \ 111 | Moltres, \ 112 | Articuno, \ 113 | Zapdos, \ 114 | Ditto, \ 115 | Meowth, \ 116 | Krabby, \ 117 | MissingNo_4F, \ 118 | MissingNo_50, \ 119 | MissingNo_51, \ 120 | Vulpix, \ 121 | Ninetales, \ 122 | Pikachu, \ 123 | Raichu, \ 124 | MissingNo_56, \ 125 | MissingNo_57, \ 126 | Dratini, \ 127 | Dragonair, \ 128 | Kabuto, \ 129 | Kabutops, \ 130 | Horsea, \ 131 | Seadra, \ 132 | MissingNo_5E, \ 133 | MissingNo_5F, \ 134 | Sandshrew, \ 135 | Sandslash, \ 136 | Omanyte, \ 137 | Omastar, \ 138 | Jigglypuff, \ 139 | Wigglytuff, \ 140 | Eevee, \ 141 | Flareon, \ 142 | Jolteon, \ 143 | Vaporeon, \ 144 | Machop, \ 145 | Zubat, \ 146 | Ekans, \ 147 | Paras, \ 148 | Poliwhirl, \ 149 | Poliwrath, \ 150 | Weedle, \ 151 | Kakuna, \ 152 | Beedrill, \ 153 | MissingNo_73, \ 154 | Dodrio, \ 155 | Primeape, \ 156 | Dugtrio, \ 157 | Venomoth, \ 158 | Dewgong, \ 159 | MissingNo_79, \ 160 | MissingNo_7A, \ 161 | Caterpie, \ 162 | Metapod, \ 163 | Butterfree, \ 164 | Machamp, \ 165 | MissingNo_7F, \ 166 | Golduck, \ 167 | Hypno, \ 168 | Golbat, \ 169 | Mewtwo, \ 170 | Snorlax, \ 171 | Magikarp, \ 172 | MissingNo_86, \ 173 | MissingNo_87, \ 174 | Muk, \ 175 | MissingNo_89, \ 176 | Kingler, \ 177 | Cloyster, \ 178 | MissingNo_8C, \ 179 | Electrode, \ 180 | Clefable, \ 181 | Weezing, \ 182 | Persian, \ 183 | Marowak, \ 184 | MissingNo_92, \ 185 | Haunter, \ 186 | Abra, \ 187 | Alakazam, \ 188 | Pidgeotto, \ 189 | Pidgeot, \ 190 | Starmie, \ 191 | Bulbasaur, \ 192 | Venusaur, \ 193 | Tentacruel, \ 194 | MissingNo_9C, \ 195 | Goldeen, \ 196 | Seaking, \ 197 | MissingNo_9F, \ 198 | MissingNo_A0, \ 199 | MissingNo_A1, \ 200 | MissingNo_A2, \ 201 | Ponyta, \ 202 | Rapidash, \ 203 | Rattata, \ 204 | Raticate, \ 205 | Nidorino, \ 206 | Nidorina, \ 207 | Geodude, \ 208 | Porygon, \ 209 | Aerodactyl, \ 210 | MissingNo_AC, \ 211 | Magnemite, \ 212 | MissingNo_AE, \ 213 | MissingNo_AF, \ 214 | Charmander, \ 215 | Squirtle, \ 216 | Charmeleon, \ 217 | Wartortle, \ 218 | Charizard, \ 219 | MissingNo_B5, \ 220 | MissingNo_B6, \ 221 | MissingNo_B7, \ 222 | MissingNo_B8, \ 223 | Oddish, \ 224 | Gloom, \ 225 | Vileplume, \ 226 | Bellsprout, \ 227 | Weepinbell, \ 228 | Victreebel = range(191) 229 | 230 | @DynamicClassAttribute 231 | def name(self): 232 | try: 233 | return special_names_rev()[self] 234 | except KeyError: 235 | return super().name 236 | -------------------------------------------------------------------------------- /poketypes/pokemon/__init__.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import math 3 | 4 | from ..basic import * 5 | from .species import Species 6 | from .moves import Move 7 | from .types import Type 8 | from .status import StatusField 9 | 10 | from . import experience 11 | from . import basestats 12 | from . import moves 13 | 14 | __all__ = ["Pokemon"] 15 | 16 | class IV(object): 17 | 18 | """Individual values (read-only) according to 19 | http://bulbapedia.bulbagarden.net/wiki/Individual_values#Generation_I_and_II""" 20 | 21 | def __init__(self, uint16): 22 | self._attack = (uint16 >> 12) & 0xF 23 | self._defense = (uint16 >> 8) & 0xF 24 | self._speed = (uint16 >> 4) & 0xF 25 | self._special = uint16 & 0xF 26 | self._hp = (self.attack & 1) << 3 | (self.defense & 1) << 2 | (self.speed & 1) << 1 | (self.special & 1) 27 | 28 | @property 29 | def attack(self): 30 | return self._attack 31 | 32 | @property 33 | def defense(self): 34 | return self._defense 35 | 36 | @property 37 | def speed(self): 38 | return self._speed 39 | 40 | @property 41 | def special(self): 42 | return self._special 43 | 44 | @property 45 | def hp(self): 46 | return self._hp 47 | 48 | @property 49 | def value(self): 50 | return self.attack << 12 | self.defense << 8 | self.speed << 4 | self.special 51 | 52 | def __int__(self): 53 | return self.value 54 | 55 | def __str__(self): 56 | return "%s(attack: %d, defense: %d, speed: %d, special: %d, hp: %d)" % \ 57 | (self.__class__.__name__, self.attack, self.defense, self.speed, self.special, self.hp) 58 | 59 | class PP(PokeStructure): 60 | 61 | _fields_ = [ 62 | ("up", ctypes.c_uint8, 2), 63 | ("pp", ctypes.c_uint8, 6) 64 | ] 65 | 66 | class PokemonGenI(PokeStructure): 67 | 68 | """Pokemon data structure of Generation I according to 69 | http://bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_data_structure_in_Generation_I""" 70 | 71 | _fields_ = [ 72 | ("_species", ctypes.c_uint8), 73 | ("hp", ctypes.c_uint16), 74 | ("level0", ctypes.c_uint8), 75 | ("_status", ctypes.c_uint8), 76 | ("_type1", ctypes.c_uint8), 77 | ("_type2", ctypes.c_uint8), 78 | ("catch_rate", ctypes.c_uint8), 79 | ("_move1", ctypes.c_uint8), 80 | ("_move2", ctypes.c_uint8), 81 | ("_move3", ctypes.c_uint8), 82 | ("_move4", ctypes.c_uint8), 83 | ("original_trainer", ctypes.c_uint16), 84 | ("_xp", Pokearray(3)), 85 | ("hp_ev", ctypes.c_uint16), 86 | ("attack_ev", ctypes.c_uint16), 87 | ("defense_ev", ctypes.c_uint16), 88 | ("speed_ev", ctypes.c_uint16), 89 | ("special_ev", ctypes.c_uint16), 90 | ("_iv", ctypes.c_uint16), 91 | ("move1_pp", PP), 92 | ("move2_pp", PP), 93 | ("move3_pp", PP), 94 | ("move4_pp", PP), 95 | ("level", ctypes.c_uint8), 96 | ("max_hp", ctypes.c_uint16), 97 | ("attack", ctypes.c_uint16), 98 | ("defense", ctypes.c_uint16), 99 | ("speed", ctypes.c_uint16), 100 | ("special", ctypes.c_uint16) 101 | ] 102 | 103 | _adapters_ = [ 104 | ("_species", Species), 105 | ("_status", StatusField), 106 | ("_type1", Type), 107 | ("_type2", Type), 108 | ("_move1", Move), 109 | ("_move2", Move), 110 | ("_move3", Move), 111 | ("_move4", Move), 112 | ("_iv", IV) 113 | ] 114 | 115 | @property 116 | def xp(self): 117 | return struct.unpack(">I", b'\0' + self._xp.bytes())[0] 118 | 119 | @xp.setter 120 | def xp(self, value): 121 | self._xp = Pokearray(3).fromBytes(struct.pack(">I", value)[1:]) 122 | 123 | #################### 124 | 125 | def __repr__(self): 126 | 127 | def field_str(attr): 128 | return "%s=%d" % (attr, getattr(self, attr)) 129 | 130 | return "%s.%s(species=%s.%s, %s)" % (__name__, self.__class__.__name__, __name__, self.species, ", ".join(field_str(a) for a in ("level", "hp_ev", "attack_ev", "defense_ev", "speed_ev", "special_ev", "iv", "original_trainer"))) 131 | 132 | #################### 133 | 134 | @property 135 | def basestats(self): 136 | return basestats.basestats_from_species[self.species] 137 | 138 | def _calc_stat_main_term(self, base, iv, ev): 139 | return int(((base + iv) * 2 + int(math.sqrt(ev) / 4)) * self.level / 100.0) 140 | 141 | def _calc_hp(self): 142 | return self._calc_stat_main_term(self.basestats.hp, self.iv.hp, self.hp_ev) + self.level + 10 143 | 144 | def _calc_stat(self, stat_name): 145 | return self._calc_stat_main_term(getattr(self.basestats, stat_name), getattr(self.iv, stat_name), getattr(self, stat_name + "_ev")) + 5 146 | 147 | #################### 148 | 149 | def sanitize_xp(self): 150 | """Adjust xp according to level""" 151 | xpClass = experience.class_for_species[self.species] 152 | if self.level != experience.level_for_exp[xpClass](self.xp): 153 | self.xp = experience.exp_for_level[xpClass](self.level) 154 | 155 | def sanitize_types(self): 156 | self.type1, self.type2 = types.type_for_species[self.species] 157 | 158 | def sanitize_hp(self): 159 | self.max_hp = self._calc_hp() 160 | self.hp = min(self.hp, self.max_hp) 161 | 162 | def sanitize_stats(self): 163 | self.attack = self._calc_stat("attack") 164 | self.defense = self._calc_stat("defense") 165 | self.speed = self._calc_stat("speed") 166 | self.special = self._calc_stat("special") 167 | 168 | def sanitize_pp(self): 169 | self.move1_pp.pp = moves.pp_for_move[self.move1] 170 | self.move2_pp.pp = moves.pp_for_move[self.move2] 171 | self.move3_pp.pp = moves.pp_for_move[self.move3] 172 | self.move4_pp.pp = moves.pp_for_move[self.move4] 173 | 174 | def sanitize(self): 175 | """Adjust xp, types, hp, stats, pp to the values the game 176 | had calculated from species, level and moves""" 177 | self.sanitize_xp() 178 | self.sanitize_types() 179 | self.sanitize_hp() 180 | self.sanitize_stats() 181 | self.sanitize_pp() 182 | self.level0 = self.level 183 | 184 | def heal(self): 185 | """Pokecenter! (and sanitize)""" 186 | self.sanitize() 187 | self.hp = self.max_hp 188 | self.status = 0 189 | 190 | 191 | 192 | Pokemon = PokemonGenI 193 | -------------------------------------------------------------------------------- /poketypes/pokemon/moves.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | class Move(enum.Enum): 4 | NONE, \ 5 | Pound, \ 6 | KarateChop, \ 7 | DoubleSlap, \ 8 | CometPunch, \ 9 | MegaPunch, \ 10 | PayDay, \ 11 | FirePunch, \ 12 | IcePunch, \ 13 | ThunderPunch, \ 14 | Scratch, \ 15 | ViceGrip, \ 16 | Guillotine, \ 17 | RazorWind, \ 18 | SwordsDance, \ 19 | Cut, \ 20 | Gust, \ 21 | WingAttack, \ 22 | Whirlwind, \ 23 | Fly, \ 24 | Bind, \ 25 | Slam, \ 26 | VineWhip, \ 27 | Stomp, \ 28 | DoubleKick, \ 29 | MegaKick, \ 30 | JumpKick, \ 31 | RollingKick, \ 32 | SandAttack, \ 33 | Headbutt, \ 34 | HornAttack, \ 35 | FuryAttack, \ 36 | HornDrill, \ 37 | Tackle, \ 38 | BodySlam, \ 39 | Wrap, \ 40 | TakeDown, \ 41 | Thrash, \ 42 | DoubleEdge, \ 43 | TailWhip, \ 44 | PoisonSting, \ 45 | Twineedle, \ 46 | PinMissile, \ 47 | Leer, \ 48 | Bite, \ 49 | Growl, \ 50 | Roar, \ 51 | Sing, \ 52 | Supersonic, \ 53 | SonicBoom, \ 54 | Disable, \ 55 | Acid, \ 56 | Ember, \ 57 | Flamethrower, \ 58 | Mist, \ 59 | WaterGun, \ 60 | HydroPump, \ 61 | Surf, \ 62 | IceBeam, \ 63 | Blizzard, \ 64 | Psybeam, \ 65 | BubbleBeam, \ 66 | AuroraBeam, \ 67 | HyperBeam, \ 68 | Peck, \ 69 | DrillPeck, \ 70 | Submission, \ 71 | LowKick, \ 72 | Counter, \ 73 | SeismicToss, \ 74 | Strength, \ 75 | Absorb, \ 76 | MegaDrain, \ 77 | LeechSeed, \ 78 | Growth, \ 79 | RazorLeaf, \ 80 | SolarBeam, \ 81 | PoisonPowder, \ 82 | StunSpore, \ 83 | SleepPowder, \ 84 | PetalDance, \ 85 | StringShot, \ 86 | DragonRage, \ 87 | FireSpin, \ 88 | ThunderShock, \ 89 | Thunderbolt, \ 90 | ThunderWave, \ 91 | Thunder, \ 92 | RockThrow, \ 93 | Earthquake, \ 94 | Fissure, \ 95 | Dig, \ 96 | Toxic, \ 97 | Confusion, \ 98 | Psychic, \ 99 | Hypnosis, \ 100 | Meditate, \ 101 | Agility, \ 102 | QuickAttack, \ 103 | Rage, \ 104 | Teleport, \ 105 | NightShade, \ 106 | Mimic, \ 107 | Screech, \ 108 | DoubleTeam, \ 109 | Recover, \ 110 | Harden, \ 111 | Minimize, \ 112 | Smokescreen, \ 113 | ConfuseRay, \ 114 | Withdraw, \ 115 | DefenseCurl, \ 116 | Barrier, \ 117 | LightScreen, \ 118 | Haze, \ 119 | Reflect, \ 120 | FocusEnergy, \ 121 | Bide, \ 122 | Metronome, \ 123 | MirrorMove, \ 124 | SelfDestruct, \ 125 | EggBomb, \ 126 | Lick, \ 127 | Smog, \ 128 | Sludge, \ 129 | BoneClub, \ 130 | FireBlast, \ 131 | Waterfall, \ 132 | Clamp, \ 133 | Swift, \ 134 | SkullBash, \ 135 | SpikeCannon, \ 136 | Constrict, \ 137 | Amnesia, \ 138 | Kinesis, \ 139 | SoftBoiled, \ 140 | HighJumpKick, \ 141 | Glare, \ 142 | DreamEater, \ 143 | PoisonGas, \ 144 | Barrage, \ 145 | LeechLife, \ 146 | LovelyKiss, \ 147 | SkyAttack, \ 148 | Transform, \ 149 | Bubble, \ 150 | DizzyPunch, \ 151 | Spore, \ 152 | Flash, \ 153 | Psywave, \ 154 | Splash, \ 155 | AcidArmor, \ 156 | Crabhammer, \ 157 | Explosion, \ 158 | FurySwipes, \ 159 | Bonemerang, \ 160 | Rest, \ 161 | RockSlide, \ 162 | HyperFang, \ 163 | Sharpen, \ 164 | Conversion, \ 165 | TriAttack, \ 166 | SuperFang, \ 167 | Slash, \ 168 | Substitute, \ 169 | Struggle = range(166) 170 | 171 | pp_for_move = { 172 | Move.NONE: 0, 173 | Move.Pound: 35, 174 | Move.KarateChop: 25, 175 | Move.DoubleSlap: 10, 176 | Move.CometPunch: 15, 177 | Move.MegaPunch: 20, 178 | Move.PayDay: 20, 179 | Move.FirePunch: 15, 180 | Move.IcePunch: 15, 181 | Move.ThunderPunch: 15, 182 | Move.Scratch: 35, 183 | Move.ViceGrip: 30, 184 | Move.Guillotine: 5, 185 | Move.RazorWind: 10, 186 | Move.SwordsDance: 30, 187 | Move.Cut: 30, 188 | Move.Gust: 35, 189 | Move.WingAttack: 35, 190 | Move.Whirlwind: 20, 191 | Move.Fly: 15, 192 | Move.Bind: 20, 193 | Move.Slam: 20, 194 | Move.VineWhip: 25, 195 | Move.Stomp: 20, 196 | Move.DoubleKick: 30, 197 | Move.MegaKick: 5, 198 | Move.JumpKick: 25, 199 | Move.RollingKick: 15, 200 | Move.SandAttack: 15, 201 | Move.Headbutt: 15, 202 | Move.HornAttack: 25, 203 | Move.FuryAttack: 20, 204 | Move.HornDrill: 5, 205 | Move.Tackle: 35, 206 | Move.BodySlam: 15, 207 | Move.Wrap: 20, 208 | Move.TakeDown: 20, 209 | Move.Thrash: 20, 210 | Move.DoubleEdge: 15, 211 | Move.TailWhip: 30, 212 | Move.PoisonSting: 35, 213 | Move.Twineedle: 20, 214 | Move.PinMissile: 20, 215 | Move.Leer: 30, 216 | Move.Bite: 25, 217 | Move.Growl: 40, 218 | Move.Roar: 20, 219 | Move.Sing: 15, 220 | Move.Supersonic: 20, 221 | Move.SonicBoom: 20, 222 | Move.Disable: 20, 223 | Move.Acid: 30, 224 | Move.Ember: 25, 225 | Move.Flamethrower: 15, 226 | Move.Mist: 30, 227 | Move.WaterGun: 25, 228 | Move.HydroPump: 5, 229 | Move.Surf: 15, 230 | Move.IceBeam: 10, 231 | Move.Blizzard: 5, 232 | Move.Psybeam: 20, 233 | Move.BubbleBeam: 20, 234 | Move.AuroraBeam: 20, 235 | Move.HyperBeam: 5, 236 | Move.Peck: 35, 237 | Move.DrillPeck: 20, 238 | Move.Submission: 25, 239 | Move.LowKick: 20, 240 | Move.Counter: 20, 241 | Move.SeismicToss: 20, 242 | Move.Strength: 15, 243 | Move.Absorb: 20, 244 | Move.MegaDrain: 10, 245 | Move.LeechSeed: 10, 246 | Move.Growth: 40, 247 | Move.RazorLeaf: 25, 248 | Move.SolarBeam: 10, 249 | Move.PoisonPowder: 35, 250 | Move.StunSpore: 30, 251 | Move.SleepPowder: 15, 252 | Move.PetalDance: 20, 253 | Move.StringShot: 40, 254 | Move.DragonRage: 10, 255 | Move.FireSpin: 15, 256 | Move.ThunderShock: 30, 257 | Move.Thunderbolt: 15, 258 | Move.ThunderWave: 20, 259 | Move.Thunder: 10, 260 | Move.RockThrow: 15, 261 | Move.Earthquake: 10, 262 | Move.Fissure: 5, 263 | Move.Dig: 10, 264 | Move.Toxic: 10, 265 | Move.Confusion: 25, 266 | Move.Psychic: 10, 267 | Move.Hypnosis: 20, 268 | Move.Meditate: 40, 269 | Move.Agility: 30, 270 | Move.QuickAttack: 30, 271 | Move.Rage: 20, 272 | Move.Teleport: 20, 273 | Move.NightShade: 15, 274 | Move.Mimic: 10, 275 | Move.Screech: 40, 276 | Move.DoubleTeam: 15, 277 | Move.Recover: 20, 278 | Move.Harden: 30, 279 | Move.Minimize: 20, 280 | Move.Smokescreen: 20, 281 | Move.ConfuseRay: 10, 282 | Move.Withdraw: 40, 283 | Move.DefenseCurl: 40, 284 | Move.Barrier: 30, 285 | Move.LightScreen: 30, 286 | Move.Haze: 30, 287 | Move.Reflect: 20, 288 | Move.FocusEnergy: 30, 289 | Move.Bide: 10, 290 | Move.Metronome: 10, 291 | Move.MirrorMove: 20, 292 | Move.SelfDestruct: 5, 293 | Move.EggBomb: 10, 294 | Move.Lick: 30, 295 | Move.Smog: 20, 296 | Move.Sludge: 20, 297 | Move.BoneClub: 20, 298 | Move.FireBlast: 5, 299 | Move.Waterfall: 15, 300 | Move.Clamp: 10, 301 | Move.Swift: 20, 302 | Move.SkullBash: 15, 303 | Move.SpikeCannon: 15, 304 | Move.Constrict: 35, 305 | Move.Amnesia: 20, 306 | Move.Kinesis: 15, 307 | Move.SoftBoiled: 10, 308 | Move.HighJumpKick: 20, 309 | Move.Glare: 30, 310 | Move.DreamEater: 15, 311 | Move.PoisonGas: 40, 312 | Move.Barrage: 20, 313 | Move.LeechLife: 15, 314 | Move.LovelyKiss: 10, 315 | Move.SkyAttack: 5, 316 | Move.Transform: 10, 317 | Move.Bubble: 30, 318 | Move.DizzyPunch: 10, 319 | Move.Spore: 15, 320 | Move.Flash: 20, 321 | Move.Psywave: 15, 322 | Move.Splash: 40, 323 | Move.AcidArmor: 40, 324 | Move.Crabhammer: 10, 325 | Move.Explosion: 5, 326 | Move.FurySwipes: 15, 327 | Move.Bonemerang: 10, 328 | Move.Rest: 10, 329 | Move.RockSlide: 10, 330 | Move.HyperFang: 15, 331 | Move.Sharpen: 30, 332 | Move.Conversion: 30, 333 | Move.TriAttack: 10, 334 | Move.SuperFang: 10, 335 | Move.Slash: 20, 336 | Move.Substitute: 10, 337 | Move.Struggle: 10 338 | } 339 | -------------------------------------------------------------------------------- /poketypes/pokemon/types.py: -------------------------------------------------------------------------------- 1 | import enum 2 | from .species import Species 3 | 4 | class Type(enum.Enum): 5 | Normal = 0x00 6 | Fighting = 0x01 7 | Flying = 0x02 8 | Poison = 0x03 9 | Ground = 0x04 10 | Rock = 0x05 11 | Bug = 0x07 12 | Ghost = 0x08 13 | Fire = 0x14 14 | Water = 0x15 15 | Grass = 0x16 16 | Electric = 0x17 17 | Psychic = 0x18 18 | Ice = 0x19 19 | Dragon = 0x1A 20 | 21 | type_for_species = { 22 | Species.Bulbasaur: (Type.Grass, Type.Poison), 23 | Species.Ivysaur: (Type.Grass, Type.Poison), 24 | Species.Venusaur: (Type.Grass, Type.Poison), 25 | Species.Charmander: (Type.Fire, Type.Fire), 26 | Species.Charmeleon: (Type.Fire, Type.Fire), 27 | Species.Charizard: (Type.Fire, Type.Flying), 28 | Species.Squirtle: (Type.Water, Type.Water), 29 | Species.Wartortle: (Type.Water, Type.Water), 30 | Species.Blastoise: (Type.Water, Type.Water), 31 | Species.Caterpie: (Type.Bug, Type.Bug), 32 | Species.Metapod: (Type.Bug, Type.Bug), 33 | Species.Butterfree: (Type.Bug, Type.Flying), 34 | Species.Weedle: (Type.Bug, Type.Poison), 35 | Species.Kakuna: (Type.Bug, Type.Poison), 36 | Species.Beedrill: (Type.Bug, Type.Poison), 37 | Species.Pidgey: (Type.Normal, Type.Flying), 38 | Species.Pidgeotto: (Type.Normal, Type.Flying), 39 | Species.Pidgeot: (Type.Normal, Type.Flying), 40 | Species.Rattata: (Type.Normal, Type.Normal), 41 | Species.Raticate: (Type.Normal, Type.Normal), 42 | Species.Spearow: (Type.Normal, Type.Flying), 43 | Species.Fearow: (Type.Normal, Type.Flying), 44 | Species.Ekans: (Type.Poison, Type.Poison), 45 | Species.Arbok: (Type.Poison, Type.Poison), 46 | Species.Pikachu: (Type.Electric, Type.Electric), 47 | Species.Raichu: (Type.Electric, Type.Electric), 48 | Species.Sandshrew: (Type.Ground, Type.Ground), 49 | Species.Sandslash: (Type.Ground, Type.Ground), 50 | Species.Nidoran_f: (Type.Poison, Type.Poison), 51 | Species.Nidorina: (Type.Poison, Type.Poison), 52 | Species.Nidoqueen: (Type.Poison, Type.Ground), 53 | Species.Nidoran_m: (Type.Poison, Type.Poison), 54 | Species.Nidorino: (Type.Poison, Type.Poison), 55 | Species.Nidoking: (Type.Poison, Type.Ground), 56 | Species.Clefairy: (Type.Normal, Type.Normal), 57 | Species.Clefable: (Type.Normal, Type.Normal), 58 | Species.Vulpix: (Type.Fire, Type.Fire), 59 | Species.Ninetales: (Type.Fire, Type.Fire), 60 | Species.Jigglypuff: (Type.Normal, Type.Normal), 61 | Species.Wigglytuff: (Type.Normal, Type.Normal), 62 | Species.Zubat: (Type.Poison, Type.Flying), 63 | Species.Golbat: (Type.Poison, Type.Flying), 64 | Species.Oddish: (Type.Grass, Type.Poison), 65 | Species.Gloom: (Type.Grass, Type.Poison), 66 | Species.Vileplume: (Type.Grass, Type.Poison), 67 | Species.Paras: (Type.Bug, Type.Grass), 68 | Species.Parasect: (Type.Bug, Type.Grass), 69 | Species.Venonat: (Type.Bug, Type.Poison), 70 | Species.Venomoth: (Type.Bug, Type.Poison), 71 | Species.Diglett: (Type.Ground, Type.Ground), 72 | Species.Dugtrio: (Type.Ground, Type.Ground), 73 | Species.Meowth: (Type.Normal, Type.Normal), 74 | Species.Persian: (Type.Normal, Type.Normal), 75 | Species.Psyduck: (Type.Water, Type.Water), 76 | Species.Golduck: (Type.Water, Type.Water), 77 | Species.Mankey: (Type.Fighting, Type.Fighting), 78 | Species.Primeape: (Type.Fighting, Type.Fighting), 79 | Species.Growlithe: (Type.Fire, Type.Fire), 80 | Species.Arcanine: (Type.Fire, Type.Fire), 81 | Species.Poliwag: (Type.Water, Type.Water), 82 | Species.Poliwhirl: (Type.Water, Type.Water), 83 | Species.Poliwrath: (Type.Water, Type.Fighting), 84 | Species.Abra: (Type.Psychic, Type.Psychic), 85 | Species.Kadabra: (Type.Psychic, Type.Psychic), 86 | Species.Alakazam: (Type.Psychic, Type.Psychic), 87 | Species.Machop: (Type.Fighting, Type.Fighting), 88 | Species.Machoke: (Type.Fighting, Type.Fighting), 89 | Species.Machamp: (Type.Fighting, Type.Fighting), 90 | Species.Bellsprout: (Type.Grass, Type.Poison), 91 | Species.Weepinbell: (Type.Grass, Type.Poison), 92 | Species.Victreebel: (Type.Grass, Type.Poison), 93 | Species.Tentacool: (Type.Water, Type.Poison), 94 | Species.Tentacruel: (Type.Water, Type.Poison), 95 | Species.Geodude: (Type.Rock, Type.Ground), 96 | Species.Graveler: (Type.Rock, Type.Ground), 97 | Species.Golem: (Type.Rock, Type.Ground), 98 | Species.Ponyta: (Type.Fire, Type.Fire), 99 | Species.Rapidash: (Type.Fire, Type.Fire), 100 | Species.Slowpoke: (Type.Water, Type.Psychic), 101 | Species.Slowbro: (Type.Water, Type.Psychic), 102 | Species.Magnemite: (Type.Electric, Type.Electric), 103 | Species.Magneton: (Type.Electric, Type.Electric), 104 | Species.Farfetchd: (Type.Normal, Type.Flying), 105 | Species.Doduo: (Type.Normal, Type.Flying), 106 | Species.Dodrio: (Type.Normal, Type.Flying), 107 | Species.Seel: (Type.Water, Type.Water), 108 | Species.Dewgong: (Type.Water, Type.Ice), 109 | Species.Grimer: (Type.Poison, Type.Poison), 110 | Species.Muk: (Type.Poison, Type.Poison), 111 | Species.Shellder: (Type.Water, Type.Water), 112 | Species.Cloyster: (Type.Water, Type.Ice), 113 | Species.Gastly: (Type.Ghost, Type.Poison), 114 | Species.Haunter: (Type.Ghost, Type.Poison), 115 | Species.Gengar: (Type.Ghost, Type.Poison), 116 | Species.Onix: (Type.Rock, Type.Ground), 117 | Species.Drowzee: (Type.Psychic, Type.Psychic), 118 | Species.Hypno: (Type.Psychic, Type.Psychic), 119 | Species.Krabby: (Type.Water, Type.Water), 120 | Species.Kingler: (Type.Water, Type.Water), 121 | Species.Voltorb: (Type.Electric, Type.Electric), 122 | Species.Electrode: (Type.Electric, Type.Electric), 123 | Species.Exeggcute: (Type.Grass, Type.Psychic), 124 | Species.Exeggutor: (Type.Grass, Type.Psychic), 125 | Species.Cubone: (Type.Ground, Type.Ground), 126 | Species.Marowak: (Type.Ground, Type.Ground), 127 | Species.Hitmonlee: (Type.Fighting, Type.Fighting), 128 | Species.Hitmonchan: (Type.Fighting, Type.Fighting), 129 | Species.Lickitung: (Type.Normal, Type.Normal), 130 | Species.Koffing: (Type.Poison, Type.Poison), 131 | Species.Weezing: (Type.Poison, Type.Poison), 132 | Species.Rhyhorn: (Type.Ground, Type.Rock), 133 | Species.Rhydon: (Type.Ground, Type.Rock), 134 | Species.Chansey: (Type.Normal, Type.Normal), 135 | Species.Tangela: (Type.Grass, Type.Grass), 136 | Species.Kangaskhan: (Type.Normal, Type.Normal), 137 | Species.Horsea: (Type.Water, Type.Water), 138 | Species.Seadra: (Type.Water, Type.Water), 139 | Species.Goldeen: (Type.Water, Type.Water), 140 | Species.Seaking: (Type.Water, Type.Water), 141 | Species.Staryu: (Type.Water, Type.Water), 142 | Species.Starmie: (Type.Water, Type.Psychic), 143 | Species.Mr_Mime: (Type.Psychic, Type.Psychic), 144 | Species.Scyther: (Type.Bug, Type.Flying), 145 | Species.Jynx: (Type.Ice, Type.Psychic), 146 | Species.Electabuzz: (Type.Electric, Type.Electric), 147 | Species.Magmar: (Type.Fire, Type.Fire), 148 | Species.Pinsir: (Type.Bug, Type.Bug), 149 | Species.Tauros: (Type.Normal, Type.Normal), 150 | Species.Magikarp: (Type.Water, Type.Water), 151 | Species.Gyarados: (Type.Water, Type.Flying), 152 | Species.Lapras: (Type.Water, Type.Ice), 153 | Species.Ditto: (Type.Normal, Type.Normal), 154 | Species.Eevee: (Type.Normal, Type.Normal), 155 | Species.Vaporeon: (Type.Water, Type.Water), 156 | Species.Jolteon: (Type.Electric, Type.Electric), 157 | Species.Flareon: (Type.Fire, Type.Fire), 158 | Species.Porygon: (Type.Normal, Type.Normal), 159 | Species.Omanyte: (Type.Rock, Type.Water), 160 | Species.Omastar: (Type.Rock, Type.Water), 161 | Species.Kabuto: (Type.Rock, Type.Water), 162 | Species.Kabutops: (Type.Rock, Type.Water), 163 | Species.Aerodactyl: (Type.Rock, Type.Flying), 164 | Species.Snorlax: (Type.Normal, Type.Normal), 165 | Species.Articuno: (Type.Ice, Type.Flying), 166 | Species.Zapdos: (Type.Electric, Type.Flying), 167 | Species.Moltres: (Type.Fire, Type.Flying), 168 | Species.Dratini: (Type.Dragon, Type.Dragon), 169 | Species.Dragonair: (Type.Dragon, Type.Dragon), 170 | Species.Dragonite: (Type.Dragon, Type.Flying), 171 | Species.Mewtwo: (Type.Psychic, Type.Psychic), 172 | Species.Mew: (Type.Psychic, Type.Psychic) 173 | } 174 | -------------------------------------------------------------------------------- /poketypes/pokemon/basestats.py: -------------------------------------------------------------------------------- 1 | from .species import Species 2 | 3 | __doc__ = """Base stats (species strengths) according to 4 | http://bulbapedia.bulbagarden.net/wiki/List_of_Pok%C3%A9mon_by_base_stats_(Generation_I)""" 5 | 6 | class BaseStats(object): 7 | 8 | def __init__(self, hp, attack, defense, speed, special): 9 | self.hp = hp 10 | self.attack = attack 11 | self.defense = defense 12 | self.speed = speed 13 | self.special = special 14 | 15 | basestats_from_species = { 16 | Species.Bulbasaur: BaseStats(45, 49, 49, 45, 65), 17 | Species.Ivysaur: BaseStats(60, 62, 63, 60, 80), 18 | Species.Venusaur: BaseStats(80, 82, 83, 80, 100), 19 | Species.Charmander: BaseStats(39, 52, 43, 65, 50), 20 | Species.Charmeleon: BaseStats(58, 64, 58, 80, 65), 21 | Species.Charizard: BaseStats(78, 84, 78, 100, 85), 22 | Species.Squirtle: BaseStats(44, 48, 65, 43, 50), 23 | Species.Wartortle: BaseStats(59, 63, 80, 58, 65), 24 | Species.Blastoise: BaseStats(79, 83, 100, 78, 85), 25 | Species.Caterpie: BaseStats(45, 30, 35, 45, 20), 26 | Species.Metapod: BaseStats(50, 20, 55, 30, 25), 27 | Species.Butterfree: BaseStats(60, 45, 50, 70, 80), 28 | Species.Weedle: BaseStats(40, 35, 30, 50, 20), 29 | Species.Kakuna: BaseStats(45, 25, 50, 35, 25), 30 | Species.Beedrill: BaseStats(65, 80, 40, 75, 45), 31 | Species.Pidgey: BaseStats(40, 45, 40, 56, 35), 32 | Species.Pidgeotto: BaseStats(63, 60, 55, 71, 50), 33 | Species.Pidgeot: BaseStats(83, 80, 75, 91, 70), 34 | Species.Rattata: BaseStats(30, 56, 35, 72, 25), 35 | Species.Raticate: BaseStats(55, 81, 60, 97, 50), 36 | Species.Spearow: BaseStats(40, 60, 30, 70, 31), 37 | Species.Fearow: BaseStats(65, 90, 65, 100, 61), 38 | Species.Ekans: BaseStats(35, 60, 44, 55, 40), 39 | Species.Arbok: BaseStats(60, 85, 69, 80, 65), 40 | Species.Pikachu: BaseStats(35, 55, 30, 90, 50), 41 | Species.Raichu: BaseStats(60, 90, 55, 100, 90), 42 | Species.Sandshrew: BaseStats(50, 75, 85, 40, 30), 43 | Species.Sandslash: BaseStats(75, 100, 110, 65, 55), 44 | Species.Nidoran_f: BaseStats(55, 47, 52, 41, 40), 45 | Species.Nidorina: BaseStats(70, 62, 67, 56, 55), 46 | Species.Nidoqueen: BaseStats(90, 82, 87, 76, 75), 47 | Species.Nidoran_m: BaseStats(46, 57, 40, 50, 40), 48 | Species.Nidorino: BaseStats(61, 72, 57, 65, 55), 49 | Species.Nidoking: BaseStats(81, 92, 77, 85, 75), 50 | Species.Clefairy: BaseStats(70, 45, 48, 35, 60), 51 | Species.Clefable: BaseStats(95, 70, 73, 60, 85), 52 | Species.Vulpix: BaseStats(38, 41, 40, 65, 65), 53 | Species.Ninetales: BaseStats(73, 76, 75, 100, 100), 54 | Species.Jigglypuff: BaseStats(115, 45, 20, 20, 25), 55 | Species.Wigglytuff: BaseStats(140, 70, 45, 45, 50), 56 | Species.Zubat: BaseStats(40, 45, 35, 55, 40), 57 | Species.Golbat: BaseStats(75, 80, 70, 90, 75), 58 | Species.Oddish: BaseStats(45, 50, 55, 30, 75), 59 | Species.Gloom: BaseStats(60, 65, 70, 40, 85), 60 | Species.Vileplume: BaseStats(75, 80, 85, 50, 100), 61 | Species.Paras: BaseStats(35, 70, 55, 25, 55), 62 | Species.Parasect: BaseStats(60, 95, 80, 30, 80), 63 | Species.Venonat: BaseStats(60, 55, 50, 45, 40), 64 | Species.Venomoth: BaseStats(70, 65, 60, 90, 90), 65 | Species.Diglett: BaseStats(10, 55, 25, 95, 45), 66 | Species.Dugtrio: BaseStats(35, 80, 50, 120, 70), 67 | Species.Meowth: BaseStats(40, 45, 35, 90, 40), 68 | Species.Persian: BaseStats(65, 70, 60, 115, 65), 69 | Species.Psyduck: BaseStats(50, 52, 48, 55, 50), 70 | Species.Golduck: BaseStats(80, 82, 78, 85, 80), 71 | Species.Mankey: BaseStats(40, 80, 35, 70, 35), 72 | Species.Primeape: BaseStats(65, 105, 60, 95, 60), 73 | Species.Growlithe: BaseStats(55, 70, 45, 60, 50), 74 | Species.Arcanine: BaseStats(90, 110, 80, 95, 80), 75 | Species.Poliwag: BaseStats(40, 50, 40, 90, 40), 76 | Species.Poliwhirl: BaseStats(65, 65, 65, 90, 50), 77 | Species.Poliwrath: BaseStats(90, 85, 95, 70, 70), 78 | Species.Abra: BaseStats(25, 20, 15, 90, 105), 79 | Species.Kadabra: BaseStats(40, 35, 30, 105, 120), 80 | Species.Alakazam: BaseStats(55, 50, 45, 120, 135), 81 | Species.Machop: BaseStats(70, 80, 50, 35, 35), 82 | Species.Machoke: BaseStats(80, 100, 70, 45, 50), 83 | Species.Machamp: BaseStats(90, 130, 80, 55, 65), 84 | Species.Bellsprout: BaseStats(50, 75, 35, 40, 70), 85 | Species.Weepinbell: BaseStats(65, 90, 50, 55, 85), 86 | Species.Victreebel: BaseStats(80, 105, 65, 70, 100), 87 | Species.Tentacool: BaseStats(40, 40, 35, 70, 100), 88 | Species.Tentacruel: BaseStats(80, 70, 65, 100, 120), 89 | Species.Geodude: BaseStats(40, 80, 100, 20, 30), 90 | Species.Graveler: BaseStats(55, 95, 115, 35, 45), 91 | Species.Golem: BaseStats(80, 110, 130, 45, 55), 92 | Species.Ponyta: BaseStats(50, 85, 55, 90, 65), 93 | Species.Rapidash: BaseStats(65, 100, 70, 105, 80), 94 | Species.Slowpoke: BaseStats(90, 65, 65, 15, 40), 95 | Species.Slowbro: BaseStats(95, 75, 110, 30, 80), 96 | Species.Magnemite: BaseStats(25, 35, 70, 45, 95), 97 | Species.Magneton: BaseStats(50, 60, 95, 70, 120), 98 | Species.Farfetchd: BaseStats(52, 65, 55, 60, 58), 99 | Species.Doduo: BaseStats(35, 85, 45, 75, 35), 100 | Species.Dodrio: BaseStats(60, 110, 70, 100, 60), 101 | Species.Seel: BaseStats(65, 45, 55, 45, 70), 102 | Species.Dewgong: BaseStats(90, 70, 80, 70, 95), 103 | Species.Grimer: BaseStats(80, 80, 50, 25, 40), 104 | Species.Muk: BaseStats(105, 105, 75, 50, 65), 105 | Species.Shellder: BaseStats(30, 65, 100, 40, 45), 106 | Species.Cloyster: BaseStats(50, 95, 180, 70, 85), 107 | Species.Gastly: BaseStats(30, 35, 30, 80, 100), 108 | Species.Haunter: BaseStats(45, 50, 45, 95, 115), 109 | Species.Gengar: BaseStats(60, 65, 60, 110, 130), 110 | Species.Onix: BaseStats(35, 45, 160, 70, 30), 111 | Species.Drowzee: BaseStats(60, 48, 45, 42, 90), 112 | Species.Hypno: BaseStats(85, 73, 70, 67, 115), 113 | Species.Krabby: BaseStats(30, 105, 90, 50, 25), 114 | Species.Kingler: BaseStats(55, 130, 115, 75, 50), 115 | Species.Voltorb: BaseStats(40, 30, 50, 100, 55), 116 | Species.Electrode: BaseStats(60, 50, 70, 140, 80), 117 | Species.Exeggcute: BaseStats(60, 40, 80, 40, 60), 118 | Species.Exeggutor: BaseStats(95, 95, 85, 55, 125), 119 | Species.Cubone: BaseStats(50, 50, 95, 35, 40), 120 | Species.Marowak: BaseStats(60, 80, 110, 45, 50), 121 | Species.Hitmonlee: BaseStats(50, 120, 53, 87, 35), 122 | Species.Hitmonchan: BaseStats(50, 105, 79, 76, 35), 123 | Species.Lickitung: BaseStats(90, 55, 75, 30, 60), 124 | Species.Koffing: BaseStats(40, 65, 95, 35, 60), 125 | Species.Weezing: BaseStats(65, 90, 120, 60, 85), 126 | Species.Rhyhorn: BaseStats(80, 85, 95, 25, 30), 127 | Species.Rhydon: BaseStats(105, 130, 120, 40, 45), 128 | Species.Chansey: BaseStats(250, 5, 5, 50, 105), 129 | Species.Tangela: BaseStats(65, 55, 115, 60, 100), 130 | Species.Kangaskhan: BaseStats(105, 95, 80, 90, 40), 131 | Species.Horsea: BaseStats(30, 40, 70, 60, 70), 132 | Species.Seadra: BaseStats(55, 65, 95, 85, 95), 133 | Species.Goldeen: BaseStats(45, 67, 60, 63, 50), 134 | Species.Seaking: BaseStats(80, 92, 65, 68, 80), 135 | Species.Staryu: BaseStats(30, 45, 55, 85, 70), 136 | Species.Starmie: BaseStats(60, 75, 85, 115, 100), 137 | Species.Mr_Mime: BaseStats(40, 45, 65, 90, 100), 138 | Species.Scyther: BaseStats(70, 110, 80, 105, 55), 139 | Species.Jynx: BaseStats(65, 50, 35, 95, 95), 140 | Species.Electabuzz: BaseStats(65, 83, 57, 105, 85), 141 | Species.Magmar: BaseStats(65, 95, 57, 93, 85), 142 | Species.Pinsir: BaseStats(65, 125, 100, 85, 55), 143 | Species.Tauros: BaseStats(75, 100, 95, 110, 70), 144 | Species.Magikarp: BaseStats(20, 10, 55, 80, 20), 145 | Species.Gyarados: BaseStats(95, 125, 79, 81, 100), 146 | Species.Lapras: BaseStats(130, 85, 80, 60, 95), 147 | Species.Ditto: BaseStats(48, 48, 48, 48, 48), 148 | Species.Eevee: BaseStats(55, 55, 50, 55, 65), 149 | Species.Vaporeon: BaseStats(130, 65, 60, 65, 110), 150 | Species.Jolteon: BaseStats(65, 65, 60, 130, 110), 151 | Species.Flareon: BaseStats(65, 130, 60, 65, 110), 152 | Species.Porygon: BaseStats(65, 60, 70, 40, 75), 153 | Species.Omanyte: BaseStats(35, 40, 100, 35, 90), 154 | Species.Omastar: BaseStats(70, 60, 125, 55, 115), 155 | Species.Kabuto: BaseStats(30, 80, 90, 55, 45), 156 | Species.Kabutops: BaseStats(60, 115, 105, 80, 70), 157 | Species.Aerodactyl: BaseStats(80, 105, 65, 130, 60), 158 | Species.Snorlax: BaseStats(160, 110, 65, 30, 65), 159 | Species.Articuno: BaseStats(90, 85, 100, 85, 125), 160 | Species.Zapdos: BaseStats(90, 90, 85, 100, 125), 161 | Species.Moltres: BaseStats(90, 100, 90, 90, 125), 162 | Species.Dratini: BaseStats(41, 64, 45, 50, 50), 163 | Species.Dragonair: BaseStats(61, 84, 65, 70, 70), 164 | Species.Dragonite: BaseStats(91, 134, 95, 80, 100), 165 | Species.Mewtwo: BaseStats(106, 110, 90, 130, 154), 166 | Species.Mew: BaseStats(100, 100, 100, 100, 100) 167 | } 168 | -------------------------------------------------------------------------------- /pokeduino.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import enum 5 | import argparse 6 | import serial 7 | 8 | import poketypes 9 | 10 | def connect(dev, baud, callback): 11 | """Connects as slave to the game link proxy on arduino 12 | callback is called when a new byte is received and shall be answered""" 13 | con = serial.Serial(dev, baud) 14 | data = 0 15 | try: 16 | while True: 17 | con.write(bytes([data])) 18 | data = callback(con.read(1)[0]) 19 | finally: 20 | con.close() 21 | 22 | ################## 23 | 24 | class ConnectionState(enum.Enum): 25 | NotConnected, Ack, TradeCenter, Colosseum = range(4) 26 | 27 | class TradeState(enum.Enum): 28 | Init, Ready, SeenFirstWait, Random, WaitingToSend, SendingData, \ 29 | SendingPatch, Pending, Confirmation, Done = range(10) 30 | 31 | Master = 0x01 32 | Slave = 0x02 33 | Connected = 0x60 34 | Waiting = 0x7F 35 | 36 | TradeEntry, ColosseumEntry, CancelEntry = 0xD0, 0xD1, 0xD2 37 | selected = lambda i: i | 4 38 | 39 | class TradeData(poketypes.basic.PokeStructure): 40 | 41 | _fields_ = [ 42 | ("trainer_name", poketypes.basic.Pokestring(11)), 43 | ("team", poketypes.Team), 44 | poketypes.basic.PaddingBytes(3) 45 | ] 46 | 47 | class PokemonSession(object): 48 | 49 | def __init__(self, trainer_name, team, receivePokemonHook=None, replaceTeamUponReceive=True): 50 | self.c_state = ConnectionState.NotConnected 51 | self.t_state = TradeState.Init 52 | self.__buildStateTransitions() 53 | 54 | self.receivePokemonHook = receivePokemonHook 55 | self.replaceTeamUponReceive = replaceTeamUponReceive 56 | self.trainer_name = trainer_name 57 | self.__buildSendData(team) 58 | 59 | self.trade_receive = bytes() 60 | self.trade_pokemon_idx = None 61 | self.trade_counter = 0 62 | 63 | def __buildStateTransitions(self): 64 | # Simple Mealy machine lookup: 65 | # [state](input) -> (output, new_state) 66 | self.handle_c_state = { 67 | ConnectionState.NotConnected: self.handleNotConnected, 68 | ConnectionState.Ack: self.handleAck, 69 | ConnectionState.TradeCenter: self.handleTradeCenter, 70 | ConnectionState.Colosseum: self.handleColosseum 71 | } 72 | 73 | self.handle_t_state = { 74 | TradeState.Init: self.handleT_Init, 75 | TradeState.Ready: self.handleT_Ready, 76 | TradeState.SeenFirstWait: self.handleT_SeenFirstWait, 77 | TradeState.Random: self.handleT_Random, 78 | TradeState.WaitingToSend: self.handleT_WaitingToSend, 79 | TradeState.SendingData: self.handleT_SendingData, 80 | TradeState.SendingPatch: self.handleT_SendingPatch, 81 | TradeState.Pending: self.handleT_Pending, 82 | TradeState.Confirmation: self.handleT_Confirmation, 83 | TradeState.Done: self.handleT_Done 84 | } 85 | 86 | def __buildSendData(self, team): 87 | self.trade_send = TradeData(trainer_name=poketypes.basic.Pokestring(11).fromString(self.trainer_name), 88 | team=team).bytes() 89 | 90 | def _saverecv(self, byte): 91 | self.trade_receive += bytes([byte]) 92 | 93 | ################################################## 94 | 95 | def __call__(self, data): 96 | try: 97 | output, new_state = self.handle_c_state[self.c_state](data) 98 | except TypeError: 99 | output, new_state = data, self.c_state 100 | self.c_state = new_state 101 | return output 102 | 103 | def handleNotConnected(self, data): 104 | if data == Master: 105 | return Slave, ConnectionState.NotConnected 106 | elif data == 0: 107 | return 0, ConnectionState.NotConnected 108 | elif data == Connected: 109 | return Connected, ConnectionState.Ack 110 | 111 | def handleAck(self, data): 112 | if data == Connected: 113 | return Connected, ConnectionState.Ack 114 | elif data == selected(TradeEntry): 115 | return 0, ConnectionState.TradeCenter 116 | elif data == selected(ColosseumEntry): 117 | return 0, ConnectionState.Colosseum 118 | elif data in [Master, selected(CancelEntry)]: 119 | return selected(CancelEntry), ConnectionState.NotConnected 120 | else: 121 | return data, ConnectionState.Ack 122 | 123 | def handleColosseum(self, data): 124 | return data, ConnectionState.Colosseum 125 | 126 | ################################################## 127 | 128 | def handleTradeCenter(self, data): 129 | try: 130 | output, new_state = self.handle_t_state[self.t_state](data) 131 | except TypeError: 132 | output, new_state = data, self.t_state 133 | self.t_state = new_state 134 | return output, ConnectionState.TradeCenter 135 | 136 | def handleT_Init(self, data): 137 | if data == 0: return 0, TradeState.Ready 138 | 139 | def handleT_Ready(self, data): 140 | if data == 0xFD: return 0xFD, TradeState.SeenFirstWait 141 | 142 | def handleT_SeenFirstWait(self, data): 143 | if data != 0xFD: return data, TradeState.Random 144 | 145 | def handleT_Random(self, data): 146 | if data == 0xFD: return data, TradeState.WaitingToSend 147 | 148 | def handleT_WaitingToSend(self, data): 149 | self.trade_counter = 0 150 | self.trade_receive = bytes() 151 | if data != 0xFD: 152 | self._saverecv(data) 153 | return self.trade_send[self.trade_counter], TradeState.SendingData 154 | 155 | def handleT_SendingData(self, data): 156 | self._saverecv(data) 157 | self.trade_counter += 1 158 | out = self.trade_send[self.trade_counter] 159 | next_state = (TradeState.SendingPatch if self.trade_counter >= len(self.trade_send) - 1 else TradeState.SendingData) 160 | return out, next_state 161 | 162 | def handleT_SendingPatch(self, data): 163 | if data == 0xFD: 164 | self.trade_counter = 0 165 | return 0xFD, TradeState.SendingPatch 166 | else: 167 | self.trade_counter += 1 168 | return data, (TradeState.SendingPatch if self.trade_counter < 197 else TradeState.Pending) 169 | 170 | def handleT_Pending(self, data): 171 | if data == 0: 172 | return 0, TradeState.Confirmation 173 | elif data & 0x60 == 0x60: 174 | if data == 0x6f: 175 | return 0x6f, TradeState.Ready 176 | else: 177 | self.trade_pokemon_idx = data - 0x60 178 | # Trade my first pokemon 179 | return 0x60, TradeState.Pending 180 | 181 | def handleT_Confirmation(self, data): 182 | if data == 0x61: 183 | self.trade_pokemon_idx = None 184 | return 0x61, TradeState.Pending 185 | elif data & 0x60 == 0x60: 186 | self.__received_pokemon() 187 | return data, TradeState.Done 188 | 189 | def handleT_Done(self, data): 190 | if data == 0: 191 | return 0, TradeState.Init 192 | 193 | ################################################## 194 | 195 | def __received_pokemon(self): 196 | recvdata = TradeData.fromBytes(self.trade_receive) 197 | pokemon, ot_name, nickname = recvdata.team[self.trade_pokemon_idx] 198 | if self.replaceTeamUponReceive: 199 | self.__buildSendData(poketypes.Team([recvdata.team[self.trade_pokemon_idx]])) 200 | if self.receivePokemonHook is not None: 201 | self.receivePokemonHook(pokemon, ot_name, nickname) 202 | 203 | 204 | def receivedPokemonHandler(pokemon, ot_name, nickname): 205 | sys.stdout.buffer.write(pokemon.bytes()) 206 | print("Pushed a %s %sfrom %s to stdout" % \ 207 | (pokemon.species.name, "(called %s) " % nickname if nickname else "", ot_name), 208 | file=sys.stderr) 209 | 210 | 211 | def parseArguments(): 212 | parser = argparse.ArgumentParser( 213 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, 214 | description="Trade a Pokemon read from stdin over an Arduino proxy. Received Pokemon are printed to stdout." 215 | ) 216 | 217 | parser.add_argument("--baudrate", "-b", type=int, 218 | default=115200, 219 | help="Baudrate of the serial connection") 220 | parser.add_argument("--device", "-d", type=str, 221 | default="/dev/tty.usbmodemfa131", 222 | help="Device name of the serial port") 223 | 224 | parser.add_argument("--trainer", "-t", type=str, 225 | default="POKEDUINO", 226 | help="The simulated gameboys player name") 227 | parser.add_argument("--otname", "-o", type=str, 228 | help="Original trainer name of the send pokemon (copies from --trainer if not specified)") 229 | parser.add_argument("--nickname", "-n", type=str, 230 | help="Nickname of the send pokemon") 231 | 232 | try: 233 | args = parser.parse_args() 234 | except argparse.ArgumentTypeError as err: 235 | parser.error(err) 236 | 237 | if args.otname is None: 238 | args.otname = args.trainer 239 | 240 | return args 241 | 242 | 243 | if __name__ == "__main__": 244 | args = parseArguments() 245 | 246 | pokemon = poketypes.Pokemon.fromBytes(sys.stdin.buffer.read()) 247 | 248 | team = poketypes.Team([(pokemon, args.otname, args.nickname)]) 249 | session = PokemonSession(args.trainer, team, receivedPokemonHandler) 250 | 251 | connect(args.device, args.baudrate, session) 252 | 253 | -------------------------------------------------------------------------------- /poketypes/pokemon/experience.py: -------------------------------------------------------------------------------- 1 | import enum 2 | import math 3 | 4 | from .species import Species 5 | 6 | __all__ = ["ExperienceClass", "exp_for_level", "level_for_exp", "class_for_species"] 7 | 8 | class ExperienceClass(enum.Enum): 9 | Fast, \ 10 | MediumFast, \ 11 | MediumSlow, \ 12 | Slow = range(4) 13 | 14 | exp_for_level = { 15 | ExperienceClass.Fast: lambda n: (4 * n ** 3) // 5, 16 | ExperienceClass.MediumFast: lambda n: n ** 3, 17 | ExperienceClass.MediumSlow: lambda n: (6 * n ** 3) // 5 - 15 * n ** 2 + 100*n - 140, 18 | ExperienceClass.Slow: lambda n: (5 * n ** 3) // 4 19 | } 20 | 21 | def _findlevel_factory(xpClass): 22 | 23 | l2xp = exp_for_level[xpClass] 24 | 25 | def binsearch(xp, start, end): 26 | if start + 1 >= end: 27 | return start 28 | mid = (start + end) // 2 29 | if xp >= l2xp(mid): 30 | return binsearch(xp, mid, end) 31 | else: 32 | return binsearch(xp, start, mid) 33 | 34 | return lambda xp: max(0, min(100, binsearch(xp, 0, 101))) 35 | 36 | level_for_exp = {xpClass: _findlevel_factory(xpClass) for xpClass in ExperienceClass} 37 | 38 | # level_for_exp = { 39 | # ExperienceClass.Fast: lambda x: (5 * x / 4) ** (1.0 / 3), 40 | # ExperienceClass.MediumFast: lambda x: x ** (1.0 / 3), 41 | # ExperienceClass.MediumSlow: lambda x: 25.0/6 - (25 * 5 ** (2./3))/(2*(-1855 + 18*x + 2*math.sqrt(1387600 - 16695*x + 81*x**2))**(1./3)) + (5**(1./3)*(-1855 + 18*x + 2*math.sqrt(1387600 - 16695*x + 81*x**2))**(1./3))/6, 42 | # ExperienceClass.Slow: lambda x: (4 * x / 5) ** (1.0 / 3), 43 | # } 44 | 45 | class_for_species = { 46 | Species.Bulbasaur: ExperienceClass.MediumSlow, 47 | Species.Ivysaur: ExperienceClass.MediumSlow, 48 | Species.Venusaur: ExperienceClass.MediumSlow, 49 | Species.Charmander: ExperienceClass.MediumSlow, 50 | Species.Charmeleon: ExperienceClass.MediumSlow, 51 | Species.Charizard: ExperienceClass.MediumSlow, 52 | Species.Squirtle: ExperienceClass.MediumSlow, 53 | Species.Wartortle: ExperienceClass.MediumSlow, 54 | Species.Blastoise: ExperienceClass.MediumSlow, 55 | Species.Caterpie: ExperienceClass.MediumFast, 56 | Species.Metapod: ExperienceClass.MediumFast, 57 | Species.Butterfree: ExperienceClass.MediumFast, 58 | Species.Weedle: ExperienceClass.MediumFast, 59 | Species.Kakuna: ExperienceClass.MediumFast, 60 | Species.Beedrill: ExperienceClass.MediumFast, 61 | Species.Pidgey: ExperienceClass.MediumSlow, 62 | Species.Pidgeotto: ExperienceClass.MediumSlow, 63 | Species.Pidgeot: ExperienceClass.MediumSlow, 64 | Species.Rattata: ExperienceClass.MediumFast, 65 | Species.Raticate: ExperienceClass.MediumFast, 66 | Species.Spearow: ExperienceClass.MediumFast, 67 | Species.Fearow: ExperienceClass.MediumFast, 68 | Species.Ekans: ExperienceClass.MediumFast, 69 | Species.Arbok: ExperienceClass.MediumFast, 70 | Species.Pikachu: ExperienceClass.MediumFast, 71 | Species.Raichu: ExperienceClass.MediumFast, 72 | Species.Sandshrew: ExperienceClass.MediumFast, 73 | Species.Sandslash: ExperienceClass.MediumFast, 74 | Species.Nidoran_f: ExperienceClass.MediumSlow, 75 | Species.Nidorina: ExperienceClass.MediumSlow, 76 | Species.Nidoqueen: ExperienceClass.MediumSlow, 77 | Species.Nidoran_m: ExperienceClass.MediumSlow, 78 | Species.Nidorino: ExperienceClass.MediumSlow, 79 | Species.Nidoking: ExperienceClass.MediumSlow, 80 | Species.Clefairy: ExperienceClass.Fast, 81 | Species.Clefable: ExperienceClass.Fast, 82 | Species.Vulpix: ExperienceClass.MediumFast, 83 | Species.Ninetales: ExperienceClass.MediumFast, 84 | Species.Jigglypuff: ExperienceClass.Fast, 85 | Species.Wigglytuff: ExperienceClass.Fast, 86 | Species.Zubat: ExperienceClass.MediumFast, 87 | Species.Golbat: ExperienceClass.MediumFast, 88 | Species.Oddish: ExperienceClass.MediumSlow, 89 | Species.Gloom: ExperienceClass.MediumSlow, 90 | Species.Vileplume: ExperienceClass.MediumSlow, 91 | Species.Paras: ExperienceClass.MediumFast, 92 | Species.Parasect: ExperienceClass.MediumFast, 93 | Species.Venonat: ExperienceClass.MediumFast, 94 | Species.Venomoth: ExperienceClass.MediumFast, 95 | Species.Diglett: ExperienceClass.MediumFast, 96 | Species.Dugtrio: ExperienceClass.MediumFast, 97 | Species.Meowth: ExperienceClass.MediumFast, 98 | Species.Persian: ExperienceClass.MediumFast, 99 | Species.Psyduck: ExperienceClass.MediumFast, 100 | Species.Golduck: ExperienceClass.MediumFast, 101 | Species.Mankey: ExperienceClass.MediumFast, 102 | Species.Primeape: ExperienceClass.MediumFast, 103 | Species.Growlithe: ExperienceClass.Slow, 104 | Species.Arcanine: ExperienceClass.Slow, 105 | Species.Poliwag: ExperienceClass.MediumSlow, 106 | Species.Poliwhirl: ExperienceClass.MediumSlow, 107 | Species.Poliwrath: ExperienceClass.MediumSlow, 108 | Species.Abra: ExperienceClass.MediumSlow, 109 | Species.Kadabra: ExperienceClass.MediumSlow, 110 | Species.Alakazam: ExperienceClass.MediumSlow, 111 | Species.Machop: ExperienceClass.MediumSlow, 112 | Species.Machoke: ExperienceClass.MediumSlow, 113 | Species.Machamp: ExperienceClass.MediumSlow, 114 | Species.Bellsprout: ExperienceClass.MediumSlow, 115 | Species.Weepinbell: ExperienceClass.MediumSlow, 116 | Species.Victreebel: ExperienceClass.MediumSlow, 117 | Species.Tentacool: ExperienceClass.Slow, 118 | Species.Tentacruel: ExperienceClass.Slow, 119 | Species.Geodude: ExperienceClass.MediumSlow, 120 | Species.Graveler: ExperienceClass.MediumSlow, 121 | Species.Golem: ExperienceClass.MediumSlow, 122 | Species.Ponyta: ExperienceClass.MediumFast, 123 | Species.Rapidash: ExperienceClass.MediumFast, 124 | Species.Slowpoke: ExperienceClass.MediumFast, 125 | Species.Slowbro: ExperienceClass.MediumFast, 126 | Species.Magnemite: ExperienceClass.MediumFast, 127 | Species.Magneton: ExperienceClass.MediumFast, 128 | Species.Farfetchd: ExperienceClass.MediumFast, 129 | Species.Doduo: ExperienceClass.MediumFast, 130 | Species.Dodrio: ExperienceClass.MediumFast, 131 | Species.Seel: ExperienceClass.MediumFast, 132 | Species.Dewgong: ExperienceClass.MediumFast, 133 | Species.Grimer: ExperienceClass.MediumFast, 134 | Species.Muk: ExperienceClass.MediumFast, 135 | Species.Shellder: ExperienceClass.Slow, 136 | Species.Cloyster: ExperienceClass.Slow, 137 | Species.Gastly: ExperienceClass.MediumSlow, 138 | Species.Haunter: ExperienceClass.MediumSlow, 139 | Species.Gengar: ExperienceClass.MediumSlow, 140 | Species.Onix: ExperienceClass.MediumFast, 141 | Species.Drowzee: ExperienceClass.MediumFast, 142 | Species.Hypno: ExperienceClass.MediumFast, 143 | Species.Krabby: ExperienceClass.MediumFast, 144 | Species.Kingler: ExperienceClass.MediumFast, 145 | Species.Voltorb: ExperienceClass.MediumFast, 146 | Species.Electrode: ExperienceClass.MediumFast, 147 | Species.Exeggcute: ExperienceClass.Slow, 148 | Species.Exeggutor: ExperienceClass.Slow, 149 | Species.Cubone: ExperienceClass.MediumFast, 150 | Species.Marowak: ExperienceClass.MediumFast, 151 | Species.Hitmonlee: ExperienceClass.MediumFast, 152 | Species.Hitmonchan: ExperienceClass.MediumFast, 153 | Species.Lickitung: ExperienceClass.MediumFast, 154 | Species.Koffing: ExperienceClass.MediumFast, 155 | Species.Weezing: ExperienceClass.MediumFast, 156 | Species.Rhyhorn: ExperienceClass.Slow, 157 | Species.Rhydon: ExperienceClass.Slow, 158 | Species.Chansey: ExperienceClass.Fast, 159 | Species.Tangela: ExperienceClass.MediumFast, 160 | Species.Kangaskhan: ExperienceClass.MediumFast, 161 | Species.Horsea: ExperienceClass.MediumFast, 162 | Species.Seadra: ExperienceClass.MediumFast, 163 | Species.Goldeen: ExperienceClass.MediumFast, 164 | Species.Seaking: ExperienceClass.MediumFast, 165 | Species.Staryu: ExperienceClass.Slow, 166 | Species.Starmie: ExperienceClass.Slow, 167 | Species.Mr_Mime: ExperienceClass.MediumFast, 168 | Species.Scyther: ExperienceClass.MediumFast, 169 | Species.Jynx: ExperienceClass.MediumFast, 170 | Species.Electabuzz: ExperienceClass.MediumFast, 171 | Species.Magmar: ExperienceClass.MediumFast, 172 | Species.Pinsir: ExperienceClass.Slow, 173 | Species.Tauros: ExperienceClass.Slow, 174 | Species.Magikarp: ExperienceClass.Slow, 175 | Species.Gyarados: ExperienceClass.Slow, 176 | Species.Lapras: ExperienceClass.Slow, 177 | Species.Ditto: ExperienceClass.MediumFast, 178 | Species.Eevee: ExperienceClass.MediumFast, 179 | Species.Vaporeon: ExperienceClass.MediumFast, 180 | Species.Jolteon: ExperienceClass.MediumFast, 181 | Species.Flareon: ExperienceClass.MediumFast, 182 | Species.Porygon: ExperienceClass.MediumFast, 183 | Species.Omanyte: ExperienceClass.MediumFast, 184 | Species.Omastar: ExperienceClass.MediumFast, 185 | Species.Kabuto: ExperienceClass.MediumFast, 186 | Species.Kabutops: ExperienceClass.MediumFast, 187 | Species.Aerodactyl: ExperienceClass.Slow, 188 | Species.Snorlax: ExperienceClass.Slow, 189 | Species.Articuno: ExperienceClass.Slow, 190 | Species.Zapdos: ExperienceClass.Slow, 191 | Species.Moltres: ExperienceClass.Slow, 192 | Species.Dratini: ExperienceClass.Slow, 193 | Species.Dragonair: ExperienceClass.Slow, 194 | Species.Dragonite: ExperienceClass.Slow, 195 | Species.Mewtwo: ExperienceClass.Slow, 196 | Species.Mew: ExperienceClass.MediumSlow 197 | } 198 | --------------------------------------------------------------------------------