Fix database constraints and team builder save functionality
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
60dbcae113
commit
08f7aa8ea8
3 changed files with 382 additions and 20 deletions
159
src/database.py
159
src/database.py
|
|
@ -315,6 +315,58 @@ class Database:
|
|||
)
|
||||
""")
|
||||
|
||||
# Create species_moves table for move learning system
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS species_moves (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
species_id INTEGER NOT NULL,
|
||||
move_id INTEGER NOT NULL,
|
||||
learn_level INTEGER NOT NULL,
|
||||
learn_method TEXT DEFAULT 'level',
|
||||
FOREIGN KEY (species_id) REFERENCES pet_species (id),
|
||||
FOREIGN KEY (move_id) REFERENCES moves (id),
|
||||
UNIQUE(species_id, move_id, learn_level)
|
||||
)
|
||||
""")
|
||||
|
||||
# Create location type compatibility table
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS location_type_compatibility (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
location_id INTEGER NOT NULL,
|
||||
pet_type TEXT NOT NULL,
|
||||
compatibility_modifier REAL DEFAULT 1.0,
|
||||
FOREIGN KEY (location_id) REFERENCES locations (id),
|
||||
UNIQUE(location_id, pet_type)
|
||||
)
|
||||
""")
|
||||
|
||||
# Create indexes for performance optimization
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS idx_pets_player_active ON pets (player_id, is_active)")
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS idx_pets_species ON pets (species_id)")
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS idx_player_encounters_player ON player_encounters (player_id)")
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS idx_player_encounters_species ON player_encounters (species_id)")
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS idx_location_spawns_location ON location_spawns (location_id)")
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS idx_species_moves_species ON species_moves (species_id)")
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS idx_species_moves_level ON species_moves (learn_level)")
|
||||
|
||||
# Add team size validation trigger
|
||||
await db.execute("""
|
||||
CREATE TRIGGER IF NOT EXISTS validate_team_size_on_update
|
||||
AFTER UPDATE OF is_active ON pets
|
||||
WHEN NEW.is_active != OLD.is_active
|
||||
BEGIN
|
||||
UPDATE pets SET is_active = OLD.is_active
|
||||
WHERE id = NEW.id AND (
|
||||
SELECT COUNT(*) FROM pets
|
||||
WHERE player_id = NEW.player_id AND is_active = TRUE
|
||||
) < 1;
|
||||
END
|
||||
""")
|
||||
|
||||
# Enable foreign key constraints
|
||||
await db.execute("PRAGMA foreign_keys = ON")
|
||||
|
||||
await db.commit()
|
||||
|
||||
async def get_player(self, nickname: str) -> Optional[Dict]:
|
||||
|
|
@ -863,10 +915,10 @@ class Database:
|
|||
"levels_gained": new_level - old_level
|
||||
}
|
||||
|
||||
# Update experience
|
||||
# Update experience and level
|
||||
await db.execute("""
|
||||
UPDATE pets SET experience = ? WHERE id = ?
|
||||
""", (new_exp, pet_id))
|
||||
UPDATE pets SET experience = ?, level = ? WHERE id = ?
|
||||
""", (new_exp, new_level, pet_id))
|
||||
|
||||
# Handle level up if it occurred
|
||||
if new_level > old_level:
|
||||
|
|
@ -1585,4 +1637,105 @@ class Database:
|
|||
"success": True,
|
||||
"pins_cleaned": pins_cleaned,
|
||||
"changes_cleaned": changes_cleaned
|
||||
}
|
||||
|
||||
# Species Moves System Methods
|
||||
async def get_species_moves(self, species_id: int, max_level: int = None) -> List[Dict]:
|
||||
"""Get moves that a species can learn up to a certain level"""
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
|
||||
query = """
|
||||
SELECT sm.*, m.name, m.type, m.category, m.power, m.accuracy, m.pp, m.description
|
||||
FROM species_moves sm
|
||||
JOIN moves m ON sm.move_id = m.id
|
||||
WHERE sm.species_id = ?
|
||||
"""
|
||||
params = [species_id]
|
||||
|
||||
if max_level:
|
||||
query += " AND sm.learn_level <= ?"
|
||||
params.append(max_level)
|
||||
|
||||
query += " ORDER BY sm.learn_level ASC"
|
||||
|
||||
cursor = await db.execute(query, params)
|
||||
rows = await cursor.fetchall()
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
async def get_learnable_moves_for_pet(self, pet_id: int) -> List[Dict]:
|
||||
"""Get moves a specific pet can learn at their current level"""
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
|
||||
# Get pet info
|
||||
cursor = await db.execute("""
|
||||
SELECT p.species_id, p.level, p.id
|
||||
FROM pets p WHERE p.id = ?
|
||||
""", (pet_id,))
|
||||
pet = await cursor.fetchone()
|
||||
|
||||
if not pet:
|
||||
return []
|
||||
|
||||
# Get moves the pet can learn but doesn't know yet
|
||||
cursor = await db.execute("""
|
||||
SELECT sm.*, m.name, m.type, m.category, m.power, m.accuracy, m.pp, m.description
|
||||
FROM species_moves sm
|
||||
JOIN moves m ON sm.move_id = m.id
|
||||
WHERE sm.species_id = ? AND sm.learn_level <= ?
|
||||
AND sm.move_id NOT IN (
|
||||
SELECT pm.move_id FROM pet_moves pm WHERE pm.pet_id = ?
|
||||
)
|
||||
ORDER BY sm.learn_level DESC
|
||||
""", (pet["species_id"], pet["level"], pet_id))
|
||||
|
||||
rows = await cursor.fetchall()
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
async def validate_team_composition(self, player_id: int, proposed_changes: Dict) -> Dict:
|
||||
"""Validate team changes before applying them"""
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
|
||||
# Get current pet states
|
||||
cursor = await db.execute("""
|
||||
SELECT id, is_active FROM pets WHERE player_id = ?
|
||||
""", (player_id,))
|
||||
current_pets = {str(row["id"]): bool(row["is_active"]) for row in await cursor.fetchall()}
|
||||
|
||||
# Apply proposed changes to current state
|
||||
new_state = current_pets.copy()
|
||||
for pet_id, new_active_state in proposed_changes.items():
|
||||
if pet_id in new_state:
|
||||
new_state[pet_id] = new_active_state
|
||||
|
||||
# Count active pets in new state
|
||||
active_count = sum(1 for is_active in new_state.values() if is_active)
|
||||
|
||||
# Validate constraints
|
||||
if active_count < 1:
|
||||
return {"valid": False, "error": "Must have at least 1 active pet"}
|
||||
|
||||
if active_count > 6: # Pokemon-style 6 pet limit
|
||||
return {"valid": False, "error": "Cannot have more than 6 active pets"}
|
||||
|
||||
return {"valid": True, "active_count": active_count}
|
||||
|
||||
async def get_team_composition(self, player_id: int) -> Dict:
|
||||
"""Get current team composition stats"""
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute("""
|
||||
SELECT
|
||||
COUNT(*) as total_pets,
|
||||
SUM(CASE WHEN is_active THEN 1 ELSE 0 END) as active_pets,
|
||||
SUM(CASE WHEN NOT is_active THEN 1 ELSE 0 END) as storage_pets
|
||||
FROM pets WHERE player_id = ?
|
||||
""", (player_id,))
|
||||
|
||||
result = await cursor.fetchone()
|
||||
return {
|
||||
"total_pets": result[0],
|
||||
"active_pets": result[1],
|
||||
"storage_pets": result[2]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue