Fix forage/find_water : loot réel selon food/water_level de la zone
This commit is contained in:
+107
-8
@@ -44,6 +44,42 @@ _ROLE_SKILL_BONUS: dict[str, dict[str, int]] = {
|
|||||||
"survivant_generique":{"survie": 1},
|
"survivant_generique":{"survie": 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tables de loot par action de survie
|
||||||
|
# Chance de succès = food_level ou water_level de la zone (0-100)
|
||||||
|
# divisé par un facteur de difficulté (plus la zone est riche, plus c'est facile)
|
||||||
|
#
|
||||||
|
# Format : (item_slug, item_name, item_type, weight, hp_heal, properties)
|
||||||
|
# TODO : enrichir avec animaux/cadavres/types de zone
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_FORAGE_LOOT: list[tuple] = [
|
||||||
|
# (slug, nom, type, poids, hp_heal, props)
|
||||||
|
("cram", "Cram", "food", 0.5, 5, {}),
|
||||||
|
("igname_sauvage","Igname sauvage", "food", 0.3, 3, {}),
|
||||||
|
("champignon", "Champignons des marais","food", 0.2, 2, {"cooked": False}),
|
||||||
|
("rat_grille", "Rat grillé", "food", 0.4, 4, {}),
|
||||||
|
("nuka_cola", "Nuka-Cola", "drink", 0.5, 2, {"thirst_gain": 1}),
|
||||||
|
("cram", "Cram", "food", 0.5, 5, {}), # double poids pour fréquence
|
||||||
|
]
|
||||||
|
|
||||||
|
_WATER_LOOT: list[tuple] = [
|
||||||
|
("eau_sale", "Eau sale", "drink", 0.5, 0, {"irradiated": True, "rad_on_drink": 1}),
|
||||||
|
("eau_sale", "Eau sale", "drink", 0.5, 0, {"irradiated": True, "rad_on_drink": 1}),
|
||||||
|
("eau_purifiee", "Eau purifiée", "drink", 0.5, 0, {}),
|
||||||
|
("nuka_cola", "Nuka-Cola", "drink", 0.5, 2, {"thirst_gain": 1}),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Seuils de chance de succès selon le niveau de ressource de la zone
|
||||||
|
def _forage_chance(food_level: float) -> float:
|
||||||
|
"""0-100 → probabilité de trouver quelque chose (0.05 à 0.70)"""
|
||||||
|
return max(0.05, min(0.70, food_level / 100 * 0.65 + 0.05))
|
||||||
|
|
||||||
|
def _water_chance(water_level: float) -> float:
|
||||||
|
"""0-100 → probabilité de trouver de l'eau (0.10 à 0.80)"""
|
||||||
|
return max(0.10, min(0.80, water_level / 100 * 0.70 + 0.10))
|
||||||
|
|
||||||
|
|
||||||
# Paires de factions hostiles l'une envers l'autre
|
# Paires de factions hostiles l'une envers l'autre
|
||||||
_HOSTILE_PAIRS: set[frozenset] = {
|
_HOSTILE_PAIRS: set[frozenset] = {
|
||||||
frozenset({"ecumeurs", "union"}),
|
frozenset({"ecumeurs", "union"}),
|
||||||
@@ -306,6 +342,68 @@ def _decide_action(char: dict, all_chars: list[dict],
|
|||||||
"description": f"{char['name']} en veille"}
|
"description": f"{char['name']} en veille"}
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_forage(char: dict, session_id: int, day: int, tick: int,
|
||||||
|
world_map: dict, rng: random.Random | None) -> None:
|
||||||
|
"""Tente de trouver de la nourriture selon le food_level de la zone."""
|
||||||
|
import survival as sv_module
|
||||||
|
rng = rng or random.Random()
|
||||||
|
loc = char.get("location_slug", "")
|
||||||
|
ws = world_map.get(loc, {})
|
||||||
|
food_lvl = ws.get("food_level", 20)
|
||||||
|
chance = _forage_chance(food_lvl)
|
||||||
|
|
||||||
|
if rng.random() < chance:
|
||||||
|
item = rng.choice(_FORAGE_LOOT)
|
||||||
|
slug, name, itype, weight, hp_heal, props = item
|
||||||
|
db.add_item(char["id"], session_id, slug, name, itype,
|
||||||
|
quantity=1, weight=weight, hp_heal=hp_heal, properties=props)
|
||||||
|
# Consommer immédiatement si c'est de la nourriture
|
||||||
|
if itype == "food":
|
||||||
|
sv_module.feed_character(session_id, char["id"], day, tick, "normal")
|
||||||
|
db.remove_item(char["id"], session_id, slug, 1)
|
||||||
|
desc = f"{char['name']} trouve {name} en fouillant ({loc}, food={food_lvl:.0f})"
|
||||||
|
effect = {"found": slug, "food_level": food_lvl, "chance": round(chance, 2)}
|
||||||
|
else:
|
||||||
|
desc = f"{char['name']} ne trouve rien à manger ({loc}, food={food_lvl:.0f})"
|
||||||
|
effect = {"found": None, "food_level": food_lvl, "chance": round(chance, 2)}
|
||||||
|
|
||||||
|
db.log_event(session_id=session_id, day=day, tick=tick,
|
||||||
|
event_type="fouille_nourriture",
|
||||||
|
description=desc, actor_id=char["id"],
|
||||||
|
location_slug=loc, mechanical_effect=effect)
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_find_water(char: dict, session_id: int, day: int, tick: int,
|
||||||
|
world_map: dict, rng: random.Random | None) -> None:
|
||||||
|
"""Tente de trouver de l'eau selon le water_level de la zone."""
|
||||||
|
import survival as sv_module
|
||||||
|
rng = rng or random.Random()
|
||||||
|
loc = char.get("location_slug", "")
|
||||||
|
ws = world_map.get(loc, {})
|
||||||
|
water_lvl = ws.get("water_level", 20)
|
||||||
|
chance = _water_chance(water_lvl)
|
||||||
|
|
||||||
|
if rng.random() < chance:
|
||||||
|
item = rng.choice(_WATER_LOOT)
|
||||||
|
slug, name, itype, weight, hp_heal, props = item
|
||||||
|
db.add_item(char["id"], session_id, slug, name, itype,
|
||||||
|
quantity=1, weight=weight, hp_heal=hp_heal, properties=props)
|
||||||
|
# Consommer immédiatement
|
||||||
|
sv_module.hydrate_character(session_id, char["id"], day, tick,
|
||||||
|
"purified" if slug == "eau_purifiee" else "normal")
|
||||||
|
db.remove_item(char["id"], session_id, slug, 1)
|
||||||
|
desc = f"{char['name']} trouve {name} ({loc}, water={water_lvl:.0f})"
|
||||||
|
effect = {"found": slug, "water_level": water_lvl, "chance": round(chance, 2)}
|
||||||
|
else:
|
||||||
|
desc = f"{char['name']} ne trouve pas d'eau ({loc}, water={water_lvl:.0f})"
|
||||||
|
effect = {"found": None, "water_level": water_lvl, "chance": round(chance, 2)}
|
||||||
|
|
||||||
|
db.log_event(session_id=session_id, day=day, tick=tick,
|
||||||
|
event_type="fouille_eau",
|
||||||
|
description=desc, actor_id=char["id"],
|
||||||
|
location_slug=loc, mechanical_effect=effect)
|
||||||
|
|
||||||
|
|
||||||
def _execute_action(char: dict, decision: dict,
|
def _execute_action(char: dict, decision: dict,
|
||||||
chars_map: dict, inventories: dict,
|
chars_map: dict, inventories: dict,
|
||||||
session_id: int, day: int, tick: int) -> None:
|
session_id: int, day: int, tick: int) -> None:
|
||||||
@@ -387,14 +485,12 @@ def _execute_action(char: dict, decision: dict,
|
|||||||
location_slug=char.get("location_slug"),
|
location_slug=char.get("location_slug"),
|
||||||
)
|
)
|
||||||
|
|
||||||
elif action in ("forage", "find_water"):
|
elif action == "forage":
|
||||||
db.log_event(
|
_resolve_forage(char, session_id, day, tick, world_map=decision.get("_world_map", {}), rng=decision.get("_rng"))
|
||||||
session_id=session_id, day=day, tick=tick,
|
|
||||||
event_type="survie_action",
|
elif action == "find_water":
|
||||||
description=decision["description"],
|
_resolve_find_water(char, session_id, day, tick, world_map=decision.get("_world_map", {}), rng=decision.get("_rng"))
|
||||||
actor_id=char_id,
|
|
||||||
location_slug=char.get("location_slug"),
|
|
||||||
)
|
|
||||||
# idle : pas de log
|
# idle : pas de log
|
||||||
|
|
||||||
|
|
||||||
@@ -443,6 +539,9 @@ def process_actions_tick(session_id: int, day: int, tick: int,
|
|||||||
char, list(chars_map.values()), states, inventories,
|
char, list(chars_map.values()), states, inventories,
|
||||||
world_map, rng, phase, mode
|
world_map, rng, phase, mode
|
||||||
)
|
)
|
||||||
|
# Injecter world_map et rng pour les actions de fouille
|
||||||
|
decision["_world_map"] = world_map
|
||||||
|
decision["_rng"] = rng
|
||||||
_execute_action(char, decision, chars_map, inventories, session_id, day, tick)
|
_execute_action(char, decision, chars_map, inventories, session_id, day, tick)
|
||||||
|
|
||||||
# Mise à jour locale de chars_map si le PNJ est mort (évite une requête DB par PNJ)
|
# Mise à jour locale de chars_map si le PNJ est mort (évite une requête DB par PNJ)
|
||||||
|
|||||||
Reference in New Issue
Block a user