# game_state.py : structures de donnees broadcastees au client a chaque tick from dataclasses import dataclass, field @dataclass class ProjectileState: id: str owner_id: str x: float y: float vx: float vy: float damage: int radius: float ttl: float def to_dict(self): # vx/vy/damage/ttl : info serveur, pas envoyes au client return { "id": self.id, "owner": self.owner_id, "x": self.x, "y": self.y, "radius": self.radius, } @dataclass class EnemyState: id: str type: str x: float y: float hp: int max_hp: int attack_cooldown: float = 0.0 is_boss: bool = False frozen_timer: float = 0.0 # gel du sort divin Seris data: dict = field(default_factory=dict) # interne au boss (phases, charge), pas broadcast def to_dict(self): return { "id": self.id, "type": self.type, "x": self.x, "y": self.y, "hp": self.hp, "max_hp": self.max_hp, "is_boss": self.is_boss, "frozen": self.frozen_timer > 0, "is_charging": self.data.get("is_charging", False) if self.is_boss else False, } @dataclass class PlayerState: id: str class_name: str username: str x: float y: float hp: int max_hp: int alive: bool revive_progress: float = 0.0 cooldowns: dict = field(default_factory=lambda: { "attack": 0, "ability_1": 0, "ability_2": 0, "displacement": 0, }) # buffs actifs : {"type": "...", "timer": float, ...} # types : invulnerable, intangible, flying, casting, damage_mult, speed_mult, combat_buff, divine_freeze_dps buffs: list = field(default_factory=list) souls: int = 0 enemies_killed: int = 0 # upgrade_id -> nb stacks (damage_up, cooldown_down, hp_up, speed_up) upgrades: dict = field(default_factory=dict) def to_dict(self): return { "id": self.id, "class": self.class_name, "username": self.username, "x": self.x, "y": self.y, "hp": self.hp, "max_hp": self.max_hp, "alive": self.alive, "revive_progress": self.revive_progress, "cooldowns": self.cooldowns, "buffs": self.buffs, "souls": self.souls, "enemies_killed": self.enemies_killed, "upgrades": self.upgrades, } @dataclass class AoeZone: id: str owner_id: str zone_type: str x: float y: float radius: float damage_per_tick: int ticks_remaining: int max_ticks: int def to_dict(self): return { "id": self.id, "type": self.zone_type, "x": self.x, "y": self.y, "radius": self.radius, "ticks": self.ticks_remaining, "max_ticks": self.max_ticks, } @dataclass class SoulgateState: hp: int max_hp: int def to_dict(self): return {"hp": self.hp, "max_hp": self.max_hp} @dataclass class WaveState: number: int phase: int enemies_remaining: int state: str # combat | preparation | boss | victory prep_timer: float = 0.0 boss_hp: int = 0 boss_max_hp: int = 0 boss_name: str = "" def to_dict(self): return { "number": self.number, "phase": self.phase, "enemies_remaining": self.enemies_remaining, "state": self.state, "prep_timer": self.prep_timer, "boss_hp": self.boss_hp, "boss_max_hp": self.boss_max_hp, "boss_name": self.boss_name, } @dataclass class GameState: tick: int players: list enemies: list projectiles: list soulgate: SoulgateState wave: WaveState effects: list aoe_zones: list = field(default_factory=list) def to_dict(self): return { "type": "game_state", "tick": self.tick, "players": [p.to_dict() for p in self.players], "enemies": [e.to_dict() for e in self.enemies], "projectiles": [p.to_dict() for p in self.projectiles], "aoe_zones": [z.to_dict() for z in self.aoe_zones], "soulgate": self.soulgate.to_dict(), "wave": self.wave.to_dict(), "effects": self.effects, }