mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
Merge branch 'encukou-media-accessors'
This commit is contained in:
commit
3d25d125f4
3 changed files with 771 additions and 0 deletions
|
@ -484,6 +484,7 @@ item_id,item_flag_id
|
||||||
245,7
|
245,7
|
||||||
246,5
|
246,5
|
||||||
246,7
|
246,7
|
||||||
|
246,8
|
||||||
247,5
|
247,5
|
||||||
247,7
|
247,7
|
||||||
248,5
|
248,5
|
||||||
|
@ -501,6 +502,7 @@ item_id,item_flag_id
|
||||||
254,7
|
254,7
|
||||||
255,5
|
255,5
|
||||||
255,7
|
255,7
|
||||||
|
255,8
|
||||||
256,5
|
256,5
|
||||||
256,7
|
256,7
|
||||||
257,5
|
257,5
|
||||||
|
|
|
514
pokedex/db/media.py
Normal file
514
pokedex/db/media.py
Normal file
|
@ -0,0 +1,514 @@
|
||||||
|
|
||||||
|
"""Media accessors
|
||||||
|
|
||||||
|
Most media accessor __init__s take an ORM object from the pokedex package.
|
||||||
|
Their various methods take a number of arguments specifying exactly which
|
||||||
|
file you want (such as the female sprite, backsprite, etc.).
|
||||||
|
ValueError is raised when the specified file cannot be found.
|
||||||
|
|
||||||
|
The accessors use fallbacks: for example Bulbasaur's males and females look the
|
||||||
|
same, so if you request Bulbasaur's female sprite, it will give you the common
|
||||||
|
image. Or for a Pokemon without individual form sprites, you will get the
|
||||||
|
common base sprite. Or for versions witout shiny Pokemon, you will always
|
||||||
|
get the non-shiny version (that's how shiny Pokemon looked there!).
|
||||||
|
However arguments such as `animated` don't use fallbacks.
|
||||||
|
You can set `strict` to True to disable these fallbacks and cause ValueError
|
||||||
|
to be raised when the exact specific file you asked for is not found. This is
|
||||||
|
useful for listing non-duplicate sprites, for example.
|
||||||
|
|
||||||
|
Use keyword arguments when calling the media-getting methods, unless noted
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
The returned "file" objects have useful attributes like relative_path,
|
||||||
|
path, and open().
|
||||||
|
|
||||||
|
All images are in the PNG format, except animations (GIF). All sounds are OGGs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
class MediaFile(object):
|
||||||
|
"""Represents a file: picture, sound, etc.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
relative_path: Filesystem path relative to the media directory
|
||||||
|
path: Absolute path to the file
|
||||||
|
|
||||||
|
exists: True if the file exists
|
||||||
|
|
||||||
|
open(): Open the file
|
||||||
|
"""
|
||||||
|
def __init__(self, *path_elements):
|
||||||
|
self.path_elements = path_elements
|
||||||
|
self._dexpath = '/'.join(('data', 'media') + path_elements)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def relative_path(self):
|
||||||
|
return os.path.join(*self.path_elements)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
return pkg_resources.resource_filename('pokedex', self._dexpath)
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
"""Open this file for reading, in the appropriate mode (i.e. binary)
|
||||||
|
"""
|
||||||
|
return open(self.path, 'rb')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def exists(self):
|
||||||
|
return pkg_resources.resource_exists('pokedex', self._dexpath)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.path == other.path
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return self.path != other.path
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '<Pokedex file %s>' % self.relative_path
|
||||||
|
|
||||||
|
class BaseMedia(object):
|
||||||
|
def from_path_elements(self, path_elements, basename, extension,
|
||||||
|
surely_exists=False):
|
||||||
|
filename = basename + extension
|
||||||
|
path_elements = [self.toplevel_dir] + path_elements + [filename]
|
||||||
|
mfile = MediaFile(*path_elements)
|
||||||
|
if surely_exists or mfile.exists:
|
||||||
|
return mfile
|
||||||
|
else:
|
||||||
|
raise ValueError('File %s not found' % mfile.relative_path)
|
||||||
|
|
||||||
|
class _BasePokemonMedia(BaseMedia):
|
||||||
|
toplevel_dir = 'pokemon'
|
||||||
|
has_gender_differences = False
|
||||||
|
form = None
|
||||||
|
introduced_in = 0
|
||||||
|
|
||||||
|
# Info about of what's inside the pokemon main sprite directories, so we
|
||||||
|
# don't have to check directory existence all the time.
|
||||||
|
_pokemon_sprite_info = {
|
||||||
|
'red-blue': (1, set('back gray'.split())),
|
||||||
|
'red-green': (1, set('back gray'.split())),
|
||||||
|
'yellow': (1, set('back gray gbc'.split())),
|
||||||
|
'gold': (2, set('back shiny'.split())),
|
||||||
|
'silver': (2, set('back shiny'.split())),
|
||||||
|
'crystal': (2, set('animated back shiny'.split())),
|
||||||
|
'ruby-sapphire': (3, set('back shiny'.split())),
|
||||||
|
'emerald': (3, set('animated back shiny frame2'.split())),
|
||||||
|
'firered-leafgreen': (3, set('back shiny'.split())),
|
||||||
|
'diamond-pearl': (4, set('back shiny female frame2'.split())),
|
||||||
|
'platinum': (4, set('back shiny female frame2'.split())),
|
||||||
|
'heartgold-soulsilver': (4, set('back shiny female frame2'.split())),
|
||||||
|
'black-white': (5, set('back shiny female'.split())),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, pokemon_id, form_postfix=None):
|
||||||
|
BaseMedia.__init__(self)
|
||||||
|
self.pokemon_id = str(pokemon_id)
|
||||||
|
self.form_postfix = form_postfix
|
||||||
|
|
||||||
|
def _get_file(self, path_elements, extension, strict, surely_exists=False):
|
||||||
|
basename = str(self.pokemon_id)
|
||||||
|
if self.form_postfix:
|
||||||
|
fullname = basename + self.form_postfix
|
||||||
|
try:
|
||||||
|
return self.from_path_elements(
|
||||||
|
path_elements, fullname, extension,
|
||||||
|
surely_exists=surely_exists)
|
||||||
|
except ValueError:
|
||||||
|
if strict:
|
||||||
|
raise
|
||||||
|
return self.from_path_elements(path_elements, basename, extension,
|
||||||
|
surely_exists=surely_exists)
|
||||||
|
|
||||||
|
def sprite(self,
|
||||||
|
version='black-white',
|
||||||
|
|
||||||
|
# The media directories are in this order:
|
||||||
|
animated=False,
|
||||||
|
back=False,
|
||||||
|
color=None,
|
||||||
|
shiny=False,
|
||||||
|
female=False,
|
||||||
|
frame=None,
|
||||||
|
|
||||||
|
strict=False,
|
||||||
|
):
|
||||||
|
"""Get a main sprite sprite for a pokemon.
|
||||||
|
|
||||||
|
Everything except version should be given as a keyword argument.
|
||||||
|
|
||||||
|
Either specify version as an ORM object, or give the version path as
|
||||||
|
a string (which is the only way to get 'red-green'). Leave the default
|
||||||
|
for the latest version.
|
||||||
|
|
||||||
|
animated: get a GIF animation (currently Crystal & Emerald only)
|
||||||
|
back: get a backsprite instead of a front one
|
||||||
|
color: can be 'color' (RGBY only) or 'gbc' (Yellow only)
|
||||||
|
shiny: get a shiny sprite. In old versions, gives a normal sprite unless
|
||||||
|
`strict` is set
|
||||||
|
female: get a female sprite instead of male. For pokemon with no sexual
|
||||||
|
dimorphism, gets the common sprite unless `strict` is set.
|
||||||
|
frame: set to 2 to get the second frame of the animation
|
||||||
|
(Emerald, DPP, and HG/SS only)
|
||||||
|
|
||||||
|
If the sprite is not found, raise a ValueError.
|
||||||
|
"""
|
||||||
|
if isinstance(version, basestring):
|
||||||
|
version_dir = version
|
||||||
|
try:
|
||||||
|
generation, info = self._pokemon_sprite_info[version_dir]
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError('Version directory %s not found', version_dir)
|
||||||
|
else:
|
||||||
|
version_dir = version.identifier
|
||||||
|
try:
|
||||||
|
generation, info = self._pokemon_sprite_info[version_dir]
|
||||||
|
except KeyError:
|
||||||
|
version_group = version.version_group
|
||||||
|
version_dir = '-'.join(
|
||||||
|
v.identifier for v in version_group.versions)
|
||||||
|
generation, info = self._pokemon_sprite_info[version_dir]
|
||||||
|
if generation < self.introduced_in:
|
||||||
|
raise ValueError("Pokemon %s didn't exist in %s" % (
|
||||||
|
self.pokemon_id, version_dir))
|
||||||
|
path_elements = ['main-sprites', version_dir]
|
||||||
|
if animated:
|
||||||
|
if 'animated' not in info:
|
||||||
|
raise ValueError("No animated sprites for %s" % version_dir)
|
||||||
|
path_elements.append('animated')
|
||||||
|
extension = '.gif'
|
||||||
|
else:
|
||||||
|
extension = '.png'
|
||||||
|
if back:
|
||||||
|
if version_dir == 'emerald':
|
||||||
|
# Emerald backsprites are the same as ruby/sapphire
|
||||||
|
if strict:
|
||||||
|
raise ValueError("Emerald uses R/S backsprites")
|
||||||
|
if animated:
|
||||||
|
raise ValueError("No animated backsprites for Emerald")
|
||||||
|
path_elements[1] = version_dir = 'ruby-sapphire'
|
||||||
|
if version_dir == 'crystal' and animated:
|
||||||
|
raise ValueError("No animated backsprites for Crystal")
|
||||||
|
path_elements.append('back')
|
||||||
|
if color == 'gray':
|
||||||
|
if 'gray' not in info:
|
||||||
|
raise ValueError("No grayscale sprites for %s" % version_dir)
|
||||||
|
path_elements.append('gray')
|
||||||
|
elif color == 'gbc':
|
||||||
|
if 'gbc' not in info:
|
||||||
|
raise ValueError("No GBC sprites for %s" % version_dir)
|
||||||
|
path_elements.append('gbc')
|
||||||
|
elif color:
|
||||||
|
raise ValueError("Unknown color scheme: %s" % color)
|
||||||
|
if shiny:
|
||||||
|
if 'shiny' in info:
|
||||||
|
path_elements.append('shiny')
|
||||||
|
elif strict:
|
||||||
|
raise ValueError("No shiny sprites for %s" % version_dir)
|
||||||
|
if female:
|
||||||
|
female_sprite = self.has_gender_differences
|
||||||
|
# Chimecho's female back frame 2 sprite has one hand in
|
||||||
|
# a slightly different pose, in Platinum and HGSS
|
||||||
|
# (we have duplicate sprites frame 1, for convenience)
|
||||||
|
if self.pokemon_id == '358' and back and version_dir in (
|
||||||
|
'platinum', 'heartgold-soulsilver'):
|
||||||
|
female_sprite = True
|
||||||
|
female_sprite = female_sprite and 'female' in info
|
||||||
|
if female_sprite:
|
||||||
|
path_elements.append('female')
|
||||||
|
elif strict:
|
||||||
|
raise ValueError(
|
||||||
|
'Pokemon %s has no gender differences' % self.pokemon_id)
|
||||||
|
if not frame or frame == 1:
|
||||||
|
pass
|
||||||
|
elif frame == 2:
|
||||||
|
if 'frame2' in info:
|
||||||
|
path_elements.append('frame%s' % frame)
|
||||||
|
else:
|
||||||
|
raise ValueError("No frame 2 for %s" % version_dir)
|
||||||
|
else:
|
||||||
|
raise ValueError("Bad frame %s" % frame)
|
||||||
|
return self._get_file(path_elements, extension, strict=strict,
|
||||||
|
# Avoid a stat in the common case
|
||||||
|
surely_exists=(self.form and version_dir == 'black-white'
|
||||||
|
and not back and not female
|
||||||
|
and not self.form_postfix))
|
||||||
|
|
||||||
|
def _maybe_female(self, path_elements, female, strict):
|
||||||
|
if female:
|
||||||
|
if self.has_gender_differences:
|
||||||
|
elements = path_elements + ['female']
|
||||||
|
try:
|
||||||
|
return self._get_file(elements, '.png', strict=strict)
|
||||||
|
except ValueError:
|
||||||
|
if strict:
|
||||||
|
raise
|
||||||
|
elif strict:
|
||||||
|
raise ValueError(
|
||||||
|
'Pokemon %s has no gender differences' % self.pokemon_id)
|
||||||
|
return self._get_file(path_elements, '.png', strict=strict)
|
||||||
|
|
||||||
|
def icon(self, female=False, strict=False):
|
||||||
|
"""Get the Pokemon's menu icon"""
|
||||||
|
return self._maybe_female(['icons'], female, strict)
|
||||||
|
|
||||||
|
def sugimori(self, female=False, strict=False):
|
||||||
|
"""Get the Pokemon's official art, drawn by Ken Sugimori"""
|
||||||
|
return self._maybe_female(['sugimori'], female, strict)
|
||||||
|
|
||||||
|
def overworld(self,
|
||||||
|
direction='down',
|
||||||
|
shiny=False,
|
||||||
|
female=False,
|
||||||
|
frame=1,
|
||||||
|
strict=False,
|
||||||
|
):
|
||||||
|
"""Get an overworld sprite
|
||||||
|
|
||||||
|
direction: 'up', 'down', 'left', or 'right'
|
||||||
|
shiny: true for a shiny sprite
|
||||||
|
female: true for female sprite (or the common one for both M & F)
|
||||||
|
frame: 2 for the second animation frame
|
||||||
|
|
||||||
|
strict: disable fallback for `female`
|
||||||
|
"""
|
||||||
|
path_elements = ['overworld']
|
||||||
|
if shiny:
|
||||||
|
path_elements.append('shiny')
|
||||||
|
if female:
|
||||||
|
if self.has_gender_differences:
|
||||||
|
path_elements.append('female')
|
||||||
|
elif strict:
|
||||||
|
raise ValueError('No female overworld sprite')
|
||||||
|
else:
|
||||||
|
female = False
|
||||||
|
path_elements.append(direction)
|
||||||
|
if frame and frame > 1:
|
||||||
|
path_elements.append('frame%s' % frame)
|
||||||
|
try:
|
||||||
|
return self._get_file(path_elements, '.png', strict=strict)
|
||||||
|
except ValueError:
|
||||||
|
if female and not strict:
|
||||||
|
path_elements.remove('female')
|
||||||
|
return self._get_file(path_elements, '.png', strict=strict)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def footprint(self, strict=False):
|
||||||
|
"""Get the Pokemon's footprint"""
|
||||||
|
return self._get_file(['footprints'], '.png', strict=strict)
|
||||||
|
|
||||||
|
def trozei(self, strict=False):
|
||||||
|
"""Get the Pokemon's animated Trozei sprite"""
|
||||||
|
return self._get_file(['trozei'], '.gif', strict=strict)
|
||||||
|
|
||||||
|
def cry(self, strict=False):
|
||||||
|
"""Get the Pokemon's cry"""
|
||||||
|
return self._get_file(['cries'], '.ogg', strict=strict)
|
||||||
|
|
||||||
|
def cropped_sprite(self, strict=False):
|
||||||
|
"""Get the Pokemon's cropped sprite"""
|
||||||
|
return self._get_file(['cropped'], '.png', strict=strict)
|
||||||
|
|
||||||
|
class PokemonFormMedia(_BasePokemonMedia):
|
||||||
|
"""Media related to a Pokemon form
|
||||||
|
"""
|
||||||
|
def __init__(self, pokemon_form):
|
||||||
|
pokemon_id = pokemon_form.form_base_pokemon_id
|
||||||
|
if pokemon_form.identifier:
|
||||||
|
form_postfix = '-' + pokemon_form.identifier
|
||||||
|
else:
|
||||||
|
form_postfix = None
|
||||||
|
_BasePokemonMedia.__init__(self, pokemon_id, form_postfix)
|
||||||
|
self.form = pokemon_form
|
||||||
|
pokemon = pokemon_form.form_base_pokemon
|
||||||
|
self.has_gender_differences = pokemon.has_gender_differences
|
||||||
|
self.introduced_in = pokemon.generation_id
|
||||||
|
|
||||||
|
class PokemonMedia(_BasePokemonMedia):
|
||||||
|
"""Media related to a Pokemon
|
||||||
|
"""
|
||||||
|
def __init__(self, pokemon):
|
||||||
|
_BasePokemonMedia.__init__(self, pokemon.id)
|
||||||
|
self.form = pokemon.default_form
|
||||||
|
self.has_gender_differences = (pokemon.has_gender_differences)
|
||||||
|
self.introduced_in = pokemon.generation_id
|
||||||
|
|
||||||
|
class UnknownPokemonMedia(_BasePokemonMedia):
|
||||||
|
"""Media related to the unknown Pokemon ("?")
|
||||||
|
|
||||||
|
Note that not a lot of files are available for it.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
_BasePokemonMedia.__init__(self, '0')
|
||||||
|
|
||||||
|
class EggMedia(_BasePokemonMedia):
|
||||||
|
"""Media related to a pokemon egg
|
||||||
|
|
||||||
|
Note that not a lot of files are available for these.
|
||||||
|
|
||||||
|
Give a Manaphy as `pokemon` to get the Manaphy egg.
|
||||||
|
"""
|
||||||
|
def __init__(self, pokemon=None):
|
||||||
|
if pokemon and pokemon.identifier == 'manaphy':
|
||||||
|
postfix = '-manaphy'
|
||||||
|
else:
|
||||||
|
postfix = None
|
||||||
|
_BasePokemonMedia.__init__(self, 'egg', postfix)
|
||||||
|
|
||||||
|
class SubstituteMedia(_BasePokemonMedia):
|
||||||
|
"""Media related to the Substitute sprite
|
||||||
|
|
||||||
|
Note that not a lot of files are available for Substitute.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
_BasePokemonMedia.__init__(self, 'substitute')
|
||||||
|
|
||||||
|
class _BaseItemMedia(BaseMedia):
|
||||||
|
toplevel_dir = 'items'
|
||||||
|
def underground(self, rotation=0):
|
||||||
|
"""Get the item's sprite as it appears in the Sinnoh underground
|
||||||
|
|
||||||
|
Rotation can be 0, 90, 180, or 270.
|
||||||
|
"""
|
||||||
|
if rotation:
|
||||||
|
basename = self.identifier + '-%s' % rotation
|
||||||
|
else:
|
||||||
|
basename = self.identifier
|
||||||
|
return self.from_path_elements(['underground'], basename, '.png')
|
||||||
|
|
||||||
|
class ItemMedia(_BaseItemMedia):
|
||||||
|
"""Media related to an item
|
||||||
|
"""
|
||||||
|
def __init__(self, item):
|
||||||
|
self.item = item
|
||||||
|
self.identifier = item.identifier
|
||||||
|
|
||||||
|
def sprite(self, version=None):
|
||||||
|
"""Get the item's sprite
|
||||||
|
|
||||||
|
If version is not given, use the latest version.
|
||||||
|
"""
|
||||||
|
identifier = self.identifier
|
||||||
|
# Handle machines
|
||||||
|
# We check the identifier, so that we don't query the machine
|
||||||
|
# information for any item.
|
||||||
|
if identifier.startswith(('tm', 'hm')):
|
||||||
|
try:
|
||||||
|
int(identifier[2:])
|
||||||
|
except ValueError:
|
||||||
|
# Not really a TM/HM
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
machines = self.item.machines
|
||||||
|
if version:
|
||||||
|
try:
|
||||||
|
machine = [
|
||||||
|
m for m in machines
|
||||||
|
if m.version_group == version.version_group
|
||||||
|
][0]
|
||||||
|
except IndexError:
|
||||||
|
raise ValueError("%s doesn't exist in %s" % (
|
||||||
|
identifier, version.identifier))
|
||||||
|
else:
|
||||||
|
# They're ordered, so get the last one
|
||||||
|
machine = machines[-1]
|
||||||
|
type_identifier = machine.move.type.identifier
|
||||||
|
identifier = identifier[:2] + '-' + type_identifier
|
||||||
|
elif identifier.startswith('data-card-'):
|
||||||
|
try:
|
||||||
|
int(identifier[10:])
|
||||||
|
except ValueError:
|
||||||
|
# Not a real data card???
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
identifier = 'data-card'
|
||||||
|
if version is not None:
|
||||||
|
generation_id = version.generation.id
|
||||||
|
if generation_id <= 3 and identifier == 'dowsing-mchn':
|
||||||
|
identifier = 'itemfinder'
|
||||||
|
try:
|
||||||
|
gen = 'gen%s' % generation_id
|
||||||
|
return self.from_path_elements([gen], identifier, '.png')
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return self.from_path_elements([], identifier, '.png',
|
||||||
|
surely_exists=True)
|
||||||
|
|
||||||
|
def underground(self, rotation=0):
|
||||||
|
"""Get the item's sprite as it appears in the Sinnoh underground
|
||||||
|
|
||||||
|
Rotation can be 0, 90, 180, or 270.
|
||||||
|
"""
|
||||||
|
if not self.item.appears_underground:
|
||||||
|
raise ValueError("%s doesn't appear underground" % self.identifier)
|
||||||
|
return super(ItemMedia, self).underground(rotation=rotation)
|
||||||
|
|
||||||
|
def berry_image(self):
|
||||||
|
"""Get a berry's big sprite
|
||||||
|
"""
|
||||||
|
if not self.item.berry:
|
||||||
|
raise ValueError("%s is not a berry" % self.identifier)
|
||||||
|
return self.from_path_elements(['berries'], self.identifier, '.png')
|
||||||
|
|
||||||
|
class UndergroundRockMedia(_BaseItemMedia):
|
||||||
|
"""Media related to a rock in the Sinnoh underground
|
||||||
|
|
||||||
|
rock_type can be one of: i, ii, o, o-big, s, t, z
|
||||||
|
"""
|
||||||
|
def __init__(self, rock_type):
|
||||||
|
self.identifier = 'rock-%s' % rock_type
|
||||||
|
|
||||||
|
class UndergroundSphereMedia(_BaseItemMedia):
|
||||||
|
"""Media related to a sphere in the Sinnoh underground
|
||||||
|
|
||||||
|
color can be one of: red, blue, green, pale, prism
|
||||||
|
"""
|
||||||
|
def __init__(self, color, big=False):
|
||||||
|
self.identifier = '%s-sphere' % color
|
||||||
|
if big:
|
||||||
|
self.identifier += '-big'
|
||||||
|
|
||||||
|
class _SimpleIconMedia(BaseMedia):
|
||||||
|
def __init__(self, thing):
|
||||||
|
self.identifier = thing.identifier
|
||||||
|
|
||||||
|
def icon(self):
|
||||||
|
return self.from_path_elements([], self.identifier, '.png')
|
||||||
|
|
||||||
|
class DamageClassMedia(_SimpleIconMedia):
|
||||||
|
toplevel_dir = 'damage-classes'
|
||||||
|
|
||||||
|
class HabitatMedia(_SimpleIconMedia):
|
||||||
|
toplevel_dir = 'habitats'
|
||||||
|
|
||||||
|
class ShapeMedia(_SimpleIconMedia):
|
||||||
|
toplevel_dir = 'shapes'
|
||||||
|
|
||||||
|
class ItemPocketMedia(_SimpleIconMedia):
|
||||||
|
toplevel_dir = 'item-pockets'
|
||||||
|
def icon(self, selected=False):
|
||||||
|
if selected:
|
||||||
|
return self.from_path_elements(
|
||||||
|
['selected'], self.identifier, '.png')
|
||||||
|
else:
|
||||||
|
return self.from_path_elements([], self.identifier, '.png')
|
||||||
|
|
||||||
|
class _LanguageIconMedia(_SimpleIconMedia):
|
||||||
|
def icon(self, lang='en'):
|
||||||
|
return self.from_path_elements([lang], self.identifier, '.png')
|
||||||
|
|
||||||
|
class ContestTypeMedia(_LanguageIconMedia):
|
||||||
|
toplevel_dir = 'contest-types'
|
||||||
|
|
||||||
|
class TypeMedia(_LanguageIconMedia):
|
||||||
|
toplevel_dir = 'types'
|
||||||
|
|
||||||
|
''' XXX: No accessors for:
|
||||||
|
chrome
|
||||||
|
fonts
|
||||||
|
ribbons
|
||||||
|
'''
|
255
pokedex/tests/test_media.py
Normal file
255
pokedex/tests/test_media.py
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
|
||||||
|
"""Test the media accessors.
|
||||||
|
|
||||||
|
If run directly from the command line, also tests the accessors and the names
|
||||||
|
of all the media by getting just about everything in a naive brute-force way.
|
||||||
|
This, of course, takes a lot of time to run.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
from nose.tools import *
|
||||||
|
from nose.plugins.skip import SkipTest
|
||||||
|
import nose
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
from pokedex.db import tables, connect, media
|
||||||
|
|
||||||
|
session = connect()
|
||||||
|
basedir = pkg_resources.resource_filename('pokedex', 'data/media')
|
||||||
|
|
||||||
|
path_re = re.compile('^[-a-z0-9./]*$')
|
||||||
|
|
||||||
|
def test_totodile():
|
||||||
|
"""Totodile's female sprite -- same as male"""
|
||||||
|
totodile = session.query(tables.Pokemon).filter_by(identifier=u'totodile').one()
|
||||||
|
accessor = media.PokemonMedia(totodile)
|
||||||
|
assert accessor.sprite() == accessor.sprite(female=True)
|
||||||
|
|
||||||
|
def test_chimecho():
|
||||||
|
"""Chimecho's Platinum female backsprite -- diffeent from male"""
|
||||||
|
chimecho = session.query(tables.Pokemon).filter_by(identifier=u'chimecho').one()
|
||||||
|
accessor = media.PokemonMedia(chimecho)
|
||||||
|
male = accessor.sprite('platinum', back=True, frame=2)
|
||||||
|
female = accessor.sprite('platinum', back=True, female=True, frame=2)
|
||||||
|
assert male != female
|
||||||
|
|
||||||
|
def test_venonat():
|
||||||
|
"""Venonat's shiny Yellow sprite -- same as non-shiny"""
|
||||||
|
venonat = session.query(tables.Pokemon).filter_by(identifier=u'venonat').one()
|
||||||
|
accessor = media.PokemonMedia(venonat)
|
||||||
|
assert accessor.sprite('yellow') == accessor.sprite('yellow', shiny=True)
|
||||||
|
|
||||||
|
def test_arceus_icon():
|
||||||
|
"""Arceus fire-form icon -- same as base icon"""
|
||||||
|
arceus = session.query(tables.Pokemon).filter_by(identifier=u'arceus').one()
|
||||||
|
accessor = media.PokemonMedia(arceus)
|
||||||
|
fire_arceus = [f for f in arceus.forms if f.identifier == 'fire'][0]
|
||||||
|
fire_accessor = media.PokemonFormMedia(fire_arceus)
|
||||||
|
assert accessor.icon() == fire_accessor.icon()
|
||||||
|
|
||||||
|
@raises(ValueError)
|
||||||
|
def test_strict_castform():
|
||||||
|
"""Castform rainy form overworld with strict -- unavailable"""
|
||||||
|
castform = session.query(tables.Pokemon).filter_by(identifier=u'castform').first()
|
||||||
|
rainy_castform = [f for f in castform.forms if f.identifier == 'rainy'][0]
|
||||||
|
rainy_castform = media.PokemonFormMedia(rainy_castform)
|
||||||
|
rainy_castform.overworld('up', strict=True)
|
||||||
|
|
||||||
|
@raises(ValueError)
|
||||||
|
def test_strict_exeggcute():
|
||||||
|
"""Exeggcutes's female backsprite, with strict -- unavailable"""
|
||||||
|
exeggcute = session.query(tables.Pokemon).filter_by(identifier=u'exeggcute').one()
|
||||||
|
accessor = media.PokemonMedia(exeggcute)
|
||||||
|
accessor.sprite(female=True, strict=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_filenames():
|
||||||
|
print 'Reading all filenames...'
|
||||||
|
|
||||||
|
all_filenames = set()
|
||||||
|
|
||||||
|
for dirpath, dirnames, filenames in os.walk(basedir):
|
||||||
|
for filename in filenames:
|
||||||
|
path = os.path.join(dirpath, filename)
|
||||||
|
assert path_re.match(path), path
|
||||||
|
all_filenames.add(path)
|
||||||
|
|
||||||
|
return all_filenames
|
||||||
|
|
||||||
|
def hit(filenames, method, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Run the given accessor method with args & kwargs; if found remove the
|
||||||
|
result path from filenames and return True, else return False.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
medium = method(*args, **kwargs)
|
||||||
|
#print 'Hit', medium.relative_path
|
||||||
|
assert medium.exists
|
||||||
|
except ValueError, e:
|
||||||
|
#print 'DNF', e
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
print 'Error while processing', method, args, kwargs
|
||||||
|
raise
|
||||||
|
try:
|
||||||
|
filenames.remove(medium.path)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_get_everything():
|
||||||
|
"""
|
||||||
|
For every the accessor method, loop over the Cartesian products of all
|
||||||
|
possible values for its arguments.
|
||||||
|
Make sure we get every file in the repo, and that we get a file whenever
|
||||||
|
we should.
|
||||||
|
|
||||||
|
Well, there are exceptions of course.
|
||||||
|
"""
|
||||||
|
|
||||||
|
versions = list(session.query(tables.Version).all())
|
||||||
|
versions.append('red-green')
|
||||||
|
|
||||||
|
black = session.query(tables.Version).filter_by(identifier=u'black').one()
|
||||||
|
|
||||||
|
filenames = get_all_filenames()
|
||||||
|
|
||||||
|
# Some small stuff first
|
||||||
|
|
||||||
|
for damage_class in session.query(tables.MoveDamageClass).all():
|
||||||
|
assert hit(filenames, media.DamageClassMedia(damage_class).icon)
|
||||||
|
|
||||||
|
for habitat in session.query(tables.PokemonHabitat).all():
|
||||||
|
assert hit(filenames, media.HabitatMedia(habitat).icon)
|
||||||
|
|
||||||
|
for shape in session.query(tables.PokemonShape).all():
|
||||||
|
assert hit(filenames, media.ShapeMedia(shape).icon)
|
||||||
|
|
||||||
|
for item_pocket in session.query(tables.ItemPocket).all():
|
||||||
|
assert hit(filenames, media.ItemPocketMedia(item_pocket).icon)
|
||||||
|
assert hit(filenames, media.ItemPocketMedia(item_pocket).icon, selected=True)
|
||||||
|
|
||||||
|
for contest_type in session.query(tables.ContestType).all():
|
||||||
|
assert hit(filenames, media.ContestTypeMedia(contest_type).icon)
|
||||||
|
|
||||||
|
for elemental_type in session.query(tables.Type).all():
|
||||||
|
assert hit(filenames, media.TypeMedia(elemental_type).icon)
|
||||||
|
|
||||||
|
# Items
|
||||||
|
versions_for_items = [
|
||||||
|
None,
|
||||||
|
session.query(tables.Version).filter_by(identifier='emerald').one(),
|
||||||
|
]
|
||||||
|
|
||||||
|
for item in session.query(tables.Item).all():
|
||||||
|
accessor = media.ItemMedia(item)
|
||||||
|
assert hit(filenames, accessor.berry_image) or not item.berry
|
||||||
|
for rotation in (0, 90, 180, 270):
|
||||||
|
assert hit(filenames, accessor.underground, rotation=rotation) or (
|
||||||
|
not item.appears_underground or rotation)
|
||||||
|
for version in versions_for_items:
|
||||||
|
success = hit(filenames, accessor.sprite, version=version)
|
||||||
|
if version is None:
|
||||||
|
assert success
|
||||||
|
|
||||||
|
for color in 'red green blue pale prism'.split():
|
||||||
|
for big in (True, False):
|
||||||
|
accessor = media.UndergroundSphereMedia(color=color, big=big)
|
||||||
|
assert hit(filenames, accessor.underground)
|
||||||
|
|
||||||
|
for rock_type in 'i ii o o-big s t z'.split():
|
||||||
|
accessor = media.UndergroundRockMedia(rock_type)
|
||||||
|
for rotation in (0, 90, 180, 270):
|
||||||
|
success = hit(filenames, accessor.underground, rotation=rotation)
|
||||||
|
assert success or rotation
|
||||||
|
|
||||||
|
# Pokemon!
|
||||||
|
accessors = []
|
||||||
|
|
||||||
|
accessors.append(media.UnknownPokemonMedia())
|
||||||
|
accessors.append(media.EggMedia())
|
||||||
|
manaphy = session.query(tables.Pokemon).filter_by(identifier=u'manaphy').one()
|
||||||
|
accessors.append(media.EggMedia(manaphy))
|
||||||
|
accessors.append(media.SubstituteMedia())
|
||||||
|
|
||||||
|
print 'Loading pokemon'
|
||||||
|
|
||||||
|
for form in session.query(tables.PokemonForm).filter(tables.PokemonForm.identifier != '').all():
|
||||||
|
accessors.append(media.PokemonFormMedia(form))
|
||||||
|
|
||||||
|
for pokemon in session.query(tables.Pokemon).all():
|
||||||
|
accessors.append(media.PokemonMedia(pokemon))
|
||||||
|
|
||||||
|
for accessor in accessors:
|
||||||
|
assert hit(filenames, accessor.footprint) or not accessor.form
|
||||||
|
assert hit(filenames, accessor.trozei) or not accessor.form or (
|
||||||
|
accessor.form.pokemon.generation.id > 3)
|
||||||
|
assert hit(filenames, accessor.cry) or not accessor.form
|
||||||
|
assert hit(filenames, accessor.cropped_sprite) or not accessor.form
|
||||||
|
for female in (True, False):
|
||||||
|
assert hit(filenames, accessor.icon, female=female) or not accessor.form
|
||||||
|
assert hit(filenames, accessor.sugimori, female=female) or (
|
||||||
|
not accessor.form or accessor.form.pokemon.id >= 647)
|
||||||
|
for shiny in (True, False):
|
||||||
|
for frame in (1, 2):
|
||||||
|
for direction in 'up down left right'.split():
|
||||||
|
assert hit(filenames, accessor.overworld,
|
||||||
|
direction=direction,
|
||||||
|
shiny=shiny,
|
||||||
|
female=female,
|
||||||
|
frame=frame,
|
||||||
|
) or not accessor.form or (
|
||||||
|
accessor.form.pokemon.generation.id > 4)
|
||||||
|
for version in versions:
|
||||||
|
for animated in (True, False):
|
||||||
|
for back in (True, False):
|
||||||
|
for color in (None, 'gray', 'gbc'):
|
||||||
|
success = hit(filenames,
|
||||||
|
accessor.sprite,
|
||||||
|
version,
|
||||||
|
animated=animated,
|
||||||
|
back=back,
|
||||||
|
color=color,
|
||||||
|
shiny=shiny,
|
||||||
|
female=female,
|
||||||
|
frame=frame,
|
||||||
|
)
|
||||||
|
if (version == black and not animated
|
||||||
|
and not back and not color and not
|
||||||
|
shiny and not female and
|
||||||
|
frame == 1):
|
||||||
|
# All pokemon are in Black
|
||||||
|
assert success or not accessor.form
|
||||||
|
if (str(accessor.pokemon_id) == '1'
|
||||||
|
and not animated and not color and
|
||||||
|
frame == 1):
|
||||||
|
# Bulbasaur is in all versions
|
||||||
|
assert success
|
||||||
|
|
||||||
|
# Remove exceptions
|
||||||
|
exceptions = [os.path.join(basedir, dirname) for dirname in
|
||||||
|
'chrome fonts ribbons'.split()]
|
||||||
|
exceptions.append(os.path.join(basedir, 'items', 'hm-'))
|
||||||
|
exceptions = tuple(exceptions)
|
||||||
|
|
||||||
|
for filename in tuple(filenames):
|
||||||
|
if filename.startswith(exceptions):
|
||||||
|
filenames.remove(filename)
|
||||||
|
|
||||||
|
if len(filenames):
|
||||||
|
print
|
||||||
|
print '-----------------'
|
||||||
|
print 'Unaccessed stuff:'
|
||||||
|
for filename in sorted(filenames):
|
||||||
|
print filename
|
||||||
|
print len(filenames), 'unaccessed files :('
|
||||||
|
|
||||||
|
return (not filenames)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
result = nose.run(defaultTest=__file__)
|
||||||
|
result = result and check_get_everything()
|
||||||
|
exit(not result)
|
Loading…
Reference in a new issue