Fixed description for one-Pokémon targeting, and effect for Me First. #135
[zzz-pokedex.git] / pokedex / db / tables.py
index eb521e1..491bfd2 100644 (file)
@@ -4,8 +4,8 @@ from sqlalchemy import Column, ForeignKey, MetaData, Table
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy.ext.associationproxy import association_proxy
 from sqlalchemy.orm import backref, relation
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy.ext.associationproxy import association_proxy
 from sqlalchemy.orm import backref, relation
+from sqlalchemy.sql import and_
 from sqlalchemy.types import *
 from sqlalchemy.types import *
-from sqlalchemy.databases.mysql import *
 
 from pokedex.db import rst
 
 
 from pokedex.db import rst
 
@@ -20,12 +20,17 @@ class Ability(TableBase):
     flavor_text = Column(Unicode(64), nullable=False)
     effect = Column(Unicode(255), nullable=False)
 
     flavor_text = Column(Unicode(64), nullable=False)
     effect = Column(Unicode(255), nullable=False)
 
+class ContestCombo(TableBase):
+    __tablename__ = 'contest_combos'
+    first_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
+    second_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
+
 class ContestEffect(TableBase):
     __tablename__ = 'contest_effects'
     id = Column(Integer, primary_key=True, nullable=False)
     appeal = Column(SmallInteger, nullable=False)
     jam = Column(SmallInteger, nullable=False)
 class ContestEffect(TableBase):
     __tablename__ = 'contest_effects'
     id = Column(Integer, primary_key=True, nullable=False)
     appeal = Column(SmallInteger, nullable=False)
     jam = Column(SmallInteger, nullable=False)
-    flavor = Column(Unicode(255), nullable=False)
+    flavor_text = Column(Unicode(64), nullable=False)
     effect = Column(Unicode(255), nullable=False)
 
 class EggGroup(TableBase):
     effect = Column(Unicode(255), nullable=False)
 
 class EggGroup(TableBase):
@@ -34,25 +39,25 @@ class EggGroup(TableBase):
     name = Column(Unicode(16), nullable=False)
 
 class Encounter(TableBase):
     name = Column(Unicode(16), nullable=False)
 
 class Encounter(TableBase):
-    """Rows in this table represent encounters with wild Pokémon.
+    """Rows in this table represent encounters with wild Pokémon.  Bear with
+    me, here.
 
     Within a given area in a given game, encounters are differentiated by the
 
     Within a given area in a given game, encounters are differentiated by the
-    slot they are in and a world condition.
+    "slot" they are in and the state of the game world.
 
 
-    Groups of slots belong to encounter types; these are what the player is
-    doing to get an encounter, such as surfing or walking through tall grass.
+    What the player is doing to get an encounter, such as surfing or walking
+    through tall grass, is called terrain.  Each terrain has its own set of
+    encounter slots.
 
 
-    Within an encounter type, slots are defined primarily by rarity.  Each slot
-    can also be affected by a world condition; for example, the 20% slot for
-    walking in tall grass is affected by whether a swarm is in effect in the
-    areas.  "There is a swarm" and "there is not a swarm" are conditions, and
-    together they make a condition group.  However, since "not a swarm" is a
-    base state rather than any sort of new state, it is omitted and instead
-    referred to by a NULL.
+    Within a terrain, slots are defined primarily by rarity.  Each slot can
+    also be affected by world conditions; for example, the 20% slot for walking
+    in tall grass is affected by whether a swarm is in effect in that area.
+    "Is there a swarm?" is a condition; "there is a swarm" and "there is not a
+    swarm" are the possible values of this condition.
 
 
-    A slot (20% walking in grass) and single world condition (NULL, i.e. no
+    A slot (20% walking in grass) and any appropriate world conditions (no
     swarm) are thus enough to define a specific encounter.
     swarm) are thus enough to define a specific encounter.
-    
+
     Well, okay, almost: each slot actually appears twice.
     """
 
     Well, okay, almost: each slot actually appears twice.
     """
 
@@ -60,56 +65,68 @@ class Encounter(TableBase):
     id = Column(Integer, primary_key=True, nullable=False)
     version_id = Column(Integer, ForeignKey('versions.id'), nullable=False, autoincrement=False)
     location_area_id = Column(Integer, ForeignKey('location_areas.id'), nullable=False, autoincrement=False)
     id = Column(Integer, primary_key=True, nullable=False)
     version_id = Column(Integer, ForeignKey('versions.id'), nullable=False, autoincrement=False)
     location_area_id = Column(Integer, ForeignKey('location_areas.id'), nullable=False, autoincrement=False)
