From 8da09532414b63bbf06e4669d3420df7b818bab2 Mon Sep 17 00:00:00 2001 From: perco Date: Mon, 6 Jul 2026 09:10:56 +0200 Subject: [PATCH] =?UTF-8?q?feat(skill-mh):=20skill=20"sort/attaque=20?= =?UTF-8?q?=C3=A0=20cible"=20(Vampirisme=20test=C3=A9=20sur=20un=20Sphinx)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mh_action_target.py gère le flux différent des formulaires classiques (Play_action.php avec ai_ToDo -> redirection vers une page ` listant les cibles disponibles sur la case +(monstres et/ou trolls). Le flux est différent de `mh_submit.py` (qui rejoue +un `
` classique) — voir `../mh/LLM_TUTORIAL.md` section 6 pour le détail +HTTP complet. + +Table des `ai_ToDo` connus (identifiant numérique de l'action) : + +| ai_ToDo | Action | PA | +|---|---|---| +| `3` | Attaque Normale | 4 | +| `107` | Frénésie | 6 | +| `149` | Lancer de Créatures | 4 | +| `203` | Vampirisme | 4 | +| `228` | Griffe du Sorcier | 4 | + +### Cycle pour "lance [sort] sur [cible]" + +**Étape A — Vérifier la cible (sans rien soumettre)** +```bash +.venv/bin/python3 mh_action_target.py "sous-texte de la cible" +``` +Exemple : `.venv/bin/python3 mh_action_target.py 203 "sphinx"` + +Renvoie un JSON `dry_run: true` avec la cible trouvée (recherche insensible à +la casse et aux accents approximatifs — si aucun résultat, le script renvoie +la liste de toutes les cibles présentes sur la case pour comparaison, utile +si Perco s'est trompé d'orthographe, ex. "sphynx" au lieu de "Sphinx"). + +**Étape B — Rapporter à Perco et demander confirmation** +> "Je vois **[cible_choisie.label]** sur la case. Veux-tu que je lance +> [nom du sort] dessus (coût [PA] PA) ?" + +**Étape C — Attendre le "oui" explicite**, comme pour toute autre action. + +**Étape D — Si confirmé, exécuter réellement** +```bash +.venv/bin/python3 mh_action_target.py "sous-texte de la cible" --execute +``` +Le script relit une page fraîche (token `cp` à jour) avant de soumettre. +Renvoie le texte complet du résultat du jeu (réussite/échec, dégâts, PX +gagnés, PA consommés). Résume ce résultat à Perco. + +**Piège évité** : ne jamais chercher la cible "à l'œil" dans un texte déjà lu +précédemment — toujours relire une page fraîche juste avant `--execute`, le +token `cp` change à chaque chargement et une cible peut disparaître/changer +entre deux tours si un monstre a bougé ou est mort entre-temps. + +**Testé et validé le 2026-07-06** : Vampirisme (`ai_ToDo=203`) sur "un Sphinx +[Adulte]" — sortilège réussi (jet 60/85%), cible touchée (130 vs esquive 72), +108 dégâts infligés (100 PV réels après armure), 2 PX gagnés, 4 PA dépensés. + +--- + ## Référence complémentaire Le repo `mh` (projet compagnon MountyHall de Perco, plus large) contient diff --git a/skill-mh/mh_action_target.py b/skill-mh/mh_action_target.py new file mode 100644 index 0000000..ecb5b87 --- /dev/null +++ b/skill-mh/mh_action_target.py @@ -0,0 +1,122 @@ +"""Lance une action a cible (sort/attaque speciale) sur un monstre/troll de la +case courante : Vampirisme, Griffe du Sorcier, etc. (cf. table ai_ToDo dans +../mh/LLM_TUTORIAL.md). + +Flux (different de mh_submit.py, qui lui rejoue un vu sur une page +"Lieu" classique) : + 1. GET Play_action.php?as_Action=ACTION!&ai_ToDo= -> redirige (302) vers + Play_a_Action.php?type=S&id=, qui affiche un trouve sur la page de cible", "html_extrait": r.text[:1500]}, None + + cibles = [ + (o["value"], o.get_text(strip=True)) + for o in select.find_all("option") + if o.get("value") + ] + + info = { + "cp": cp_input["value"] if cp_input else None, + "type": type_input["value"] if type_input else None, + "id": id_input["value"] if id_input else None, + "cibles": [{"value": v, "label": l} for v, l in cibles], + } + return info, r.text + + +if __name__ == "__main__": + if len(sys.argv) < 3: + print("Usage: python3 mh_action_target.py \"sous-texte cible\" [--execute]", file=sys.stderr) + sys.exit(1) + + ai_todo = sys.argv[1] + needle = sys.argv[2].lower() + execute = "--execute" in sys.argv + + session = get_session() + info, raw_html = load_target_page(session, ai_todo) + + if "erreur" in info: + print(json.dumps(info, ensure_ascii=False, indent=2)) + sys.exit(1) + + matches = [c for c in info["cibles"] if needle in c["label"].lower()] + + if not matches: + print(json.dumps({ + "erreur": f"Aucune cible ne contient '{sys.argv[2]}'", + "cibles_disponibles": info["cibles"], + }, ensure_ascii=False, indent=2)) + sys.exit(1) + + target = matches[0] + + if not execute: + print(json.dumps({ + "dry_run": True, + "action_type": info["type"], + "action_id": info["id"], + "cible_choisie": target, + "cp": info["cp"], + "message": "Rien soumis (pas de --execute). Confirme avec Perco avant de relancer avec --execute.", + }, ensure_ascii=False, indent=2)) + sys.exit(0) + + payload = { + "as_Action": "ACTION !!", + "type": info["type"], + "id": info["id"], + "id_target": target["value"], + "cp": info["cp"], + } + r = session.post(f"{BASE}/Play_a_ActionResult.php", data=payload) + + soup = BeautifulSoup(r.text, "html.parser") + for tag in soup.find_all(["nav", "script", "style"]): + tag.decompose() + main = soup.find("main") or soup + + print(json.dumps({ + "cible_visee": target, + "resultat": main.get_text("\n", strip=True), + }, ensure_ascii=False, indent=2))