feat(engine): persistenza enigmi, inventario strutturato, HP mostri
test / test (push) Has been cancelled
test / test (push) Has been cancelled
Fase 2 del remediation plan: - set_puzzle_state / is_puzzle_solved per enigmi area - update_inventory su character_items con add/remove/equip/list - combat() legge HP da combat_state e persiste danno - dm_cycle inietta campaign_id nei tool che ne hanno bisogno - helpers db: dedup inventario, remove/equip per nome Fase 4 del remediation plan: - test integrazione dm_cycle con LLM mockato (5 test) - test WebSocket handler con mock (4 test) - CI minimo .gitea/workflows/test.yml
This commit is contained in:
1 parent
cb45172717
commit
6b048dc0cc
7 files changed
+386
-18
No files matched your search
+67
-9
@@ -62,6 +62,8 @@ def combat(
|
||||
mighty_deed_die: str = "",
|
||||
luck_mod: int = 0,
|
||||
armor_type: str = "none",
|
||||
monster_id: str = "",
|
||||
campaign_id: int = 0,
|
||||
) -> ToolResult:
|
||||
char = storage.get_character(character_id)
|
||||
if not char:
|
||||
@@ -71,7 +73,6 @@ def combat(
|
||||
atk_roll = dice.roll("d20", modifier=attack_bonus)
|
||||
raw_total = atk_roll["total"]
|
||||
|
||||
|
||||
deed_roll = None
|
||||
if mighty_deed_die:
|
||||
deed_roll = dice.roll(mighty_deed_die)
|
||||
@@ -81,6 +82,17 @@ def combat(
|
||||
result_parts = []
|
||||
crit_res = None
|
||||
|
||||
# HP persistenti: carica mostro da combat_state se disponibile
|
||||
monster_name = ""
|
||||
monster_hp_before = None
|
||||
if monster_id and campaign_id:
|
||||
state = storage.get_combat_state(campaign_id)
|
||||
monster_state = next((m for m in state if m["monster_id"] == monster_id), None)
|
||||
if monster_state:
|
||||
monster_name = monster_state["monster_name"]
|
||||
monster_hp_before = monster_state["current_hp"]
|
||||
result_parts.append(f"{monster_name}: PF {monster_state['current_hp']}/{monster_state['max_hp']}")
|
||||
|
||||
if atk_roll["fumble"]:
|
||||
fumble_die_name = storage.get_fumble_die(armor_type) or "d4"
|
||||
fumble_roll = dice.roll(fumble_die_name)
|
||||
@@ -101,6 +113,10 @@ def combat(
|
||||
+ (crit_res["result"] if crit_res else "Nessun risultato trovato.")
|
||||
)
|
||||
|
||||
# Calcolo danno e persistenza
|
||||
total_dmg = 0
|
||||
new_hp = None
|
||||
monster_defeated = False
|
||||
if hit and not atk_roll["fumble"]:
|
||||
dmg = dice.roll(damage_die, modifier=damage_bonus)
|
||||
crit_dmg_bonus = 0
|
||||
@@ -108,6 +124,15 @@ def combat(
|
||||
crit_dmg_bonus = _parse_extra_damage(crit_res.get("result", ""))
|
||||
total_dmg = dmg["total"] + crit_dmg_bonus
|
||||
result_parts.append(f"Colpito! Danni: {dmg['results']} + {crit_dmg_bonus} extra = {total_dmg}")
|
||||
|
||||
if monster_id and campaign_id and monster_hp_before is not None:
|
||||
hp_result = storage.update_combat_hp(campaign_id, monster_id, total_dmg)
|
||||
if hp_result:
|
||||
new_hp = hp_result["new_hp"]
|
||||
monster_defeated = hp_result["status"] == "defeated"
|
||||
result_parts.append(f"PF {monster_name}: {monster_hp_before} → {new_hp}")
|
||||
if monster_defeated:
|
||||
result_parts.append(f"{monster_name} sconfitto!")
|
||||
elif not atk_roll["fumble"]:
|
||||
result_parts.append("Mancato!")
|
||||
|
||||
@@ -125,6 +150,9 @@ def combat(
|
||||
"crit": atk_roll["crit"],
|
||||
"fumble": atk_roll["fumble"],
|
||||
"deed": deed_roll["total"] if deed_roll else None,
|
||||
"monster_hp_before": monster_hp_before,
|
||||
"monster_hp_after": new_hp,
|
||||
"monster_defeated": monster_defeated,
|
||||
"message": f"Iniziativa: {init_roll['total']} | Attacco d20+{attack_bonus}: {atk_roll['results'][0]}+{attack_bonus}={atk_roll['total']} vs CA {target_ac} | {'COLPI TO!' if hit else 'Mancato.'}{deed_msg} | " + " | ".join(result_parts),
|
||||
}
|
||||
|
||||
@@ -273,25 +301,55 @@ def spellcasting(character_id: int, spell_name: str, spell_check: int, dc: int =
|
||||
|
||||
|
||||
@tool
|
||||
def update_inventory(character_id: int, item: str, action: str = "add") -> ToolResult:
|
||||
def update_inventory(character_id: int, item: str = "", action: str = "add") -> ToolResult:
|
||||
char = storage.get_character(character_id)
|
||||
if not char:
|
||||
return {"success": False, "tool": "update-inventory", "message": "Personaggio non trovato."}
|
||||
inv = json.loads(char.get("equipment", "[]"))
|
||||
|
||||
if action == "add":
|
||||
inv.append(item)
|
||||
if not item:
|
||||
return {"success": False, "tool": "update-inventory", "message": "Specificare un oggetto da aggiungere."}
|
||||
storage.add_character_item(character_id, item)
|
||||
elif action == "remove":
|
||||
inv = [i for i in inv if i != item]
|
||||
if not item:
|
||||
return {"success": False, "tool": "update-inventory", "message": "Specificare un oggetto da rimuovere."}
|
||||
if not storage.remove_character_item(character_id, item):
|
||||
return {"success": False, "tool": "update-inventory", "message": f"Oggetto '{item}' non trovato."}
|
||||
elif action == "equip":
|
||||
if not item:
|
||||
return {"success": False, "tool": "update-inventory", "message": "Specificare un oggetto da equipaggiare."}
|
||||
if not storage.equip_character_item(character_id, item, True):
|
||||
return {"success": False, "tool": "update-inventory", "message": f"Oggetto '{item}' non trovato."}
|
||||
elif action == "unequip":
|
||||
if not item:
|
||||
return {"success": False, "tool": "update-inventory", "message": "Specificare un oggetto da rimuovere."}
|
||||
if not storage.equip_character_item(character_id, item, False):
|
||||
return {"success": False, "tool": "update-inventory", "message": f"Oggetto '{item}' non trovato."}
|
||||
elif action == "list":
|
||||
items = storage.get_character_items(character_id)
|
||||
names = [f"{i['name']} x{i['quantity']}{' (eq.)' if i['equipped'] else ''}" for i in items]
|
||||
return {
|
||||
"success": True,
|
||||
"tool": "update-inventory",
|
||||
"action": action,
|
||||
"inventory": [dict(i) for i in items],
|
||||
"message": f"Inventario: {len(items)} oggetti — " + (", ".join(names) if names else "vuoto."),
|
||||
}
|
||||
else:
|
||||
return {"success": False, "tool": "update-inventory", "message": f"Azione '{action}' non valida. Usa 'add' o 'remove'."}
|
||||
storage.update_character(character_id, equipment=json.dumps(inv))
|
||||
return {"success": False, "tool": "update-inventory", "message": f"Azione '{action}' non valida. Usa: add, remove, equip, unequip, list."}
|
||||
|
||||
# Sincronizza campo equipment legacy per backward compat
|
||||
items = storage.get_character_items(character_id)
|
||||
names = [i["name"] for i in items if i.get("quantity", 1) > 0]
|
||||
storage.update_character(character_id, equipment=json.dumps(names))
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"tool": "update-inventory",
|
||||
"action": action,
|
||||
"item": item,
|
||||
"inventory": inv,
|
||||
"message": f"Inventario: {action} '{item}' → {len(inv)} oggetti.",
|
||||
"inventory": [dict(i) for i in storage.get_character_items(character_id)],
|
||||
"message": f"Inventario: {action} '{item}' → aggiornato.",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user