-    encounter_type_slot_id = Column(Integer, ForeignKey('encounter_type_slots.id'), nullable=False, autoincrement=False)
-    encounter_condition_id = Column(Integer, ForeignKey('encounter_conditions.id'), nullable=True, autoincrement=False)
+    encounter_slot_id = Column(Integer, ForeignKey('encounter_slots.id'), nullable=False, autoincrement=False)
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, autoincrement=False)
     min_level = Column(Integer, nullable=False, autoincrement=False)
     max_level = Column(Integer, nullable=False, autoincrement=False)
 
 class EncounterCondition(TableBase):
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, autoincrement=False)
     min_level = Column(Integer, nullable=False, autoincrement=False)
     max_level = Column(Integer, nullable=False, autoincrement=False)
 
 class EncounterCondition(TableBase):
-    """Rows in this table represent something different about the world that
-    can affect what Pokémon are encountered.
+    """Rows in this table represent varying conditions in the game world, such
+    as time of day.
     """
 
     __tablename__ = 'encounter_conditions'
     id = Column(Integer, primary_key=True, nullable=False)
     """
 
     __tablename__ = 'encounter_conditions'
     id = Column(Integer, primary_key=True, nullable=False)
-    encounter_condition_group_id = Column(Integer, ForeignKey('encounter_condition_groups.id'), primary_key=False, nullable=False, autoincrement=False)
     name = Column(Unicode(64), nullable=False)
 
     name = Column(Unicode(64), nullable=False)
 
-class EncounterConditionGroup(TableBase):
-    """Rows in this table represent a group of mutually exclusive conditions,
-    such as morning/day/night.  "Conditions" that are part of the default state
-    of the world, such as "not during a swarm" or "not using the PokéRadar",
-    are not included in this table and are referred to by NULLs in other
-    tables.
+class EncounterConditionValue(TableBase):
+    """Rows in this table represent possible states for a condition; for
+    example, the state of 'swarm' could be 'swarm' or 'no swarm'.
     """
 
     """
 
-    __tablename__ = 'encounter_condition_groups'
+    __tablename__ = 'encounter_condition_values'
     id = Column(Integer, primary_key=True, nullable=False)
     id = Column(Integer, primary_key=True, nullable=False)
+    encounter_condition_id = Column(Integer, ForeignKey('encounter_conditions.id'), primary_key=False, nullable=False, autoincrement=False)
     name = Column(Unicode(64), nullable=False)
     name = Column(Unicode(64), nullable=False)
+    is_default = Column(Boolean, nullable=False)
+
+class EncounterConditionValueMap(TableBase):
+    """Maps encounters to the specific conditions under which they occur."""
 
 
-class EncounterType(TableBase):
-    """Rows in this table represent ways the player can enter a wild encounter;
-    i.e. surfing, fishing, or walking through tall grass.
+    __tablename__ = 'encounter_condition_value_map'
+    encounter_id = Column(Integer, ForeignKey('encounters.id'), primary_key=True, nullable=False, autoincrement=False)
+    encounter_condition_value_id = Column(Integer, ForeignKey('encounter_condition_values.id'), primary_key=True, nullable=False, autoincrement=False)
+
+class EncounterTerrain(TableBase):
+    """Rows in this table represent ways the player can enter a wild encounter,
+    e.g., surfing, fishing, or walking through tall grass.
     """
 
     """
 
-    __tablename__ = 'encounter_types'
+    __tablename__ = 'encounter_terrain'
     id = Column(Integer, primary_key=True, nullable=False)
     name = Column(Unicode(64), nullable=False)
 
     id = Column(Integer, primary_key=True, nullable=False)
     name = Column(Unicode(64), nullable=False)
 
-class EncounterTypeSlot(TableBase):
-    """Rows in this table represent an abstract "slot" within an encounter
-    type, associated with both a condition group and a rarity.
+class EncounterSlot(TableBase):
+    """Rows in this table represent an abstract "slot" within a terrain,
+    associated with both some set of conditions and a rarity.
 
     Note that there are two encounters per slot, so the rarities will only add
     up to 50.
     """
 
 
     Note that there are two encounters per slot, so the rarities will only add
     up to 50.
     """
 
-    __tablename__ = 'encounter_type_slots'
+    __tablename__ = 'encounter_slots'
     id = Column(Integer, primary_key=True, nullable=False)
     id = Column(Integer, primary_key=True, nullable=False)
