import json from typing import Any # ZQSD (convention AZERTY, comme le reste de l'éditeur) + E pour interagir # — un objet de scène kind="personnage" se déplace et s'anime TOUT SEUL # avec ces touches dès qu'il est posé sur la scène (voir # static/game/js/play/personnage-controller.js), sans le moindre nœud de flow # à poser. "axe" ("libre"/"horizontal"/"vertical") permet de bloquer un # axe de déplacement (ex. un personnage qui ne se déplace que de gauche à # droite). "vitesse" est en pixels par tick (50ms, voir # personnage-controller.js — même cadence que touche_maintenue). # "extra" : touches SUPPLÉMENTAIRES, chacune liée à une pose du personnage # au choix (ex. "sauter" -> "jump") — au-delà des 4 touches de déplacement # et de "interagir", qui restent les deux seules touches à usage FIXE. DEFAULT_PERSONNAGE_COMMANDS: dict[str, Any] = { "haut": "z", "bas": "s", "gauche": "q", "droite": "d", "interagir": "e", "axe": "libre", "vitesse": 4, "extra": [], } # Nombre maximum de touches supplémentaires — large marge pour un vrai # jeu (sauter, attaquer, courir...) sans laisser un panneau grossir sans # fin par erreur de manipulation. _MAX_EXTRA_COMMANDS = 20 def sanitize_extra_commands(raw_extra: Any) -> list[dict[str, str]]: if not isinstance(raw_extra, list): return [] cleaned: list[dict[str, str]] = [] for item in raw_extra[:_MAX_EXTRA_COMMANDS]: if not isinstance(item, dict): continue touche = item.get("touche") animation = item.get("animation") if isinstance(touche, str) and touche and isinstance(animation, str) and animation: cleaned.append({"touche": touche, "animation": animation}) return cleaned def resolve_personnage_commands(el: dict[str, Any]) -> dict[str, Any]: """Décode attributes._personnage_commandes (voir game_engine/scenes/set_scene_object_commands.py) en dict Python, jamais None — un attribut absent/corrompu retombe sur les touches par défaut ci-dessus (comportement inchangé pour tout personnage déjà posé avant l'ajout de ce réglage).""" raw = (el.get("attributes") or {}).get("_personnage_commandes") commands = dict(DEFAULT_PERSONNAGE_COMMANDS) commands["extra"] = [] # jamais l'alias de la liste DEFAULT (mutable partagée) if raw: try: parsed = json.loads(raw) except (TypeError, ValueError): parsed = None if isinstance(parsed, dict): for key in ("haut", "bas", "gauche", "droite", "interagir", "axe", "vitesse"): if key in parsed: commands[key] = parsed[key] commands["extra"] = sanitize_extra_commands(parsed.get("extra")) return commands