344 lines
11 KiB
Python
344 lines
11 KiB
Python
# Description: Loadout database operations for Lemontropia Suite
|
|
# Manages saving, loading, and retrieving complete gear configurations
|
|
# Standards: Python 3.11+, type hints, Decimal precision for PED/PEC
|
|
|
|
import json
|
|
import logging
|
|
from decimal import Decimal
|
|
from typing import Optional, List, Dict, Any
|
|
from dataclasses import asdict
|
|
|
|
from core.database import DatabaseManager
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class LoadoutDatabase:
|
|
"""Database operations for loadout management."""
|
|
|
|
def __init__(self, db_manager: Optional[DatabaseManager] = None):
|
|
"""Initialize with database manager."""
|
|
self.db = db_manager or DatabaseManager()
|
|
|
|
def save_loadout(self, name: str, config: 'LoadoutConfig', description: str = "") -> int:
|
|
"""
|
|
Save a loadout configuration to database.
|
|
|
|
Args:
|
|
name: Unique loadout name
|
|
config: LoadoutConfig object
|
|
description: Optional description
|
|
|
|
Returns:
|
|
Loadout ID
|
|
"""
|
|
conn = self.db.get_connection()
|
|
|
|
# Convert config to JSON-serializable dict
|
|
plates_json = json.dumps(config.armor_plates) if config.armor_plates else "{}"
|
|
enhancers_json = json.dumps(
|
|
{str(k): {"name": v.name, "decay": float(v.decay)}
|
|
for k, v in config.enhancers.items()}
|
|
) if config.enhancers else "{}"
|
|
accessories_json = json.dumps({
|
|
"left_ring": config.left_ring,
|
|
"right_ring": config.right_ring,
|
|
"clothing": config.clothing_items,
|
|
"pet": config.pet
|
|
})
|
|
|
|
cursor = conn.execute("""
|
|
INSERT OR REPLACE INTO loadouts (
|
|
name, description, updated_at,
|
|
weapon_name, weapon_damage, weapon_decay_pec, weapon_ammo_pec,
|
|
weapon_dpp, weapon_efficiency,
|
|
amplifier_name, amplifier_decay_pec,
|
|
scope_name, scope_decay_pec,
|
|
absorber_name, absorber_decay_pec,
|
|
armor_name, armor_decay_per_hp,
|
|
plates_json,
|
|
healing_tool_name, healing_decay_pec, healing_amount,
|
|
mindforce_implant_name, mindforce_decay_pec,
|
|
left_ring, right_ring, pet_name, accessories_json,
|
|
enhancers_json,
|
|
cost_per_shot_ped, cost_per_hit_ped, cost_per_heal_ped
|
|
) VALUES (
|
|
?, ?, CURRENT_TIMESTAMP,
|
|
?, ?, ?, ?, ?, ?,
|
|
?, ?,
|
|
?, ?,
|
|
?, ?,
|
|
?, ?,
|
|
?,
|
|
?, ?, ?,
|
|
?, ?,
|
|
?, ?, ?, ?,
|
|
?,
|
|
?, ?, ?
|
|
)
|
|
""", (
|
|
name, description,
|
|
config.weapon_name,
|
|
float(config.weapon_damage),
|
|
float(config.weapon_decay_pec),
|
|
float(config.weapon_ammo_pec),
|
|
float(config.weapon_dpp),
|
|
float(config.weapon_efficiency),
|
|
config.weapon_amplifier.name if config.weapon_amplifier else None,
|
|
float(config.weapon_amplifier.decay_pec) if config.weapon_amplifier else 0.0,
|
|
config.weapon_scope.name if config.weapon_scope else None,
|
|
float(config.weapon_scope.decay_pec) if config.weapon_scope else 0.0,
|
|
config.weapon_absorber.name if config.weapon_absorber else None,
|
|
float(config.weapon_absorber.decay_pec) if config.weapon_absorber else 0.0,
|
|
config.armor_name,
|
|
float(config.armor_decay_per_hp),
|
|
plates_json,
|
|
config.heal_name,
|
|
float(config.heal_cost_pec),
|
|
float(config.heal_amount),
|
|
config.mindforce_implant,
|
|
float(config.mindforce_decay_pec),
|
|
config.left_ring,
|
|
config.right_ring,
|
|
config.pet,
|
|
accessories_json,
|
|
enhancers_json,
|
|
float(config.get_cost_per_shot()),
|
|
float(config.get_cost_per_hit()),
|
|
float(config.get_cost_per_heal())
|
|
))
|
|
|
|
conn.commit()
|
|
loadout_id = cursor.lastrowid
|
|
logger.info(f"Saved loadout '{name}' (ID: {loadout_id})")
|
|
return loadout_id
|
|
|
|
def get_loadout(self, name: str) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
Retrieve a loadout by name.
|
|
|
|
Args:
|
|
name: Loadout name
|
|
|
|
Returns:
|
|
Loadout data dict or None
|
|
"""
|
|
conn = self.db.get_connection()
|
|
cursor = conn.execute(
|
|
"SELECT * FROM loadouts WHERE name = ?",
|
|
(name,)
|
|
)
|
|
row = cursor.fetchone()
|
|
|
|
if row:
|
|
return dict(row)
|
|
return None
|
|
|
|
def list_loadouts(self) -> List[Dict[str, Any]]:
|
|
"""
|
|
List all saved loadouts.
|
|
|
|
Returns:
|
|
List of loadout dicts
|
|
"""
|
|
conn = self.db.get_connection()
|
|
cursor = conn.execute(
|
|
"""SELECT id, name, description, weapon_name, armor_name,
|
|
cost_per_shot_ped, cost_per_hit_ped, cost_per_heal_ped,
|
|
is_active, created_at, updated_at
|
|
FROM loadouts ORDER BY updated_at DESC"""
|
|
)
|
|
return [dict(row) for row in cursor.fetchall()]
|
|
|
|
def set_active_loadout(self, loadout_id: int) -> bool:
|
|
"""
|
|
Set a loadout as the active one.
|
|
|
|
Args:
|
|
loadout_id: Loadout ID to activate
|
|
|
|
Returns:
|
|
True if successful
|
|
"""
|
|
conn = self.db.get_connection()
|
|
|
|
# Clear all active flags first
|
|
conn.execute("UPDATE loadouts SET is_active = 0")
|
|
|
|
# Set new active
|
|
cursor = conn.execute(
|
|
"UPDATE loadouts SET is_active = 1 WHERE id = ?",
|
|
(loadout_id,)
|
|
)
|
|
conn.commit()
|
|
|
|
if cursor.rowcount > 0:
|
|
logger.info(f"Set loadout {loadout_id} as active")
|
|
return True
|
|
return False
|
|
|
|
def get_active_loadout(self) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
Get the currently active loadout.
|
|
|
|
Returns:
|
|
Active loadout dict or None
|
|
"""
|
|
conn = self.db.get_connection()
|
|
cursor = conn.execute(
|
|
"SELECT * FROM loadouts WHERE is_active = 1 LIMIT 1"
|
|
)
|
|
row = cursor.fetchone()
|
|
|
|
if row:
|
|
return dict(row)
|
|
return None
|
|
|
|
def delete_loadout(self, name: str) -> bool:
|
|
"""
|
|
Delete a loadout by name.
|
|
|
|
Args:
|
|
name: Loadout name to delete
|
|
|
|
Returns:
|
|
True if deleted
|
|
"""
|
|
conn = self.db.get_connection()
|
|
cursor = conn.execute(
|
|
"DELETE FROM loadouts WHERE name = ?",
|
|
(name,)
|
|
)
|
|
conn.commit()
|
|
|
|
if cursor.rowcount > 0:
|
|
logger.info(f"Deleted loadout '{name}'")
|
|
return True
|
|
return False
|
|
|
|
def link_loadout_to_session(self, session_id: int, loadout_id: int) -> bool:
|
|
"""
|
|
Link a loadout to a hunting session.
|
|
|
|
Args:
|
|
session_id: Hunting session ID
|
|
loadout_id: Loadout ID
|
|
|
|
Returns:
|
|
True if successful
|
|
"""
|
|
conn = self.db.get_connection()
|
|
|
|
# Get loadout data
|
|
loadout = conn.execute(
|
|
"SELECT * FROM loadouts WHERE id = ?",
|
|
(loadout_id,)
|
|
).fetchone()
|
|
|
|
if not loadout:
|
|
logger.error(f"Loadout {loadout_id} not found")
|
|
return False
|
|
|
|
# Update hunting session with loadout reference and snapshot
|
|
conn.execute("""
|
|
UPDATE hunting_sessions
|
|
SET loadout_id = ?,
|
|
weapon_name = ?,
|
|
armor_name = ?,
|
|
fap_name = ?
|
|
WHERE session_id = ?
|
|
""", (
|
|
loadout_id,
|
|
loadout['weapon_name'],
|
|
loadout['armor_name'],
|
|
loadout['healing_tool_name'],
|
|
session_id
|
|
))
|
|
|
|
conn.commit()
|
|
logger.info(f"Linked loadout {loadout_id} to session {session_id}")
|
|
return True
|
|
|
|
def update_session_costs(self, session_id: int,
|
|
shots: int = 0,
|
|
hits: int = 0,
|
|
heals: int = 0) -> Dict[str, Decimal]:
|
|
"""
|
|
Calculate and update costs for a hunting session based on loadout.
|
|
|
|
Args:
|
|
session_id: Hunting session ID
|
|
shots: Number of shots fired
|
|
hits: Number of hits taken
|
|
heals: Number of heals used
|
|
|
|
Returns:
|
|
Dict with cost breakdown
|
|
"""
|
|
conn = self.db.get_connection()
|
|
|
|
# Get session with linked loadout
|
|
session = conn.execute("""
|
|
SELECT hs.*, l.*
|
|
FROM hunting_sessions hs
|
|
LEFT JOIN loadouts l ON hs.loadout_id = l.id
|
|
WHERE hs.session_id = ?
|
|
""", (session_id,)).fetchone()
|
|
|
|
if not session:
|
|
logger.error(f"Session {session_id} not found")
|
|
return {}
|
|
|
|
# Calculate costs
|
|
weapon_cost = Decimal(str(session['cost_per_shot_ped'] or 0)) * shots
|
|
armor_cost = Decimal(str(session['cost_per_hit_ped'] or 0)) * hits
|
|
healing_cost = Decimal(str(session['cost_per_heal_ped'] or 0)) * heals
|
|
|
|
# Add enhancer costs (if applicable)
|
|
enhancer_cost = Decimal("0")
|
|
if session['enhancers_json']:
|
|
try:
|
|
enhancers = json.loads(session['enhancers_json'])
|
|
for tier, enh in enhancers.items():
|
|
enhancer_cost += Decimal(str(enh.get('decay', 0))) * shots
|
|
except json.JSONDecodeError:
|
|
pass
|
|
|
|
# Add mindforce costs
|
|
mindforce_cost = Decimal(str(session['mindforce_decay_pec'] or 0)) * heals * Decimal("0.01")
|
|
|
|
total_cost = weapon_cost + armor_cost + healing_cost + enhancer_cost + mindforce_cost
|
|
|
|
# Update session
|
|
conn.execute("""
|
|
UPDATE hunting_sessions
|
|
SET weapon_cost_ped = ?,
|
|
armor_cost_ped = ?,
|
|
healing_cost_ped = ?,
|
|
enhancer_cost_ped = ?,
|
|
mindforce_cost_ped = ?,
|
|
total_cost_ped = ?,
|
|
shots_fired = shots_fired + ?,
|
|
hits_taken = hits_taken + ?,
|
|
heals_used = heals_used + ?
|
|
WHERE session_id = ?
|
|
""", (
|
|
float(weapon_cost), float(armor_cost), float(healing_cost),
|
|
float(enhancer_cost), float(mindforce_cost), float(total_cost),
|
|
shots, hits, heals,
|
|
session_id
|
|
))
|
|
conn.commit()
|
|
|
|
costs = {
|
|
'weapon': weapon_cost,
|
|
'armor': armor_cost,
|
|
'healing': healing_cost,
|
|
'enhancer': enhancer_cost,
|
|
'mindforce': mindforce_cost,
|
|
'total': total_cost
|
|
}
|
|
|
|
logger.debug(f"Updated session {session_id} costs: {costs}")
|
|
return costs
|