-    encounter_type_id = Column(Integer, ForeignKey('encounter_types.id'), primary_key=False, nullable=False, autoincrement=False)
-    encounter_condition_group_id = Column(Integer, ForeignKey('encounter_condition_groups.id'), primary_key=False, nullable=True, autoincrement=False)
-    rarity = Column(Integer, nullable=False, autoincrement=False)
+    version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False, autoincrement=False)
+    encounter_terrain_id = Column(Integer, ForeignKey('encounter_terrain.id'), primary_key=False, nullable=False, autoincrement=False)
+    slot = Column(Integer, nullable=True)
+    rarity = Column(Integer, nullable=False)
+
+class EncounterSlotCondition(TableBase):
+    """Lists all conditions that affect each slot."""
+
+    __tablename__ = 'encounter_slot_conditions'
+    encounter_slot_id = Column(Integer, ForeignKey('encounter_slots.id'), primary_key=True, nullable=False, autoincrement=False)
+    encounter_condition_id = Column(Integer, ForeignKey('encounter_conditions.id'), primary_key=True, nullable=False, autoincrement=False)
 
 class EvolutionChain(TableBase):
     __tablename__ = 'evolution_chains'
 
 class EvolutionChain(TableBase):
     __tablename__ = 'evolution_chains'
@@ -127,8 +144,9 @@ class EvolutionMethod(TableBase):
 class Generation(TableBase):
     __tablename__ = 'generations'
     id = Column(Integer, primary_key=True, nullable=False)
 class Generation(TableBase):
     __tablename__ = 'generations'
     id = Column(Integer, primary_key=True, nullable=False)
+    main_region_id = Column(Integer, ForeignKey('regions.id'))
+    canonical_pokedex_id = Column(Integer, ForeignKey('pokedexes.id'))
     name = Column(Unicode(16), nullable=False)
     name = Column(Unicode(16), nullable=False)
-    main_region = Column(Unicode(16), nullable=False)
 
 class GrowthRate(TableBase):
     """`formula` is written in LaTeX math notation."""
 
 class GrowthRate(TableBase):
     """`formula` is written in LaTeX math notation."""
@@ -146,13 +164,15 @@ class Item(TableBase):
 class Language(TableBase):
     __tablename__ = 'languages'
     id = Column(Integer, primary_key=True, nullable=False)
 class Language(TableBase):
     __tablename__ = 'languages'
     id = Column(Integer, primary_key=True, nullable=False)
+    iso639 = Column(Unicode(2), nullable=False)
+    iso3166 = Column(Unicode(2), nullable=False)
     name = Column(Unicode(16), nullable=False)
 
 class Location(TableBase):
     __tablename__ = 'locations'
     __singlename__ = 'location'
     id = Column(Integer, primary_key=True, nullable=False)
     name = Column(Unicode(16), nullable=False)
 
 class Location(TableBase):
     __tablename__ = 'locations'
     __singlename__ = 'location'
     id = Column(Integer, primary_key=True, nullable=False)
-    generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
+    region_id = Column(Integer, ForeignKey('regions.id'))
     name = Column(Unicode(64), nullable=False)
 
 class LocationArea(TableBase):
     name = Column(Unicode(64), nullable=False)
 
 class LocationArea(TableBase):
@@ -165,15 +185,27 @@ class LocationArea(TableBase):
 class LocationAreaEncounterRate(TableBase):
     __tablename__ = 'location_area_encounter_rates'
     location_area_id = Column(Integer, ForeignKey('location_areas.id'), primary_key=True, nullable=False, autoincrement=False)
 class LocationAreaEncounterRate(TableBase):
     __tablename__ = 'location_area_encounter_rates'
     location_area_id = Column(Integer, ForeignKey('location_areas.id'), primary_key=True, nullable=False, autoincrement=False)
-    encounter_type_id = Column(Integer, ForeignKey('encounter_types.id'), primary_key=True, nullable=False, autoincrement=False)
+    encounter_type_id = Column(Integer, ForeignKey('encounter_terrain.id'), primary_key=True, nullable=False, autoincrement=False)
     rate = Column(Integer, nullable=True)
 
 class Machine(TableBase):
     __tablename__ = 'machines'
     machine_number = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
     rate = Column(Integer, nullable=True)
 
 class Machine(TableBase):
     __tablename__ = 'machines'
     machine_number = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
-    generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, nullable=False, autoincrement=False)
+    version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False)
     move_id = Column(Integer, ForeignKey('moves.id'), nullable=False)
 
     move_id = Column(Integer, ForeignKey('moves.id'), nullable=False)
 
+class MoveEffectCategory(TableBase):
+    __tablename__ = 'move_effect_categories'
+    id = Column(Integer, primary_key=True, nullable=False)
+    name = Column(Unicode(64), nullable=False)
+    can_affect_user = Column(Boolean, nullable=False)
+
+class MoveEffectCategoryMap(TableBase):
+    __tablename__ = 'move_effect_category_map'
+    move_effect_id = Column(Integer, ForeignKey('move_effects.id'), primary_key=True, nullable=False)
+    move_effect_category_id = Column(Integer, ForeignKey('move_effect_categories.id'), primary_key=True, nullable=False)
+    affects_user = Column(Boolean, primary_key=True, nullable=False)
+
 class MoveDamageClass(TableBase):
     __tablename__ = 'move_damage_classes'
     id = Column(Integer, primary_key=True, nullable=False)
 class MoveDamageClass(TableBase):
     __tablename__ = 'move_damage_classes'
     id = Column(Integer, primary_key=True, nullable=False)
@@ -198,6 +230,12 @@ class MoveFlagType(TableBase):
     name = Column(Unicode(32), nullable=False)
     description = Column(rst.RstTextColumn(128), nullable=False)
 
     name = Column(Unicode(32), nullable=False)
     description = Column(rst.RstTextColumn(128), nullable=False)
 
+class MoveFlavorText(TableBase):
+    __tablename__ = 'move_flavor_text'
+    move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
+    generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, nullable=False, autoincrement=False)
+    flavor_text = Column(Unicode(255), nullable=False)
+
 class MoveName(TableBase):
     __tablename__ = 'move_names'
     move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
 class MoveName(TableBase):
     __tablename__ = 'move_names'
     move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
@@ -226,7 +264,27 @@ class Move(TableBase):
     effect_chance = Column(Integer)
     contest_type = Column(Unicode(8), nullable=False)
     contest_effect_id = Column(Integer, ForeignKey('contest_effects.id'), nullable=True)
     effect_chance = Column(Integer)
     contest_type = Column(Unicode(8), nullable=False)
     contest_effect_id = Column(Integer, ForeignKey('contest_effects.id'), nullable=True)
-    super_contest_effect_id = Column(Integer, nullable=False)
+    super_contest_effect_id = Column(Integer, ForeignKey('super_contest_effects.id'), nullable=False)
+
+class Nature(TableBase):
+    __tablename__ = 'natures'
+    __singlename__ = 'nature'
+    id = Column(Integer, primary_key=True, nullable=False)
+    name = Column(Unicode(8), nullable=False)
+    decreased_stat_id = Column(Integer, ForeignKey('stats.id'), nullable=False)
+    increased_stat_id = Column(Integer, ForeignKey('stats.id'), nullable=False)
+
+class Pokedex(TableBase):
+    __tablename__ = 'pokedexes'
+    id = Column(Integer, primary_key=True, nullable=False)
+    region_id = Column(Integer, ForeignKey('regions.id'), nullable=True)
+    name = Column(Unicode(16), nullable=False)
+    description = Column(Unicode(512))
+
+class PokedexVersionGroup(TableBase):
+    __tablename__ = 'pokedex_version_groups'
+    pokedex_id = Column(Integer, ForeignKey('pokedexes.id'), primary_key=True, nullable=False, autoincrement=False)
+    version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False)
 
 class Pokemon(TableBase):
     """The core to this whole mess.
 
 class Pokemon(TableBase):
     """The core to this whole mess.
@@ -251,14 +309,13 @@ class Pokemon(TableBase):
     height = Column(Integer, nullable=False)
     weight = Column(Integer, nullable=False)
     species = Column(Unicode(16), nullable=False)
     height = Column(Integer, nullable=False)
     weight = Column(Integer, nullable=False)
     species = Column(Unicode(16), nullable=False)
-    color = Column(Unicode(6), nullable=False)
+    color_id = Column(Integer, ForeignKey('pokemon_colors.id'), nullable=False)
     pokemon_shape_id = Column(Integer, ForeignKey('pokemon_shapes.id'), nullable=False)
     pokemon_shape_id = Column(Integer, ForeignKey('pokemon_shapes.id'), nullable=False)
-    habitat = Column(Unicode(16), nullable=False)
+    habitat_id = Column(Integer, ForeignKey('pokemon_habitats.id'), nullable=True)
     gender_rate = Column(Integer, nullable=False)
     capture_rate = Column(Integer, nullable=False)
     base_experience = Column(Integer, nullable=False)
     base_happiness = Column(Integer, nullable=False)
     gender_rate = Column(Integer, nullable=False)
     capture_rate = Column(Integer, nullable=False)
     base_experience = Column(Integer, nullable=False)
     base_happiness = Column(Integer, nullable=False)
-    gen1_internal_id = Column(Integer)
     is_baby = Column(Boolean, nullable=False)
     has_gen4_fem_sprite = Column(Boolean, nullable=False)
     has_gen4_fem_back_sprite = Column(Boolean, nullable=False)
     is_baby = Column(Boolean, nullable=False)
     has_gen4_fem_sprite = Column(Boolean, nullable=False)
     has_gen4_fem_back_sprite = Column(Boolean, nullable=False)
@@ -280,7 +337,7 @@ class Pokemon(TableBase):
         """Returns the name of this Pokémon, including its Forme, if any."""
 
         if self.forme_name:
         """Returns the name of this Pokémon, including its Forme, if any."""
 
         if self.forme_name:
