fix: archetypes origin_pool + faction_origin_override + lore corrections

- archetypes.json: origin -> origin_pool (liste), faction_origin_override (CDA->initie_confrerie, grand_krewe->goule)
- passeur_bayou: faction ecumeurs -> grand_krewe (lore: les bayous sont leur territoire)
- messager_faction: location_hints liste réelle (plus 'toutes zones sûres' non parsable)
- fugitif: chased_by_faction_pool ajouté pour narration LLM
- pillard_errant, scavenger, chasseur_primes, passeur: goule ajouté à origin_pool
- pnj_factory.spawn_from_archetype: résolution origin depuis faction_origin_override puis origin_pool
- character_creator._ROLE_TO_ORIGIN: lieutenant/chef_faction/chasseur_primes enrichis avec goule
This commit is contained in:
Corback
2026-06-15 23:58:32 +00:00
parent 96d2df92b2
commit 5497f3b5d0
3 changed files with 67 additions and 29 deletions
+45 -22
View File
@@ -1,13 +1,17 @@
{
"_comment": "Bibliothèque d'archétypes PNJ. Le LLM pioche ici pour instancier un PNJ de passage (tier 3) ou nommé (tier 2). Chaque archétype définit le template — le LLM choisit le nom, la faction et la localisation.",
"_comment": "Bibliothèque d'archétypes PNJ. origin_pool = liste d'origines possibles. faction_origin_override = si la faction choisie correspond, l'origine est forcée (lore). Le LLM choisit nom, faction, localisation.",
"garde_faction": {
"role": "garde",
"origin": "survivant",
"origin_pool": ["survivant", "habitant_abri", "initie_confrerie", "goule"],
"faction_origin_override": {
"cda": "initie_confrerie",
"grand_krewe": "goule"
},
"tier": 3,
"description": "Garde d'une faction, posté à un point stratégique",
"faction_pool": ["union", "cda", "syndicat_capitole", "regie", "dynaste_oak"],
"location_hints": ["zone contrôlée par la faction d'appartenance"],
"faction_pool": ["union", "cda", "syndicat_capitole", "regie", "dynaste_oak", "grand_krewe"],
"location_hints": ["nola_cbd", "independance", "baton_rouge", "la_paroisse", "oak_plantation"],
"caps_range": [50, 250],
"personality_traits": ["loyal", "méfiant envers étrangers", "obéit aux ordres"],
"motivations": ["protéger son territoire", "nourrir sa famille", "progresser dans la hiérarchie"],
@@ -16,7 +20,8 @@
"vendeur_ambulant": {
"role": "marchand",
"origin": "survivant",
"origin_pool": ["survivant", "habitant_abri", "goule"],
"faction_origin_override": {},
"tier": 3,
"description": "Marchand itinérant qui circule entre les zones sûres",
"faction_pool": [null, "consortium", "regie"],
@@ -29,7 +34,8 @@
"habitant_nola": {
"role": "survivant_generique",
"origin": "survivant",
"origin_pool": ["survivant", "habitant_abri"],
"faction_origin_override": {},
"tier": 3,
"description": "Habitant ordinaire du Vieux Carré ou d'Indépendance",
"faction_pool": [null, "union", "regie"],
@@ -42,7 +48,8 @@
"pillard_errant": {
"role": "pillard",
"origin": "survivant",
"origin_pool": ["survivant", "goule"],
"faction_origin_override": {},
"tier": 3,
"description": "Pillard solitaire ou en petite bande, sans base fixe",
"faction_pool": ["ecumeurs", null],
@@ -55,11 +62,14 @@
"passeur_bayou": {
"role": "passeur",
"origin": "survivant",
"origin_pool": ["survivant", "goule"],
"faction_origin_override": {
"grand_krewe": "goule"
},
"tier": 3,
"description": "Guide qui connaît les routes secrètes à travers les bayous",
"faction_pool": [null, "ecumeurs"],
"location_hints": ["pearl_river", "donaldsonville", "oak_plantation"],
"description": "Guide qui connaît les routes secrètes à travers les bayous — souvent une goule très ancienne",
"faction_pool": [null, "grand_krewe"],
"location_hints": ["pearl_river", "donaldsonville", "la_paroisse", "nola_fleuve"],
"caps_range": [100, 600],
"personality_traits": ["taciturne", "fiable si payé", "indépendant", "survivaliste"],
"motivations": ["caps", "indépendance totale", "connaître les secrets des factions"],
@@ -68,7 +78,8 @@
"chasseur_primes": {
"role": "chasseur_primes",
"origin": "survivant",
"origin_pool": ["survivant", "goule", "habitant_abri"],
"faction_origin_override": {},
"tier": 3,
"description": "Mercenaire qui traite les contrats d'élimination ou de capture",
"faction_pool": [null, "syndicat_capitole"],
@@ -81,7 +92,8 @@
"scavenger_freelance": {
"role": "scavenger",
"origin": "survivant",
"origin_pool": ["survivant", "goule"],
"faction_origin_override": {},
"tier": 3,
"description": "Récupérateur de ferraille et de technologie pré-guerre",
"faction_pool": [null],
@@ -94,7 +106,8 @@
"medecin_freelance": {
"role": "docteur",
"origin": "habitant_abri",
"origin_pool": ["habitant_abri", "survivant", "goule"],
"faction_origin_override": {},
"tier": 3,
"description": "Médecin indépendant qui soigne sans question contre paiement",
"faction_pool": [null, "union", "consortium"],
@@ -107,11 +120,12 @@
"messager_faction": {
"role": "passeur",
"origin": "survivant",
"origin_pool": ["survivant", "habitant_abri"],
"faction_origin_override": {},
"tier": 3,
"description": "Courrier entre factions, transporte messages et petits colis",
"faction_pool": ["union", "consortium", "regie", "syndicat_capitole"],
"location_hints": ["toutes zones sûres"],
"location_hints": ["independance", "laplace", "nola_vieux_carre", "baton_rouge", "nola_cbd"],
"caps_range": [50, 300],
"personality_traits": ["discret", "rapide", "mémorise sans noter", "refuse de lire les messages"],
"motivations": ["caps par livraison", "rester neutre", "éviter d'être intercepté"],
@@ -120,11 +134,13 @@
"fugitif": {
"role": "survivant_generique",
"origin": "survivant",
"origin_pool": ["survivant", "habitant_abri", "goule"],
"faction_origin_override": {},
"tier": 3,
"description": "Quelqu'un qui fuit une faction — esclave évadé, déserteur, criminel",
"description": "Quelqu'un qui fuit une faction — esclave évadé, déserteur, criminel, témoin gênant",
"faction_pool": [null],
"location_hints": ["pearl_river", "nola_cbd", "donaldsonville"],
"chased_by_faction_pool": ["union", "cda", "syndicat_capitole", "regie", "dynaste_oak", "ecumeurs"],
"location_hints": ["pearl_river", "nola_cbd", "donaldsonville", "la_paroisse"],
"caps_range": [0, 50],
"personality_traits": ["terrorisé", "méfiant extrême", "désespéré", "potentiellement dangereux"],
"motivations": ["ne pas être retrouvé", "atteindre une zone sûre", "survivre une journée de plus"],
@@ -133,7 +149,10 @@
"technicien_freelance": {
"role": "technicien",
"origin": "initie_confrerie",
"origin_pool": ["initie_confrerie", "habitant_abri", "survivant"],
"faction_origin_override": {
"cda": "initie_confrerie"
},
"tier": 3,
"description": "Ingénieur indépendant qui répare armes, générateurs et pip-boys",
"faction_pool": [null, "consortium"],
@@ -146,11 +165,15 @@
"lieutenant_faction": {
"role": "lieutenant",
"origin": "survivant",
"origin_pool": ["survivant", "habitant_abri", "initie_confrerie", "goule"],
"faction_origin_override": {
"cda": "initie_confrerie",
"grand_krewe": "goule"
},
"tier": 2,
"description": "Officier de rang intermédiaire, bras droit opérationnel d'un chef",
"faction_pool": ["union", "cda", "syndicat_capitole", "regie", "dynaste_oak", "ecumeurs", "consortium", "grand_krewe"],
"location_hints": ["QG ou zone clé de la faction"],
"location_hints": ["independance", "baton_rouge", "nola_vieux_carre", "la_paroisse", "oak_plantation", "pearl_river", "laplace", "nola_cbd"],
"caps_range": [200, 800],
"personality_traits": ["ambitieux", "dévoué à la faction", "respecté des gardes", "jaloux du boss"],
"motivations": ["monter en grade", "protéger la faction", "accumuler des ressources personnelles"],
+3 -3
View File
@@ -203,11 +203,11 @@ _ROLE_TO_ORIGIN: dict[str, list[str]] = {
"docteur": ["survivant", "habitant_abri"],
"technicien": ["initie_confrerie", "habitant_abri", "survivant"],
"raider_boss": ["survivant", "super_mutant"],
"lieutenant": ["survivant", "habitant_abri"],
"chasseur_primes": ["survivant"],
"lieutenant": ["survivant", "habitant_abri", "initie_confrerie", "goule"],
"chasseur_primes": ["survivant", "goule", "habitant_abri"],
"passeur": ["survivant", "goule"],
"scavenger": ["survivant", "goule"],
"chef_faction": ["survivant", "habitant_abri"],
"chef_faction": ["survivant", "habitant_abri", "goule"],
"survivant_generique": ["survivant", "habitant_abri"],
}
+17 -2
View File
@@ -123,13 +123,26 @@ def spawn_from_archetype(
rng = random.Random(seed)
role = arch["role"]
origin = arch.get("origin", "survivant")
tier = tier_override if tier_override is not None else arch.get("tier", 3)
if faction_slug is None:
pool = [f for f in arch.get("faction_pool", [None])]
pool = arch.get("faction_pool", [None])
faction_slug = rng.choice(pool)
# Résolution de l'origine : faction_origin_override prime sur origin_pool
origin_override = arch.get("faction_origin_override", {})
if faction_slug and faction_slug in origin_override:
origin = origin_override[faction_slug]
elif "origin_pool" in arch:
origin = rng.choice(arch["origin_pool"])
else:
origin = arch.get("origin", "survivant")
# Métadonnées narratives supplémentaires (ex: fugitif → chased_by)
chased_by = None
if "chased_by_faction_pool" in arch:
chased_by = rng.choice(arch["chased_by_faction_pool"])
char = cc.generate_character(
session_id = session_id,
role = role,
@@ -148,6 +161,8 @@ def spawn_from_archetype(
items = equip_character(char["id"], equip_role, session_id, seed=seed)
char["items_equipped"] = items
if chased_by:
char["chased_by_faction"] = chased_by
return char