-            return "%s %s" % (self.forme_name.capitalize(), self.name)
+            return "%s %s" % (self.forme_name.title(), self.name)
         return self.name
 
     @property
         return self.name
 
     @property
@@ -294,16 +351,37 @@ class Pokemon(TableBase):
 
         return self
 
 
         return self
 
+    ### Not forms!
+
+    def stat(self, stat_name):
+        """Returns a PokemonStat record for the given stat name (or Stat row
+        object).  Uses the normal has-many machinery, so all the stats are
+        effectively cached.
+        """
+        if isinstance(stat_name, Stat):
+            stat_name = stat_name.name
+
+        for pokemon_stat in self.stats:
+            if pokemon_stat.stat.name == stat_name:
+                return pokemon_stat
+
+        return None
+
 class PokemonAbility(TableBase):
     __tablename__ = 'pokemon_abilities'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
     ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False)
     slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
 
 class PokemonAbility(TableBase):
     __tablename__ = 'pokemon_abilities'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
     ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False)
     slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
 
+class PokemonColor(TableBase):
+    __tablename__ = 'pokemon_colors'
+    id = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
+    name = Column(Unicode(6), nullable=False)
+
 class PokemonDexNumber(TableBase):
     __tablename__ = 'pokemon_dex_numbers'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
 class PokemonDexNumber(TableBase):
     __tablename__ = 'pokemon_dex_numbers'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
-    generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, nullable=False, autoincrement=False)
+    pokedex_id = Column(Integer, ForeignKey('pokedexes.id'), primary_key=True, nullable=False, autoincrement=False)
     pokedex_number = Column(Integer, nullable=False)
 
 class PokemonEggGroup(TableBase):
     pokedex_number = Column(Integer, nullable=False)
 
 class PokemonEggGroup(TableBase):
@@ -320,13 +398,21 @@ class PokemonFlavorText(TableBase):
 class PokemonFormGroup(TableBase):
     __tablename__ = 'pokemon_form_groups'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
 class PokemonFormGroup(TableBase):
     __tablename__ = 'pokemon_form_groups'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
+    is_battle_only = Column(Boolean, nullable=False)
     description = Column(Unicode(512), nullable=False)
 
 class PokemonFormSprite(TableBase):
     __tablename__ = 'pokemon_form_sprites'
     id = Column(Integer, primary_key=True, nullable=False)
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
     description = Column(Unicode(512), nullable=False)
 
 class PokemonFormSprite(TableBase):
     __tablename__ = 'pokemon_form_sprites'
     id = Column(Integer, primary_key=True, nullable=False)
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
+    introduced_in_version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False)
     name = Column(Unicode(16), nullable=True)
     name = Column(Unicode(16), nullable=True)
+    is_default = Column(Boolean, nullable=True)
+
+class PokemonHabitat(TableBase):
+    __tablename__ = 'pokemon_habitats'
+    id = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
+    name = Column(Unicode(16), nullable=False)
 
 class PokemonItem(TableBase):
     __tablename__ = 'pokemon_items'
 
 class PokemonItem(TableBase):
     __tablename__ = 'pokemon_items'
@@ -375,11 +461,28 @@ class PokemonType(TableBase):
     type_id = Column(Integer, ForeignKey('types.id'), nullable=False)
     slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
 
     type_id = Column(Integer, ForeignKey('types.id'), nullable=False)
     slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
 
+class Region(TableBase):
+    """Major areas of the world: Kanto, Johto, etc."""
+    __tablename__ = 'regions'
+    id = Column(Integer, primary_key=True, nullable=False)
+    name = Column(Unicode(16), nullable=False)
+
 class Stat(TableBase):
     __tablename__ = 'stats'
     id = Column(Integer, primary_key=True, nullable=False)
     name = Column(Unicode(16), nullable=False)
 
 class Stat(TableBase):
     __tablename__ = 'stats'
     id = Column(Integer, primary_key=True, nullable=False)
     name = Column(Unicode(16), nullable=False)
 
+class SuperContestCombo(TableBase):
+    __tablename__ = 'super_contest_combos'
+    first_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
+    second_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
+
+class SuperContestEffect(TableBase):
+    __tablename__ = 'super_contest_effects'
+    id = Column(Integer, primary_key=True, nullable=False)
+    appeal = Column(SmallInteger, nullable=False)
+    flavor_text = Column(Unicode(64), nullable=False)
+
 class TypeEfficacy(TableBase):
     __tablename__ = 'type_efficacy'
     damage_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False)
 class TypeEfficacy(TableBase):
     __tablename__ = 'type_efficacy'
     damage_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False)
@@ -392,12 +495,19 @@ class Type(TableBase):
     id = Column(Integer, primary_key=True, nullable=False)
     name = Column(Unicode(8), nullable=False)
     abbreviation = Column(Unicode(3), nullable=False)
     id = Column(Integer, primary_key=True, nullable=False)
     name = Column(Unicode(8), nullable=False)
     abbreviation = Column(Unicode(3), nullable=False)
+    generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
+    damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=False) ## ??? is none; everything else is physical or special
 
 class VersionGroup(TableBase):
     __tablename__ = 'version_groups'
     id = Column(Integer, primary_key=True, nullable=False)
     generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
 
 
 class VersionGroup(TableBase):
     __tablename__ = 'version_groups'
     id = Column(Integer, primary_key=True, nullable=False)
     generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
 
+class VersionGroupRegion(TableBase):
+    __tablename__ = 'version_group_regions'
+    version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False)
+    region_id = Column(Integer, ForeignKey('regions.id'), primary_key=True, nullable=False)
+
 class Version(TableBase):
     __tablename__ = 'versions'
     id = Column(Integer, primary_key=True, nullable=False)
 class Version(TableBase):
     __tablename__ = 'versions'
     id = Column(Integer, primary_key=True, nullable=False)
@@ -406,32 +516,56 @@ class Version(TableBase):
 
 
 ### Relations down here, to avoid ordering problems
 
 
 ### Relations down here, to avoid ordering problems
+ContestCombo.first = relation(Move, primaryjoin=ContestCombo.first_move_id==Move.id,
+                                    backref='contest_combo_first')
+ContestCombo.second = relation(Move, primaryjoin=ContestCombo.second_move_id==Move.id,
+                                     backref='contest_combo_second')
+
+Encounter.location_area = relation(LocationArea, backref='encounters')
 Encounter.pokemon = relation(Pokemon, backref='encounters')
 Encounter.version = relation(Version, backref='encounters')
 Encounter.pokemon = relation(Pokemon, backref='encounters')
 Encounter.version = relation(Version, backref='encounters')
-Encounter.location_area = relation(LocationArea, backref='encounters')
-Encounter.slot = relation(EncounterTypeSlot, backref='encounters')
-Encounter.condition = relation(EncounterCondition, backref='encounters')
+Encounter.slot = relation(EncounterSlot, backref='encounters')
+
+EncounterConditionValue.condition = relation(EncounterCondition, backref='values')
 
 
-EncounterCondition.group = relation(EncounterConditionGroup,
-                                    backref='conditions')
+Encounter.condition_value_map = relation(EncounterConditionValueMap, backref='encounter')
+Encounter.condition_values = association_proxy('condition_value_map', 'condition_value')
+EncounterConditionValueMap.condition_value = relation(EncounterConditionValue,
+                                                      backref='encounter_map')
 
 
-EncounterTypeSlot.type = relation(EncounterType, backref='slots')
+EncounterSlot.terrain = relation(EncounterTerrain, backref='slots')
+
+EncounterSlot.condition_map = relation(EncounterSlotCondition, backref='slot')
+EncounterSlot.conditions = association_proxy('condition_map', 'condition')
+EncounterSlotCondition.condition = relation(EncounterCondition,
+                                            backref='slot_map')
 
 EvolutionChain.growth_rate = relation(GrowthRate, backref='evolution_chains')
 
 
 EvolutionChain.growth_rate = relation(GrowthRate, backref='evolution_chains')
 
+Generation.canonical_pokedex = relation(Pokedex, backref='canonical_for_generation')
 Generation.versions = relation(Version, secondary=VersionGroup.__table__)
 Generation.versions = relation(Version, secondary=VersionGroup.__table__)
+Generation.main_region = relation(Region)
+
+Location.region = relation(Region, backref='locations')
 
 LocationArea.location = relation(Location, backref='areas')
 
 
 LocationArea.location = relation(Location, backref='areas')
 
-Machine.generation = relation(Generation)
+Machine.version_group = relation(VersionGroup)
 
 
+Move.contest_effect = relation(ContestEffect, backref='moves')
+Move.contest_combo_next = association_proxy('contest_combo_first', 'second')
+Move.contest_combo_prev = association_proxy('contest_combo_second', 'first')
 Move.damage_class = relation(MoveDamageClass, backref='moves')
 Move.flags = association_proxy('move_flags', 'flag')
 Move.damage_class = relation(MoveDamageClass, backref='moves')
 Move.flags = association_proxy('move_flags', 'flag')
+Move.flavor_text = relation(MoveFlavorText, order_by=MoveFlavorText.generation_id, backref='move')
 Move.foreign_names = relation(MoveName, backref='pokemon')
 Move.generation = relation(Generation, backref='moves')
 Move.machines = relation(Machine, backref='move')
 Move.move_effect = relation(MoveEffect, backref='moves')
 Move.move_flags = relation(MoveFlag, backref='move')
 Move.foreign_names = relation(MoveName, backref='pokemon')
 Move.generation = relation(Generation, backref='moves')
 Move.machines = relation(Machine, backref='move')
 Move.move_effect = relation(MoveEffect, backref='moves')
 Move.move_flags = relation(MoveFlag, backref='move')
+Move.super_contest_effect = relation(SuperContestEffect, backref='moves')
+Move.super_contest_combo_next = association_proxy('super_contest_combo_first', 'second')
+Move.super_contest_combo_prev = association_proxy('super_contest_combo_second', 'first')
 Move.target = relation(MoveTarget, backref='moves')
 Move.type = relation(Type, backref='moves')
 
 Move.target = relation(MoveTarget, backref='moves')
 Move.type = relation(Type, backref='moves')
 
@@ -439,17 +573,39 @@ Move.effect = rst.MoveEffectProperty('effect')
 Move.priority = association_proxy('move_effect', 'priority')
 Move.short_effect = rst.MoveEffectProperty('short_effect')
 
 Move.priority = association_proxy('move_effect', 'priority')
 Move.short_effect = rst.MoveEffectProperty('short_effect')
 
+MoveEffect.category_map = relation(MoveEffectCategoryMap)
+MoveEffect.categories = association_proxy('category_map', 'category')
+MoveEffectCategoryMap.category = relation(MoveEffectCategory)
+
 MoveFlag.flag = relation(MoveFlagType)
 
 MoveFlag.flag = relation(MoveFlagType)
 
+MoveFlavorText.generation = relation(Generation)
+
 MoveName.language = relation(Language)
 
 MoveName.language = relation(Language)
 
+Nature.decreased_stat = relation(Stat, primaryjoin=Nature.decreased_stat_id==Stat.id,
+                                       backref='decreasing_natures')
+Nature.increased_stat = relation(Stat, primaryjoin=Nature.increased_stat_id==Stat.id,
+                                       backref='increasing_natures')
+
+Pokedex.region = relation(Region, backref='pokedexes')
+Pokedex.version_groups = relation(VersionGroup, secondary=PokedexVersionGroup.__table__, backref='pokedexes')
+
 Pokemon.abilities = relation(Ability, secondary=PokemonAbility.__table__,
                                       order_by=PokemonAbility.slot,
                                       backref='pokemon')
 Pokemon.formes = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.forme_base_pokemon_id,
                                                backref=backref('forme_base_pokemon',
                                                                remote_side=[Pokemon.id]))
 Pokemon.abilities = relation(Ability, secondary=PokemonAbility.__table__,
                                       order_by=PokemonAbility.slot,
                                       backref='pokemon')
 Pokemon.formes = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.forme_base_pokemon_id,
                                                backref=backref('forme_base_pokemon',
                                                                remote_side=[Pokemon.id]))
+Pokemon.pokemon_color = relation(PokemonColor, backref='pokemon')
+Pokemon.color = association_proxy('pokemon_color', 'name')
 Pokemon.dex_numbers = relation(PokemonDexNumber, backref='pokemon')
 Pokemon.dex_numbers = relation(PokemonDexNumber, backref='pokemon')
+Pokemon.default_form_sprite = relation(PokemonFormSprite,
+                                       primaryjoin=and_(
+                                            Pokemon.id==PokemonFormSprite.pokemon_id,
+                                            PokemonFormSprite.is_default==True,
+                                       ),
+                                       uselist=False)
 Pokemon.egg_groups = relation(EggGroup, secondary=PokemonEggGroup.__table__,
                                         order_by=PokemonEggGroup.egg_group_id,
                                         backref='pokemon')
 Pokemon.egg_groups = relation(EggGroup, secondary=PokemonEggGroup.__table__,
                                         order_by=PokemonEggGroup.egg_group_id,
                                         backref='pokemon')
@@ -458,15 +614,17 @@ Pokemon.evolution_method = relation(EvolutionMethod)
 Pokemon.evolution_children = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.evolution_parent_pokemon_id,
                                                backref=backref('evolution_parent',
                                                                remote_side=[Pokemon.id]))
 Pokemon.evolution_children = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.evolution_parent_pokemon_id,
                                                backref=backref('evolution_parent',
                                                                remote_side=[Pokemon.id]))
-Pokemon.flavor_text = relation(PokemonFlavorText, backref='pokemon')
+Pokemon.flavor_text = relation(PokemonFlavorText, order_by=PokemonFlavorText.pokemon_id, backref='pokemon')
 Pokemon.foreign_names = relation(PokemonName, backref='pokemon')
 Pokemon.foreign_names = relation(PokemonName, backref='pokemon')
+Pokemon.pokemon_habitat = relation(PokemonHabitat, backref='pokemon')
+Pokemon.habitat = association_proxy('pokemon_habitat', 'name')
 Pokemon.items = relation(PokemonItem)
 Pokemon.generation = relation(Generation, backref='pokemon')
 Pokemon.shape = relation(PokemonShape, backref='pokemon')
 Pokemon.stats = relation(PokemonStat, backref='pokemon')
 Pokemon.types = relation(Type, secondary=PokemonType.__table__)
 
 Pokemon.items = relation(PokemonItem)
 Pokemon.generation = relation(Generation, backref='pokemon')
 Pokemon.shape = relation(PokemonShape, backref='pokemon')
 Pokemon.stats = relation(PokemonStat, backref='pokemon')
 Pokemon.types = relation(Type, secondary=PokemonType.__table__)
 
-PokemonDexNumber.generation = relation(Generation)
+PokemonDexNumber.pokedex = relation(Pokedex)
 
 PokemonFlavorText.version = relation(Version)
 
 
 PokemonFlavorText.version = relation(Version)
 
@@ -476,9 +634,15 @@ PokemonItem.version = relation(Version)
 PokemonFormGroup.pokemon = relation(Pokemon, backref=backref('form_group',
                                                              uselist=False))
 PokemonFormSprite.pokemon = relation(Pokemon, backref='form_sprites')
 PokemonFormGroup.pokemon = relation(Pokemon, backref=backref('form_group',
                                                              uselist=False))
 PokemonFormSprite.pokemon = relation(Pokemon, backref='form_sprites')
+PokemonFormSprite.introduced_in = relation(VersionGroup)
 
 PokemonMove.pokemon = relation(Pokemon, backref='pokemon_moves')
 PokemonMove.version_group = relation(VersionGroup)
 
 PokemonMove.pokemon = relation(Pokemon, backref='pokemon_moves')
 PokemonMove.version_group = relation(VersionGroup)
+PokemonMove.machine = relation(Machine, backref='pokemon_moves',
+                               primaryjoin=and_(Machine.version_group_id==PokemonMove.version_group_id,
+                                                Machine.move_id==PokemonMove.move_id),
+                                foreign_keys=[Machine.version_group_id, Machine.move_id],
+                                uselist=False)
 PokemonMove.move = relation(Move, backref='pokemon_moves')
 PokemonMove.method = relation(PokemonMoveMethod)
 
 PokemonMove.move = relation(Move, backref='pokemon_moves')
 PokemonMove.method = relation(PokemonMoveMethod)
 
@@ -486,6 +650,17 @@ PokemonName.language = relation(Language)
 
 PokemonStat.stat = relation(Stat)
 
 
 PokemonStat.stat = relation(Stat)
 
+# This is technically a has-many; Generation.main_region_id -> Region.id
+Region.generation = relation(Generation, uselist=False)
+Region.version_group_regions = relation(VersionGroupRegion, backref='region',
+                                        order_by='VersionGroupRegion.version_group_id')
+Region.version_groups = association_proxy('version_group_regions', 'version_group')
+
+SuperContestCombo.first = relation(Move, primaryjoin=SuperContestCombo.first_move_id==Move.id,
+                                        backref='super_contest_combo_first')
+SuperContestCombo.second = relation(Move, primaryjoin=SuperContestCombo.second_move_id==Move.id,
+                                         backref='super_contest_combo_second')
+
 Type.damage_efficacies = relation(TypeEfficacy,
                                   primaryjoin=Type.id
                                       ==TypeEfficacy.damage_type_id,
 Type.damage_efficacies = relation(TypeEfficacy,
                                   primaryjoin=Type.id
                                       ==TypeEfficacy.damage_type_id,
@@ -495,7 +670,12 @@ Type.target_efficacies = relation(TypeEfficacy,
                                       ==TypeEfficacy.target_type_id,
                                   backref='target_type')
 
                                       ==TypeEfficacy.target_type_id,
                                   backref='target_type')
 
+Type.generation = relation(Generation, backref='types')
+Type.damage_class = relation(MoveDamageClass, backref='types')
+
 Version.version_group = relation(VersionGroup, backref='versions')
 Version.generation = association_proxy('version_group', 'generation')
 
 VersionGroup.generation = relation(Generation, backref='version_groups')
 Version.version_group = relation(VersionGroup, backref='versions')
 Version.generation = association_proxy('version_group', 'generation')
 
 VersionGroup.generation = relation(Generation, backref='version_groups')
+VersionGroup.version_group_regions = relation(VersionGroupRegion, backref='version_group')
+VersionGroup.regions = association_proxy('version_group_regions', 'region')