Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b75a15e3f | |||
| d69144f506 | |||
| 44cc07db73 | |||
| 7a2be0cc0e | |||
| 3358dea306 |
228
_source/pretires/Bao_Wang.json
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
{
|
||||||
|
"_id": "preBaoWang00001",
|
||||||
|
"_key": "!actors!preBaoWang00001",
|
||||||
|
"name": "Bao Wang",
|
||||||
|
"type": "character",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/ui/logo_jeu.png",
|
||||||
|
"system": {
|
||||||
|
"concept": "L’escroc",
|
||||||
|
"metier": "Videur",
|
||||||
|
"faction": "Agence du Lys blanc",
|
||||||
|
"initiative": 7,
|
||||||
|
"anomaly": {
|
||||||
|
"type": "entropie",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"ame": {
|
||||||
|
"label": "CELESTOPOL.Stat.ame",
|
||||||
|
"res": 0,
|
||||||
|
"artifice": {
|
||||||
|
"label": "CELESTOPOL.Skill.artifice",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"attraction": {
|
||||||
|
"label": "CELESTOPOL.Skill.attraction",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"coercition": {
|
||||||
|
"label": "CELESTOPOL.Skill.coercition",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"faveur": {
|
||||||
|
"label": "CELESTOPOL.Skill.faveur",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"label": "CELESTOPOL.Stat.corps",
|
||||||
|
"res": 0,
|
||||||
|
"echauffouree": {
|
||||||
|
"label": "CELESTOPOL.Skill.echauffouree",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"effacement": {
|
||||||
|
"label": "CELESTOPOL.Skill.effacement",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"mobilite": {
|
||||||
|
"label": "CELESTOPOL.Skill.mobilite",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"prouesse": {
|
||||||
|
"label": "CELESTOPOL.Skill.prouesse",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"label": "CELESTOPOL.Stat.coeur",
|
||||||
|
"res": 0,
|
||||||
|
"appreciation": {
|
||||||
|
"label": "CELESTOPOL.Skill.appreciation",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"arts": {
|
||||||
|
"label": "CELESTOPOL.Skill.arts",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"inspiration": {
|
||||||
|
"label": "CELESTOPOL.Skill.inspiration",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"traque": {
|
||||||
|
"label": "CELESTOPOL.Skill.traque",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"label": "CELESTOPOL.Stat.esprit",
|
||||||
|
"res": 0,
|
||||||
|
"instruction": {
|
||||||
|
"label": "CELESTOPOL.Skill.instruction",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mtechnologique": {
|
||||||
|
"label": "CELESTOPOL.Skill.mtechnologique",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"raisonnement": {
|
||||||
|
"label": "CELESTOPOL.Skill.raisonnement",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"traitement": {
|
||||||
|
"label": "CELESTOPOL.Skill.traitement",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blessures": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"destin": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"spleen": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"fortune": {
|
||||||
|
"value": 2,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"reve": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"vision": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"police": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"okhrana": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"lunanovatek": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"oto": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"syndicats": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"vorovskoymir": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"cour": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso1": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso2": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefs": {
|
||||||
|
"rollMoonDie": false,
|
||||||
|
"difficulty": "normal"
|
||||||
|
},
|
||||||
|
"xp": {
|
||||||
|
"actuel": 0,
|
||||||
|
"log": []
|
||||||
|
},
|
||||||
|
"descriptionPhysique": "<p>Toujours tiré à quatre épingles, Bao est reconnu par ses coéquipiers comme le plus nonchalant du groupe.</p>",
|
||||||
|
"descriptionPsychologique": "<p>Ancienne petite frappe de Shanghai devenue videur au casino flottant La Libellule, Bao a rejoint Célestopol pour disparaître des radars de la police chinoise.</p><p>Peu enclin à la violence, joueur, séducteur et débrouillard, il a été recruté par Ernest pour mettre son passé houleux au service de l’agence du Lys blanc.</p>",
|
||||||
|
"historique": "",
|
||||||
|
"portraitImage": "",
|
||||||
|
"notes": "<p>Compétences reconstruites à partir des résistances, des aspects et de l'archétype du prétiré, la source PDF ne fournissant pas de dots de spécialisation différenciés de manière exploitable.</p>",
|
||||||
|
"biodata": {
|
||||||
|
"age": "32 ans",
|
||||||
|
"genre": "Homme",
|
||||||
|
"taille": "",
|
||||||
|
"yeux": "",
|
||||||
|
"naissance": "",
|
||||||
|
"cheveux": "",
|
||||||
|
"origine": "Chine"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"_id": "preBaoWangAnom",
|
||||||
|
"name": "Entropie",
|
||||||
|
"type": "anomaly",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/anomaly.svg",
|
||||||
|
"system": {
|
||||||
|
"subtype": "entropie",
|
||||||
|
"level": 2,
|
||||||
|
"usesRemaining": 1,
|
||||||
|
"technique": "<p>Durant un scénario, Bao peut <strong>relancer le Dé de Lune une seule fois</strong> et conserver le résultat qu’il préfère.</p><p>Cette capacité ne s’applique pas aux tests de chance.</p>",
|
||||||
|
"narratif": "<p>Le protagoniste peut <strong>influencer le hasard</strong> à sa façon, en déclenchant ou en évitant de petits événements aléatoires dans son environnement proche. Ces manifestations sont mineures, subtiles, et ne semblent jamais surnaturelles aux yeux des témoins.</p>",
|
||||||
|
"exemples": "<ul><li>Obtenir une bonne main au jeu de cartes lors de la distribution.</li><li>Voir le feu passer au vert en tournant le coin de la rue.</li><li>Faire tomber le verre d'un convive gênant au bon moment.</li></ul>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preBaoWang00001.preBaoWangAnom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preBaoWangAsp1",
|
||||||
|
"name": "Aime le jeu",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Aime le jeu</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preBaoWang00001.preBaoWangAsp1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preBaoWangAsp2",
|
||||||
|
"name": "Belle gueule",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 2,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Belle gueule</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preBaoWang00001.preBaoWangAsp2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"effects": [],
|
||||||
|
"folder": null,
|
||||||
|
"sort": 0,
|
||||||
|
"ownership": {
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
228
_source/pretires/Elemiah_Cowen.json
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
{
|
||||||
|
"_id": "preElemiah0001",
|
||||||
|
"_key": "!actors!preElemiah0001",
|
||||||
|
"name": "Elemiah Cowen",
|
||||||
|
"type": "character",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/ui/logo_jeu.png",
|
||||||
|
"system": {
|
||||||
|
"concept": "L’ouvrier",
|
||||||
|
"metier": "Manutentionnaire",
|
||||||
|
"faction": "Agence du Lys blanc",
|
||||||
|
"initiative": 7,
|
||||||
|
"anomaly": {
|
||||||
|
"type": "tarotdivinatoire",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"ame": {
|
||||||
|
"label": "CELESTOPOL.Stat.ame",
|
||||||
|
"res": 0,
|
||||||
|
"artifice": {
|
||||||
|
"label": "CELESTOPOL.Skill.artifice",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"attraction": {
|
||||||
|
"label": "CELESTOPOL.Skill.attraction",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"coercition": {
|
||||||
|
"label": "CELESTOPOL.Skill.coercition",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"faveur": {
|
||||||
|
"label": "CELESTOPOL.Skill.faveur",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"label": "CELESTOPOL.Stat.corps",
|
||||||
|
"res": 0,
|
||||||
|
"echauffouree": {
|
||||||
|
"label": "CELESTOPOL.Skill.echauffouree",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"effacement": {
|
||||||
|
"label": "CELESTOPOL.Skill.effacement",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mobilite": {
|
||||||
|
"label": "CELESTOPOL.Skill.mobilite",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"prouesse": {
|
||||||
|
"label": "CELESTOPOL.Skill.prouesse",
|
||||||
|
"value": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"label": "CELESTOPOL.Stat.coeur",
|
||||||
|
"res": 0,
|
||||||
|
"appreciation": {
|
||||||
|
"label": "CELESTOPOL.Skill.appreciation",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"arts": {
|
||||||
|
"label": "CELESTOPOL.Skill.arts",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"inspiration": {
|
||||||
|
"label": "CELESTOPOL.Skill.inspiration",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"traque": {
|
||||||
|
"label": "CELESTOPOL.Skill.traque",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"label": "CELESTOPOL.Stat.esprit",
|
||||||
|
"res": 0,
|
||||||
|
"instruction": {
|
||||||
|
"label": "CELESTOPOL.Skill.instruction",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"mtechnologique": {
|
||||||
|
"label": "CELESTOPOL.Skill.mtechnologique",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"raisonnement": {
|
||||||
|
"label": "CELESTOPOL.Skill.raisonnement",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"traitement": {
|
||||||
|
"label": "CELESTOPOL.Skill.traitement",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blessures": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"destin": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"spleen": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": {
|
||||||
|
"value": 1,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"fortune": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"reve": {
|
||||||
|
"value": 1,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"vision": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"police": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"okhrana": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"lunanovatek": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"oto": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"syndicats": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"vorovskoymir": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"cour": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso1": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso2": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefs": {
|
||||||
|
"rollMoonDie": false,
|
||||||
|
"difficulty": "normal"
|
||||||
|
},
|
||||||
|
"xp": {
|
||||||
|
"actuel": 0,
|
||||||
|
"log": []
|
||||||
|
},
|
||||||
|
"descriptionPhysique": "<p>Ancien ouvrier d’une trentaine d’années, Elemiah impressionne par son torse large, ses sourcils broussailleux, sa moustache travaillée et l’absence de deux doigts à la main gauche.</p>",
|
||||||
|
"descriptionPsychologique": "<p>Après la mort de son frère Rubben dans un accident industriel, Elemiah s’est juré de soutenir les miséreux de Célestopol.</p><p>Taiseux mais profondément solidaire, il s’émerveille désormais de l’architecture et des automates de la cité tout en travaillant aux galeries Sabline.</p>",
|
||||||
|
"historique": "",
|
||||||
|
"portraitImage": "",
|
||||||
|
"notes": "<p>Compétences reconstruites à partir des résistances, des aspects et de l'archétype du prétiré, la source PDF ne fournissant pas de dots de spécialisation différenciés de manière exploitable.</p>",
|
||||||
|
"biodata": {
|
||||||
|
"age": "28 ans",
|
||||||
|
"genre": "Homme",
|
||||||
|
"taille": "",
|
||||||
|
"yeux": "",
|
||||||
|
"naissance": "",
|
||||||
|
"cheveux": "",
|
||||||
|
"origine": "Célestopol"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"_id": "preElemiahAnom",
|
||||||
|
"name": "Tarot divinatoire",
|
||||||
|
"type": "anomaly",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/anomaly.svg",
|
||||||
|
"system": {
|
||||||
|
"subtype": "tarotdivinatoire",
|
||||||
|
"level": 2,
|
||||||
|
"usesRemaining": 2,
|
||||||
|
"technique": "<p>Durant un scénario, lors d'un test d'une <strong>Spécialisation du Cœur</strong> (Appréciation, Arts, Inspiration, Traque), le protagoniste gagne la possibilité de relancer les 2d8 un nombre de fois égal à son Niveau d'Anomalie.</p><p>Il doit conserver le dernier résultat.</p>",
|
||||||
|
"narratif": "<p>En <strong>tirant les cartes</strong>, le protagoniste peut apprendre une information sur une personne concernant son <em>passé, son présent ou son avenir</em>. L'information reste sujette à interprétation et le narrateur peut choisir de la formuler de façon symbolique ou métaphorique.</p>",
|
||||||
|
"exemples": "<ul><li>Deviner où se trouvera une cible le lendemain.</li><li>Connaître les antécédents douloureux d'un voisin mystérieux.</li><li>Obtenir une image symbolique du danger qui attend un allié.</li></ul>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preElemiah0001.preElemiahAnom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preElemiahAsp1",
|
||||||
|
"name": "Digne de confiance",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 2,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Digne de confiance</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preElemiah0001.preElemiahAsp1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preElemiahAsp2",
|
||||||
|
"name": "Robuste",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Robuste</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preElemiah0001.preElemiahAsp2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"effects": [],
|
||||||
|
"folder": null,
|
||||||
|
"sort": 0,
|
||||||
|
"ownership": {
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
228
_source/pretires/Maribel_Vargas.json
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
{
|
||||||
|
"_id": "preMaribel0001",
|
||||||
|
"_key": "!actors!preMaribel0001",
|
||||||
|
"name": "Maribel Vargas",
|
||||||
|
"type": "character",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/ui/logo_jeu.png",
|
||||||
|
"system": {
|
||||||
|
"concept": "La scientifique",
|
||||||
|
"metier": "Mécanicienne",
|
||||||
|
"faction": "Agence du Lys blanc",
|
||||||
|
"initiative": 7,
|
||||||
|
"anomaly": {
|
||||||
|
"type": "communicationaveclesmorts",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"ame": {
|
||||||
|
"label": "CELESTOPOL.Stat.ame",
|
||||||
|
"res": 0,
|
||||||
|
"artifice": {
|
||||||
|
"label": "CELESTOPOL.Skill.artifice",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"attraction": {
|
||||||
|
"label": "CELESTOPOL.Skill.attraction",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"coercition": {
|
||||||
|
"label": "CELESTOPOL.Skill.coercition",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"faveur": {
|
||||||
|
"label": "CELESTOPOL.Skill.faveur",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"label": "CELESTOPOL.Stat.corps",
|
||||||
|
"res": 0,
|
||||||
|
"echauffouree": {
|
||||||
|
"label": "CELESTOPOL.Skill.echauffouree",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"effacement": {
|
||||||
|
"label": "CELESTOPOL.Skill.effacement",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mobilite": {
|
||||||
|
"label": "CELESTOPOL.Skill.mobilite",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"prouesse": {
|
||||||
|
"label": "CELESTOPOL.Skill.prouesse",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"label": "CELESTOPOL.Stat.coeur",
|
||||||
|
"res": 0,
|
||||||
|
"appreciation": {
|
||||||
|
"label": "CELESTOPOL.Skill.appreciation",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"arts": {
|
||||||
|
"label": "CELESTOPOL.Skill.arts",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"inspiration": {
|
||||||
|
"label": "CELESTOPOL.Skill.inspiration",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"traque": {
|
||||||
|
"label": "CELESTOPOL.Skill.traque",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"label": "CELESTOPOL.Stat.esprit",
|
||||||
|
"res": 0,
|
||||||
|
"instruction": {
|
||||||
|
"label": "CELESTOPOL.Skill.instruction",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"mtechnologique": {
|
||||||
|
"label": "CELESTOPOL.Skill.mtechnologique",
|
||||||
|
"value": 4
|
||||||
|
},
|
||||||
|
"raisonnement": {
|
||||||
|
"label": "CELESTOPOL.Skill.raisonnement",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"traitement": {
|
||||||
|
"label": "CELESTOPOL.Skill.traitement",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blessures": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"destin": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"spleen": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"fortune": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"reve": {
|
||||||
|
"value": 2,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"vision": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"police": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"okhrana": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"lunanovatek": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"oto": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"syndicats": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"vorovskoymir": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"cour": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso1": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso2": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefs": {
|
||||||
|
"rollMoonDie": false,
|
||||||
|
"difficulty": "normal"
|
||||||
|
},
|
||||||
|
"xp": {
|
||||||
|
"actuel": 0,
|
||||||
|
"log": []
|
||||||
|
},
|
||||||
|
"descriptionPhysique": "<p>Jeune scientifique mexicaine, Maribel possède l’allure vive et appliquée de celles qui pensent plus vite qu’elles ne parlent.</p>",
|
||||||
|
"descriptionPsychologique": "<p>Mécanicienne brillante et major de promotion de l’université de Célestopol, Maribel s’est spécialisée dans l’ingénierie de pointe et le sélénium.</p><p>Longtemps freinée par son origine et son genre, elle a finalement trouvé sa place auprès d’Ernest, même si son habitude de parler seule amuse encore le reste de l’équipe.</p>",
|
||||||
|
"historique": "",
|
||||||
|
"portraitImage": "",
|
||||||
|
"notes": "<p>Compétences reconstruites à partir des résistances, des aspects et de l'archétype du prétiré, la source PDF ne fournissant pas de dots de spécialisation différenciés de manière exploitable.</p>",
|
||||||
|
"biodata": {
|
||||||
|
"age": "25 ans",
|
||||||
|
"genre": "Femme",
|
||||||
|
"taille": "",
|
||||||
|
"yeux": "",
|
||||||
|
"naissance": "",
|
||||||
|
"cheveux": "",
|
||||||
|
"origine": "Mexique"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"_id": "preMaribelAnom",
|
||||||
|
"name": "Communication avec les morts",
|
||||||
|
"type": "anomaly",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/anomaly.svg",
|
||||||
|
"system": {
|
||||||
|
"subtype": "communicationaveclesmorts",
|
||||||
|
"level": 2,
|
||||||
|
"usesRemaining": 2,
|
||||||
|
"technique": "<p>Durant un scénario, lors d'un test d'une <strong>Spécialisation de l'Esprit</strong> (Instruction, Merveilleux technologique, Raisonnement, Traitement), le protagoniste gagne la possibilité de relancer les 2d8 un nombre de fois égal à son Niveau d'Anomalie.</p><p>Il doit conserver le dernier résultat.</p>",
|
||||||
|
"narratif": "<p>Le protagoniste entre <strong>en contact avec l'esprit d'un défunt</strong>. Il peut lui poser une <em>question fermée</em> (réponse par oui ou par non uniquement). Le contact est bref et les réponses peuvent être fragmentées ou métaphoriques, à la discrétion du narrateur.</p>",
|
||||||
|
"exemples": "<ul><li>Interroger la victime d'un meurtre sur l'identité de son agresseur.</li><li>Consulter l'esprit d'un ancêtre pour retrouver un objet caché.</li><li>Demander à un fantôme si quelqu'un l'a aidé à mourir.</li></ul>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preMaribel0001.preMaribelAnom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preMaribelAsp1",
|
||||||
|
"name": "Déterminée",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Déterminée</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preMaribel0001.preMaribelAsp1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preMaribelAsp2",
|
||||||
|
"name": "Sagace",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 2,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Sagace</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preMaribel0001.preMaribelAsp2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"effects": [],
|
||||||
|
"folder": null,
|
||||||
|
"sort": 0,
|
||||||
|
"ownership": {
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
228
_source/pretires/Nadeja_Danilo.json
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
{
|
||||||
|
"_id": "preNadeja00001",
|
||||||
|
"_key": "!actors!preNadeja00001",
|
||||||
|
"name": "Nadeja Danilo",
|
||||||
|
"type": "character",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/ui/logo_jeu.png",
|
||||||
|
"system": {
|
||||||
|
"concept": "La pilote",
|
||||||
|
"metier": "Pilote",
|
||||||
|
"faction": "Agence du Lys blanc",
|
||||||
|
"initiative": 10,
|
||||||
|
"anomaly": {
|
||||||
|
"type": "telekinesie",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"ame": {
|
||||||
|
"label": "CELESTOPOL.Stat.ame",
|
||||||
|
"res": 0,
|
||||||
|
"artifice": {
|
||||||
|
"label": "CELESTOPOL.Skill.artifice",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"attraction": {
|
||||||
|
"label": "CELESTOPOL.Skill.attraction",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"coercition": {
|
||||||
|
"label": "CELESTOPOL.Skill.coercition",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"faveur": {
|
||||||
|
"label": "CELESTOPOL.Skill.faveur",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"label": "CELESTOPOL.Stat.corps",
|
||||||
|
"res": 0,
|
||||||
|
"echauffouree": {
|
||||||
|
"label": "CELESTOPOL.Skill.echauffouree",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"effacement": {
|
||||||
|
"label": "CELESTOPOL.Skill.effacement",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mobilite": {
|
||||||
|
"label": "CELESTOPOL.Skill.mobilite",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"prouesse": {
|
||||||
|
"label": "CELESTOPOL.Skill.prouesse",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"label": "CELESTOPOL.Stat.coeur",
|
||||||
|
"res": 0,
|
||||||
|
"appreciation": {
|
||||||
|
"label": "CELESTOPOL.Skill.appreciation",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"arts": {
|
||||||
|
"label": "CELESTOPOL.Skill.arts",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"inspiration": {
|
||||||
|
"label": "CELESTOPOL.Skill.inspiration",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"traque": {
|
||||||
|
"label": "CELESTOPOL.Skill.traque",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"label": "CELESTOPOL.Stat.esprit",
|
||||||
|
"res": 0,
|
||||||
|
"instruction": {
|
||||||
|
"label": "CELESTOPOL.Skill.instruction",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"mtechnologique": {
|
||||||
|
"label": "CELESTOPOL.Skill.mtechnologique",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"raisonnement": {
|
||||||
|
"label": "CELESTOPOL.Skill.raisonnement",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"traitement": {
|
||||||
|
"label": "CELESTOPOL.Skill.traitement",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blessures": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"destin": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"spleen": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"fortune": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"reve": {
|
||||||
|
"value": 1,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"vision": {
|
||||||
|
"value": 1,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"police": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"okhrana": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"lunanovatek": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"oto": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"syndicats": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"vorovskoymir": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"cour": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso1": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso2": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefs": {
|
||||||
|
"rollMoonDie": false,
|
||||||
|
"difficulty": "normal"
|
||||||
|
},
|
||||||
|
"xp": {
|
||||||
|
"actuel": 0,
|
||||||
|
"log": []
|
||||||
|
},
|
||||||
|
"descriptionPhysique": "<p>Nadeja donne d’abord l’image d’une femme sérieuse et farouche, toujours parfaitement concentrée.</p>",
|
||||||
|
"descriptionPsychologique": "<p>Née à Célestopol dans une famille d’universitaires, Nadeja s’est tournée très tôt vers les étoiles et les destinations lointaines.</p><p>Pilote extrêmement douée, elle travaille pour Columbia après avoir subi le sexisme du milieu aéronautique et supporte mal le manque de respect.</p>",
|
||||||
|
"historique": "",
|
||||||
|
"portraitImage": "",
|
||||||
|
"notes": "<p>Compétences reconstruites à partir des résistances, des aspects et de l'archétype du prétiré, la source PDF ne fournissant pas de dots de spécialisation différenciés de manière exploitable.</p>",
|
||||||
|
"biodata": {
|
||||||
|
"age": "35 ans",
|
||||||
|
"genre": "Femme",
|
||||||
|
"taille": "",
|
||||||
|
"yeux": "",
|
||||||
|
"naissance": "",
|
||||||
|
"cheveux": "",
|
||||||
|
"origine": "Célestopol"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"_id": "preNadeja0Anom",
|
||||||
|
"name": "Télékinésie",
|
||||||
|
"type": "anomaly",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/anomaly.svg",
|
||||||
|
"system": {
|
||||||
|
"subtype": "telekinesie",
|
||||||
|
"level": 2,
|
||||||
|
"usesRemaining": 2,
|
||||||
|
"technique": "<p>Durant un scénario, lors d'un test d'une <strong>Spécialisation du Corps</strong> (Échauffourée, Effacement, Mobilité, Prouesse), le protagoniste gagne la possibilité de relancer les 2d8 un nombre de fois égal à son Niveau d'Anomalie.</p><p>Il doit conserver le dernier résultat.</p>",
|
||||||
|
"narratif": "<p>Dans un rayon de <strong>8 mètres</strong>, le protagoniste peut <strong>déplacer par la pensée</strong> un petit objet léger sans attaches, sur <strong>4 mètres</strong> (dans n'importe quelle direction) pendant <strong>2 tours</strong>. L'objet doit être visible et accessible par le regard.</p>",
|
||||||
|
"exemples": "<ul><li>Déplacer une cuillère pour la faire tomber d'une table au bon moment.</li><li>Faire léviter un jeu de tarot ou un trousseau de clés.</li><li>Pousser doucement un verre pour attirer l'attention d'un interlocuteur.</li></ul>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preNadeja00001.preNadeja0Anom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preNadeja0Asp1",
|
||||||
|
"name": "Résiliente",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Résiliente</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preNadeja00001.preNadeja0Asp1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preNadeja0Asp2",
|
||||||
|
"name": "Tête froide",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 2,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Tête froide</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preNadeja00001.preNadeja0Asp2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"effects": [],
|
||||||
|
"folder": null,
|
||||||
|
"sort": 0,
|
||||||
|
"ownership": {
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
228
_source/pretires/Niels_Bielke.json
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
{
|
||||||
|
"_id": "preNielsBielke1",
|
||||||
|
"_key": "!actors!preNielsBielke1",
|
||||||
|
"name": "Niels Bielke",
|
||||||
|
"type": "character",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/ui/logo_jeu.png",
|
||||||
|
"system": {
|
||||||
|
"concept": "L’Artiste maudit",
|
||||||
|
"metier": "Acteur",
|
||||||
|
"faction": "Agence du Lys blanc",
|
||||||
|
"initiative": 8,
|
||||||
|
"anomaly": {
|
||||||
|
"type": "suggestion",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"ame": {
|
||||||
|
"label": "CELESTOPOL.Stat.ame",
|
||||||
|
"res": 0,
|
||||||
|
"artifice": {
|
||||||
|
"label": "CELESTOPOL.Skill.artifice",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"attraction": {
|
||||||
|
"label": "CELESTOPOL.Skill.attraction",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"coercition": {
|
||||||
|
"label": "CELESTOPOL.Skill.coercition",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"faveur": {
|
||||||
|
"label": "CELESTOPOL.Skill.faveur",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"label": "CELESTOPOL.Stat.corps",
|
||||||
|
"res": 0,
|
||||||
|
"echauffouree": {
|
||||||
|
"label": "CELESTOPOL.Skill.echauffouree",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"effacement": {
|
||||||
|
"label": "CELESTOPOL.Skill.effacement",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mobilite": {
|
||||||
|
"label": "CELESTOPOL.Skill.mobilite",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"prouesse": {
|
||||||
|
"label": "CELESTOPOL.Skill.prouesse",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"label": "CELESTOPOL.Stat.coeur",
|
||||||
|
"res": 0,
|
||||||
|
"appreciation": {
|
||||||
|
"label": "CELESTOPOL.Skill.appreciation",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"arts": {
|
||||||
|
"label": "CELESTOPOL.Skill.arts",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"inspiration": {
|
||||||
|
"label": "CELESTOPOL.Skill.inspiration",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"traque": {
|
||||||
|
"label": "CELESTOPOL.Skill.traque",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"label": "CELESTOPOL.Stat.esprit",
|
||||||
|
"res": 0,
|
||||||
|
"instruction": {
|
||||||
|
"label": "CELESTOPOL.Skill.instruction",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"mtechnologique": {
|
||||||
|
"label": "CELESTOPOL.Skill.mtechnologique",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"raisonnement": {
|
||||||
|
"label": "CELESTOPOL.Skill.raisonnement",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"traitement": {
|
||||||
|
"label": "CELESTOPOL.Skill.traitement",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blessures": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"destin": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"spleen": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"fortune": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"reve": {
|
||||||
|
"value": 2,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"vision": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"police": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"okhrana": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"lunanovatek": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"oto": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"syndicats": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"vorovskoymir": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"cour": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso1": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso2": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefs": {
|
||||||
|
"rollMoonDie": false,
|
||||||
|
"difficulty": "normal"
|
||||||
|
},
|
||||||
|
"xp": {
|
||||||
|
"actuel": 0,
|
||||||
|
"log": []
|
||||||
|
},
|
||||||
|
"descriptionPhysique": "<p>Niels approche de la quarantaine. Malgré une allure en apparence négligée et un tic nerveux persistant, il conserve un charisme et une aura certains.</p>",
|
||||||
|
"descriptionPsychologique": "<p>Ancienne vedette des scènes suédoises, Niels a quitté la Terre pour conquérir Célestopol avant de sombrer dans la disgrâce et l’alcool.</p><p>Sauvé par Ernest alors qu’il était au bord du gouffre, il sert désormais l’équipe du Lys blanc avec la volonté de payer sa dette et de tenir sa parole de ne plus toucher à l’alcool.</p>",
|
||||||
|
"historique": "",
|
||||||
|
"portraitImage": "",
|
||||||
|
"notes": "<p>Compétences reconstruites à partir des résistances, des aspects et de l'archétype du prétiré, la source PDF ne fournissant pas de dots de spécialisation différenciés de manière exploitable.</p>",
|
||||||
|
"biodata": {
|
||||||
|
"age": "38 ans",
|
||||||
|
"genre": "Homme",
|
||||||
|
"taille": "",
|
||||||
|
"yeux": "",
|
||||||
|
"naissance": "",
|
||||||
|
"cheveux": "",
|
||||||
|
"origine": "Suède"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"_id": "preNielsBiAnom",
|
||||||
|
"name": "Suggestion",
|
||||||
|
"type": "anomaly",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/anomaly.svg",
|
||||||
|
"system": {
|
||||||
|
"subtype": "suggestion",
|
||||||
|
"level": 2,
|
||||||
|
"usesRemaining": 2,
|
||||||
|
"technique": "<p>Durant un scénario, lors d'un test d'une <strong>Spécialisation de l'Âme</strong> (Artifice, Attraction, Coercition, Faveur), le protagoniste gagne la possibilité de relancer les 2d8 un nombre de fois égal à son Niveau d'Anomalie.</p><p>Il doit conserver le dernier résultat.</p>",
|
||||||
|
"narratif": "<p>Le protagoniste est capable d'<strong>influencer la prise de décision</strong> d'une personne en lui parlant à voix haute et en la regardant dans les yeux. Cette décision doit avoir un <em>impact immédiat</em> sur l'action de la personne concernée.</p><p>Cette capacité fonctionne également sur les <strong>automates sophistiqués de 4e et 5e génération</strong>.</p>",
|
||||||
|
"exemples": "<ul><li>Convaincre un garde de laisser passer sans vérifier les laissez-passer.</li><li>Pousser un prisonnier à donner son nom ou à s'asseoir.</li><li>Inciter un chauffeur de taxi à emprunter un itinéraire détourné.</li></ul>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preNielsBielke1.preNielsBiAnom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preNielsBiAsp1",
|
||||||
|
"name": "Charismatique",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 2,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Charismatique</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preNielsBielke1.preNielsBiAsp1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preNielsBiAsp2",
|
||||||
|
"name": "Sensible",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Sensible</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preNielsBielke1.preNielsBiAsp2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"effects": [],
|
||||||
|
"folder": null,
|
||||||
|
"sort": 0,
|
||||||
|
"ownership": {
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
250
_source/pretires/Polypheme.json
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
{
|
||||||
|
"_id": "prePolyphme0001",
|
||||||
|
"_key": "!actors!prePolyphme0001",
|
||||||
|
"name": "Polyphème",
|
||||||
|
"type": "character",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/ui/logo_jeu.png",
|
||||||
|
"system": {
|
||||||
|
"concept": "Le chroniqueur",
|
||||||
|
"metier": "Assistant-archiviste",
|
||||||
|
"faction": "Agence du Lys blanc",
|
||||||
|
"initiative": 7,
|
||||||
|
"anomaly": {
|
||||||
|
"type": "voyageastral",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"ame": {
|
||||||
|
"label": "CELESTOPOL.Stat.ame",
|
||||||
|
"res": 0,
|
||||||
|
"artifice": {
|
||||||
|
"label": "CELESTOPOL.Skill.artifice",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"attraction": {
|
||||||
|
"label": "CELESTOPOL.Skill.attraction",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"coercition": {
|
||||||
|
"label": "CELESTOPOL.Skill.coercition",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"faveur": {
|
||||||
|
"label": "CELESTOPOL.Skill.faveur",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"label": "CELESTOPOL.Stat.corps",
|
||||||
|
"res": 0,
|
||||||
|
"echauffouree": {
|
||||||
|
"label": "CELESTOPOL.Skill.echauffouree",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"effacement": {
|
||||||
|
"label": "CELESTOPOL.Skill.effacement",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mobilite": {
|
||||||
|
"label": "CELESTOPOL.Skill.mobilite",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"prouesse": {
|
||||||
|
"label": "CELESTOPOL.Skill.prouesse",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"label": "CELESTOPOL.Stat.coeur",
|
||||||
|
"res": 0,
|
||||||
|
"appreciation": {
|
||||||
|
"label": "CELESTOPOL.Skill.appreciation",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"arts": {
|
||||||
|
"label": "CELESTOPOL.Skill.arts",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"inspiration": {
|
||||||
|
"label": "CELESTOPOL.Skill.inspiration",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"traque": {
|
||||||
|
"label": "CELESTOPOL.Skill.traque",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"label": "CELESTOPOL.Stat.esprit",
|
||||||
|
"res": 0,
|
||||||
|
"instruction": {
|
||||||
|
"label": "CELESTOPOL.Skill.instruction",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mtechnologique": {
|
||||||
|
"label": "CELESTOPOL.Skill.mtechnologique",
|
||||||
|
"value": 4
|
||||||
|
},
|
||||||
|
"raisonnement": {
|
||||||
|
"label": "CELESTOPOL.Skill.raisonnement",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"traitement": {
|
||||||
|
"label": "CELESTOPOL.Skill.traitement",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blessures": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"destin": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"spleen": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"fortune": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"reve": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"vision": {
|
||||||
|
"value": 2,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"police": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"okhrana": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"lunanovatek": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"oto": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"syndicats": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"vorovskoymir": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"cour": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso1": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso2": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefs": {
|
||||||
|
"rollMoonDie": false,
|
||||||
|
"difficulty": "normal"
|
||||||
|
},
|
||||||
|
"xp": {
|
||||||
|
"actuel": 0,
|
||||||
|
"log": []
|
||||||
|
},
|
||||||
|
"descriptionPhysique": "<p>Automate de 4e génération remis en état par Ernest, Polyphème se distingue par son apparence artificielle et son obsession de ne pas avoir le « bon visage ».</p>",
|
||||||
|
"descriptionPsychologique": "<p>Retrouvé à demi détruit au fond d’une impasse, Polyphème a été réparé puis intégré à l’agence du Lys blanc comme assistant-archiviste.</p><p>Parfaitement intégré à l’équipe, il demeure hanté par la violence subie avant sa remise en état et par l’absence de souvenirs de sa vie passée.</p>",
|
||||||
|
"historique": "",
|
||||||
|
"portraitImage": "",
|
||||||
|
"notes": "<p>Compétences reconstruites à partir des résistances, des aspects et de l'archétype du prétiré, la source PDF ne fournissant pas de dots de spécialisation différenciés de manière exploitable.</p>",
|
||||||
|
"biodata": {
|
||||||
|
"age": "Inconnu",
|
||||||
|
"genre": "Automate",
|
||||||
|
"taille": "",
|
||||||
|
"yeux": "",
|
||||||
|
"naissance": "",
|
||||||
|
"cheveux": "",
|
||||||
|
"origine": "Inconnue"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"_id": "prePolyphmAnom",
|
||||||
|
"name": "Voyage astral",
|
||||||
|
"type": "anomaly",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/anomaly.svg",
|
||||||
|
"system": {
|
||||||
|
"subtype": "voyageastral",
|
||||||
|
"level": 2,
|
||||||
|
"usesRemaining": 2,
|
||||||
|
"technique": "<p>Durant un scénario, lors d'un test d'<strong>Appréciation, Merveilleux technologique, Traitement ou Traque</strong>, le protagoniste gagne la possibilité de relancer les 2d8 un nombre de fois égal à son Niveau d'Anomalie.</p><p>Il doit conserver le dernier résultat.</p>",
|
||||||
|
"narratif": "<p>L'<strong>esprit du protagoniste quitte son enveloppe corporelle</strong> et se déplace de <strong>8 mètres par tour</strong> pendant <strong>4 tours</strong>, dans n'importe quelle direction. L'esprit est <em>invisible</em> et peut traverser tous les obstacles matériels. Les sens du protagoniste restent les mêmes durant le voyage.</p><p>Le corps reste immobile et vulnérable durant le voyage.</p>",
|
||||||
|
"exemples": "<ul><li>Accéder aux toits d'une maison pour effectuer une reconnaissance sans risque physique.</li><li>Inspecter une pièce adjacente verrouillée avant d'y pénétrer.</li><li>Voir à quelle distance un éboulement bloque le passage dans un tunnel et s'il y a des survivants.</li></ul>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!prePolyphme0001.prePolyphmAnom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "prePolyphmAsp1",
|
||||||
|
"name": "Difficile à lire",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 2,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Difficile à lire</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!prePolyphme0001.prePolyphmAsp1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "prePolyphmAsp2",
|
||||||
|
"name": "Étrangeté",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 2,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Étrangeté</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!prePolyphme0001.prePolyphmAsp2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "prePolyphmAsp3",
|
||||||
|
"name": "Mémoire eidétique",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 2,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Mémoire eidétique</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!prePolyphme0001.prePolyphmAsp3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "prePolyphmAsp4",
|
||||||
|
"name": "Vision aiguisée",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Vision aiguisée</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!prePolyphme0001.prePolyphmAsp4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"effects": [],
|
||||||
|
"folder": null,
|
||||||
|
"sort": 0,
|
||||||
|
"ownership": {
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
228
_source/pretires/Sedami_Alassane.json
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
{
|
||||||
|
"_id": "preSedami000001",
|
||||||
|
"_key": "!actors!preSedami000001",
|
||||||
|
"name": "Sèdami Alassane",
|
||||||
|
"type": "character",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/ui/logo_jeu.png",
|
||||||
|
"system": {
|
||||||
|
"concept": "La diplomate",
|
||||||
|
"metier": "Guerrière / diplomate",
|
||||||
|
"faction": "Agence du Lys blanc",
|
||||||
|
"initiative": 9,
|
||||||
|
"anomaly": {
|
||||||
|
"type": "telepathie",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"ame": {
|
||||||
|
"label": "CELESTOPOL.Stat.ame",
|
||||||
|
"res": 0,
|
||||||
|
"artifice": {
|
||||||
|
"label": "CELESTOPOL.Skill.artifice",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"attraction": {
|
||||||
|
"label": "CELESTOPOL.Skill.attraction",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"coercition": {
|
||||||
|
"label": "CELESTOPOL.Skill.coercition",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"faveur": {
|
||||||
|
"label": "CELESTOPOL.Skill.faveur",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"label": "CELESTOPOL.Stat.corps",
|
||||||
|
"res": 0,
|
||||||
|
"echauffouree": {
|
||||||
|
"label": "CELESTOPOL.Skill.echauffouree",
|
||||||
|
"value": 4
|
||||||
|
},
|
||||||
|
"effacement": {
|
||||||
|
"label": "CELESTOPOL.Skill.effacement",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mobilite": {
|
||||||
|
"label": "CELESTOPOL.Skill.mobilite",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"prouesse": {
|
||||||
|
"label": "CELESTOPOL.Skill.prouesse",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"label": "CELESTOPOL.Stat.coeur",
|
||||||
|
"res": 0,
|
||||||
|
"appreciation": {
|
||||||
|
"label": "CELESTOPOL.Skill.appreciation",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"arts": {
|
||||||
|
"label": "CELESTOPOL.Skill.arts",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"inspiration": {
|
||||||
|
"label": "CELESTOPOL.Skill.inspiration",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"traque": {
|
||||||
|
"label": "CELESTOPOL.Skill.traque",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"label": "CELESTOPOL.Stat.esprit",
|
||||||
|
"res": 0,
|
||||||
|
"instruction": {
|
||||||
|
"label": "CELESTOPOL.Skill.instruction",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mtechnologique": {
|
||||||
|
"label": "CELESTOPOL.Skill.mtechnologique",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"raisonnement": {
|
||||||
|
"label": "CELESTOPOL.Skill.raisonnement",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"traitement": {
|
||||||
|
"label": "CELESTOPOL.Skill.traitement",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blessures": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"destin": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"spleen": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": {
|
||||||
|
"value": 1,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"fortune": {
|
||||||
|
"value": 1,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"reve": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"vision": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"police": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"okhrana": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"lunanovatek": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"oto": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"syndicats": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"vorovskoymir": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"cour": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso1": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso2": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefs": {
|
||||||
|
"rollMoonDie": false,
|
||||||
|
"difficulty": "normal"
|
||||||
|
},
|
||||||
|
"xp": {
|
||||||
|
"actuel": 0,
|
||||||
|
"log": []
|
||||||
|
},
|
||||||
|
"descriptionPhysique": "<p>Sèdami est une femme d’âge mûr, confiante dans ses capacités et dotée d’un esprit très ouvert.</p>",
|
||||||
|
"descriptionPsychologique": "<p>Ancienne officière des Mino du Dahomey, Sèdami s’est illustrée au combat comme en stratégie avant de devenir diplomate.</p><p>Fascinée par Célestopol lors d’une mission de courtoisie, elle a choisi de rester vivre sur la Lune, malgré la nostalgie de son pays natal.</p>",
|
||||||
|
"historique": "",
|
||||||
|
"portraitImage": "",
|
||||||
|
"notes": "<p>Compétences reconstruites à partir des résistances, des aspects et de l'archétype du prétiré, la source PDF ne fournissant pas de dots de spécialisation différenciés de manière exploitable.</p>",
|
||||||
|
"biodata": {
|
||||||
|
"age": "58 ans",
|
||||||
|
"genre": "Femme",
|
||||||
|
"taille": "",
|
||||||
|
"yeux": "",
|
||||||
|
"naissance": "",
|
||||||
|
"cheveux": "",
|
||||||
|
"origine": "Dahomey"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"_id": "preSedami0Anom",
|
||||||
|
"name": "Télépathie",
|
||||||
|
"type": "anomaly",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/anomaly.svg",
|
||||||
|
"system": {
|
||||||
|
"subtype": "telepathie",
|
||||||
|
"level": 2,
|
||||||
|
"usesRemaining": 2,
|
||||||
|
"technique": "<p>Durant un scénario, lors d'un test d'<strong>Appréciation, Attraction, Échauffourée ou Faveur</strong>, le protagoniste gagne la possibilité de relancer les 2d8 un nombre de fois égal à son Niveau d'Anomalie.</p><p>Il doit conserver le dernier résultat.</p><p>Cette capacité fonctionne également sur les <strong>automates sophistiqués de 4e et 5e génération</strong>.</p>",
|
||||||
|
"narratif": "<p>Le protagoniste est capable de <strong>percevoir les pensées superficielles</strong> d'un tiers. Il peut comprendre l'état émotionnel d'une personne ou capter une image ou un mot dans son esprit (à la discrétion du narrateur), simplement en <em>l'observant</em>.</p>",
|
||||||
|
"exemples": "<ul><li>Percevoir l'image d'un cristal de cyanure dans l'esprit d'un serviteur soupçonné de tentative de meurtre.</li><li>Détecter, malgré un visage parfaitement contrôlé, qu'un magistrat est en réalité terrifié.</li><li>Ressentir la culpabilité d'un homme qui ment avec aplomb.</li></ul>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preSedami000001.preSedami0Anom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preSedami0Asp1",
|
||||||
|
"name": "Comportementaliste",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Comportementaliste</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preSedami000001.preSedami0Asp1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preSedami0Asp2",
|
||||||
|
"name": "Stratège",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 2,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Stratège</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preSedami000001.preSedami0Asp2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"effects": [],
|
||||||
|
"folder": null,
|
||||||
|
"sort": 0,
|
||||||
|
"ownership": {
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
239
_source/pretires/Wiktoria_Razny.json
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
{
|
||||||
|
"_id": "preWiktoria001",
|
||||||
|
"_key": "!actors!preWiktoria001",
|
||||||
|
"name": "Wiktoria Raźny",
|
||||||
|
"type": "character",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/ui/logo_jeu.png",
|
||||||
|
"system": {
|
||||||
|
"concept": "La vétérane",
|
||||||
|
"metier": "Soldate",
|
||||||
|
"faction": "Agence du Lys blanc",
|
||||||
|
"initiative": 6,
|
||||||
|
"anomaly": {
|
||||||
|
"type": "illusion",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"ame": {
|
||||||
|
"label": "CELESTOPOL.Stat.ame",
|
||||||
|
"res": 0,
|
||||||
|
"artifice": {
|
||||||
|
"label": "CELESTOPOL.Skill.artifice",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"attraction": {
|
||||||
|
"label": "CELESTOPOL.Skill.attraction",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"coercition": {
|
||||||
|
"label": "CELESTOPOL.Skill.coercition",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"faveur": {
|
||||||
|
"label": "CELESTOPOL.Skill.faveur",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"label": "CELESTOPOL.Stat.corps",
|
||||||
|
"res": 0,
|
||||||
|
"echauffouree": {
|
||||||
|
"label": "CELESTOPOL.Skill.echauffouree",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"effacement": {
|
||||||
|
"label": "CELESTOPOL.Skill.effacement",
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
"mobilite": {
|
||||||
|
"label": "CELESTOPOL.Skill.mobilite",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"prouesse": {
|
||||||
|
"label": "CELESTOPOL.Skill.prouesse",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"label": "CELESTOPOL.Stat.coeur",
|
||||||
|
"res": 0,
|
||||||
|
"appreciation": {
|
||||||
|
"label": "CELESTOPOL.Skill.appreciation",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"arts": {
|
||||||
|
"label": "CELESTOPOL.Skill.arts",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"inspiration": {
|
||||||
|
"label": "CELESTOPOL.Skill.inspiration",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"traque": {
|
||||||
|
"label": "CELESTOPOL.Skill.traque",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"label": "CELESTOPOL.Stat.esprit",
|
||||||
|
"res": 0,
|
||||||
|
"instruction": {
|
||||||
|
"label": "CELESTOPOL.Skill.instruction",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"mtechnologique": {
|
||||||
|
"label": "CELESTOPOL.Skill.mtechnologique",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"raisonnement": {
|
||||||
|
"label": "CELESTOPOL.Skill.raisonnement",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"traitement": {
|
||||||
|
"label": "CELESTOPOL.Skill.traitement",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blessures": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"destin": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"spleen": {
|
||||||
|
"lvl": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": {
|
||||||
|
"value": 2,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"fortune": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"reve": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"vision": {
|
||||||
|
"value": 0,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"police": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"okhrana": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"lunanovatek": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"oto": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"syndicats": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"vorovskoymir": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"cour": {
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso1": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"perso2": {
|
||||||
|
"label": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefs": {
|
||||||
|
"rollMoonDie": false,
|
||||||
|
"difficulty": "normal"
|
||||||
|
},
|
||||||
|
"xp": {
|
||||||
|
"actuel": 0,
|
||||||
|
"log": []
|
||||||
|
},
|
||||||
|
"descriptionPhysique": "<p>Wiktoria est une Polonaise dégingandée portant un œil de verre à la place de l’œil perdu durant la guerre.</p>",
|
||||||
|
"descriptionPsychologique": "<p>Ancienne soldate de la Seconde Guerre de Crimée, Wiktoria a quitté la Pologne par dégoût après avoir été rejetée pour son handicap.</p><p>Amie d’Ernest et agente du Lys blanc, elle aime les soirées mondaines où elle glane des informations utiles tout en cultivant un goût affirmé pour la compagnie et l’élégance.</p>",
|
||||||
|
"historique": "",
|
||||||
|
"portraitImage": "",
|
||||||
|
"notes": "<p>Compétences reconstruites à partir des résistances, des aspects et de l'archétype du prétiré, la source PDF ne fournissant pas de dots de spécialisation différenciés de manière exploitable.</p>",
|
||||||
|
"biodata": {
|
||||||
|
"age": "39 ans",
|
||||||
|
"genre": "Femme",
|
||||||
|
"taille": "",
|
||||||
|
"yeux": "",
|
||||||
|
"naissance": "",
|
||||||
|
"cheveux": "",
|
||||||
|
"origine": "Pologne"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"_id": "preWiktoriAnom",
|
||||||
|
"name": "Illusion",
|
||||||
|
"type": "anomaly",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/anomaly.svg",
|
||||||
|
"system": {
|
||||||
|
"subtype": "illusion",
|
||||||
|
"level": 2,
|
||||||
|
"usesRemaining": 2,
|
||||||
|
"technique": "<p>Durant un scénario, lors d'un test de <strong>Coercition, Échauffourée, Effacement ou Traque</strong>, le protagoniste gagne la possibilité de relancer les 2d8 un nombre de fois égal à son Niveau d'Anomalie.</p><p>Il doit conserver le dernier résultat.</p>",
|
||||||
|
"narratif": "<p>Le protagoniste peut <strong>générer une petite illusion mineure</strong> — visuelle, auditive ou olfactive, au choix — sans détail ni précision, pour une durée d'<strong>une minute</strong>. L'illusion ne peut représenter un être vivant en détail et ne résiste pas à un examen rapproché.</p>",
|
||||||
|
"exemples": "<ul><li>Le son d'un chat qui miaule ou d'un livre qui tombe dans la pièce voisine.</li><li>Un reflet métallique ou une ombre fugace au bout d'un couloir.</li><li>L'odeur de la pluie ou d'une fumée inquiétante.</li></ul>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preWiktoria001.preWiktoriAnom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preWiktoriAsp1",
|
||||||
|
"name": "Affable",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Affable</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preWiktoria001.preWiktoriAsp1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preWiktoriAsp2",
|
||||||
|
"name": "Grande",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Grande</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preWiktoria001.preWiktoriAsp2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "preWiktoriAsp3",
|
||||||
|
"name": "Expérience militaire",
|
||||||
|
"type": "aspect",
|
||||||
|
"img": "systems/fvtt-celestopol/assets/icons/aspect.svg",
|
||||||
|
"system": {
|
||||||
|
"valeur": 1,
|
||||||
|
"description": "<p>Aspect du prétiré : <strong>Expérience militaire</strong>.</p>"
|
||||||
|
},
|
||||||
|
"_key": "!actors.items!preWiktoria001.preWiktoriAsp3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"effects": [],
|
||||||
|
"folder": null,
|
||||||
|
"sort": 0,
|
||||||
|
"ownership": {
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
BIN
assets/scenes/Jr7lGxYk2RETlXRv-thumb.webp
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
assets/tokens/bao.webp.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
assets/tokens/elemia.webp
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/tokens/maribel.webp
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
assets/tokens/nadeja.webp
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
assets/tokens/niels_bielke.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/tokens/polypheme.webp
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
assets/tokens/sedami.webp
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
assets/tokens/wiktoria.webp
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/ui/celestopol_carte.webp
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
assets/ui/help/aide-aspects-dialogue.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/ui/help/aide-combat-chat.png
Normal file
|
After Width: | Height: | Size: 275 KiB |
BIN
assets/ui/help/aide-domaines.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/ui/help/aide-factions.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
assets/ui/help/aide-jet.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
debug_aspects_left.png
Normal file
|
After Width: | Height: | Size: 770 KiB |
BIN
debug_attributs.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
debug_attributs2.png
Normal file
|
After Width: | Height: | Size: 423 KiB |
BIN
debug_blessures.png
Normal file
|
After Width: | Height: | Size: 324 KiB |
154
extract_pretires.py
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import fitz, re, json
|
||||||
|
|
||||||
|
pdf_path = '/home/morr/work/uberwald/fvtt-celestopol/__regles/Célestopol 1922 Fiches de prêts à jouer v1_cdjdr.pdf'
|
||||||
|
doc = fitz.open(pdf_path)
|
||||||
|
|
||||||
|
def is_green(color, tol=0.008):
|
||||||
|
if not color: return False
|
||||||
|
return (abs(color[0]-0.131) < tol and abs(color[1]-0.284) < tol and abs(color[2]-0.160) < tol)
|
||||||
|
|
||||||
|
SKILL_LIST = ['ARTIFICE','ATTRACTION','COERCITION','FAVEUR',
|
||||||
|
'ÉCHAUFFOURÉE','EFFACEMENT','MOBILITÉ','PROUESSE',
|
||||||
|
'APPRÉCIATION','ARTS','INSPIRATION','TRAQUE',
|
||||||
|
'INSTRUCTION','MERV.TECH.','RAISONNEMENT','TRAITEMENT']
|
||||||
|
|
||||||
|
def norm(s):
|
||||||
|
return s.strip().upper().replace('É','E').replace('È','E').replace('Ê','E').replace('Ô','O').replace('Â','A').replace('Î','I').replace('Œ','OE').replace('.','')
|
||||||
|
|
||||||
|
def get_skill_values(page):
|
||||||
|
words = page.get_text("words")
|
||||||
|
skill_y = {}
|
||||||
|
for w in words:
|
||||||
|
wn = norm(w[4])
|
||||||
|
for sk in SKILL_LIST:
|
||||||
|
if wn == norm(sk) and w[0] < 430:
|
||||||
|
skill_y[sk] = (w[1]+w[3])/2
|
||||||
|
# MERV.TECH. special
|
||||||
|
for w in words:
|
||||||
|
if 'MERV' in w[4].upper() and w[0] < 430:
|
||||||
|
skill_y['MERV.TECH.'] = (w[1]+w[3])/2
|
||||||
|
|
||||||
|
green_circles = []
|
||||||
|
for d in page.get_drawings():
|
||||||
|
if is_green(d.get('fill')):
|
||||||
|
rect = d['rect']
|
||||||
|
ws = rect.x1 - rect.x0
|
||||||
|
if 5.5 < ws < 8.5:
|
||||||
|
green_circles.append(((rect.x0+rect.x1)/2, (rect.y0+rect.y1)/2))
|
||||||
|
|
||||||
|
skills = {}
|
||||||
|
for sk in SKILL_LIST:
|
||||||
|
sy = skill_y.get(sk, None)
|
||||||
|
if sy is not None:
|
||||||
|
skills[sk] = sum(1 for cx, cy in green_circles if abs(cy - sy) < 7)
|
||||||
|
else:
|
||||||
|
skills[sk] = 0
|
||||||
|
return skills
|
||||||
|
|
||||||
|
def get_resistances(page):
|
||||||
|
words = page.get_text("words")
|
||||||
|
domain_y = {}
|
||||||
|
for w in sorted(words, key=lambda x: x[1]):
|
||||||
|
t = norm(w[4])
|
||||||
|
x0 = w[0]
|
||||||
|
if t == 'AME' and 300 < x0 < 500:
|
||||||
|
domain_y['ame'] = w[1]
|
||||||
|
elif t == 'CORPS' and 300 < x0 < 500:
|
||||||
|
domain_y['corps'] = w[1]
|
||||||
|
elif t == 'COEUR' and 300 < x0 < 500:
|
||||||
|
domain_y['coeur'] = w[1]
|
||||||
|
elif t == 'ESPRIT' and 300 < x0 < 500:
|
||||||
|
domain_y['esprit'] = w[1]
|
||||||
|
res = {}
|
||||||
|
for dom, dy in domain_y.items():
|
||||||
|
for w in words:
|
||||||
|
if w[4].strip().isdigit() and w[0] > 480 and abs(w[1]-dy) < 35:
|
||||||
|
res[dom] = int(w[4].strip())
|
||||||
|
break
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_anomalie_name(stats_page):
|
||||||
|
# Extract from text: the anomalie name appears in the bottom-right of the page
|
||||||
|
# Parse cleanly using raw text
|
||||||
|
text = stats_page.get_text("text")
|
||||||
|
lines = [l.strip() for l in text.split('\n') if l.strip()]
|
||||||
|
|
||||||
|
skip_words = {'ANOMALIE','NV','RÉSISTANCE','RESISTANCE'}
|
||||||
|
skip_starts = ['pour ','lors ','gagner ','trouver ','éviter ','sortir ','obtenir ',
|
||||||
|
'lors d', 'Vider ', 'Gain ', 'en pui', 'pour ne', 'pour ré']
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if 'ANOMALIE' in line.upper() or 'NV' in line:
|
||||||
|
# Look in next few lines for the name
|
||||||
|
for j in range(i+1, min(i+10, len(lines))):
|
||||||
|
l = lines[j]
|
||||||
|
if not any(l.startswith(s) for s in skip_starts) and l not in skip_words:
|
||||||
|
if l and l[0].isupper() and len(l) > 1:
|
||||||
|
return l
|
||||||
|
return "?"
|
||||||
|
|
||||||
|
def get_anomalie_niveau(stats_page):
|
||||||
|
words = stats_page.get_text("words")
|
||||||
|
for w in sorted(words, key=lambda x: (x[1],x[0])):
|
||||||
|
if w[4].strip().isdigit() and w[0] > 480 and w[1] > 650:
|
||||||
|
return int(w[4].strip())
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_char_base_info(stats_page):
|
||||||
|
blocks = stats_page.get_text("dict")["blocks"]
|
||||||
|
name = None
|
||||||
|
for block in blocks:
|
||||||
|
for line in block.get("lines", []):
|
||||||
|
for span in line.get("spans", []):
|
||||||
|
if span.get("size", 0) > 11 and 'Bold' in span.get("font",""):
|
||||||
|
y = span["origin"][1]
|
||||||
|
t = span["text"].strip()
|
||||||
|
if t and len(t) > 3 and 150 < y < 250:
|
||||||
|
name = t
|
||||||
|
return {'name': name}
|
||||||
|
|
||||||
|
def get_raw_text(page):
|
||||||
|
html = page.get_text("html")
|
||||||
|
clean = re.sub(r'<[^>]+>', ' ', html)
|
||||||
|
clean = re.sub(r'&#x([0-9a-fA-F]+);', lambda m: chr(int(m.group(1),16)), clean)
|
||||||
|
clean = re.sub(r'&#([0-9]+);', lambda m: chr(int(m.group(1))), clean)
|
||||||
|
return re.sub(r'\s+', ' ', clean).strip()
|
||||||
|
|
||||||
|
def parse_aspects_page(asp_page):
|
||||||
|
text = asp_page.get_text("text")
|
||||||
|
lines = [l.strip() for l in text.split('\n') if l.strip()]
|
||||||
|
return lines
|
||||||
|
|
||||||
|
# Characters: (name_idx, anom_desc_idx, stats_idx, aspects_idx)
|
||||||
|
CHARACTERS = [
|
||||||
|
(0, 1, 2, 3),
|
||||||
|
(4, 5, 6, 7),
|
||||||
|
(8, 9, 10, 11),
|
||||||
|
(12, 13, 14, 15),
|
||||||
|
(16, 17, 18, 19),
|
||||||
|
(20, 21, 22, 23),
|
||||||
|
(24, 25, 26, 27),
|
||||||
|
(28, 29, 30, 31),
|
||||||
|
]
|
||||||
|
|
||||||
|
for n_idx, a_idx, s_idx, asp_idx in CHARACTERS:
|
||||||
|
sp = doc[s_idx]
|
||||||
|
skills = get_skill_values(sp)
|
||||||
|
res = get_resistances(sp)
|
||||||
|
anom_name = get_anomalie_name(sp)
|
||||||
|
anom_nv = get_anomalie_niveau(sp)
|
||||||
|
char_info = get_char_base_info(sp)
|
||||||
|
anom_desc = get_raw_text(doc[a_idx])
|
||||||
|
asp_lines = parse_aspects_page(doc[asp_idx])
|
||||||
|
|
||||||
|
print(f"\n{'='*70}")
|
||||||
|
print(f"NAME: {char_info.get('name','?')}")
|
||||||
|
print(f"SKILLS: {json.dumps(skills, ensure_ascii=False)}")
|
||||||
|
print(f"RESISTANCES: {res}")
|
||||||
|
print(f"ANOMALIE: {anom_name} nv{anom_nv}")
|
||||||
|
print(f"ANOM DESC (first 300 chars): {anom_desc[:300]}")
|
||||||
|
print("ASPECTS LINES:")
|
||||||
|
for i,l in enumerate(asp_lines[:60]): print(f" {i:2d}: {l}")
|
||||||
|
|
||||||
|
doc.close()
|
||||||
|
print("\nDONE")
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* fvtt-celestopol.mjs — Point d'entrée principal du système Célestopol 1922
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
* FoundryVTT v13+ / DataModels / ApplicationV2
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { SYSTEM, SYSTEM_ID, ASCII } from "./module/config/system.mjs"
|
import { SYSTEM, SYSTEM_ID, ASCII } from "./module/config/system.mjs"
|
||||||
@@ -30,6 +38,11 @@ import {
|
|||||||
CelestopolArmureSheet,
|
CelestopolArmureSheet,
|
||||||
} from "./module/applications/_module.mjs"
|
} from "./module/applications/_module.mjs"
|
||||||
|
|
||||||
|
const DAMAGE_APPLICATION_FLAG = "damageApplication"
|
||||||
|
const FACTION_ASPECT_STATE_SETTING = "factionAspectState"
|
||||||
|
const PREGENS_IMPORTED_SETTING = "pregensImported"
|
||||||
|
const WELCOME_SCENE_IMPORTED_SETTING = "welcomeSceneImported"
|
||||||
|
|
||||||
/* ─── Init hook ──────────────────────────────────────────────────────────── */
|
/* ─── Init hook ──────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
Hooks.once("init", () => {
|
Hooks.once("init", () => {
|
||||||
@@ -52,6 +65,11 @@ Hooks.once("init", () => {
|
|||||||
game.celestopol = {
|
game.celestopol = {
|
||||||
SYSTEM,
|
SYSTEM,
|
||||||
rollMoonStandalone: (actor = null) => CelestopolRoll.rollMoonStandalone(actor),
|
rollMoonStandalone: (actor = null) => CelestopolRoll.rollMoonStandalone(actor),
|
||||||
|
manageFactionAspects: (actor = null) => _manageFactionAspects(actor),
|
||||||
|
getFactionAspectState: () => _getFactionAspectState(),
|
||||||
|
getFactionAspectSummary: (actor = null) => _getFactionAspectSummary(actor),
|
||||||
|
getFactionDisplayLabel: (value) => _getFactionDisplayLabel(value),
|
||||||
|
normalizeFactionId: (value) => _normalizeFactionId(value),
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── DataModels ──────────────────────────────────────────────────────────
|
// ── DataModels ──────────────────────────────────────────────────────────
|
||||||
@@ -140,7 +158,7 @@ Hooks.once("init", () => {
|
|||||||
|
|
||||||
/* ─── Ready hook ─────────────────────────────────────────────────────────── */
|
/* ─── Ready hook ─────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
Hooks.once("ready", () => {
|
Hooks.once("ready", async () => {
|
||||||
console.log(`${SYSTEM_ID} | System ready`)
|
console.log(`${SYSTEM_ID} | System ready`)
|
||||||
|
|
||||||
// Socket handler for GM-only operations (e.g. wound application)
|
// Socket handler for GM-only operations (e.g. wound application)
|
||||||
@@ -148,12 +166,27 @@ Hooks.once("ready", () => {
|
|||||||
game.socket.on(`system.${SYSTEM_ID}`, _onSocketMessage)
|
game.socket.on(`system.${SYSTEM_ID}`, _onSocketMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Hooks.on("renderChatMessageHTML", (message, html) => {
|
||||||
|
_activateChatCardListeners(message, html)
|
||||||
|
})
|
||||||
|
Hooks.on("updateChatMessage", (message, changed) => {
|
||||||
|
if (foundry.utils.hasProperty(changed, `flags.${SYSTEM_ID}.${DAMAGE_APPLICATION_FLAG}`)) {
|
||||||
|
_updateRenderedChatMessageState(message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
_activateExistingChatCards()
|
||||||
|
|
||||||
// Migration : supprime les items de types obsolètes (ex: "attribute")
|
// Migration : supprime les items de types obsolètes (ex: "attribute")
|
||||||
if (game.user.isGM) {
|
if (game.user.isGM) {
|
||||||
_migrateObsoleteItems()
|
_migrateObsoleteItems()
|
||||||
_migrateIntegerTracks()
|
_migrateIntegerTracks()
|
||||||
_setupAnomaliesFolder()
|
_migrateBiographyFields()
|
||||||
|
await _setupAnomaliesFolder()
|
||||||
|
await _setupPregensFolder()
|
||||||
|
await _setupWelcomeScene()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _createWelcomeChatMessage()
|
||||||
})
|
})
|
||||||
|
|
||||||
/** Supprime les items dont le type n'est plus reconnu par le système. */
|
/** Supprime les items dont le type n'est plus reconnu par le système. */
|
||||||
@@ -241,6 +274,31 @@ async function _migrateIntegerTracks() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Migration : ajoute les champs de biographie manquants sur les fiches existantes. */
|
||||||
|
async function _migrateBiographyFields() {
|
||||||
|
const actors = game.actors.contents.filter(actor => ["character", "npc"].includes(actor.type))
|
||||||
|
|
||||||
|
for (const actor of actors) {
|
||||||
|
const src = actor._source?.system
|
||||||
|
if (!src) continue
|
||||||
|
|
||||||
|
const updateData = {}
|
||||||
|
|
||||||
|
if (actor.type === "character" && !("historique" in src)) {
|
||||||
|
updateData["system.historique"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("portraitImage" in src)) {
|
||||||
|
updateData["system.portraitImage"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(updateData).length > 0) {
|
||||||
|
console.log(`${SYSTEM_ID} | Migration biographie : ${actor.name}`, updateData)
|
||||||
|
await actor.update(updateData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ─── Handlebars helpers ─────────────────────────────────────────────────── */
|
/* ─── Handlebars helpers ─────────────────────────────────────────────────── */
|
||||||
|
|
||||||
function _registerHandlebarsHelpers() {
|
function _registerHandlebarsHelpers() {
|
||||||
@@ -317,6 +375,119 @@ function _registerSettings() {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
game.settings.register(SYSTEM_ID, PREGENS_IMPORTED_SETTING, {
|
||||||
|
scope: "world",
|
||||||
|
config: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
game.settings.register(SYSTEM_ID, FACTION_ASPECT_STATE_SETTING, {
|
||||||
|
scope: "world",
|
||||||
|
config: false,
|
||||||
|
type: Object,
|
||||||
|
default: _getDefaultFactionAspectState(),
|
||||||
|
})
|
||||||
|
game.settings.register(SYSTEM_ID, WELCOME_SCENE_IMPORTED_SETTING, {
|
||||||
|
scope: "world",
|
||||||
|
config: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _createWelcomeChatMessage() {
|
||||||
|
const activeGM = game.users.activeGM
|
||||||
|
if (!game.user.isGM || (activeGM && activeGM.id !== game.user.id)) return
|
||||||
|
|
||||||
|
const helpPack = game.packs.get(`${SYSTEM_ID}.aides-de-jeu`)
|
||||||
|
const helpDocs = helpPack ? await helpPack.getDocuments() : []
|
||||||
|
const helpEntry = helpDocs[0] ?? null
|
||||||
|
const helpReference = helpEntry
|
||||||
|
? `@UUID[${helpEntry.uuid}]{${helpEntry.name}}`
|
||||||
|
: `**${helpPack?.metadata?.label ?? game.i18n.localize("CELESTOPOL.Welcome.helpFallback")}**`
|
||||||
|
const rawContent = `
|
||||||
|
<div class="cel-welcome-message chat-system-card">
|
||||||
|
<div class="welcome-header">
|
||||||
|
<span class="welcome-mark">✦</span>
|
||||||
|
<span class="welcome-title">${game.i18n.localize("CELESTOPOL.Welcome.title")}</span>
|
||||||
|
</div>
|
||||||
|
<div class="welcome-body">
|
||||||
|
<p>${game.i18n.localize("CELESTOPOL.Welcome.intro")}</p>
|
||||||
|
<div class="welcome-note">
|
||||||
|
<span class="welcome-label">${game.i18n.localize("CELESTOPOL.Welcome.helpLabel")}</span>
|
||||||
|
<span class="welcome-value">${game.i18n.format("CELESTOPOL.Welcome.helpCompendium", { help: helpReference })}</span>
|
||||||
|
</div>
|
||||||
|
<div class="welcome-note">
|
||||||
|
<span class="welcome-label">${game.i18n.localize("CELESTOPOL.Welcome.bookLabel")}</span>
|
||||||
|
<span class="welcome-value"><a href="https://antre-monde.com/produit/celestopol-1922-le-jeu-de-role-livre-de-base/" target="_blank" rel="noopener noreferrer">${game.i18n.localize("CELESTOPOL.Welcome.bookLinkLabel")}</a></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
await ChatMessage.create({
|
||||||
|
style: CONST.CHAT_MESSAGE_STYLES.OOC,
|
||||||
|
speaker: { alias: game.system.title },
|
||||||
|
content: await foundry.applications.ux.TextEditor.implementation.enrichHTML(rawContent, { async: true }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _setupWelcomeScene() {
|
||||||
|
const activeGM = game.users.activeGM
|
||||||
|
if (!game.user.isGM || (activeGM && activeGM.id !== game.user.id)) return
|
||||||
|
if (game.settings.get(SYSTEM_ID, WELCOME_SCENE_IMPORTED_SETTING)) return
|
||||||
|
|
||||||
|
const sceneName = "Accueil Celestopol 1922"
|
||||||
|
let scene = game.scenes.getName(sceneName)
|
||||||
|
|
||||||
|
if (!scene) {
|
||||||
|
const pack = game.packs.get(`${SYSTEM_ID}.scenes`)
|
||||||
|
if (!pack) {
|
||||||
|
console.warn(`${SYSTEM_ID} | Compendium de scènes introuvable`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const docs = await pack.getDocuments()
|
||||||
|
const sourceScene = docs.find(doc => doc.name === sceneName)
|
||||||
|
if (!sourceScene) {
|
||||||
|
console.warn(`${SYSTEM_ID} | Scène d'accueil introuvable dans le compendium`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
scene = await Scene.create(sourceScene.toObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
await scene.activate()
|
||||||
|
await scene.view()
|
||||||
|
await game.settings.set(SYSTEM_ID, WELCOME_SCENE_IMPORTED_SETTING, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _setupPregensFolder() {
|
||||||
|
const activeGM = game.users.activeGM
|
||||||
|
if (!game.user.isGM || (activeGM && activeGM.id !== game.user.id)) return
|
||||||
|
if (game.settings.get(SYSTEM_ID, PREGENS_IMPORTED_SETTING)) return
|
||||||
|
|
||||||
|
const pack = game.packs.get(`${SYSTEM_ID}.pretires`)
|
||||||
|
if (!pack) {
|
||||||
|
console.warn(`${SYSTEM_ID} | Compendium de prétirés introuvable`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderName = game.i18n.localize("CELESTOPOL.Pregens.folderName")
|
||||||
|
let folder = game.folders.contents.find(f => f.type === "Actor" && f.name === folderName)
|
||||||
|
if (!folder) {
|
||||||
|
folder = await Folder.create({
|
||||||
|
name: folderName,
|
||||||
|
type: "Actor",
|
||||||
|
color: "#1b3828",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`${SYSTEM_ID} | Premier lancement : import des prétirés dans le monde`)
|
||||||
|
await pack.importAll({ folderId: folder.id, keepId: true })
|
||||||
|
await game.settings.set(SYSTEM_ID, PREGENS_IMPORTED_SETTING, true)
|
||||||
|
console.log(`${SYSTEM_ID} | Prétirés importés avec succès dans le dossier "${folder.name}"`)
|
||||||
|
ui.notifications.info(game.i18n.localize("CELESTOPOL.Pregens.imported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Template preload ───────────────────────────────────────────────────── */
|
/* ─── Template preload ───────────────────────────────────────────────────── */
|
||||||
@@ -349,17 +520,706 @@ function _preloadTemplates() {
|
|||||||
|
|
||||||
/* ─── Socket handler ─────────────────────────────────────────────────────── */
|
/* ─── Socket handler ─────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
function _onSocketMessage(data) {
|
async function _onSocketMessage(data) {
|
||||||
if (!game.user.isGM) return
|
const activeGM = game.users.activeGM
|
||||||
|
if (!game.user.isGM || (activeGM && activeGM.id !== game.user.id)) return
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case "applyWound": {
|
case "applyWound": {
|
||||||
const actor = game.actors.get(data.actorId)
|
const actor = game.actors.get(data.actorId)
|
||||||
if (actor) actor.update({ "system.blessures.lvl": data.level })
|
if (actor) await actor.update({ "system.blessures.lvl": data.level })
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "applyWeaponDamage": {
|
||||||
|
await _applyWeaponDamage(data)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _getChatHtmlRoot(html) {
|
||||||
|
if (html instanceof HTMLElement) return html
|
||||||
|
if (html?.[0] instanceof HTMLElement) return html[0]
|
||||||
|
if (html?.element instanceof HTMLElement) return html.element
|
||||||
|
if (html?.element?.[0] instanceof HTMLElement) return html.element[0]
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function _activateExistingChatCards() {
|
||||||
|
document.querySelectorAll(".message[data-message-id]").forEach(messageEl => {
|
||||||
|
const messageId = messageEl.dataset.messageId
|
||||||
|
const message = game.messages.get(messageId)
|
||||||
|
const root = messageEl.querySelector(".celestopol.chat-roll")
|
||||||
|
if (!message || !root) return
|
||||||
|
_activateChatCardListeners(message, root)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function _activateChatCardListeners(message, html) {
|
||||||
|
const root = _getChatHtmlRoot(html)
|
||||||
|
if (!root) return
|
||||||
|
|
||||||
|
_renderWeaponDamageState(message, root)
|
||||||
|
|
||||||
|
root.querySelectorAll('[data-action="apply-weapon-damage"]').forEach(button => {
|
||||||
|
if (button.dataset.bound === "true") return
|
||||||
|
button.dataset.bound = "true"
|
||||||
|
button.addEventListener("click", event => _onApplyWeaponDamageClick(event, message))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _onApplyWeaponDamageClick(event, message) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
const button = event.currentTarget
|
||||||
|
const card = button.closest(".celestopol.chat-roll")
|
||||||
|
const select = button.closest(".weapon-damage-actions")?.querySelector('select[name="targetActorUuid"]')
|
||||||
|
const selectedOption = select?.selectedOptions?.[0] ?? null
|
||||||
|
const actorUuid = button.dataset.actorUuid || select?.value || ""
|
||||||
|
const actorId = button.dataset.actorId || selectedOption?.dataset.actorId || ""
|
||||||
|
const incomingWounds = Number.parseInt(button.dataset.incomingWounds ?? "", 10)
|
||||||
|
const currentState = _getDamageApplicationState(message)
|
||||||
|
|
||||||
|
if (currentState?.applied) {
|
||||||
|
if (card) _renderWeaponDamageState(message, card)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!actorUuid && !actorId) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CELESTOPOL.Combat.selectCharacterFirst"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Number.isFinite(incomingWounds) || incomingWounds < 0) return
|
||||||
|
|
||||||
|
if (card) _renderPendingWeaponDamageState(card)
|
||||||
|
button.disabled = true
|
||||||
|
await _requestWeaponDamageApplication({
|
||||||
|
actorId,
|
||||||
|
actorUuid,
|
||||||
|
incomingWounds,
|
||||||
|
chatMessageId: message?.id ?? null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _requestWeaponDamageApplication({ actorId, actorUuid = null, incomingWounds, chatMessageId = null }) {
|
||||||
|
if (game.user.isGM) {
|
||||||
|
return _applyWeaponDamage({ actorId, actorUuid, incomingWounds, chatMessageId })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!game.socket) return
|
||||||
|
|
||||||
|
game.socket.emit(`system.${SYSTEM_ID}`, {
|
||||||
|
type: "applyWeaponDamage",
|
||||||
|
actorId,
|
||||||
|
actorUuid,
|
||||||
|
incomingWounds,
|
||||||
|
chatMessageId,
|
||||||
|
})
|
||||||
|
|
||||||
|
ui.notifications.info(game.i18n.localize("CELESTOPOL.Combat.damageRequestSent"))
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getDamageApplicationState(message) {
|
||||||
|
return message?.getFlag(SYSTEM_ID, DAMAGE_APPLICATION_FLAG) ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
function _updateRenderedChatMessageState(message) {
|
||||||
|
const root = document.querySelector(`.message[data-message-id="${message.id}"] .celestopol.chat-roll`)
|
||||||
|
if (!root) return
|
||||||
|
_renderWeaponDamageState(message, root)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _removeDamageStatus(root) {
|
||||||
|
root.querySelector(".damage-application-status")?.remove()
|
||||||
|
root.querySelector(".weapon-damage-summary")?.classList.remove("is-applied", "is-pending")
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setDamageStatus(root, { text, cssClass = "" }) {
|
||||||
|
const summary = root.querySelector(".weapon-damage-summary")
|
||||||
|
if (!summary) return
|
||||||
|
|
||||||
|
_removeDamageStatus(root)
|
||||||
|
summary.classList.add(cssClass)
|
||||||
|
|
||||||
|
const status = document.createElement("div")
|
||||||
|
status.className = `damage-application-status ${cssClass}`.trim()
|
||||||
|
status.textContent = text
|
||||||
|
summary.append(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _renderPendingWeaponDamageState(root) {
|
||||||
|
const button = root.querySelector('[data-action="apply-weapon-damage"]')
|
||||||
|
const select = root.querySelector('select[name="targetActorUuid"]')
|
||||||
|
if (button) {
|
||||||
|
button.disabled = true
|
||||||
|
button.textContent = game.i18n.localize("CELESTOPOL.Combat.damageApplying")
|
||||||
|
}
|
||||||
|
if (select) select.disabled = true
|
||||||
|
_setDamageStatus(root, {
|
||||||
|
text: game.i18n.localize("CELESTOPOL.Combat.damageApplyingNotice"),
|
||||||
|
cssClass: "is-pending",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function _renderWeaponDamageState(message, root) {
|
||||||
|
const button = root.querySelector('[data-action="apply-weapon-damage"]')
|
||||||
|
const select = root.querySelector('select[name="targetActorUuid"]')
|
||||||
|
const state = _getDamageApplicationState(message)
|
||||||
|
|
||||||
|
if (!state?.applied) {
|
||||||
|
if (button) button.textContent = game.i18n.localize("CELESTOPOL.Combat.applyDamage")
|
||||||
|
if (button) button.disabled = false
|
||||||
|
if (select) select.disabled = false
|
||||||
|
_removeDamageStatus(root)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button) {
|
||||||
|
button.disabled = true
|
||||||
|
button.textContent = game.i18n.localize("CELESTOPOL.Combat.damageAppliedDone")
|
||||||
|
}
|
||||||
|
if (select) select.disabled = true
|
||||||
|
|
||||||
|
const text = state.appliedWounds > 0
|
||||||
|
? game.i18n.format("CELESTOPOL.Combat.damageAppliedCard", {
|
||||||
|
actor: state.actorName,
|
||||||
|
wounds: state.appliedWounds,
|
||||||
|
armor: state.armorProtection,
|
||||||
|
})
|
||||||
|
: game.i18n.format("CELESTOPOL.Combat.damageNoEffectCard", {
|
||||||
|
actor: state.actorName,
|
||||||
|
armor: state.armorProtection,
|
||||||
|
})
|
||||||
|
|
||||||
|
_setDamageStatus(root, { text, cssClass: "is-applied" })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _markChatMessageDamageApplied(chatMessageId, data) {
|
||||||
|
if (!chatMessageId) return
|
||||||
|
const message = game.messages.get(chatMessageId)
|
||||||
|
if (!message) return
|
||||||
|
await message.setFlag(SYSTEM_ID, DAMAGE_APPLICATION_FLAG, {
|
||||||
|
applied: true,
|
||||||
|
...data,
|
||||||
|
})
|
||||||
|
_updateRenderedChatMessageState(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _applyWeaponDamage({ actorId, actorUuid = null, incomingWounds, chatMessageId = null }) {
|
||||||
|
const actor = await CelestopolRoll.resolveActor({ actorId, actorUuid })
|
||||||
|
if (!actor) return null
|
||||||
|
|
||||||
|
const message = chatMessageId ? game.messages.get(chatMessageId) : null
|
||||||
|
if (_getDamageApplicationState(message)?.applied) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const armorProtection = CelestopolRoll.getActorArmorProtection(actor)
|
||||||
|
const appliedWounds = Math.max(0, incomingWounds - armorProtection)
|
||||||
|
const currentWounds = actor.system?.blessures?.lvl ?? 0
|
||||||
|
const nextWounds = Math.min(8, currentWounds + appliedWounds)
|
||||||
|
|
||||||
|
if (appliedWounds > 0 && nextWounds !== currentWounds) {
|
||||||
|
await actor.update({ "system.blessures.lvl": nextWounds })
|
||||||
|
}
|
||||||
|
|
||||||
|
await _markChatMessageDamageApplied(chatMessageId, {
|
||||||
|
actorId,
|
||||||
|
actorName: actor.name,
|
||||||
|
appliedWounds,
|
||||||
|
armorProtection,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (appliedWounds > 0) {
|
||||||
|
ui.notifications.info(game.i18n.format("CELESTOPOL.Combat.damageAppliedNotify", {
|
||||||
|
actor: actor.name,
|
||||||
|
wounds: appliedWounds,
|
||||||
|
armor: armorProtection,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
ui.notifications.info(game.i18n.format("CELESTOPOL.Combat.damageNoEffectNotify", {
|
||||||
|
actor: actor.name,
|
||||||
|
armor: armorProtection,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return { actorName: actor.name, appliedWounds, armorProtection }
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getDefaultFactionAspectState() {
|
||||||
|
return {
|
||||||
|
pointsMax: 8,
|
||||||
|
activatedAspects: [],
|
||||||
|
customCell: {
|
||||||
|
enabled: false,
|
||||||
|
mode: "replace",
|
||||||
|
name: "",
|
||||||
|
aspectIds: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _normalizeFactionId(value) {
|
||||||
|
const raw = `${value ?? ""}`.trim()
|
||||||
|
if (!raw) return ""
|
||||||
|
const direct = raw.toLowerCase()
|
||||||
|
if (SYSTEM.FACTIONS[direct]) return direct
|
||||||
|
|
||||||
|
for (const [id, faction] of Object.entries(SYSTEM.FACTIONS)) {
|
||||||
|
if (game.i18n.localize(faction.label).trim().toLowerCase() === direct) return id
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getFactionDisplayLabel(value) {
|
||||||
|
const factionId = _normalizeFactionId(value)
|
||||||
|
if (!factionId) return `${value ?? ""}`.trim()
|
||||||
|
return game.i18n.localize(SYSTEM.FACTIONS[factionId].label)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sanitizeFactionAspectState(state = {}) {
|
||||||
|
const base = foundry.utils.mergeObject(_getDefaultFactionAspectState(), foundry.utils.deepClone(state), {
|
||||||
|
inplace: false,
|
||||||
|
insertKeys: true,
|
||||||
|
insertValues: true,
|
||||||
|
overwrite: true,
|
||||||
|
recursive: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
base.pointsMax = Math.max(0, Number.parseInt(base.pointsMax ?? 8, 10) || 0)
|
||||||
|
base.customCell.enabled = Boolean(base.customCell?.enabled)
|
||||||
|
base.customCell.mode = base.customCell?.mode === "extend" ? "extend" : "replace"
|
||||||
|
base.customCell.name = `${base.customCell?.name ?? ""}`.trim()
|
||||||
|
base.customCell.aspectIds = Array.from(new Set((base.customCell?.aspectIds ?? [])
|
||||||
|
.filter(id => SYSTEM.FACTION_ASPECTS[id])))
|
||||||
|
|
||||||
|
base.activatedAspects = (base.activatedAspects ?? [])
|
||||||
|
.map(entry => {
|
||||||
|
const id = `${entry?.id ?? ""}`.trim()
|
||||||
|
const aspect = SYSTEM.FACTION_ASPECTS[id]
|
||||||
|
if (!aspect) return null
|
||||||
|
const value = Math.max(1, Math.min(4, Number.parseInt(entry.value ?? 1, 10) || 1))
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
value,
|
||||||
|
label: game.i18n.localize(aspect.label),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getFactionAspectState() {
|
||||||
|
const stored = game.settings.get(SYSTEM_ID, FACTION_ASPECT_STATE_SETTING) ?? {}
|
||||||
|
return _sanitizeFactionAspectState(stored)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _setFactionAspectState(state) {
|
||||||
|
const cleanState = _sanitizeFactionAspectState(state)
|
||||||
|
await game.settings.set(SYSTEM_ID, FACTION_ASPECT_STATE_SETTING, cleanState)
|
||||||
|
_refreshFactionAspectSheets()
|
||||||
|
return cleanState
|
||||||
|
}
|
||||||
|
|
||||||
|
function _refreshFactionAspectSheets() {
|
||||||
|
for (const actor of game.actors.contents) {
|
||||||
|
if (actor.type !== "character") continue
|
||||||
|
if (!actor.sheet?.rendered) continue
|
||||||
|
actor.sheet.render(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getRepresentedFactionIds() {
|
||||||
|
return Array.from(new Set(
|
||||||
|
game.actors.contents
|
||||||
|
.filter(actor => actor.type === "character")
|
||||||
|
.map(actor => _normalizeFactionId(actor.system?.faction))
|
||||||
|
.filter(Boolean)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getFactionAspectSourceData(state = _getFactionAspectState()) {
|
||||||
|
const representedFactionIds = _getRepresentedFactionIds()
|
||||||
|
const sourceFactionIds = state.customCell.enabled && state.customCell.mode === "replace"
|
||||||
|
? []
|
||||||
|
: representedFactionIds
|
||||||
|
|
||||||
|
const sourceAspectIds = new Set()
|
||||||
|
const officialSourceLabels = []
|
||||||
|
const sourceLabels = []
|
||||||
|
|
||||||
|
for (const factionId of sourceFactionIds) {
|
||||||
|
const label = game.i18n.localize(SYSTEM.FACTIONS[factionId].label)
|
||||||
|
officialSourceLabels.push(label)
|
||||||
|
sourceLabels.push(label)
|
||||||
|
for (const aspectId of SYSTEM.FACTION_ASPECTS_BY_FACTION[factionId] ?? []) {
|
||||||
|
sourceAspectIds.add(aspectId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let customCellLabel = ""
|
||||||
|
if (state.customCell.enabled) {
|
||||||
|
customCellLabel = state.customCell.name || game.i18n.localize("CELESTOPOL.FactionAspect.customCell")
|
||||||
|
sourceLabels.push(customCellLabel)
|
||||||
|
for (const aspectId of state.customCell.aspectIds ?? []) {
|
||||||
|
sourceAspectIds.add(aspectId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
representedFactionIds,
|
||||||
|
officialSourceLabels,
|
||||||
|
availableAspectIds: Array.from(sourceAspectIds),
|
||||||
|
sourceLabels,
|
||||||
|
customCellLabel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getActorAvailableFactionAspectIds(actor, state = _getFactionAspectState()) {
|
||||||
|
const ids = new Set()
|
||||||
|
const factionId = _normalizeFactionId(actor?.system?.faction)
|
||||||
|
|
||||||
|
if (state.customCell.enabled && state.customCell.mode === "replace") {
|
||||||
|
for (const aspectId of state.customCell.aspectIds ?? []) ids.add(aspectId)
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
if (factionId) {
|
||||||
|
for (const aspectId of SYSTEM.FACTION_ASPECTS_BY_FACTION[factionId] ?? []) ids.add(aspectId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.customCell.enabled && state.customCell.mode === "extend") {
|
||||||
|
for (const aspectId of state.customCell.aspectIds ?? []) ids.add(aspectId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getFactionAspectSummary(actor = null) {
|
||||||
|
const state = _getFactionAspectState()
|
||||||
|
const sourceData = _getFactionAspectSourceData(state)
|
||||||
|
const primaryFactionId = actor ? _normalizeFactionId(actor.system?.faction) : ""
|
||||||
|
const actorAvailableIds = actor ? _getActorAvailableFactionAspectIds(actor, state) : new Set(sourceData.availableAspectIds)
|
||||||
|
const pointsSpent = state.activatedAspects.reduce((sum, aspect) => sum + aspect.value, 0)
|
||||||
|
const pointsRemaining = Math.max(0, state.pointsMax - pointsSpent)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
pointsSpent,
|
||||||
|
pointsRemaining,
|
||||||
|
sourceLabels: sourceData.sourceLabels,
|
||||||
|
officialSourceLabels: sourceData.officialSourceLabels,
|
||||||
|
customCellLabel: sourceData.customCellLabel,
|
||||||
|
hasOfficialSources: sourceData.officialSourceLabels.length > 0,
|
||||||
|
needsSourceConfiguration: !sourceData.officialSourceLabels.length && !state.customCell.enabled,
|
||||||
|
representedFactions: sourceData.representedFactionIds.map(id => ({
|
||||||
|
id,
|
||||||
|
label: game.i18n.localize(SYSTEM.FACTIONS[id].label),
|
||||||
|
})),
|
||||||
|
primaryFactionId,
|
||||||
|
primaryFactionLabel: primaryFactionId ? game.i18n.localize(SYSTEM.FACTIONS[primaryFactionId].label) : _getFactionDisplayLabel(actor?.system?.faction),
|
||||||
|
availableAspectChoices: state.activatedAspects.map(aspect => ({
|
||||||
|
id: aspect.id,
|
||||||
|
value: aspect.value,
|
||||||
|
label: aspect.label,
|
||||||
|
})),
|
||||||
|
activatableAspectChoices: sourceData.availableAspectIds
|
||||||
|
.filter(id => !state.activatedAspects.some(aspect => aspect.id === id))
|
||||||
|
.map(id => ({
|
||||||
|
id,
|
||||||
|
label: game.i18n.localize(SYSTEM.FACTION_ASPECTS[id].label),
|
||||||
|
})),
|
||||||
|
availableAspectLabels: sourceData.availableAspectIds.map(id => game.i18n.localize(SYSTEM.FACTION_ASPECTS[id].label)),
|
||||||
|
activatedAspects: state.activatedAspects.map(aspect => ({
|
||||||
|
...aspect,
|
||||||
|
relevantToActor: actor ? actorAvailableIds.has(aspect.id) : true,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _parseFactionAspectManagerForm(form) {
|
||||||
|
return {
|
||||||
|
pointsMax: Math.max(0, Number.parseInt(form.querySelector('[name="pointsMax"]')?.value ?? 8, 10) || 0),
|
||||||
|
customCellEnabled: form.querySelector('[name="customCellEnabled"]')?.checked ?? false,
|
||||||
|
customCellMode: form.querySelector('[name="customCellMode"]')?.value === "extend" ? "extend" : "replace",
|
||||||
|
customCellName: `${form.querySelector('[name="customCellName"]')?.value ?? ""}`.trim(),
|
||||||
|
customCellAspectIds: Array.from(form.querySelectorAll('input[name="customCellAspectIds"]:checked')).map(input => input.value),
|
||||||
|
activateAspectId: `${form.querySelector('[name="activateAspectId"]')?.value ?? ""}`.trim(),
|
||||||
|
activateAspectValue: Math.max(1, Math.min(4, Number.parseInt(form.querySelector('[name="activateAspectValue"]')?.value ?? 1, 10) || 1)),
|
||||||
|
removeAspectId: `${form.querySelector('[name="removeAspectId"]')?.value ?? ""}`.trim(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _renderFactionAspectManagerContent(summary) {
|
||||||
|
const i18n = game.i18n
|
||||||
|
const hint = (text) => ` <span class="faction-aspect-help-tip" title="${foundry.utils.escapeHTML(text)}">?</span>`
|
||||||
|
const checkedAspectIds = new Set(summary.customCell.aspectIds ?? [])
|
||||||
|
const customAspectCheckboxes = Object.values(SYSTEM.FACTION_ASPECTS).map(aspect => `
|
||||||
|
<label class="faction-aspect-cell-option">
|
||||||
|
<input type="checkbox" name="customCellAspectIds" value="${aspect.id}" ${checkedAspectIds.has(aspect.id) ? "checked" : ""}>
|
||||||
|
<span>${i18n.localize(aspect.label)}</span>
|
||||||
|
</label>
|
||||||
|
`).join("")
|
||||||
|
|
||||||
|
const activatableOptions = summary.activatableAspectChoices.length
|
||||||
|
? summary.activatableAspectChoices.map(aspect => `<option value="${aspect.id}">${aspect.label}</option>`).join("")
|
||||||
|
: `<option value="">${i18n.localize("CELESTOPOL.FactionAspect.noAspectAvailable")}</option>`
|
||||||
|
|
||||||
|
const activatedRows = summary.activatedAspects.length
|
||||||
|
? summary.activatedAspects.map(aspect => `
|
||||||
|
<div class="faction-aspect-active-row ${aspect.relevantToActor ? "is-relevant" : ""}">
|
||||||
|
<span class="faction-aspect-active-name">${aspect.label}</span>
|
||||||
|
<span class="faction-aspect-active-value">+${aspect.value}</span>
|
||||||
|
</div>
|
||||||
|
`).join("")
|
||||||
|
: `<div class="faction-aspect-empty">${i18n.localize("CELESTOPOL.FactionAspect.noneActive")}</div>`
|
||||||
|
|
||||||
|
const sourceLabels = summary.sourceLabels.length
|
||||||
|
? summary.sourceLabels.join(" • ")
|
||||||
|
: i18n.localize("CELESTOPOL.FactionAspect.noSource")
|
||||||
|
|
||||||
|
const availableAspectList = summary.availableAspectLabels.length
|
||||||
|
? summary.availableAspectLabels.map(label => `<span class="faction-aspect-tag">${label}</span>`).join("")
|
||||||
|
: `<div class="faction-aspect-empty">${i18n.localize("CELESTOPOL.FactionAspect.noAspectAvailable")}</div>`
|
||||||
|
|
||||||
|
const removeOptions = summary.activatedAspects.length
|
||||||
|
? summary.activatedAspects.map(aspect => `<option value="${aspect.id}">${aspect.label} (+${aspect.value})</option>`).join("")
|
||||||
|
: `<option value="">${i18n.localize("CELESTOPOL.FactionAspect.noneActive")}</option>`
|
||||||
|
|
||||||
|
const officialSourcesBlock = summary.hasOfficialSources
|
||||||
|
? `
|
||||||
|
<div class="faction-aspect-source-list">
|
||||||
|
${summary.officialSourceLabels.map(label => `<span class="faction-aspect-tag">${label}</span>`).join("")}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: `<div class="faction-aspect-warning">${i18n.localize("CELESTOPOL.FactionAspect.officialSourcesEmpty")}</div>`
|
||||||
|
|
||||||
|
const customCellOpen = summary.customCell.enabled ? "open" : ""
|
||||||
|
|
||||||
|
return `
|
||||||
|
<form class="cel-dialog-form faction-aspect-manager">
|
||||||
|
<div class="faction-aspect-box">
|
||||||
|
<div class="faction-aspect-box-title">${i18n.localize("CELESTOPOL.FactionAspect.managerState")}</div>
|
||||||
|
<div class="faction-aspect-points">
|
||||||
|
<span class="faction-aspect-point-card"><strong>${i18n.localize("CELESTOPOL.FactionAspect.pointsMax")}</strong><em>${summary.pointsMax}</em></span>
|
||||||
|
<span class="faction-aspect-point-card"><strong>${i18n.localize("CELESTOPOL.FactionAspect.pointsSpent")}</strong><em>${summary.pointsSpent}</em></span>
|
||||||
|
<span class="faction-aspect-point-card"><strong>${i18n.localize("CELESTOPOL.FactionAspect.pointsRemaining")}</strong><em>${summary.pointsRemaining}</em></span>
|
||||||
|
</div>
|
||||||
|
${game.user.isGM ? `
|
||||||
|
<div class="form-group faction-aspect-pool-group">
|
||||||
|
<label>${i18n.localize("CELESTOPOL.FactionAspect.globalPoolLabel")}${hint(i18n.localize("CELESTOPOL.FactionAspect.globalPoolHint"))}</label>
|
||||||
|
<input type="number" name="pointsMax" min="0" value="${summary.pointsMax}">
|
||||||
|
</div>
|
||||||
|
` : ""}
|
||||||
|
<div class="faction-aspect-source-line"><strong>${i18n.localize("CELESTOPOL.FactionAspect.sources")}</strong> ${sourceLabels}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faction-aspect-box">
|
||||||
|
<div class="faction-aspect-box-title">${i18n.localize("CELESTOPOL.FactionAspect.officialSourcesTitle")}${hint(i18n.localize("CELESTOPOL.FactionAspect.officialSourcesHint"))}</div>
|
||||||
|
${officialSourcesBlock}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faction-aspect-box">
|
||||||
|
<div class="faction-aspect-box-title">${i18n.localize("CELESTOPOL.FactionAspect.availablePoolTitle")}${hint(i18n.localize("CELESTOPOL.FactionAspect.availablePoolHint"))}</div>
|
||||||
|
<div class="faction-aspect-tag-list">${availableAspectList}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faction-aspect-box">
|
||||||
|
<div class="faction-aspect-box-title">${i18n.localize("CELESTOPOL.FactionAspect.managerSettings")}</div>
|
||||||
|
<details class="faction-aspect-advanced" ${customCellOpen}>
|
||||||
|
<summary>${i18n.localize("CELESTOPOL.FactionAspect.customCellSection")}${hint(i18n.localize("CELESTOPOL.FactionAspect.customCellHint"))}</summary>
|
||||||
|
<div class="form-group faction-aspect-checkbox-line">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="customCellEnabled" ${summary.customCell.enabled ? "checked" : ""}>
|
||||||
|
${i18n.localize("CELESTOPOL.FactionAspect.customCellEnabled")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>${i18n.localize("CELESTOPOL.FactionAspect.customCellName")}</label>
|
||||||
|
<input type="text" name="customCellName" value="${foundry.utils.escapeHTML(summary.customCell.name ?? "")}" placeholder="${i18n.localize("CELESTOPOL.FactionAspect.customCell")}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>${i18n.localize("CELESTOPOL.FactionAspect.customCellMode")}</label>
|
||||||
|
<select name="customCellMode">
|
||||||
|
<option value="replace" ${summary.customCell.mode === "replace" ? "selected" : ""}>${i18n.localize("CELESTOPOL.FactionAspect.modeReplace")}</option>
|
||||||
|
<option value="extend" ${summary.customCell.mode === "extend" ? "selected" : ""}>${i18n.localize("CELESTOPOL.FactionAspect.modeExtend")}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="faction-aspect-cell-grid">${customAspectCheckboxes}</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faction-aspect-box">
|
||||||
|
<div class="faction-aspect-box-title">${i18n.localize("CELESTOPOL.FactionAspect.activateTitle")}${hint(i18n.localize("CELESTOPOL.FactionAspect.activateHint"))}</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>${i18n.localize("CELESTOPOL.FactionAspect.activateAspect")}</label>
|
||||||
|
<select name="activateAspectId">${activatableOptions}</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>${i18n.localize("CELESTOPOL.FactionAspect.activateValue")}</label>
|
||||||
|
<select name="activateAspectValue">
|
||||||
|
<option value="1">+1</option>
|
||||||
|
<option value="2">+2</option>
|
||||||
|
<option value="3">+3</option>
|
||||||
|
<option value="4">+4</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faction-aspect-box">
|
||||||
|
<div class="faction-aspect-box-title">${i18n.localize("CELESTOPOL.FactionAspect.activeTitle")}</div>
|
||||||
|
<div class="faction-aspect-active-list">${activatedRows}</div>
|
||||||
|
<div class="faction-aspect-remove-block">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>${i18n.localize("CELESTOPOL.FactionAspect.removeAspect")}</label>
|
||||||
|
<select name="removeAspectId">${removeOptions}</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _saveFactionAspectManagerSettings(formData, currentState) {
|
||||||
|
if (!game.user.isGM) {
|
||||||
|
formData.pointsMax = currentState.pointsMax
|
||||||
|
}
|
||||||
|
|
||||||
|
const activatedCost = currentState.activatedAspects.reduce((sum, aspect) => sum + aspect.value, 0)
|
||||||
|
if (formData.pointsMax < activatedCost) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CELESTOPOL.FactionAspect.pointsBelowSpent"))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.customCellEnabled) {
|
||||||
|
const count = formData.customCellAspectIds.length
|
||||||
|
if (count < 4 || count > 8) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CELESTOPOL.FactionAspect.customCellAspectCount"))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _setFactionAspectState({
|
||||||
|
...currentState,
|
||||||
|
pointsMax: formData.pointsMax,
|
||||||
|
customCell: {
|
||||||
|
enabled: formData.customCellEnabled,
|
||||||
|
mode: formData.customCellMode,
|
||||||
|
name: formData.customCellName,
|
||||||
|
aspectIds: formData.customCellAspectIds,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _manageFactionAspects(actor = null) {
|
||||||
|
const summary = _getFactionAspectSummary(actor)
|
||||||
|
const result = await foundry.applications.api.DialogV2.wait({
|
||||||
|
window: { title: game.i18n.localize("CELESTOPOL.FactionAspect.managerTitle") },
|
||||||
|
classes: ["fvtt-celestopol", "faction-aspect-dialog"],
|
||||||
|
content: _renderFactionAspectManagerContent(summary),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action: "save",
|
||||||
|
label: game.i18n.localize("CELESTOPOL.FactionAspect.save"),
|
||||||
|
callback: (_event, button) => ({ action: "save", ..._parseFactionAspectManagerForm(button.form) }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: "activate",
|
||||||
|
label: game.i18n.localize("CELESTOPOL.FactionAspect.activateButton"),
|
||||||
|
callback: (_event, button) => ({ action: "activate", ..._parseFactionAspectManagerForm(button.form) }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: "remove",
|
||||||
|
label: game.i18n.localize("CELESTOPOL.FactionAspect.removeButton"),
|
||||||
|
callback: (_event, button) => ({ action: "remove", ..._parseFactionAspectManagerForm(button.form) }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: "reset",
|
||||||
|
label: game.i18n.localize("CELESTOPOL.FactionAspect.resetScenario"),
|
||||||
|
callback: () => ({ action: "reset" }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rejectClose: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!result?.action) return null
|
||||||
|
|
||||||
|
if (result.action === "save") {
|
||||||
|
const updatedState = await _saveFactionAspectManagerSettings(result, _getFactionAspectState())
|
||||||
|
if (updatedState) ui.notifications.info(game.i18n.localize("CELESTOPOL.FactionAspect.saved"))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.action === "reset") {
|
||||||
|
const currentState = _getFactionAspectState()
|
||||||
|
await _setFactionAspectState({ ...currentState, activatedAspects: [] })
|
||||||
|
ui.notifications.info(game.i18n.localize("CELESTOPOL.FactionAspect.resetDone"))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.action === "activate") {
|
||||||
|
const savedState = await _saveFactionAspectManagerSettings(result, _getFactionAspectState())
|
||||||
|
if (!savedState) return null
|
||||||
|
|
||||||
|
const aspectId = result.activateAspectId
|
||||||
|
const aspectValue = result.activateAspectValue
|
||||||
|
const availableIds = new Set(_getFactionAspectSourceData(savedState).availableAspectIds)
|
||||||
|
const alreadyActive = savedState.activatedAspects.some(aspect => aspect.id === aspectId)
|
||||||
|
const currentSpent = savedState.activatedAspects.reduce((sum, aspect) => sum + aspect.value, 0)
|
||||||
|
|
||||||
|
if (!aspectId || !SYSTEM.FACTION_ASPECTS[aspectId] || !availableIds.has(aspectId)) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CELESTOPOL.FactionAspect.invalidAspect"))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (alreadyActive) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CELESTOPOL.FactionAspect.alreadyActive"))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if ((currentSpent + aspectValue) > savedState.pointsMax) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CELESTOPOL.FactionAspect.notEnoughPoints"))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const aspectLabel = game.i18n.localize(SYSTEM.FACTION_ASPECTS[aspectId].label)
|
||||||
|
await _setFactionAspectState({
|
||||||
|
...savedState,
|
||||||
|
activatedAspects: [
|
||||||
|
...savedState.activatedAspects,
|
||||||
|
{ id: aspectId, value: aspectValue, label: aspectLabel },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
ui.notifications.info(game.i18n.format("CELESTOPOL.FactionAspect.activated", {
|
||||||
|
aspect: aspectLabel,
|
||||||
|
value: aspectValue,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.action === "remove") {
|
||||||
|
const savedState = await _saveFactionAspectManagerSettings(result, _getFactionAspectState())
|
||||||
|
if (!savedState) return null
|
||||||
|
|
||||||
|
const aspectId = result.removeAspectId
|
||||||
|
const removedAspect = savedState.activatedAspects.find(aspect => aspect.id === aspectId)
|
||||||
|
if (!removedAspect) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CELESTOPOL.FactionAspect.invalidRemove"))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
await _setFactionAspectState({
|
||||||
|
...savedState,
|
||||||
|
activatedAspects: savedState.activatedAspects.filter(aspect => aspect.id !== aspectId),
|
||||||
|
})
|
||||||
|
ui.notifications.info(game.i18n.format("CELESTOPOL.FactionAspect.removed", {
|
||||||
|
aspect: removedAspect.label,
|
||||||
|
value: removedAspect.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
/* ─── Import initial des anomalies du compendium dans le monde ─────────── */
|
/* ─── Import initial des anomalies du compendium dans le monde ─────────── */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
133
lang/fr.json
@@ -20,6 +20,14 @@
|
|||||||
"anomaly": "Anomalie",
|
"anomaly": "Anomalie",
|
||||||
"descriptionPhysique": "Description physique",
|
"descriptionPhysique": "Description physique",
|
||||||
"descriptionPsychologique": "Description psychologique",
|
"descriptionPsychologique": "Description psychologique",
|
||||||
|
"historique": "Historique",
|
||||||
|
"portraitImage": "Image de portrait",
|
||||||
|
"portraitImagePlaceholder": "Chemin vers une image verticale…",
|
||||||
|
"portraitImageHint": "Cette image est indépendante du portrait affiché dans l’en-tête de la fiche.",
|
||||||
|
"portraitImageEmpty": "Aucun portrait biographique distinct n’est encore renseigné.",
|
||||||
|
"portraitImageMissing": "Aucun portrait biographique distinct n’est disponible pour cette fiche.",
|
||||||
|
"sendPortraitToChat": "Envoyer dans le tchat",
|
||||||
|
"portraitChatTitle": "Portrait",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"metier": "Métier",
|
"metier": "Métier",
|
||||||
"origine": "Origine",
|
"origine": "Origine",
|
||||||
@@ -82,7 +90,17 @@
|
|||||||
"Faction": {
|
"Faction": {
|
||||||
"label": "Faction",
|
"label": "Faction",
|
||||||
"relation": "Niveau de Relation",
|
"relation": "Niveau de Relation",
|
||||||
|
"legendTitle": "Rappel des relations",
|
||||||
"custom": "Faction personnalisée…",
|
"custom": "Faction personnalisée…",
|
||||||
|
"levelAllies": "Alliés",
|
||||||
|
"levelAmicaux": "Amicaux",
|
||||||
|
"levelPartenaires": "Partenaires",
|
||||||
|
"levelBienveillants": "Bienveillants",
|
||||||
|
"levelNeutres": "Neutres",
|
||||||
|
"levelMefiants": "Méfiants",
|
||||||
|
"levelHostiles": "Hostiles",
|
||||||
|
"levelRivaux": "Rivaux",
|
||||||
|
"levelEnnemis": "Ennemis",
|
||||||
"pinkerton": "Agence Pinkerton",
|
"pinkerton": "Agence Pinkerton",
|
||||||
"police": "Police secrète du duc",
|
"police": "Police secrète du duc",
|
||||||
"okhrana": "Okhrana",
|
"okhrana": "Okhrana",
|
||||||
@@ -92,6 +110,69 @@
|
|||||||
"vorovskoymir": "Vorovskoy Mir",
|
"vorovskoymir": "Vorovskoy Mir",
|
||||||
"cour": "Cour des merveilles"
|
"cour": "Cour des merveilles"
|
||||||
},
|
},
|
||||||
|
"FactionAspect": {
|
||||||
|
"title": "Aspects de faction",
|
||||||
|
"manage": "Gérer",
|
||||||
|
"managerTitle": "Gestion des Aspects de faction",
|
||||||
|
"managerState": "État du scénario",
|
||||||
|
"managerSettings": "Paramètres de groupe",
|
||||||
|
"officialSourcesTitle": "Factions officielles reconnues",
|
||||||
|
"officialSourcesHint": "En usage normal, les aspects disponibles proviennent automatiquement des factions principales des protagonistes.",
|
||||||
|
"officialSourcesEmpty": "Aucune faction officielle reconnue. Renseignez la faction principale des PJ sur leur fiche, ou activez une cellule indépendante si le groupe joue sa propre structure.",
|
||||||
|
"availablePoolTitle": "Aspects actuellement mobilisables",
|
||||||
|
"availablePoolHint": "Les joueurs choisissent collectivement un aspect disponible et la valeur qu'ils veulent lui attribuer, dans la limite de la réserve du scénario.",
|
||||||
|
"globalPoolLabel": "Pool global du scénario (MJ)",
|
||||||
|
"globalPoolHint": "Le MJ ajuste ici uniquement la réserve totale disponible pour le groupe au cours du scénario.",
|
||||||
|
"pointsMax": "Points disponibles",
|
||||||
|
"pointsSpent": "Points mobilisés",
|
||||||
|
"pointsRemaining": "Points restants",
|
||||||
|
"sources": "Sources disponibles",
|
||||||
|
"noSource": "Aucune faction de protagoniste reconnue pour le moment.",
|
||||||
|
"activeTitle": "Aspects mobilisés",
|
||||||
|
"noneActive": "Aucun aspect de faction mobilisé.",
|
||||||
|
"activateTitle": "Mobiliser un aspect",
|
||||||
|
"activateHint": "Une fois choisi par les joueurs et mobilisé ici, l'aspect reste figé pour tout le scénario, jusqu'au bouton « Nouveau scénario » ou à une correction manuelle du MJ.",
|
||||||
|
"activateAspect": "Aspect à mobiliser",
|
||||||
|
"activateValue": "Valeur",
|
||||||
|
"activateButton": "Mobiliser",
|
||||||
|
"removeAspect": "Retirer un aspect mobilisé",
|
||||||
|
"removeButton": "Retirer",
|
||||||
|
"customCell": "Cellule indépendante",
|
||||||
|
"customCellSection": "Cellule indépendante (optionnel)",
|
||||||
|
"customCellEnabled": "Utiliser une cellule indépendante",
|
||||||
|
"customCellHint": "Utilisez cette option uniquement si le groupe agit comme sa propre cellule ou si vous voulez compléter ou remplacer les factions officielles reconnues.",
|
||||||
|
"customCellName": "Nom de la cellule",
|
||||||
|
"customCellMode": "Mode de combinaison",
|
||||||
|
"modeReplace": "Remplace les factions officielles",
|
||||||
|
"modeExtend": "S'ajoute aux factions officielles",
|
||||||
|
"save": "Enregistrer",
|
||||||
|
"saved": "Paramètres des Aspects de faction enregistrés.",
|
||||||
|
"resetScenario": "Nouveau scénario",
|
||||||
|
"resetDone": "Les Aspects de faction mobilisés ont été réinitialisés.",
|
||||||
|
"activated": "{aspect} mobilisé à +{value}.",
|
||||||
|
"removed": "{aspect} (+{value}) retiré de la réserve mobilisée.",
|
||||||
|
"alreadyActive": "Cet aspect de faction est déjà mobilisé pour ce scénario.",
|
||||||
|
"invalidAspect": "Choisissez un aspect de faction disponible à mobiliser.",
|
||||||
|
"invalidRemove": "Choisissez un aspect de faction mobilisé à retirer.",
|
||||||
|
"notEnoughPoints": "La réserve d'Aspects de faction ne suffit pas pour cette mobilisation.",
|
||||||
|
"pointsBelowSpent": "Le total disponible ne peut pas être inférieur aux points déjà mobilisés.",
|
||||||
|
"customCellAspectCount": "Une cellule indépendante doit proposer entre 4 et 8 aspects.",
|
||||||
|
"gmOnly": "Seul le MJ peut gérer les Aspects de faction.",
|
||||||
|
"noAspectAvailable": "Aucun aspect disponible à mobiliser",
|
||||||
|
"legacyFactionValue": "Valeur héritée",
|
||||||
|
"rollLabel": "Aspect de faction",
|
||||||
|
"noneOption": "Aucun aspect de faction",
|
||||||
|
"bonnesadresses": "Bonnes adresses",
|
||||||
|
"contrebande": "Contrebande",
|
||||||
|
"corruption": "Corruption",
|
||||||
|
"diversion": "Diversion",
|
||||||
|
"falsification": "Falsification",
|
||||||
|
"passedroit": "Passe-droit",
|
||||||
|
"renforts": "Renforts",
|
||||||
|
"renseignements": "Renseignements",
|
||||||
|
"ressources": "Ressources",
|
||||||
|
"surveillance": "Surveillance"
|
||||||
|
},
|
||||||
"Track": {
|
"Track": {
|
||||||
"blessures": "Blessures",
|
"blessures": "Blessures",
|
||||||
"destin": "Destin",
|
"destin": "Destin",
|
||||||
@@ -99,7 +180,7 @@
|
|||||||
"level": "Niveau",
|
"level": "Niveau",
|
||||||
"currentMalus": "Malus actuel",
|
"currentMalus": "Malus actuel",
|
||||||
"blessuresTooltip": "Niveaux de blessures :\n1–2 : Anodin / Négligeable → aucun malus (1 min)\n3–4 : Dérisoire / Superficiel → −1 (10 min)\n5–6 : Léger / Modéré → −2 (30 min)\n7 : Grave → −3 (1 journée)\n8 : Dramatique → hors combat",
|
"blessuresTooltip": "Niveaux de blessures :\n1–2 : Anodin / Négligeable → aucun malus (1 min)\n3–4 : Dérisoire / Superficiel → −1 (10 min)\n5–6 : Léger / Modéré → −2 (30 min)\n7 : Grave → −3 (1 journée)\n8 : Dramatique → hors combat",
|
||||||
"spleenTooltip": "Le Spleen représente l'usure morale du protagoniste.\nLorsqu'il atteint son maximum, le protagoniste sombre dans la mélancolie et peut se retirer du scénario.",
|
"spleenTooltip": "Le Spleen représente l'usure morale du protagoniste.\nLa jauge de Spleen augmente avec les actions suivantes :\n• Lors d’un test de Spécialisation, en obtenant un échec et une Pleine lune sur le dé de la Lune.\n• Après un test de Spécialisation, pour transformer un échec en réussite, même après l’utilisation éventuelle d’une Anomalie.\n• Avant un test de résistance, pour réussir automatiquement le test.\n• Pour ne pas subir une blessure Dramatique.\n• En choisissant de puiser dans ses ressources.\nLorsque la jauge est remplie, le Protagoniste subit une Séquelle Dramatique et risque, à terme, de passer définitivement Hors Fiction.",
|
||||||
"destinTooltip": "Usages du Destin :\n• Réaliser un test avec 3d8\n• Gagner l'initiative lors d'un combat\n• Trouver l'ensemble des indices\n• Éviter une blessure\n• Sortir de l'inconscience\n• Obtenir un Triomphe"
|
"destinTooltip": "Usages du Destin :\n• Réaliser un test avec 3d8\n• Gagner l'initiative lors d'un combat\n• Trouver l'ensemble des indices\n• Éviter une blessure\n• Sortir de l'inconscience\n• Obtenir un Triomphe"
|
||||||
},
|
},
|
||||||
"Wound": {
|
"Wound": {
|
||||||
@@ -121,21 +202,44 @@
|
|||||||
"Combat": {
|
"Combat": {
|
||||||
"initiative": "Initiative",
|
"initiative": "Initiative",
|
||||||
"attack": "Attaquer",
|
"attack": "Attaquer",
|
||||||
|
"unarmedAttack": "Attaque à main nue",
|
||||||
|
"baseActions": "Actions de base",
|
||||||
|
"baseRangedDefense": "Esquive d'attaque à distance",
|
||||||
|
"rangedAttack": "Attaque à distance",
|
||||||
"corpsPnj": "Corps du PNJ",
|
"corpsPnj": "Corps du PNJ",
|
||||||
"tie": "ÉGALITÉ",
|
"tie": "ÉGALITÉ",
|
||||||
"tieDesc": "Personne n'est blessé",
|
"tieDesc": "Personne n'est blessé",
|
||||||
"successHit": "PNJ touché — 1 blessure",
|
"successHit": "Attaque réussie — cible touchée",
|
||||||
"failureHit": "Joueur touché — 1 blessure (mêlée)",
|
"failureHit": "Joueur touché — dégâts de l'arme appliqués (mêlée)",
|
||||||
"distanceNoWound": "Raté — pas de riposte",
|
"distanceNoWound": "Raté — pas de riposte",
|
||||||
"weaponDamage": "dégâts supplémentaires",
|
"weaponDamage": "dégâts supplémentaires",
|
||||||
"playerWounded": "Blessure infligée au joueur (mêlée)",
|
"playerWounded": "Blessures infligées au joueur (mêlée)",
|
||||||
"rangedDefenseTitle": "Esquiver (Mobilité)",
|
"rangedDefenseTitle": "Esquiver (Mobilité)",
|
||||||
"rangedDefenseTag": "Défense à distance",
|
"rangedDefenseTag": "Défense à distance",
|
||||||
"rangedDefenseSuccess": "Attaque esquivée — pas de blessure",
|
"rangedDefenseSuccess": "Attaque esquivée — pas de blessure",
|
||||||
"rangedDefenseFailure": "Touché par le PNJ — 1 blessure",
|
"rangedDefenseFailure": "Touché par le PNJ — dégâts de l'arme appliqués",
|
||||||
"rangedDefensePlayerWounded": "Blessure infligée par attaque à distance",
|
"rangedDefensePlayerWounded": "Blessures infligées par attaque à distance",
|
||||||
"targetLabel": "Cible",
|
"targetLabel": "Cible",
|
||||||
"targetAuto": "Saisir manuellement",
|
"targetAuto": "Saisir manuellement",
|
||||||
|
"targetCharacterLabel": "Personnage visé",
|
||||||
|
"targetCharacterAuto": "Aucun personnage présélectionné",
|
||||||
|
"damageLabel": "Dégâts infligés",
|
||||||
|
"damageUnit": "blessure(s)",
|
||||||
|
"damageManual": "Dégâts variables : application manuelle par le MJ.",
|
||||||
|
"damageArmorReduction": "Protection d'armure",
|
||||||
|
"damageApplied": "Blessures après armure",
|
||||||
|
"applyDamage": "Appliquer les blessures",
|
||||||
|
"damageApplying": "Application...",
|
||||||
|
"damageApplyingNotice": "Application des blessures en cours...",
|
||||||
|
"damageAppliedDone": "Blessures appliquées",
|
||||||
|
"damageAppliedCard": "{actor} subit {wounds} blessure(s) après armure ({armor}).",
|
||||||
|
"damageNoEffectCard": "{actor} ne subit aucune blessure : l'armure absorbe l'attaque ({armor}).",
|
||||||
|
"selectCharacter": "Choisir une cible",
|
||||||
|
"selectCharacterFirst": "Sélectionnez une cible avant d'appliquer les blessures.",
|
||||||
|
"noCharacterTargetAvailable": "Aucune cible de la scène active disponible pour appliquer les blessures.",
|
||||||
|
"damageRequestSent": "Demande d'application des blessures envoyée au MJ.",
|
||||||
|
"damageAppliedNotify": "{actor} : {wounds} blessure(s) appliquée(s) après armure ({armor}).",
|
||||||
|
"damageNoEffectNotify": "{actor} : l'armure absorbe entièrement l'attaque ({armor}).",
|
||||||
"rangedMod": "Modificateur de tir",
|
"rangedMod": "Modificateur de tir",
|
||||||
"rangedModNone": "Aucun modificateur",
|
"rangedModNone": "Aucun modificateur",
|
||||||
"rangedModAim": "Visée (dépense 1 tour) +2",
|
"rangedModAim": "Visée (dépense 1 tour) +2",
|
||||||
@@ -148,6 +252,7 @@
|
|||||||
"competences": "Domaines",
|
"competences": "Domaines",
|
||||||
"blessures": "Jauges",
|
"blessures": "Jauges",
|
||||||
"factions": "Factions",
|
"factions": "Factions",
|
||||||
|
"combat": "Combat",
|
||||||
"equipement": "Équipement",
|
"equipement": "Équipement",
|
||||||
"biography": "Biographie",
|
"biography": "Biographie",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
@@ -210,7 +315,8 @@
|
|||||||
"resistanceTest": "Test de résistance",
|
"resistanceTest": "Test de résistance",
|
||||||
"resistanceClickToRoll": "Lancer un test de résistance",
|
"resistanceClickToRoll": "Lancer un test de résistance",
|
||||||
"woundTaken": "Blessure cochée suite à l'échec",
|
"woundTaken": "Blessure cochée suite à l'échec",
|
||||||
"autoSuccess": "Réussite automatique"
|
"autoSuccess": "Réussite automatique",
|
||||||
|
"usedFactionAspect": "Aspect de faction mobilisé"
|
||||||
},
|
},
|
||||||
"Modifier": {
|
"Modifier": {
|
||||||
"evident": "Évident — Réussite automatique",
|
"evident": "Évident — Réussite automatique",
|
||||||
@@ -311,6 +417,19 @@
|
|||||||
"hint": "Cocher automatiquement 'Lancer le dé de la lune' dans les fenêtres de jet"
|
"hint": "Cocher automatiquement 'Lancer le dé de la lune' dans les fenêtres de jet"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Welcome": {
|
||||||
|
"title": "Bienvenue dans Célestopol 1922",
|
||||||
|
"intro": "Bienvenue dans le système FoundryVTT de Célestopol 1922.",
|
||||||
|
"helpLabel": "Aide de jeu",
|
||||||
|
"helpCompendium": "Une aide de jeu est disponible dans le compendium : {help}.",
|
||||||
|
"bookLabel": "Livre de base",
|
||||||
|
"helpFallback": "Célestopol 1922 — Aides de jeu",
|
||||||
|
"bookLinkLabel": "Voir le livre de base sur le site d’Antre-Monde Éditions"
|
||||||
|
},
|
||||||
|
"Pregens": {
|
||||||
|
"folderName": "Prétirés",
|
||||||
|
"imported": "Célestopol 1922 | Prétirés importés dans le dossier Acteurs."
|
||||||
|
},
|
||||||
"ChatCard": {
|
"ChatCard": {
|
||||||
"rollFor": "Jet de {skill} ({stat})"
|
"rollFor": "Jet de {skill} ({stat})"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
export { default as CelestopolCharacterSheet } from "./sheets/character-sheet.mjs"
|
export { default as CelestopolCharacterSheet } from "./sheets/character-sheet.mjs"
|
||||||
export { default as CelestopolNPCSheet } from "./sheets/npc-sheet.mjs"
|
export { default as CelestopolNPCSheet } from "./sheets/npc-sheet.mjs"
|
||||||
export { CelestopolAnomalySheet, CelestopolAspectSheet, CelestopolEquipmentSheet, CelestopolWeaponSheet, CelestopolArmureSheet } from "./sheets/item-sheets.mjs"
|
export { CelestopolAnomalySheet, CelestopolAspectSheet, CelestopolEquipmentSheet, CelestopolWeaponSheet, CelestopolArmureSheet } from "./sheets/item-sheets.mjs"
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
import { SYSTEM } from "../../config/system.mjs"
|
import { SYSTEM } from "../../config/system.mjs"
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||||
@@ -21,11 +34,14 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
dragDrop: [{ dragSelector: '[data-drag="true"], .rollable', dropSelector: null }],
|
dragDrop: [{ dragSelector: '[data-drag="true"], .rollable', dropSelector: null }],
|
||||||
actions: {
|
actions: {
|
||||||
editImage: CelestopolActorSheet.#onEditImage,
|
editImage: CelestopolActorSheet.#onEditImage,
|
||||||
|
sendBiographyPortrait: CelestopolActorSheet.#onSendBiographyPortrait,
|
||||||
toggleSheet: CelestopolActorSheet.#onToggleSheet,
|
toggleSheet: CelestopolActorSheet.#onToggleSheet,
|
||||||
edit: CelestopolActorSheet.#onItemEdit,
|
edit: CelestopolActorSheet.#onItemEdit,
|
||||||
delete: CelestopolActorSheet.#onItemDelete,
|
delete: CelestopolActorSheet.#onItemDelete,
|
||||||
attack: CelestopolActorSheet.#onAttack,
|
attack: CelestopolActorSheet.#onAttack,
|
||||||
rangedDefense: CelestopolActorSheet.#onRangedDefense,
|
rangedDefense: CelestopolActorSheet.#onRangedDefense,
|
||||||
|
unarmedAttack: CelestopolActorSheet.#onUnarmedAttack,
|
||||||
|
baseRangedDefense: CelestopolActorSheet.#onBaseRangedDefense,
|
||||||
trackBox: CelestopolActorSheet.#onTrackBox,
|
trackBox: CelestopolActorSheet.#onTrackBox,
|
||||||
skillLevel: CelestopolActorSheet.#onSkillLevel,
|
skillLevel: CelestopolActorSheet.#onSkillLevel,
|
||||||
factionLevel: CelestopolActorSheet.#onFactionLevel,
|
factionLevel: CelestopolActorSheet.#onFactionLevel,
|
||||||
@@ -46,6 +62,7 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
actor: this.document,
|
actor: this.document,
|
||||||
system: this.document.system,
|
system: this.document.system,
|
||||||
source: this.document.toObject(),
|
source: this.document.toObject(),
|
||||||
|
isGM: game.user.isGM,
|
||||||
isEditMode: this.isEditMode,
|
isEditMode: this.isEditMode,
|
||||||
isPlayMode: this.isPlayMode,
|
isPlayMode: this.isPlayMode,
|
||||||
isEditable: this.isEditable,
|
isEditable: this.isEditable,
|
||||||
@@ -122,6 +139,35 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
return fp.browse()
|
return fp.browse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #onSendBiographyPortrait() {
|
||||||
|
const portrait = this.document.system?.portraitImage || ""
|
||||||
|
if (!portrait) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("CELESTOPOL.Actor.portraitImageMissing"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawContent = `
|
||||||
|
<div class="cel-portrait-message chat-system-card">
|
||||||
|
<div class="portrait-message-header">
|
||||||
|
<span class="portrait-message-mark">✦</span>
|
||||||
|
<span class="portrait-message-title">${game.i18n.localize("CELESTOPOL.Actor.portraitChatTitle")}</span>
|
||||||
|
</div>
|
||||||
|
<div class="portrait-message-body">
|
||||||
|
<div class="portrait-message-name">${foundry.utils.escapeHTML(this.document.name)}</div>
|
||||||
|
<div class="portrait-message-frame">
|
||||||
|
<img src="${portrait}" alt="${foundry.utils.escapeHTML(this.document.name)}" class="portrait-message-image">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
await ChatMessage.create({
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor: this.document }),
|
||||||
|
style: CONST.CHAT_MESSAGE_STYLES.OTHER,
|
||||||
|
content: await foundry.applications.ux.TextEditor.implementation.enrichHTML(rawContent, { async: true }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
static #onToggleSheet() {
|
static #onToggleSheet() {
|
||||||
const modes = this.constructor.SHEET_MODES
|
const modes = this.constructor.SHEET_MODES
|
||||||
this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT
|
this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT
|
||||||
@@ -153,6 +199,16 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
await this.document.system.rollRangedDefense(itemId)
|
await this.document.system.rollRangedDefense(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #onUnarmedAttack() {
|
||||||
|
if (typeof this.document.system.rollUnarmedAttack !== "function") return
|
||||||
|
await this.document.system.rollUnarmedAttack()
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onBaseRangedDefense() {
|
||||||
|
if (typeof this.document.system.rollRangedDefenseBase !== "function") return
|
||||||
|
await this.document.system.rollRangedDefenseBase()
|
||||||
|
}
|
||||||
|
|
||||||
/** Met à jour une jauge de piste (blessures/destin/spleen) par clic sur une case. */
|
/** Met à jour une jauge de piste (blessures/destin/spleen) par clic sur une case. */
|
||||||
static #onTrackBox(_event, target) {
|
static #onTrackBox(_event, target) {
|
||||||
if (!this.isEditable) return
|
if (!this.isEditable) return
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||||
|
|
||||||
export default class CelestopolItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
export default class CelestopolItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
import CelestopolActorSheet from "./base-actor-sheet.mjs"
|
import CelestopolActorSheet from "./base-actor-sheet.mjs"
|
||||||
import { SYSTEM } from "../../config/system.mjs"
|
import { SYSTEM } from "../../config/system.mjs"
|
||||||
|
|
||||||
@@ -18,6 +31,7 @@ export default class CelestopolCharacterSheet extends CelestopolActorSheet {
|
|||||||
depenseXp: CelestopolCharacterSheet.#onDepenseXp,
|
depenseXp: CelestopolCharacterSheet.#onDepenseXp,
|
||||||
supprimerXpLog: CelestopolCharacterSheet.#onSupprimerXpLog,
|
supprimerXpLog: CelestopolCharacterSheet.#onSupprimerXpLog,
|
||||||
rollMoonDie: CelestopolCharacterSheet.#onRollMoonDie,
|
rollMoonDie: CelestopolCharacterSheet.#onRollMoonDie,
|
||||||
|
manageFactionAspects: CelestopolCharacterSheet.#onManageFactionAspects,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +42,7 @@ export default class CelestopolCharacterSheet extends CelestopolActorSheet {
|
|||||||
competences:{ template: "systems/fvtt-celestopol/templates/character-competences.hbs" },
|
competences:{ template: "systems/fvtt-celestopol/templates/character-competences.hbs" },
|
||||||
blessures: { template: "systems/fvtt-celestopol/templates/character-blessures.hbs" },
|
blessures: { template: "systems/fvtt-celestopol/templates/character-blessures.hbs" },
|
||||||
factions: { template: "systems/fvtt-celestopol/templates/character-factions.hbs" },
|
factions: { template: "systems/fvtt-celestopol/templates/character-factions.hbs" },
|
||||||
|
combat: { template: "systems/fvtt-celestopol/templates/character-combat.hbs" },
|
||||||
equipement: { template: "systems/fvtt-celestopol/templates/character-equipement.hbs" },
|
equipement: { template: "systems/fvtt-celestopol/templates/character-equipement.hbs" },
|
||||||
biography: { template: "systems/fvtt-celestopol/templates/character-biography.hbs" },
|
biography: { template: "systems/fvtt-celestopol/templates/character-biography.hbs" },
|
||||||
}
|
}
|
||||||
@@ -39,6 +54,7 @@ export default class CelestopolCharacterSheet extends CelestopolActorSheet {
|
|||||||
competences:{ id: "competences", group: "sheet", icon: "fa-solid fa-dice-d6", label: "CELESTOPOL.Tab.competences" },
|
competences:{ id: "competences", group: "sheet", icon: "fa-solid fa-dice-d6", label: "CELESTOPOL.Tab.competences" },
|
||||||
blessures: { id: "blessures", group: "sheet", icon: "fa-solid fa-heart-crack", label: "CELESTOPOL.Tab.blessures" },
|
blessures: { id: "blessures", group: "sheet", icon: "fa-solid fa-heart-crack", label: "CELESTOPOL.Tab.blessures" },
|
||||||
factions: { id: "factions", group: "sheet", icon: "fa-solid fa-flag", label: "CELESTOPOL.Tab.factions" },
|
factions: { id: "factions", group: "sheet", icon: "fa-solid fa-flag", label: "CELESTOPOL.Tab.factions" },
|
||||||
|
combat: { id: "combat", group: "sheet", icon: "fa-solid fa-khanda", label: "CELESTOPOL.Tab.combat" },
|
||||||
equipement: { id: "equipement", group: "sheet", icon: "fa-solid fa-shield-halved",label: "CELESTOPOL.Tab.equipement" },
|
equipement: { id: "equipement", group: "sheet", icon: "fa-solid fa-shield-halved",label: "CELESTOPOL.Tab.equipement" },
|
||||||
biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "CELESTOPOL.Tab.biography" },
|
biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "CELESTOPOL.Tab.biography" },
|
||||||
}
|
}
|
||||||
@@ -58,6 +74,11 @@ export default class CelestopolCharacterSheet extends CelestopolActorSheet {
|
|||||||
context.anomalyTypes = SYSTEM.ANOMALY_TYPES
|
context.anomalyTypes = SYSTEM.ANOMALY_TYPES
|
||||||
context.factions = SYSTEM.FACTIONS
|
context.factions = SYSTEM.FACTIONS
|
||||||
context.woundLevels = SYSTEM.WOUND_LEVELS
|
context.woundLevels = SYSTEM.WOUND_LEVELS
|
||||||
|
context.selectedPrimaryFactionId = game.celestopol?.normalizeFactionId(this.document.system.faction) || ""
|
||||||
|
context.legacyPrimaryFactionValue = this.document.system.faction && !context.selectedPrimaryFactionId
|
||||||
|
? `${this.document.system.faction}`.trim()
|
||||||
|
: ""
|
||||||
|
context.primaryFactionLabel = game.celestopol?.getFactionDisplayLabel(this.document.system.faction) || this.document.system.faction
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +115,18 @@ export default class CelestopolCharacterSheet extends CelestopolActorSheet {
|
|||||||
|
|
||||||
case "factions":
|
case "factions":
|
||||||
context.tab = context.tabs.factions
|
context.tab = context.tabs.factions
|
||||||
|
context.factionAspectSummary = game.celestopol?.getFactionAspectSummary(this.document) ?? null
|
||||||
|
context.factionLegend = [
|
||||||
|
{ value: "+4", label: game.i18n.localize("CELESTOPOL.Faction.levelAllies") },
|
||||||
|
{ value: "+3", label: game.i18n.localize("CELESTOPOL.Faction.levelAmicaux") },
|
||||||
|
{ value: "+2", label: game.i18n.localize("CELESTOPOL.Faction.levelPartenaires") },
|
||||||
|
{ value: "+1", label: game.i18n.localize("CELESTOPOL.Faction.levelBienveillants") },
|
||||||
|
{ value: "0", label: game.i18n.localize("CELESTOPOL.Faction.levelNeutres") },
|
||||||
|
{ value: "-1", label: game.i18n.localize("CELESTOPOL.Faction.levelMefiants") },
|
||||||
|
{ value: "-2", label: game.i18n.localize("CELESTOPOL.Faction.levelHostiles") },
|
||||||
|
{ value: "-3", label: game.i18n.localize("CELESTOPOL.Faction.levelRivaux") },
|
||||||
|
{ value: "-4", label: game.i18n.localize("CELESTOPOL.Faction.levelEnnemis") },
|
||||||
|
]
|
||||||
context.factionRows = Object.entries(SYSTEM.FACTIONS).map(([id, fDef]) => {
|
context.factionRows = Object.entries(SYSTEM.FACTIONS).map(([id, fDef]) => {
|
||||||
const val = this.document.system.factions[id]?.value ?? 0
|
const val = this.document.system.factions[id]?.value ?? 0
|
||||||
return {
|
return {
|
||||||
@@ -125,21 +158,29 @@ export default class CelestopolCharacterSheet extends CelestopolActorSheet {
|
|||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case "combat":
|
||||||
|
context.tab = context.tabs.combat
|
||||||
|
context.weapons = doc.itemTypes.weapon.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
context.armures = doc.itemTypes.armure.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
break
|
||||||
|
|
||||||
case "biography":
|
case "biography":
|
||||||
context.tab = context.tabs.biography
|
context.tab = context.tabs.biography
|
||||||
context.xpLogEmpty = (doc.system.xp?.log?.length ?? 0) === 0
|
context.xpLogEmpty = (doc.system.xp?.log?.length ?? 0) === 0
|
||||||
|
context.biographyPortrait = doc.system.portraitImage || ""
|
||||||
|
context.hasBiographyPortrait = !!doc.system.portraitImage
|
||||||
context.enrichedDescriptionPhysique = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
context.enrichedDescriptionPhysique = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
doc.system.descriptionPhysique, { relativeTo: this.document })
|
doc.system.descriptionPhysique, { relativeTo: this.document })
|
||||||
context.enrichedDescriptionPsychologique = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
context.enrichedDescriptionPsychologique = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
doc.system.descriptionPsychologique, { relativeTo: this.document })
|
doc.system.descriptionPsychologique, { relativeTo: this.document })
|
||||||
|
context.enrichedHistorique = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
|
doc.system.historique, { relativeTo: this.document })
|
||||||
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
doc.system.notes, { relativeTo: this.document })
|
doc.system.notes, { relativeTo: this.document })
|
||||||
break
|
break
|
||||||
|
|
||||||
case "equipement":
|
case "equipement":
|
||||||
context.tab = context.tabs.equipement
|
context.tab = context.tabs.equipement
|
||||||
context.weapons = doc.itemTypes.weapon.sort((a, b) => a.name.localeCompare(b.name))
|
|
||||||
context.armures = doc.itemTypes.armure.sort((a, b) => a.name.localeCompare(b.name))
|
|
||||||
context.equipments= doc.itemTypes.equipment.sort((a, b) => a.name.localeCompare(b.name))
|
context.equipments= doc.itemTypes.equipment.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -177,6 +218,7 @@ export default class CelestopolCharacterSheet extends CelestopolActorSheet {
|
|||||||
static async #onCreateArmure() {
|
static async #onCreateArmure() {
|
||||||
await this.document.createEmbeddedDocuments("Item", [{
|
await this.document.createEmbeddedDocuments("Item", [{
|
||||||
name: game.i18n.localize("TYPES.Item.armure"), type: "armure",
|
name: game.i18n.localize("TYPES.Item.armure"), type: "armure",
|
||||||
|
system: { protection: 1, malus: 1 },
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +241,10 @@ export default class CelestopolCharacterSheet extends CelestopolActorSheet {
|
|||||||
await anomaly.update({ "system.usesRemaining": anomaly.system.level })
|
await anomaly.update({ "system.usesRemaining": anomaly.system.level })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #onManageFactionAspects() {
|
||||||
|
await game.celestopol?.manageFactionAspects(this.document)
|
||||||
|
}
|
||||||
|
|
||||||
/** Ouvre un dialogue pour dépenser de l'XP. */
|
/** Ouvre un dialogue pour dépenser de l'XP. */
|
||||||
static async #onDepenseXp() {
|
static async #onDepenseXp() {
|
||||||
const actor = this.document
|
const actor = this.document
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
import CelestopolItemSheet from "./base-item-sheet.mjs"
|
import CelestopolItemSheet from "./base-item-sheet.mjs"
|
||||||
import { SYSTEM } from "../../config/system.mjs"
|
import { SYSTEM } from "../../config/system.mjs"
|
||||||
|
|
||||||
@@ -97,4 +110,22 @@ export class CelestopolArmureSheet extends CelestopolItemSheet {
|
|||||||
this.document.system.description, { async: true })
|
this.document.system.description, { async: true })
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onRender(context, options) {
|
||||||
|
super._onRender(context, options)
|
||||||
|
|
||||||
|
const protectionInput = this.element.querySelector('[name="system.protection"]')
|
||||||
|
const malusInput = this.element.querySelector('[name="system.malus"]')
|
||||||
|
const malusValue = this.element.querySelector('[data-armure-malus-value]')
|
||||||
|
if (!protectionInput || !malusInput || !malusValue) return
|
||||||
|
|
||||||
|
const syncMalus = () => {
|
||||||
|
malusInput.value = protectionInput.value
|
||||||
|
malusValue.textContent = protectionInput.value
|
||||||
|
}
|
||||||
|
|
||||||
|
syncMalus()
|
||||||
|
protectionInput.addEventListener("input", syncMalus)
|
||||||
|
protectionInput.addEventListener("change", syncMalus)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
import CelestopolActorSheet from "./base-actor-sheet.mjs"
|
import CelestopolActorSheet from "./base-actor-sheet.mjs"
|
||||||
import { SYSTEM } from "../../config/system.mjs"
|
import { SYSTEM } from "../../config/system.mjs"
|
||||||
|
|
||||||
@@ -9,6 +22,7 @@ export default class CelestopolNPCSheet extends CelestopolActorSheet {
|
|||||||
window: { contentClasses: ["npc-content"] },
|
window: { contentClasses: ["npc-content"] },
|
||||||
actions: {
|
actions: {
|
||||||
createAspect: CelestopolNPCSheet.#onCreateAspect,
|
createAspect: CelestopolNPCSheet.#onCreateAspect,
|
||||||
|
createEquipment: CelestopolNPCSheet.#onCreateEquipment,
|
||||||
createWeapon: CelestopolNPCSheet.#onCreateWeapon,
|
createWeapon: CelestopolNPCSheet.#onCreateWeapon,
|
||||||
createArmure: CelestopolNPCSheet.#onCreateArmure,
|
createArmure: CelestopolNPCSheet.#onCreateArmure,
|
||||||
rollMoonDie: CelestopolNPCSheet.#onRollMoonDie,
|
rollMoonDie: CelestopolNPCSheet.#onRollMoonDie,
|
||||||
@@ -21,6 +35,7 @@ export default class CelestopolNPCSheet extends CelestopolActorSheet {
|
|||||||
tabs: { template: "templates/generic/tab-navigation.hbs" },
|
tabs: { template: "templates/generic/tab-navigation.hbs" },
|
||||||
competences: { template: "systems/fvtt-celestopol/templates/npc-competences.hbs" },
|
competences: { template: "systems/fvtt-celestopol/templates/npc-competences.hbs" },
|
||||||
blessures: { template: "systems/fvtt-celestopol/templates/npc-blessures.hbs" },
|
blessures: { template: "systems/fvtt-celestopol/templates/npc-blessures.hbs" },
|
||||||
|
combat: { template: "systems/fvtt-celestopol/templates/npc-combat.hbs" },
|
||||||
equipement: { template: "systems/fvtt-celestopol/templates/npc-equipement.hbs" },
|
equipement: { template: "systems/fvtt-celestopol/templates/npc-equipement.hbs" },
|
||||||
biographie: { template: "systems/fvtt-celestopol/templates/npc-biographie.hbs" },
|
biographie: { template: "systems/fvtt-celestopol/templates/npc-biographie.hbs" },
|
||||||
}
|
}
|
||||||
@@ -31,6 +46,7 @@ export default class CelestopolNPCSheet extends CelestopolActorSheet {
|
|||||||
const tabs = {
|
const tabs = {
|
||||||
competences: { id: "competences", group: "sheet", icon: "fa-solid fa-dice-d6", label: "CELESTOPOL.Tab.competences" },
|
competences: { id: "competences", group: "sheet", icon: "fa-solid fa-dice-d6", label: "CELESTOPOL.Tab.competences" },
|
||||||
blessures: { id: "blessures", group: "sheet", icon: "fa-solid fa-heart-crack", label: "CELESTOPOL.Tab.blessures" },
|
blessures: { id: "blessures", group: "sheet", icon: "fa-solid fa-heart-crack", label: "CELESTOPOL.Tab.blessures" },
|
||||||
|
combat: { id: "combat", group: "sheet", icon: "fa-solid fa-khanda", label: "CELESTOPOL.Tab.combat" },
|
||||||
equipement: { id: "equipement", group: "sheet", icon: "fa-solid fa-shield-halved",label: "CELESTOPOL.Tab.equipement" },
|
equipement: { id: "equipement", group: "sheet", icon: "fa-solid fa-shield-halved",label: "CELESTOPOL.Tab.equipement" },
|
||||||
biographie: { id: "biographie", group: "sheet", icon: "fa-solid fa-book-open", label: "CELESTOPOL.Tab.biographie" },
|
biographie: { id: "biographie", group: "sheet", icon: "fa-solid fa-book-open", label: "CELESTOPOL.Tab.biographie" },
|
||||||
}
|
}
|
||||||
@@ -54,8 +70,9 @@ export default class CelestopolNPCSheet extends CelestopolActorSheet {
|
|||||||
|
|
||||||
const sys = this.document.system
|
const sys = this.document.system
|
||||||
context.aspects = this.document.itemTypes.aspect ?? []
|
context.aspects = this.document.itemTypes.aspect ?? []
|
||||||
context.weapons = this.document.itemTypes.weapon ?? []
|
context.weapons = this.document.itemTypes.weapon.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
context.armures = this.document.itemTypes.armure ?? []
|
context.armures = this.document.itemTypes.armure.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
context.equipments = this.document.itemTypes.equipment.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
context.armorMalus = sys.armorMalus ?? 0
|
context.armorMalus = sys.armorMalus ?? 0
|
||||||
|
|
||||||
// Label effectif de chaque domaine selon le type de PNJ
|
// Label effectif de chaque domaine selon le type de PNJ
|
||||||
@@ -91,11 +108,16 @@ export default class CelestopolNPCSheet extends CelestopolActorSheet {
|
|||||||
context.system.notes, { relativeTo: this.document }
|
context.system.notes, { relativeTo: this.document }
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
case "combat":
|
||||||
|
context.tab = context.tabs.combat
|
||||||
|
break
|
||||||
case "equipement":
|
case "equipement":
|
||||||
context.tab = context.tabs.equipement
|
context.tab = context.tabs.equipement
|
||||||
break
|
break
|
||||||
case "biographie":
|
case "biographie":
|
||||||
context.tab = context.tabs.biographie
|
context.tab = context.tabs.biographie
|
||||||
|
context.biographyPortrait = context.system.portraitImage || ""
|
||||||
|
context.hasBiographyPortrait = !!context.system.portraitImage
|
||||||
context.enrichedHistoire = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
context.enrichedHistoire = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
context.system.histoire, { relativeTo: this.document }
|
context.system.histoire, { relativeTo: this.document }
|
||||||
)
|
)
|
||||||
@@ -119,9 +141,16 @@ export default class CelestopolNPCSheet extends CelestopolActorSheet {
|
|||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #onCreateEquipment() {
|
||||||
|
await this.document.createEmbeddedDocuments("Item", [{
|
||||||
|
name: game.i18n.localize("TYPES.Item.equipment"), type: "equipment",
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
static async #onCreateArmure() {
|
static async #onCreateArmure() {
|
||||||
await this.document.createEmbeddedDocuments("Item", [{
|
await this.document.createEmbeddedDocuments("Item", [{
|
||||||
name: game.i18n.localize("TYPES.Item.armure"), type: "armure",
|
name: game.i18n.localize("TYPES.Item.armure"), type: "armure",
|
||||||
|
system: { protection: 1, malus: 1 },
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
export const SYSTEM_ID = "fvtt-celestopol"
|
export const SYSTEM_ID = "fvtt-celestopol"
|
||||||
|
|
||||||
export const ASCII = `
|
export const ASCII = `
|
||||||
@@ -88,6 +101,52 @@ export const FACTIONS = {
|
|||||||
cour: { id: "cour", label: "CELESTOPOL.Faction.cour" },
|
cour: { id: "cour", label: "CELESTOPOL.Faction.cour" },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Aspects de faction mobilisables au niveau du groupe. */
|
||||||
|
export const FACTION_ASPECTS = {
|
||||||
|
bonnesadresses: { id: "bonnesadresses", label: "CELESTOPOL.FactionAspect.bonnesadresses" },
|
||||||
|
contrebande: { id: "contrebande", label: "CELESTOPOL.FactionAspect.contrebande" },
|
||||||
|
corruption: { id: "corruption", label: "CELESTOPOL.FactionAspect.corruption" },
|
||||||
|
diversion: { id: "diversion", label: "CELESTOPOL.FactionAspect.diversion" },
|
||||||
|
falsification: { id: "falsification", label: "CELESTOPOL.FactionAspect.falsification" },
|
||||||
|
passedroit: { id: "passedroit", label: "CELESTOPOL.FactionAspect.passedroit" },
|
||||||
|
renforts: { id: "renforts", label: "CELESTOPOL.FactionAspect.renforts" },
|
||||||
|
renseignements: { id: "renseignements", label: "CELESTOPOL.FactionAspect.renseignements" },
|
||||||
|
ressources: { id: "ressources", label: "CELESTOPOL.FactionAspect.ressources" },
|
||||||
|
surveillance: { id: "surveillance", label: "CELESTOPOL.FactionAspect.surveillance" },
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tableau p.111 : aspects de faction disponibles selon l'organisation. */
|
||||||
|
export const FACTION_ASPECTS_BY_FACTION = {
|
||||||
|
police: [
|
||||||
|
"diversion", "passedroit", "renforts", "renseignements", "ressources", "surveillance",
|
||||||
|
],
|
||||||
|
vorovskoymir: [
|
||||||
|
"bonnesadresses", "contrebande", "corruption", "diversion", "falsification",
|
||||||
|
"renforts", "renseignements", "ressources", "surveillance",
|
||||||
|
],
|
||||||
|
okhrana: [
|
||||||
|
"corruption", "diversion", "falsification", "passedroit", "renforts",
|
||||||
|
"renseignements", "ressources",
|
||||||
|
],
|
||||||
|
oto: [
|
||||||
|
"contrebande", "corruption", "falsification", "renseignements", "surveillance",
|
||||||
|
],
|
||||||
|
syndicats: [
|
||||||
|
"bonnesadresses", "contrebande", "corruption", "falsification", "renseignements", "surveillance",
|
||||||
|
],
|
||||||
|
pinkerton: [
|
||||||
|
"bonnesadresses", "diversion", "falsification", "renforts",
|
||||||
|
"renseignements", "ressources", "surveillance",
|
||||||
|
],
|
||||||
|
cour: [
|
||||||
|
"bonnesadresses", "contrebande", "diversion", "renforts", "renseignements", "surveillance",
|
||||||
|
],
|
||||||
|
lunanovatek: [
|
||||||
|
"contrebande", "corruption", "falsification", "renforts",
|
||||||
|
"renseignements", "ressources", "surveillance",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
/** Niveaux de blessures avec leur malus associé. */
|
/** Niveaux de blessures avec leur malus associé. */
|
||||||
export const WOUND_LEVELS = [
|
export const WOUND_LEVELS = [
|
||||||
{ id: 0, label: "CELESTOPOL.Wound.none", malus: 0, duration: "" },
|
{ id: 0, label: "CELESTOPOL.Wound.none", malus: 0, duration: "" },
|
||||||
@@ -209,6 +268,8 @@ export const SYSTEM = {
|
|||||||
ANOMALY_TYPES,
|
ANOMALY_TYPES,
|
||||||
ANOMALY_DEFINITIONS,
|
ANOMALY_DEFINITIONS,
|
||||||
FACTIONS,
|
FACTIONS,
|
||||||
|
FACTION_ASPECTS,
|
||||||
|
FACTION_ASPECTS_BY_FACTION,
|
||||||
NPC_TYPES,
|
NPC_TYPES,
|
||||||
ANTAGONISTE_STATS,
|
ANTAGONISTE_STATS,
|
||||||
WOUND_LEVELS,
|
WOUND_LEVELS,
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
export { default as CelestopolActor } from "./actor.mjs"
|
export { default as CelestopolActor } from "./actor.mjs"
|
||||||
export { default as CelestopolItem } from "./item.mjs"
|
export { default as CelestopolItem } from "./item.mjs"
|
||||||
export { default as CelestopolChatMessage } from "./chat-message.mjs"
|
export { default as CelestopolChatMessage } from "./chat-message.mjs"
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
export default class CelestopolActor extends Actor {
|
export default class CelestopolActor extends Actor {
|
||||||
/** @override */
|
/** @override */
|
||||||
getRollData() {
|
getRollData() {
|
||||||
|
|||||||
@@ -1 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
export default class CelestopolChatMessage extends ChatMessage {}
|
export default class CelestopolChatMessage extends ChatMessage {}
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
const SYSTEM_ID = "fvtt-celestopol"
|
const SYSTEM_ID = "fvtt-celestopol"
|
||||||
|
|
||||||
export default class CelestopolCombat extends Combat {
|
export default class CelestopolCombat extends Combat {
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
export default class CelestopolItem extends Item {
|
export default class CelestopolItem extends Item {
|
||||||
/** @override */
|
/** @override */
|
||||||
getRollData() {
|
getRollData() {
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
import { SYSTEM } from "../config/system.mjs"
|
import { SYSTEM } from "../config/system.mjs"
|
||||||
|
|
||||||
/** Construit la formule de jet à partir du nombre de dés et du modificateur total. */
|
/** Construit la formule de jet à partir du nombre de dés et du modificateur total. */
|
||||||
@@ -29,6 +42,58 @@ export class CelestopolRoll extends Roll {
|
|||||||
get skillLabel() { return this.options.skillLabel }
|
get skillLabel() { return this.options.skillLabel }
|
||||||
get difficulty() { return this.options.difficulty }
|
get difficulty() { return this.options.difficulty }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convertit le niveau de dégâts d'une arme en nombre de blessures de base.
|
||||||
|
* Règle : une attaque réussie inflige toujours 1 blessure, plus le bonus de dégâts.
|
||||||
|
* @param {string|number|null} weaponDegats
|
||||||
|
* @returns {number|null}
|
||||||
|
*/
|
||||||
|
static getIncomingWounds(weaponDegats) {
|
||||||
|
const raw = `${weaponDegats ?? "0"}`
|
||||||
|
const bonus = Number.parseInt(raw, 10)
|
||||||
|
if (!Number.isFinite(bonus)) return null
|
||||||
|
return Math.max(0, 1 + bonus)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retourne la protection totale de l'armure équipée pour un acteur.
|
||||||
|
* @param {Actor|null} actor
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
static getActorArmorProtection(actor) {
|
||||||
|
if (!actor) return 0
|
||||||
|
|
||||||
|
if (typeof actor.system?.getArmorMalus === "function") {
|
||||||
|
return Math.abs(actor.system.getArmorMalus())
|
||||||
|
}
|
||||||
|
|
||||||
|
const derivedArmorMalus = actor.system?.armorMalus
|
||||||
|
if (Number.isFinite(derivedArmorMalus)) {
|
||||||
|
return Math.abs(derivedArmorMalus)
|
||||||
|
}
|
||||||
|
|
||||||
|
const armures = actor.itemTypes?.armure ?? []
|
||||||
|
return armures
|
||||||
|
.filter(a => a.system.equipped)
|
||||||
|
.reduce((sum, a) => sum + Math.abs(a.system.protection ?? a.system.malus ?? 0), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Résout un acteur à partir de son UUID si disponible, sinon via son identifiant monde.
|
||||||
|
* @param {object} ref
|
||||||
|
* @param {string|null} ref.actorUuid
|
||||||
|
* @param {string|null} ref.actorId
|
||||||
|
* @returns {Promise<Actor|null>}
|
||||||
|
*/
|
||||||
|
static async resolveActor({ actorUuid = null, actorId = null } = {}) {
|
||||||
|
if (actorUuid) {
|
||||||
|
const actorFromUuid = await fromUuid(actorUuid)
|
||||||
|
if (actorFromUuid) return actorFromUuid
|
||||||
|
}
|
||||||
|
if (actorId) return game.actors.get(actorId) ?? null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ouvre le dialogue de configuration du jet via DialogV2 et exécute le jet.
|
* Ouvre le dialogue de configuration du jet via DialogV2 et exécute le jet.
|
||||||
* @param {object} options
|
* @param {object} options
|
||||||
@@ -71,6 +136,8 @@ export class CelestopolRoll extends Roll {
|
|||||||
value: m.value,
|
value: m.value,
|
||||||
label: game.i18n.localize(m.label),
|
label: game.i18n.localize(m.label),
|
||||||
}))
|
}))
|
||||||
|
const factionAspectChoices = game.celestopol?.getFactionAspectSummary(options.actorId ? game.actors.get(options.actorId) : null)
|
||||||
|
?.availableAspectChoices ?? []
|
||||||
|
|
||||||
const dialogContext = {
|
const dialogContext = {
|
||||||
actorName: options.actorName,
|
actorName: options.actorName,
|
||||||
@@ -89,6 +156,7 @@ export class CelestopolRoll extends Roll {
|
|||||||
aspectChoices,
|
aspectChoices,
|
||||||
situationChoices,
|
situationChoices,
|
||||||
rangedModChoices,
|
rangedModChoices,
|
||||||
|
factionAspectChoices,
|
||||||
availableTargets,
|
availableTargets,
|
||||||
fortuneValue,
|
fortuneValue,
|
||||||
armorMalus,
|
armorMalus,
|
||||||
@@ -123,7 +191,7 @@ export class CelestopolRoll extends Roll {
|
|||||||
function applyTargetSelection() {
|
function applyTargetSelection() {
|
||||||
if (!targetSelect) return
|
if (!targetSelect) return
|
||||||
const selectedOption = targetSelect.options[targetSelect.selectedIndex]
|
const selectedOption = targetSelect.options[targetSelect.selectedIndex]
|
||||||
const val = parseFloat(targetSelect.value)
|
const val = parseFloat(selectedOption?.dataset.corps ?? "")
|
||||||
const corpsPnjInput = wrap.querySelector('#corpsPnj')
|
const corpsPnjInput = wrap.querySelector('#corpsPnj')
|
||||||
if (targetSelect.value && !isNaN(val)) {
|
if (targetSelect.value && !isNaN(val)) {
|
||||||
// Cible sélectionnée : masquer la valeur, afficher le nom
|
// Cible sélectionnée : masquer la valeur, afficher le nom
|
||||||
@@ -155,6 +223,8 @@ export class CelestopolRoll extends Roll {
|
|||||||
const autoSucc = rawMod === "auto"
|
const autoSucc = rawMod === "auto"
|
||||||
const modifier = autoSucc ? 0 : (parseInt(rawMod ?? 0) || 0)
|
const modifier = autoSucc ? 0 : (parseInt(rawMod ?? 0) || 0)
|
||||||
const aspectMod = parseInt(wrap.querySelector('#aspectModifier')?.value ?? 0) || 0
|
const aspectMod = parseInt(wrap.querySelector('#aspectModifier')?.value ?? 0) || 0
|
||||||
|
const selectedFactionAspect = wrap.querySelector('#factionAspectId')?.selectedOptions?.[0]
|
||||||
|
const factionAspectBonus = parseInt(selectedFactionAspect?.dataset.value ?? 0) || 0
|
||||||
const situMod = parseInt(wrap.querySelector('#situationMod')?.value ?? 0) || 0
|
const situMod = parseInt(wrap.querySelector('#situationMod')?.value ?? 0) || 0
|
||||||
const rangedMod = parseInt(wrap.querySelector('#rangedMod')?.value ?? 0) || 0
|
const rangedMod = parseInt(wrap.querySelector('#rangedMod')?.value ?? 0) || 0
|
||||||
const useDestin = wrap.querySelector('#useDestin')?.checked
|
const useDestin = wrap.querySelector('#useDestin')?.checked
|
||||||
@@ -180,7 +250,7 @@ export class CelestopolRoll extends Roll {
|
|||||||
const effSit = puiser ? Math.max(0, situMod) : situMod
|
const effSit = puiser ? Math.max(0, situMod) : situMod
|
||||||
const effArmor = puiser ? 0 : armorMalus
|
const effArmor = puiser ? 0 : armorMalus
|
||||||
const effRanged = puiser ? Math.max(0, rangedMod) : rangedMod
|
const effRanged = puiser ? Math.max(0, rangedMod) : rangedMod
|
||||||
const totalMod = skillValue + effWound + effMod + effAspect + effSit + effArmor + effRanged
|
const totalMod = skillValue + effWound + effMod + effAspect + factionAspectBonus + effSit + effArmor + effRanged
|
||||||
|
|
||||||
let formula
|
let formula
|
||||||
if (autoSucc) {
|
if (autoSucc) {
|
||||||
@@ -198,7 +268,7 @@ export class CelestopolRoll extends Roll {
|
|||||||
if (previewEl) previewEl.textContent = formula
|
if (previewEl) previewEl.textContent = formula
|
||||||
}
|
}
|
||||||
|
|
||||||
wrap.querySelectorAll('#modifier, #aspectModifier, #situationMod, #rangedMod, #useDestin, #useFortune, #puiserRessources, #corpsPnj')
|
wrap.querySelectorAll('#modifier, #aspectModifier, #factionAspectId, #situationMod, #rangedMod, #useDestin, #useFortune, #puiserRessources, #corpsPnj')
|
||||||
.forEach(el => {
|
.forEach(el => {
|
||||||
el.addEventListener('change', update)
|
el.addEventListener('change', update)
|
||||||
el.addEventListener('input', update)
|
el.addEventListener('input', update)
|
||||||
@@ -233,13 +303,26 @@ export class CelestopolRoll extends Roll {
|
|||||||
const autoSuccess = rollContext.modifier === "auto"
|
const autoSuccess = rollContext.modifier === "auto"
|
||||||
const modifier = autoSuccess ? 0 : (parseInt(rollContext.modifier ?? 0) || 0)
|
const modifier = autoSuccess ? 0 : (parseInt(rollContext.modifier ?? 0) || 0)
|
||||||
const aspectMod = parseInt(rollContext.aspectModifier ?? 0) || 0
|
const aspectMod = parseInt(rollContext.aspectModifier ?? 0) || 0
|
||||||
|
const factionAspectId = typeof rollContext.factionAspectId === "string" ? rollContext.factionAspectId : ""
|
||||||
|
const selectedFactionAspect = factionAspectChoices.find(choice => choice.id === factionAspectId) ?? null
|
||||||
|
const factionAspectBonus = selectedFactionAspect?.value ?? 0
|
||||||
|
const factionAspectLabel = selectedFactionAspect?.label ?? ""
|
||||||
const situationMod = parseInt(rollContext.situationMod ?? 0) || 0
|
const situationMod = parseInt(rollContext.situationMod ?? 0) || 0
|
||||||
const rangedMod = isRangedAttack ? (parseInt(rollContext.rangedMod ?? 0) || 0) : 0
|
const rangedMod = isRangedAttack ? (parseInt(rollContext.rangedMod ?? 0) || 0) : 0
|
||||||
const isOpposition = !isCombat && !isResistance && (rollContext.isOpposition === true || rollContext.isOpposition === "true")
|
const isOpposition = !isCombat && (rollContext.isOpposition === true || rollContext.isOpposition === "true")
|
||||||
const useDestin = destGaugeFull && (rollContext.useDestin === true || rollContext.useDestin === "true")
|
const useDestin = destGaugeFull && (rollContext.useDestin === true || rollContext.useDestin === "true")
|
||||||
const useFortune = fortuneValue > 0 && (rollContext.useFortune === true || rollContext.useFortune === "true")
|
const useFortune = fortuneValue > 0 && (rollContext.useFortune === true || rollContext.useFortune === "true")
|
||||||
const puiserRessources = rollContext.puiserRessources === true || rollContext.puiserRessources === "true"
|
const puiserRessources = rollContext.puiserRessources === true || rollContext.puiserRessources === "true"
|
||||||
const rollMoonDie = rollContext.rollMoonDie === true || rollContext.rollMoonDie === "true"
|
const rollMoonDie = rollContext.rollMoonDie === true || rollContext.rollMoonDie === "true"
|
||||||
|
const selectedCombatTargetRef = typeof rollContext.targetSelect === "string" ? rollContext.targetSelect : ""
|
||||||
|
const selectedCombatTarget = selectedCombatTargetRef
|
||||||
|
? availableTargets.find(t => t.uuid === selectedCombatTargetRef || t.id === selectedCombatTargetRef) ?? null
|
||||||
|
: null
|
||||||
|
const resolvedWeaponName = (isRangedDefense && selectedCombatTarget?.weaponName) ? selectedCombatTarget.weaponName : weaponName
|
||||||
|
const resolvedWeaponDegats = (isRangedDefense && selectedCombatTarget?.weaponDegats) ? selectedCombatTarget.weaponDegats : weaponDegats
|
||||||
|
const targetActorId = selectedCombatTarget?.id || ""
|
||||||
|
const targetActorUuid = selectedCombatTarget?.uuid || ""
|
||||||
|
const targetActorName = selectedCombatTarget?.name || ""
|
||||||
|
|
||||||
// En résistance : forcer puiser=false, lune=false, fortune=false, destin=false
|
// En résistance : forcer puiser=false, lune=false, fortune=false, destin=false
|
||||||
const effectivePuiser = isResistance ? false : puiserRessources
|
const effectivePuiser = isResistance ? false : puiserRessources
|
||||||
@@ -255,7 +338,7 @@ export class CelestopolRoll extends Roll {
|
|||||||
|
|
||||||
// Fortune : 1d8 + 8 ; Destin : 3d8 ; sinon : 2d8
|
// Fortune : 1d8 + 8 ; Destin : 3d8 ; sinon : 2d8
|
||||||
const nbDice = (!isResistance && useDestin) ? 3 : 2
|
const nbDice = (!isResistance && useDestin) ? 3 : 2
|
||||||
const totalModifier = skillValue + effectiveWoundMalus + effectiveAspectMod + effectiveModifier + effectiveSituationMod + effectiveArmorMalus + effectiveRangedMod
|
const totalModifier = skillValue + effectiveWoundMalus + effectiveAspectMod + effectiveModifier + factionAspectBonus + effectiveSituationMod + effectiveArmorMalus + effectiveRangedMod
|
||||||
const formula = (!isResistance && useFortune)
|
const formula = (!isResistance && useFortune)
|
||||||
? buildFormula(1, totalModifier + 8)
|
? buildFormula(1, totalModifier + 8)
|
||||||
: buildFormula(nbDice, totalModifier)
|
: buildFormula(nbDice, totalModifier)
|
||||||
@@ -277,6 +360,9 @@ export class CelestopolRoll extends Roll {
|
|||||||
difficultyValue: diffConfig.value,
|
difficultyValue: diffConfig.value,
|
||||||
modifier: effectiveModifier,
|
modifier: effectiveModifier,
|
||||||
aspectMod: effectiveAspectMod,
|
aspectMod: effectiveAspectMod,
|
||||||
|
factionAspectId,
|
||||||
|
factionAspectLabel,
|
||||||
|
factionAspectBonus,
|
||||||
situationMod: effectiveSituationMod,
|
situationMod: effectiveSituationMod,
|
||||||
woundMalus: effectiveWoundMalus,
|
woundMalus: effectiveWoundMalus,
|
||||||
autoSuccess,
|
autoSuccess,
|
||||||
@@ -285,8 +371,12 @@ export class CelestopolRoll extends Roll {
|
|||||||
isCombat,
|
isCombat,
|
||||||
isRangedDefense,
|
isRangedDefense,
|
||||||
weaponType,
|
weaponType,
|
||||||
weaponName,
|
weaponName: resolvedWeaponName,
|
||||||
weaponDegats,
|
weaponDegats: resolvedWeaponDegats,
|
||||||
|
targetActorId,
|
||||||
|
targetActorUuid,
|
||||||
|
targetActorName,
|
||||||
|
availableTargets,
|
||||||
rangedMod: effectiveRangedMod,
|
rangedMod: effectiveRangedMod,
|
||||||
useDestin: !isResistance && useDestin,
|
useDestin: !isResistance && useDestin,
|
||||||
useFortune: !isResistance && useFortune,
|
useFortune: !isResistance && useFortune,
|
||||||
@@ -305,7 +395,10 @@ export class CelestopolRoll extends Roll {
|
|||||||
roll.computeResult()
|
roll.computeResult()
|
||||||
|
|
||||||
// Test de résistance échoué → cocher automatiquement la prochaine case de blessure
|
// Test de résistance échoué → cocher automatiquement la prochaine case de blessure
|
||||||
const actor = game.actors.get(options.actorId)
|
const actor = await this.resolveActor({
|
||||||
|
actorUuid: options.actorUuid ?? null,
|
||||||
|
actorId: options.actorId ?? null,
|
||||||
|
})
|
||||||
if (isResistance && actor && roll.options.resultType === "failure") {
|
if (isResistance && actor && roll.options.resultType === "failure") {
|
||||||
const nextLvl = (actor.system.blessures.lvl ?? 0) + 1
|
const nextLvl = (actor.system.blessures.lvl ?? 0) + 1
|
||||||
if (nextLvl <= 8) {
|
if (nextLvl <= 8) {
|
||||||
@@ -314,16 +407,25 @@ export class CelestopolRoll extends Roll {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mêlée échouée OU défense à distance échouée → joueur prend une blessure
|
// Mêlée échouée OU défense à distance échouée → le protagoniste subit les dégâts de l'arme PNJ
|
||||||
if (isCombat && (weaponType === "melee" || isRangedDefense) && actor && roll.options.resultType === "failure") {
|
if (isCombat && (weaponType === "melee" || isRangedDefense) && actor && roll.options.resultType === "failure") {
|
||||||
const nextLvl = (actor.system.blessures.lvl ?? 0) + 1
|
const incomingWounds = this.getIncomingWounds(resolvedWeaponDegats)
|
||||||
if (nextLvl <= 8) {
|
const protection = this.getActorArmorProtection(actor)
|
||||||
|
const appliedWounds = incomingWounds === null
|
||||||
|
? 1
|
||||||
|
: Math.max(0, incomingWounds - protection)
|
||||||
|
if (appliedWounds > 0) {
|
||||||
|
const nextLvl = Math.min(8, (actor.system.blessures.lvl ?? 0) + appliedWounds)
|
||||||
await actor.update({ "system.blessures.lvl": nextLvl })
|
await actor.update({ "system.blessures.lvl": nextLvl })
|
||||||
roll.options.woundTaken = nextLvl
|
roll.options.woundTaken = nextLvl
|
||||||
|
roll.options.woundTakenCount = appliedWounds
|
||||||
|
roll.options.incomingWounds = incomingWounds
|
||||||
|
roll.options.selectedTargetProtection = protection
|
||||||
|
roll.options.selectedTargetAppliedWounds = appliedWounds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await roll.toMessage({}, { rollMode: rollData.rollMode })
|
await roll.toMessage({}, { messageMode: rollData.rollMode })
|
||||||
|
|
||||||
// Batching de toutes les mises à jour de l'acteur en un seul appel réseau
|
// Batching de toutes les mises à jour de l'acteur en un seul appel réseau
|
||||||
if (actor) {
|
if (actor) {
|
||||||
@@ -410,8 +512,10 @@ export class CelestopolRoll extends Roll {
|
|||||||
: 11
|
: 11
|
||||||
const margin = this.options.margin
|
const margin = this.options.margin
|
||||||
const woundMalus = this.options.woundMalus ?? 0
|
const woundMalus = this.options.woundMalus ?? 0
|
||||||
|
const armorMalus = this.options.armorMalus ?? 0
|
||||||
const skillValue = this.options.skillValue ?? 0
|
const skillValue = this.options.skillValue ?? 0
|
||||||
const woundLevelId = this.options.woundLevel ?? 0
|
const woundLevelId = this.options.woundLevel ?? 0
|
||||||
|
const weaponDegats = `${this.options.weaponDegats ?? "0"}`
|
||||||
const woundLabel = woundLevelId > 0
|
const woundLabel = woundLevelId > 0
|
||||||
? game.i18n.localize(SYSTEM.WOUND_LEVELS[woundLevelId]?.label ?? "")
|
? game.i18n.localize(SYSTEM.WOUND_LEVELS[woundLevelId]?.label ?? "")
|
||||||
: null
|
: null
|
||||||
@@ -430,6 +534,26 @@ export class CelestopolRoll extends Roll {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isOpposition = this.options.isOpposition ?? false
|
const isOpposition = this.options.isOpposition ?? false
|
||||||
|
const isWeaponHit = (this.options.isCombat ?? false) && !(this.options.isRangedDefense ?? false) && this.isSuccess
|
||||||
|
const incomingWounds = isWeaponHit ? this.constructor.getIncomingWounds(weaponDegats) : null
|
||||||
|
const hasVariableDamage = isWeaponHit && incomingWounds === null
|
||||||
|
const targetActorId = this.options.targetActorId ?? ""
|
||||||
|
const targetActorUuid = this.options.targetActorUuid ?? ""
|
||||||
|
const targetActorName = this.options.targetActorName ?? ""
|
||||||
|
const availableTargets = (this.options.availableTargets ?? []).map(target => ({
|
||||||
|
...target,
|
||||||
|
selected: target.uuid === targetActorUuid || target.id === targetActorId,
|
||||||
|
}))
|
||||||
|
const selectedTargetActor = await this.constructor.resolveActor({
|
||||||
|
actorUuid: targetActorUuid,
|
||||||
|
actorId: targetActorId,
|
||||||
|
})
|
||||||
|
const selectedTargetProtection = selectedTargetActor
|
||||||
|
? this.constructor.getActorArmorProtection(selectedTargetActor)
|
||||||
|
: null
|
||||||
|
const selectedTargetAppliedWounds = (incomingWounds !== null && selectedTargetActor)
|
||||||
|
? Math.max(0, incomingWounds - selectedTargetProtection)
|
||||||
|
: null
|
||||||
|
|
||||||
// Libellé de difficulté : en combat "Corps PNJ : N", en opposition "vs ?", sinon "Seuil : 11"
|
// Libellé de difficulté : en combat "Corps PNJ : N", en opposition "vs ?", sinon "Seuil : 11"
|
||||||
const difficultyLabel = this.options.isCombat
|
const difficultyLabel = this.options.isCombat
|
||||||
@@ -464,22 +588,37 @@ export class CelestopolRoll extends Roll {
|
|||||||
modifier: this.options.modifier ?? 0,
|
modifier: this.options.modifier ?? 0,
|
||||||
autoSuccess: this.options.autoSuccess ?? false,
|
autoSuccess: this.options.autoSuccess ?? false,
|
||||||
aspectMod: this.options.aspectMod ?? 0,
|
aspectMod: this.options.aspectMod ?? 0,
|
||||||
|
factionAspectLabel: this.options.factionAspectLabel ?? "",
|
||||||
|
factionAspectBonus: this.options.factionAspectBonus ?? 0,
|
||||||
skillValue,
|
skillValue,
|
||||||
useDestin: this.options.useDestin ?? false,
|
useDestin: this.options.useDestin ?? false,
|
||||||
useFortune: this.options.useFortune ?? false,
|
useFortune: this.options.useFortune ?? false,
|
||||||
puiserRessources: this.options.puiserRessources ?? false,
|
puiserRessources: this.options.puiserRessources ?? false,
|
||||||
nbDice: this.options.nbDice ?? diceResults.length,
|
nbDice: this.options.nbDice ?? diceResults.length,
|
||||||
woundMalus,
|
woundMalus,
|
||||||
|
armorMalus,
|
||||||
woundLabel,
|
woundLabel,
|
||||||
isResistance: this.options.isResistance ?? false,
|
isResistance: this.options.isResistance ?? false,
|
||||||
isCombat: this.options.isCombat ?? false,
|
isCombat: this.options.isCombat ?? false,
|
||||||
weaponName: this.options.weaponName ?? null,
|
weaponName: this.options.weaponName ?? null,
|
||||||
weaponDegats: this.options.weaponDegats ?? null,
|
weaponDegats,
|
||||||
weaponType: this.options.weaponType ?? null,
|
weaponType: this.options.weaponType ?? null,
|
||||||
isRangedDefense: this.options.isRangedDefense ?? false,
|
isRangedDefense: this.options.isRangedDefense ?? false,
|
||||||
woundTaken: this.options.woundTaken ?? null,
|
woundTaken: this.options.woundTaken ?? null,
|
||||||
|
woundTakenCount: this.options.woundTakenCount ?? null,
|
||||||
situationMod: this.options.situationMod ?? 0,
|
situationMod: this.options.situationMod ?? 0,
|
||||||
rangedMod: this.options.rangedMod ?? 0,
|
rangedMod: this.options.rangedMod ?? 0,
|
||||||
|
hasDamageSummary: isWeaponHit,
|
||||||
|
incomingWounds,
|
||||||
|
incomingWoundsDisplay: incomingWounds ?? "1 + X",
|
||||||
|
hasVariableDamage,
|
||||||
|
canApplyWeaponDamage: incomingWounds !== null,
|
||||||
|
targetActorId,
|
||||||
|
targetActorUuid,
|
||||||
|
targetActorName,
|
||||||
|
selectedTargetProtection,
|
||||||
|
selectedTargetAppliedWounds,
|
||||||
|
availableTargets,
|
||||||
// Dé de lune
|
// Dé de lune
|
||||||
hasMoonDie: moonDieResult !== null,
|
hasMoonDie: moonDieResult !== null,
|
||||||
moonDieResult,
|
moonDieResult,
|
||||||
@@ -494,14 +633,40 @@ export class CelestopolRoll extends Roll {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async toMessage(messageData = {}, { rollMode, create = true } = {}) {
|
async toMessage(messageData = {}, { messageMode, rollMode, create = true } = {}) {
|
||||||
|
if (rollMode) {
|
||||||
|
messageMode = Roll._mapLegacyRollMode(rollMode)
|
||||||
|
}
|
||||||
|
messageMode ||= game.settings.get("core", "messageMode")
|
||||||
|
if (!this._evaluated) await this.evaluate({ allowInteractive: messageMode !== "blind" })
|
||||||
|
|
||||||
const skillLocalized = this.skillLabel ? game.i18n.localize(this.skillLabel) : ""
|
const skillLocalized = this.skillLabel ? game.i18n.localize(this.skillLabel) : ""
|
||||||
const statLocalized = this.options.statLabel
|
const statLocalized = this.options.statLabel
|
||||||
? game.i18n.localize(this.options.statLabel) : ""
|
? game.i18n.localize(this.options.statLabel) : ""
|
||||||
const flavor = statLocalized
|
const flavor = statLocalized
|
||||||
? `<strong>${statLocalized} › ${skillLocalized}</strong>`
|
? `<strong>${statLocalized} › ${skillLocalized}</strong>`
|
||||||
: `<strong>${skillLocalized}</strong>`
|
: `<strong>${skillLocalized}</strong>`
|
||||||
return super.toMessage({ flavor, ...messageData }, { rollMode })
|
const speakerActor = await this.constructor.resolveActor({
|
||||||
|
actorUuid: this.options.actorUuid ?? null,
|
||||||
|
actorId: this.options.actorId ?? null,
|
||||||
|
})
|
||||||
|
const content = await this.render({ isPrivate: messageMode !== "public" })
|
||||||
|
const chatData = foundry.utils.mergeObject({
|
||||||
|
author: game.user.id,
|
||||||
|
content,
|
||||||
|
flavor,
|
||||||
|
sound: CONFIG.sounds.dice,
|
||||||
|
rolls: [this],
|
||||||
|
speaker: speakerActor ? ChatMessage.getSpeaker({ actor: speakerActor }) : undefined,
|
||||||
|
style: CONST.CHAT_MESSAGE_STYLES.OTHER,
|
||||||
|
}, messageData)
|
||||||
|
|
||||||
|
const cls = foundry.utils.getDocumentClass("ChatMessage")
|
||||||
|
const msg = new cls(chatData)
|
||||||
|
msg.applyMode(messageMode)
|
||||||
|
|
||||||
|
if (create) return cls.create(msg)
|
||||||
|
return msg.toObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -540,7 +705,7 @@ export class CelestopolRoll extends Roll {
|
|||||||
content,
|
content,
|
||||||
speaker,
|
speaker,
|
||||||
rolls: [roll],
|
rolls: [roll],
|
||||||
style: CONST.CHAT_MESSAGE_STYLES?.ROLL ?? 5,
|
style: CONST.CHAT_MESSAGE_STYLES.OTHER,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
export { default as CelestopolCharacter } from "./character.mjs"
|
export { default as CelestopolCharacter } from "./character.mjs"
|
||||||
export { default as CelestopolNPC } from "./npc.mjs"
|
export { default as CelestopolNPC } from "./npc.mjs"
|
||||||
export { CelestopolAnomaly, CelestopolAspect, CelestopolEquipment, CelestopolWeapon, CelestopolArmure } from "./items.mjs"
|
export { CelestopolAnomaly, CelestopolAspect, CelestopolEquipment, CelestopolWeapon, CelestopolArmure } from "./items.mjs"
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
import { SYSTEM } from "../config/system.mjs"
|
import { SYSTEM } from "../config/system.mjs"
|
||||||
|
|
||||||
|
const WEAPON_DAMAGE_PRIORITY = { "0": 0, "1": 1, "2": 2, X: 3 }
|
||||||
|
|
||||||
export default class CelestopolCharacter extends foundry.abstract.TypeDataModel {
|
export default class CelestopolCharacter extends foundry.abstract.TypeDataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields
|
const fields = foundry.data.fields
|
||||||
@@ -116,6 +131,8 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
// Description & notes
|
// Description & notes
|
||||||
schema.descriptionPhysique = new fields.HTMLField({ required: true, textSearch: true })
|
schema.descriptionPhysique = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
schema.descriptionPsychologique = new fields.HTMLField({ required: true, textSearch: true })
|
schema.descriptionPsychologique = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.historique = new fields.HTMLField({ required: true, initial: "", textSearch: true })
|
||||||
|
schema.portraitImage = new fields.StringField({ required: true, nullable: false, initial: "" })
|
||||||
schema.notes = new fields.HTMLField({ required: true, textSearch: true })
|
schema.notes = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
|
||||||
// Données biographiques
|
// Données biographiques
|
||||||
@@ -165,8 +182,21 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
getArmorMalus() {
|
getArmorMalus() {
|
||||||
if (!this.parent) return 0
|
if (!this.parent) return 0
|
||||||
return -(this.parent.itemTypes.armure
|
return -(this.parent.itemTypes.armure
|
||||||
.filter(a => a.system.equipped && a.system.malus > 0)
|
.filter(a => a.system.equipped && (a.system.protection ?? a.system.malus) > 0)
|
||||||
.reduce((sum, a) => sum + a.system.malus, 0))
|
.reduce((sum, a) => sum + (a.system.protection ?? a.system.malus), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retourne le malus d'armure applicable pour un jet PJ.
|
||||||
|
* Règle : uniquement sur Mobilité et Effacement si l'armure est équipée.
|
||||||
|
* @param {string} statId
|
||||||
|
* @param {string|null} skillId
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
getArmorMalusForRoll(statId, skillId = null) {
|
||||||
|
if (statId !== "corps") return 0
|
||||||
|
if (!["mobilite", "effacement"].includes(skillId)) return 0
|
||||||
|
return this.getArmorMalus()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -190,6 +220,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
|
|
||||||
return CelestopolRoll.prompt({
|
return CelestopolRoll.prompt({
|
||||||
actorId: this.parent.id,
|
actorId: this.parent.id,
|
||||||
|
actorUuid: this.parent.uuid,
|
||||||
actorName: this.parent.name,
|
actorName: this.parent.name,
|
||||||
actorImage: this.parent.img,
|
actorImage: this.parent.img,
|
||||||
statId,
|
statId,
|
||||||
@@ -198,7 +229,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
skillLabel: skill.label,
|
skillLabel: skill.label,
|
||||||
skillValue: skill.value,
|
skillValue: skill.value,
|
||||||
woundMalus: this.getWoundMalus(),
|
woundMalus: this.getWoundMalus(),
|
||||||
armorMalus: this.getArmorMalus(),
|
armorMalus: this.getArmorMalusForRoll(statId, skillId),
|
||||||
woundLevel: this.blessures.lvl,
|
woundLevel: this.blessures.lvl,
|
||||||
difficulty: this.prefs.difficulty,
|
difficulty: this.prefs.difficulty,
|
||||||
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
||||||
@@ -221,6 +252,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
|
|
||||||
return CelestopolRoll.prompt({
|
return CelestopolRoll.prompt({
|
||||||
actorId: this.parent.id,
|
actorId: this.parent.id,
|
||||||
|
actorUuid: this.parent.uuid,
|
||||||
actorName: this.parent.name,
|
actorName: this.parent.name,
|
||||||
actorImage: this.parent.img,
|
actorImage: this.parent.img,
|
||||||
statId,
|
statId,
|
||||||
@@ -229,7 +261,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
skillLabel: "CELESTOPOL.Roll.resistanceTest",
|
skillLabel: "CELESTOPOL.Roll.resistanceTest",
|
||||||
skillValue: statData.res,
|
skillValue: statData.res,
|
||||||
woundMalus: this.getWoundMalus(),
|
woundMalus: this.getWoundMalus(),
|
||||||
armorMalus: this.getArmorMalus(),
|
armorMalus: 0,
|
||||||
woundLevel: this.blessures.lvl,
|
woundLevel: this.blessures.lvl,
|
||||||
isResistance: true,
|
isResistance: true,
|
||||||
rollMoonDie: false,
|
rollMoonDie: false,
|
||||||
@@ -240,35 +272,49 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collecte les tokens PNJs disponibles comme cibles de combat.
|
* Collecte les cibles de combat sur la scène active.
|
||||||
* Priorise le combat tracker, sinon les tokens ciblés par l'utilisateur.
|
* Pour un PJ attaquant, seules les cibles PNJ présentes sur la scène sont proposées.
|
||||||
* @returns {Array<{id:string, name:string, corps:number}>}
|
* @param {object} options
|
||||||
|
* @param {boolean} [options.onlyRanged=false]
|
||||||
|
* @param {boolean} [options.fallbackToAll=false]
|
||||||
|
* @returns {Array<{id:string, uuid:string, name:string, corps:number, weaponName?:string, weaponDegats?:string}>}
|
||||||
*/
|
*/
|
||||||
_getCombatTargets() {
|
_getCombatTargets({ onlyRanged = false, fallbackToAll = false } = {}) {
|
||||||
|
const getBestRangedWeapon = actor => {
|
||||||
|
const rangedWeapons = actor.itemTypes?.weapon?.filter(item => item.system.type === "distance") ?? []
|
||||||
|
if (!rangedWeapons.length) return null
|
||||||
|
return rangedWeapons.reduce((best, item) => {
|
||||||
|
if (!best) return item
|
||||||
|
const bestPriority = WEAPON_DAMAGE_PRIORITY[best.system.degats] ?? -1
|
||||||
|
const itemPriority = WEAPON_DAMAGE_PRIORITY[item.system.degats] ?? -1
|
||||||
|
if (itemPriority !== bestPriority) return itemPriority > bestPriority ? item : best
|
||||||
|
return item.name.localeCompare(best.name) < 0 ? item : best
|
||||||
|
}, null)
|
||||||
|
}
|
||||||
|
|
||||||
const toEntry = actor => ({
|
const toEntry = actor => ({
|
||||||
id: actor.id,
|
id: actor.id,
|
||||||
|
uuid: actor.uuid,
|
||||||
name: actor.name,
|
name: actor.name,
|
||||||
corps: actor.system.stats?.corps?.res ?? 0,
|
corps: actor.system.stats?.corps?.res ?? 0,
|
||||||
|
...(onlyRanged ? (() => {
|
||||||
|
const weapon = getBestRangedWeapon(actor)
|
||||||
|
return weapon ? {
|
||||||
|
weaponName: weapon.name,
|
||||||
|
weaponDegats: weapon.system.degats,
|
||||||
|
} : {}
|
||||||
|
})() : {}),
|
||||||
})
|
})
|
||||||
// Priorité 1 : PNJs dans le combat actif
|
const sceneTokens = canvas?.scene?.isView ? (canvas.tokens?.placeables ?? []) : []
|
||||||
if (game.combat?.active) {
|
const targets = [...new Map(sceneTokens
|
||||||
const list = game.combat.combatants
|
|
||||||
.filter(c => c.actor?.type === "npc" && c.actorId !== this.parent.id)
|
|
||||||
.map(c => toEntry(c.actor))
|
|
||||||
if (list.length) return list
|
|
||||||
}
|
|
||||||
// Priorité 2 : Tokens ciblés par le joueur
|
|
||||||
const targeted = [...(game.user?.targets ?? [])]
|
|
||||||
.filter(t => t.actor?.type === "npc")
|
|
||||||
.map(t => toEntry(t.actor))
|
|
||||||
if (targeted.length) return targeted
|
|
||||||
// Priorité 3 : Tous les tokens NPC de la scène active
|
|
||||||
if (canvas?.tokens?.placeables) {
|
|
||||||
return canvas.tokens.placeables
|
|
||||||
.filter(t => t.actor?.type === "npc" && t.actor.id !== this.parent.id)
|
.filter(t => t.actor?.type === "npc" && t.actor.id !== this.parent.id)
|
||||||
.map(t => toEntry(t.actor))
|
.filter(t => !onlyRanged || getBestRangedWeapon(t.actor))
|
||||||
}
|
.map(t => {
|
||||||
return []
|
const actor = t.actor
|
||||||
|
return [actor.uuid, toEntry(actor)]
|
||||||
|
})).values()]
|
||||||
|
if (!targets.length && onlyRanged && fallbackToAll) return this._getCombatTargets()
|
||||||
|
return targets
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -288,6 +334,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
|
|
||||||
return CelestopolRoll.prompt({
|
return CelestopolRoll.prompt({
|
||||||
actorId: this.parent.id,
|
actorId: this.parent.id,
|
||||||
|
actorUuid: this.parent.uuid,
|
||||||
actorName: this.parent.name,
|
actorName: this.parent.name,
|
||||||
actorImage: this.parent.img,
|
actorImage: this.parent.img,
|
||||||
statId: "corps",
|
statId: "corps",
|
||||||
@@ -296,7 +343,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
skillLabel: SYSTEM.SKILLS.corps.echauffouree.label,
|
skillLabel: SYSTEM.SKILLS.corps.echauffouree.label,
|
||||||
skillValue: echauffouree.value,
|
skillValue: echauffouree.value,
|
||||||
woundMalus: this.getWoundMalus(),
|
woundMalus: this.getWoundMalus(),
|
||||||
armorMalus: this.getArmorMalus(),
|
armorMalus: this.getArmorMalusForRoll("corps", "echauffouree"),
|
||||||
woundLevel: this.blessures.lvl,
|
woundLevel: this.blessures.lvl,
|
||||||
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
||||||
destGaugeFull: this.destin.lvl > 0,
|
destGaugeFull: this.destin.lvl > 0,
|
||||||
@@ -310,6 +357,40 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lance une attaque de mêlée à mains nues.
|
||||||
|
* @returns {Promise<import("../documents/roll.mjs").CelestopolRoll|null>}
|
||||||
|
*/
|
||||||
|
async rollUnarmedAttack() {
|
||||||
|
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||||
|
const echauffouree = this.stats.corps.echauffouree
|
||||||
|
if (!echauffouree) return null
|
||||||
|
|
||||||
|
return CelestopolRoll.prompt({
|
||||||
|
actorId: this.parent.id,
|
||||||
|
actorUuid: this.parent.uuid,
|
||||||
|
actorName: this.parent.name,
|
||||||
|
actorImage: this.parent.img,
|
||||||
|
statId: "corps",
|
||||||
|
skillId: "echauffouree",
|
||||||
|
statLabel: SYSTEM.STATS.corps.label,
|
||||||
|
skillLabel: SYSTEM.SKILLS.corps.echauffouree.label,
|
||||||
|
skillValue: echauffouree.value,
|
||||||
|
woundMalus: this.getWoundMalus(),
|
||||||
|
armorMalus: this.getArmorMalusForRoll("corps", "echauffouree"),
|
||||||
|
woundLevel: this.blessures.lvl,
|
||||||
|
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
||||||
|
destGaugeFull: this.destin.lvl > 0,
|
||||||
|
fortuneValue: this.attributs.fortune.value,
|
||||||
|
isCombat: true,
|
||||||
|
isRangedDefense: false,
|
||||||
|
weaponType: "melee",
|
||||||
|
weaponName: game.i18n.localize("CELESTOPOL.Combat.unarmedAttack"),
|
||||||
|
weaponDegats: "0",
|
||||||
|
availableTargets: this._getCombatTargets(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lance un jet de défense contre une attaque à distance (test Mobilité vs Corps PNJ).
|
* Lance un jet de défense contre une attaque à distance (test Mobilité vs Corps PNJ).
|
||||||
* Succès → esquive réussie.
|
* Succès → esquive réussie.
|
||||||
@@ -326,6 +407,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
|
|
||||||
return CelestopolRoll.prompt({
|
return CelestopolRoll.prompt({
|
||||||
actorId: this.parent.id,
|
actorId: this.parent.id,
|
||||||
|
actorUuid: this.parent.uuid,
|
||||||
actorName: this.parent.name,
|
actorName: this.parent.name,
|
||||||
actorImage: this.parent.img,
|
actorImage: this.parent.img,
|
||||||
statId: "corps",
|
statId: "corps",
|
||||||
@@ -334,7 +416,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
skillLabel: SYSTEM.SKILLS.corps.mobilite.label,
|
skillLabel: SYSTEM.SKILLS.corps.mobilite.label,
|
||||||
skillValue: mobilite.value,
|
skillValue: mobilite.value,
|
||||||
woundMalus: this.getWoundMalus(),
|
woundMalus: this.getWoundMalus(),
|
||||||
armorMalus: this.getArmorMalus(),
|
armorMalus: this.getArmorMalusForRoll("corps", "mobilite"),
|
||||||
woundLevel: this.blessures.lvl,
|
woundLevel: this.blessures.lvl,
|
||||||
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
||||||
destGaugeFull: this.destin.lvl > 0,
|
destGaugeFull: this.destin.lvl > 0,
|
||||||
@@ -347,4 +429,38 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
availableTargets: this._getCombatTargets(),
|
availableTargets: this._getCombatTargets(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lance une esquive d'attaque à distance sans dépendre d'une arme possédée par le PJ.
|
||||||
|
* @returns {Promise<import("../documents/roll.mjs").CelestopolRoll|null>}
|
||||||
|
*/
|
||||||
|
async rollRangedDefenseBase() {
|
||||||
|
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||||
|
const mobilite = this.stats.corps.mobilite
|
||||||
|
if (!mobilite) return null
|
||||||
|
|
||||||
|
return CelestopolRoll.prompt({
|
||||||
|
actorId: this.parent.id,
|
||||||
|
actorUuid: this.parent.uuid,
|
||||||
|
actorName: this.parent.name,
|
||||||
|
actorImage: this.parent.img,
|
||||||
|
statId: "corps",
|
||||||
|
skillId: "mobilite",
|
||||||
|
statLabel: SYSTEM.STATS.corps.label,
|
||||||
|
skillLabel: SYSTEM.SKILLS.corps.mobilite.label,
|
||||||
|
skillValue: mobilite.value,
|
||||||
|
woundMalus: this.getWoundMalus(),
|
||||||
|
armorMalus: this.getArmorMalusForRoll("corps", "mobilite"),
|
||||||
|
woundLevel: this.blessures.lvl,
|
||||||
|
rollMoonDie: this.prefs.rollMoonDie ?? false,
|
||||||
|
destGaugeFull: this.destin.lvl > 0,
|
||||||
|
fortuneValue: this.attributs.fortune.value,
|
||||||
|
isCombat: true,
|
||||||
|
isRangedDefense: true,
|
||||||
|
weaponType: "distance",
|
||||||
|
weaponName: game.i18n.localize("CELESTOPOL.Combat.rangedAttack"),
|
||||||
|
weaponDegats: "0",
|
||||||
|
availableTargets: this._getCombatTargets({ onlyRanged: true, fallbackToAll: true }),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
import { SYSTEM } from "../config/system.mjs"
|
import { SYSTEM } from "../config/system.mjs"
|
||||||
|
|
||||||
/** Schéma partagé pour les bonus/malus par domaine (utilisé dans anomaly/aspect). */
|
/** Schéma partagé pour les bonus/malus par domaine (utilisé dans anomaly/aspect). */
|
||||||
@@ -83,9 +96,14 @@ export class CelestopolArmure extends foundry.abstract.TypeDataModel {
|
|||||||
const reqInt = { required: true, nullable: false, integer: true }
|
const reqInt = { required: true, nullable: false, integer: true }
|
||||||
return {
|
return {
|
||||||
protection: new fields.NumberField({ ...reqInt, initial: 1, min: 1, max: 2 }),
|
protection: new fields.NumberField({ ...reqInt, initial: 1, min: 1, max: 2 }),
|
||||||
malus: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 2 }),
|
malus: new fields.NumberField({ ...reqInt, initial: 1, min: 0, max: 2 }),
|
||||||
equipped: new fields.BooleanField({ initial: false }),
|
equipped: new fields.BooleanField({ initial: false }),
|
||||||
description: new fields.HTMLField({ required: true, textSearch: true }),
|
description: new fields.HTMLField({ required: true, textSearch: true }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareDerivedData() {
|
||||||
|
super.prepareDerivedData()
|
||||||
|
this.malus = this.protection
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Célestopol 1922 — Système FoundryVTT
|
||||||
|
*
|
||||||
|
* Célestopol 1922 est un jeu de rôle édité par Antre-Monde Éditions.
|
||||||
|
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||||
|
* affilié à Antre-Monde Éditions,
|
||||||
|
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||||
|
*
|
||||||
|
* @author LeRatierBretonnien
|
||||||
|
* @copyright 2025–2026 LeRatierBretonnien
|
||||||
|
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
import { SYSTEM } from "../config/system.mjs"
|
import { SYSTEM } from "../config/system.mjs"
|
||||||
|
|
||||||
export default class CelestopolNPC extends foundry.abstract.TypeDataModel {
|
export default class CelestopolNPC extends foundry.abstract.TypeDataModel {
|
||||||
@@ -21,7 +34,7 @@ export default class CelestopolNPC extends foundry.abstract.TypeDataModel {
|
|||||||
// PNJs : 4 domaines uniquement (pas de sous-compétences)
|
// PNJs : 4 domaines uniquement (pas de sous-compétences)
|
||||||
const domainField = (statId) => new fields.SchemaField({
|
const domainField = (statId) => new fields.SchemaField({
|
||||||
label: new fields.StringField({ required: true, initial: SYSTEM.STATS[statId].label }),
|
label: new fields.StringField({ required: true, initial: SYSTEM.STATS[statId].label }),
|
||||||
res: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 8 }),
|
res: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||||
actuel: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
actuel: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -38,6 +51,7 @@ export default class CelestopolNPC extends foundry.abstract.TypeDataModel {
|
|||||||
|
|
||||||
schema.histoire = new fields.HTMLField({ required: true, textSearch: true })
|
schema.histoire = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
schema.descriptionPhysique = new fields.HTMLField({ required: true, textSearch: true })
|
schema.descriptionPhysique = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.portraitImage = new fields.StringField({ required: true, nullable: false, initial: "" })
|
||||||
schema.notes = new fields.HTMLField({ required: true, textSearch: true })
|
schema.notes = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
|
||||||
return schema
|
return schema
|
||||||
@@ -66,7 +80,20 @@ export default class CelestopolNPC extends foundry.abstract.TypeDataModel {
|
|||||||
const armures = this.parent?.itemTypes?.armure ?? []
|
const armures = this.parent?.itemTypes?.armure ?? []
|
||||||
return armures
|
return armures
|
||||||
.filter(a => a.system.equipped)
|
.filter(a => a.system.equipped)
|
||||||
.reduce((sum, a) => sum + (a.system.malus ? -Math.abs(a.system.malus) : 0), 0)
|
.reduce((sum, a) => {
|
||||||
|
const value = a.system.protection ?? a.system.malus
|
||||||
|
return sum + (value ? -Math.abs(value) : 0)
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retourne le malus d'armure applicable pour un jet PNJ.
|
||||||
|
* Règle : sur tous les jets de Corps uniquement.
|
||||||
|
* @param {string} statId
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
getArmorMalusForRoll(statId) {
|
||||||
|
return statId === "corps" ? this.getArmorMalus() : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,13 +112,14 @@ export default class CelestopolNPC extends foundry.abstract.TypeDataModel {
|
|||||||
|
|
||||||
return CelestopolRoll.prompt({
|
return CelestopolRoll.prompt({
|
||||||
actorId: this.parent.id,
|
actorId: this.parent.id,
|
||||||
|
actorUuid: this.parent.uuid,
|
||||||
actorName: this.parent.name,
|
actorName: this.parent.name,
|
||||||
actorImage: this.parent.img,
|
actorImage: this.parent.img,
|
||||||
statId,
|
statId,
|
||||||
skillLabel,
|
skillLabel,
|
||||||
skillValue: statData.res,
|
skillValue: statData.res,
|
||||||
woundMalus: this.getWoundMalus(),
|
woundMalus: this.getWoundMalus(),
|
||||||
armorMalus: this.getArmorMalus(),
|
armorMalus: this.getArmorMalusForRoll(statId),
|
||||||
woundLevel: this.blessures.lvl,
|
woundLevel: this.blessures.lvl,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -100,4 +128,95 @@ export default class CelestopolNPC extends foundry.abstract.TypeDataModel {
|
|||||||
async rollResistance(statId) {
|
async rollResistance(statId) {
|
||||||
return this.roll(statId)
|
return this.roll(statId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collecte les cibles protagonistes de la scène active pour les jets de combat PNJ.
|
||||||
|
* @returns {Array<{id:string, name:string, corps:number}>}
|
||||||
|
*/
|
||||||
|
_getCombatTargets() {
|
||||||
|
const toEntry = actor => ({
|
||||||
|
id: actor.id,
|
||||||
|
uuid: actor.uuid,
|
||||||
|
name: actor.name,
|
||||||
|
corps: actor.system.stats?.corps?.res ?? 0,
|
||||||
|
})
|
||||||
|
const sceneTokens = canvas?.scene?.isView ? (canvas.tokens?.placeables ?? []) : []
|
||||||
|
return [...new Map(sceneTokens
|
||||||
|
.filter(t => t.actor?.type === "character" && t.actor.id !== this.parent.id)
|
||||||
|
.map(t => {
|
||||||
|
const actor = t.actor
|
||||||
|
return [actor.id, toEntry(actor)]
|
||||||
|
})).values()]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lance une attaque PNJ avec une arme.
|
||||||
|
* Le test utilise le domaine Corps et transmet explicitement les dégâts de l'arme.
|
||||||
|
* @param {string} itemId
|
||||||
|
* @returns {Promise<import("../documents/roll.mjs").CelestopolRoll|null>}
|
||||||
|
*/
|
||||||
|
async rollAttack(itemId) {
|
||||||
|
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||||
|
const item = this.parent.items.get(itemId)
|
||||||
|
if (!item || item.type !== "weapon") return null
|
||||||
|
|
||||||
|
return CelestopolRoll.prompt({
|
||||||
|
actorId: this.parent.id,
|
||||||
|
actorUuid: this.parent.uuid,
|
||||||
|
actorName: this.parent.name,
|
||||||
|
actorImage: this.parent.img,
|
||||||
|
statId: "corps",
|
||||||
|
skillId: null,
|
||||||
|
statLabel: SYSTEM.STATS.corps.label,
|
||||||
|
skillLabel: "CELESTOPOL.Combat.attack",
|
||||||
|
skillValue: this.stats.corps.res,
|
||||||
|
woundMalus: this.getWoundMalus(),
|
||||||
|
armorMalus: this.getArmorMalusForRoll("corps"),
|
||||||
|
woundLevel: this.blessures.lvl,
|
||||||
|
rollMoonDie: false,
|
||||||
|
destGaugeFull: false,
|
||||||
|
fortuneValue: 0,
|
||||||
|
isCombat: true,
|
||||||
|
isRangedDefense: false,
|
||||||
|
weaponType: item.system.type,
|
||||||
|
weaponName: item.name,
|
||||||
|
weaponDegats: item.system.degats,
|
||||||
|
availableTargets: this._getCombatTargets(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lance un jet de tir/esquive PNJ avec une arme à distance.
|
||||||
|
* @param {string} itemId
|
||||||
|
* @returns {Promise<import("../documents/roll.mjs").CelestopolRoll|null>}
|
||||||
|
*/
|
||||||
|
async rollRangedDefense(itemId) {
|
||||||
|
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||||
|
const item = this.parent.items.get(itemId)
|
||||||
|
if (!item || item.type !== "weapon" || item.system.type !== "distance") return null
|
||||||
|
|
||||||
|
return CelestopolRoll.prompt({
|
||||||
|
actorId: this.parent.id,
|
||||||
|
actorUuid: this.parent.uuid,
|
||||||
|
actorName: this.parent.name,
|
||||||
|
actorImage: this.parent.img,
|
||||||
|
statId: "corps",
|
||||||
|
skillId: null,
|
||||||
|
statLabel: SYSTEM.STATS.corps.label,
|
||||||
|
skillLabel: "CELESTOPOL.Combat.rangedDefenseTitle",
|
||||||
|
skillValue: this.stats.corps.res,
|
||||||
|
woundMalus: this.getWoundMalus(),
|
||||||
|
armorMalus: this.getArmorMalusForRoll("corps"),
|
||||||
|
woundLevel: this.blessures.lvl,
|
||||||
|
rollMoonDie: false,
|
||||||
|
destGaugeFull: false,
|
||||||
|
fortuneValue: 0,
|
||||||
|
isCombat: true,
|
||||||
|
isRangedDefense: true,
|
||||||
|
weaponType: "distance",
|
||||||
|
weaponName: item.name,
|
||||||
|
weaponDegats: item.system.degats,
|
||||||
|
availableTargets: this._getCombatTargets(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
packs-system/aides-de-jeu/000005.ldb
Normal file
0
packs-system/aides-de-jeu/000064.log
Normal file
1
packs-system/aides-de-jeu/CURRENT
Normal file
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000062
|
||||||
0
packs-system/aides-de-jeu/LOCK
Normal file
8
packs-system/aides-de-jeu/LOG
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/04/13-13:11:31.249954 7f2a6b7fe6c0 Recovering log #60
|
||||||
|
2026/04/13-13:11:31.302525 7f2a6b7fe6c0 Delete type=3 #58
|
||||||
|
2026/04/13-13:11:31.302579 7f2a6b7fe6c0 Delete type=0 #60
|
||||||
|
2026/04/13-14:20:41.118813 7f2a69ffb6c0 Level-0 table #65: started
|
||||||
|
2026/04/13-14:20:41.118847 7f2a69ffb6c0 Level-0 table #65: 0 bytes OK
|
||||||
|
2026/04/13-14:20:41.156390 7f2a69ffb6c0 Delete type=0 #63
|
||||||
|
2026/04/13-14:20:41.210923 7f2a69ffb6c0 Manual compaction at level-0 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end)
|
||||||
|
2026/04/13-14:20:41.272745 7f2a69ffb6c0 Manual compaction at level-1 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end)
|
||||||
8
packs-system/aides-de-jeu/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/04/13-13:10:44.862648 7ff582bff6c0 Recovering log #56
|
||||||
|
2026/04/13-13:10:44.908015 7ff582bff6c0 Delete type=3 #54
|
||||||
|
2026/04/13-13:10:44.908061 7ff582bff6c0 Delete type=0 #56
|
||||||
|
2026/04/13-13:10:47.939764 7ff580bfb6c0 Level-0 table #61: started
|
||||||
|
2026/04/13-13:10:47.939788 7ff580bfb6c0 Level-0 table #61: 0 bytes OK
|
||||||
|
2026/04/13-13:10:47.972855 7ff580bfb6c0 Delete type=0 #59
|
||||||
|
2026/04/13-13:10:47.973045 7ff580bfb6c0 Manual compaction at level-0 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end)
|
||||||
|
2026/04/13-13:10:48.030502 7ff580bfb6c0 Manual compaction at level-1 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end)
|
||||||
BIN
packs-system/aides-de-jeu/MANIFEST-000062
Normal file
0
packs-system/anomalies/000114.log
Normal file
@@ -1 +1 @@
|
|||||||
MANIFEST-000018
|
MANIFEST-000112
|
||||||
|
|||||||
@@ -1,3 +1,15 @@
|
|||||||
2026/04/06-17:46:52.532955 7f67ebfff6c0 Recovering log #15
|
2026/04/13-13:11:31.130509 7f2a6bfff6c0 Recovering log #109
|
||||||
2026/04/06-17:46:52.543005 7f67ebfff6c0 Delete type=3 #13
|
2026/04/13-13:11:31.189864 7f2a6bfff6c0 Delete type=3 #107
|
||||||
2026/04/06-17:46:52.543081 7f67ebfff6c0 Delete type=0 #15
|
2026/04/13-13:11:31.189928 7f2a6bfff6c0 Delete type=0 #109
|
||||||
|
2026/04/13-14:20:41.156517 7f2a69ffb6c0 Level-0 table #115: started
|
||||||
|
2026/04/13-14:20:41.173655 7f2a69ffb6c0 Level-0 table #115: 3524 bytes OK
|
||||||
|
2026/04/13-14:20:41.210761 7f2a69ffb6c0 Delete type=0 #113
|
||||||
|
2026/04/13-14:20:41.210932 7f2a69ffb6c0 Manual compaction at level-0 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
|
||||||
|
2026/04/13-14:20:41.210963 7f2a69ffb6c0 Manual compaction at level-1 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 97 : 1
|
||||||
|
2026/04/13-14:20:41.210970 7f2a69ffb6c0 Compacting 1@1 + 1@2 files
|
||||||
|
2026/04/13-14:20:41.229806 7f2a69ffb6c0 Generated table #116@1: 9 keys, 6617 bytes
|
||||||
|
2026/04/13-14:20:41.229844 7f2a69ffb6c0 Compacted 1@1 + 1@2 files => 6617 bytes
|
||||||
|
2026/04/13-14:20:41.272334 7f2a69ffb6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
|
||||||
|
2026/04/13-14:20:41.272481 7f2a69ffb6c0 Delete type=2 #111
|
||||||
|
2026/04/13-14:20:41.272658 7f2a69ffb6c0 Delete type=2 #115
|
||||||
|
2026/04/13-14:20:41.365704 7f2a69ffb6c0 Manual compaction at level-1 from '!items!null' @ 97 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
2026/04/05-21:02:44.634018 7f8249dff6c0 Recovering log #10
|
2026/04/13-13:10:44.750651 7ff581bfd6c0 Recovering log #104
|
||||||
2026/04/05-21:02:44.729398 7f8249dff6c0 Delete type=3 #8
|
2026/04/13-13:10:44.797855 7ff581bfd6c0 Delete type=3 #102
|
||||||
2026/04/05-21:02:44.729470 7f8249dff6c0 Delete type=0 #10
|
2026/04/13-13:10:44.797919 7ff581bfd6c0 Delete type=0 #104
|
||||||
2026/04/06-00:09:38.933436 7f82177fe6c0 Level-0 table #16: started
|
2026/04/13-13:10:47.796281 7ff580bfb6c0 Level-0 table #110: started
|
||||||
2026/04/06-00:09:38.937122 7f82177fe6c0 Level-0 table #16: 3525 bytes OK
|
2026/04/13-13:10:47.824966 7ff580bfb6c0 Level-0 table #110: 3524 bytes OK
|
||||||
2026/04/06-00:09:38.943462 7f82177fe6c0 Delete type=0 #14
|
2026/04/13-13:10:47.857042 7ff580bfb6c0 Delete type=0 #108
|
||||||
2026/04/06-00:09:38.943723 7f82177fe6c0 Manual compaction at level-0 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
|
2026/04/13-13:10:47.973011 7ff580bfb6c0 Manual compaction at level-0 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
|
||||||
2026/04/06-00:09:38.966124 7f82177fe6c0 Manual compaction at level-1 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 17 : 1
|
2026/04/13-13:10:47.973057 7ff580bfb6c0 Manual compaction at level-1 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 93 : 1
|
||||||
2026/04/06-00:09:38.966141 7f82177fe6c0 Compacting 1@1 + 1@2 files
|
2026/04/13-13:10:47.973063 7ff580bfb6c0 Compacting 1@1 + 1@2 files
|
||||||
2026/04/06-00:09:38.969869 7f82177fe6c0 Generated table #17@1: 9 keys, 6617 bytes
|
2026/04/13-13:10:47.995133 7ff580bfb6c0 Generated table #111@1: 9 keys, 6617 bytes
|
||||||
2026/04/06-00:09:38.969906 7f82177fe6c0 Compacted 1@1 + 1@2 files => 6617 bytes
|
2026/04/13-13:10:47.995159 7ff580bfb6c0 Compacted 1@1 + 1@2 files => 6617 bytes
|
||||||
2026/04/06-00:09:38.976148 7f82177fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
|
2026/04/13-13:10:48.030149 7ff580bfb6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
|
||||||
2026/04/06-00:09:38.976266 7f82177fe6c0 Delete type=2 #12
|
2026/04/13-13:10:48.030263 7ff580bfb6c0 Delete type=2 #106
|
||||||
2026/04/06-00:09:38.976457 7f82177fe6c0 Delete type=2 #16
|
2026/04/13-13:10:48.030388 7ff580bfb6c0 Delete type=2 #110
|
||||||
2026/04/06-00:09:38.987710 7f82177fe6c0 Manual compaction at level-1 from '!items!null' @ 17 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
|
2026/04/13-13:10:48.030511 7ff580bfb6c0 Manual compaction at level-1 from '!items!null' @ 93 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
BIN
packs-system/anomalies/MANIFEST-000112
Normal file
BIN
packs-system/pretires/000018.ldb
Normal file
0
packs-system/pretires/000025.log
Normal file
1
packs-system/pretires/CURRENT
Normal file
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000023
|
||||||
0
packs-system/pretires/LOCK
Normal file
8
packs-system/pretires/LOG
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/04/13-13:11:31.193586 7f2a6b7fe6c0 Recovering log #21
|
||||||
|
2026/04/13-13:11:31.247393 7f2a6b7fe6c0 Delete type=3 #19
|
||||||
|
2026/04/13-13:11:31.247448 7f2a6b7fe6c0 Delete type=0 #21
|
||||||
|
2026/04/13-14:20:41.081350 7f2a69ffb6c0 Level-0 table #26: started
|
||||||
|
2026/04/13-14:20:41.081377 7f2a69ffb6c0 Level-0 table #26: 0 bytes OK
|
||||||
|
2026/04/13-14:20:41.118613 7f2a69ffb6c0 Delete type=0 #24
|
||||||
|
2026/04/13-14:20:41.210913 7f2a69ffb6c0 Manual compaction at level-0 from '!actors!6RZ6IzJUHm4dB5Ut' @ 72057594037927935 : 1 .. '!folders!MbFQgPdF6Gtbj5AU' @ 0 : 0; will stop at (end)
|
||||||
|
2026/04/13-14:20:41.210940 7f2a69ffb6c0 Manual compaction at level-1 from '!actors!6RZ6IzJUHm4dB5Ut' @ 72057594037927935 : 1 .. '!folders!MbFQgPdF6Gtbj5AU' @ 0 : 0; will stop at (end)
|
||||||
8
packs-system/pretires/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/04/13-13:10:44.800502 7ff582bff6c0 Recovering log #16
|
||||||
|
2026/04/13-13:10:44.846821 7ff582bff6c0 Delete type=3 #14
|
||||||
|
2026/04/13-13:10:44.846886 7ff582bff6c0 Delete type=0 #16
|
||||||
|
2026/04/13-13:10:47.902261 7ff580bfb6c0 Level-0 table #22: started
|
||||||
|
2026/04/13-13:10:47.902285 7ff580bfb6c0 Level-0 table #22: 0 bytes OK
|
||||||
|
2026/04/13-13:10:47.939594 7ff580bfb6c0 Delete type=0 #20
|
||||||
|
2026/04/13-13:10:47.973036 7ff580bfb6c0 Manual compaction at level-0 from '!actors!6RZ6IzJUHm4dB5Ut' @ 72057594037927935 : 1 .. '!folders!MbFQgPdF6Gtbj5AU' @ 0 : 0; will stop at (end)
|
||||||
|
2026/04/13-13:10:48.030491 7ff580bfb6c0 Manual compaction at level-1 from '!actors!6RZ6IzJUHm4dB5Ut' @ 72057594037927935 : 1 .. '!folders!MbFQgPdF6Gtbj5AU' @ 0 : 0; will stop at (end)
|
||||||
BIN
packs-system/pretires/MANIFEST-000023
Normal file
BIN
packs-system/scenes/000057.ldb
Normal file
0
packs-system/scenes/000064.log
Normal file
1
packs-system/scenes/CURRENT
Normal file
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000062
|
||||||
0
packs-system/scenes/LOCK
Normal file
8
packs-system/scenes/LOG
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/04/13-13:11:31.305113 7f2a6a7fc6c0 Recovering log #60
|
||||||
|
2026/04/13-13:11:31.358709 7f2a6a7fc6c0 Delete type=3 #58
|
||||||
|
2026/04/13-13:11:31.358775 7f2a6a7fc6c0 Delete type=0 #60
|
||||||
|
2026/04/13-14:20:41.045418 7f2a69ffb6c0 Level-0 table #65: started
|
||||||
|
2026/04/13-14:20:41.045487 7f2a69ffb6c0 Level-0 table #65: 0 bytes OK
|
||||||
|
2026/04/13-14:20:41.081154 7f2a69ffb6c0 Delete type=0 #63
|
||||||
|
2026/04/13-14:20:41.210899 7f2a69ffb6c0 Manual compaction at level-0 from '!scenes!Jr7lGxYk2RETlXRv' @ 72057594037927935 : 1 .. '!scenes.tokens.delta.items!Jr7lGxYk2RETlXRv.6urwC5SVcou6UOAG.CTg4yBE12iMee1RU.BYT1CrA37R3Og0nu' @ 0 : 0; will stop at (end)
|
||||||
|
2026/04/13-14:20:41.210948 7f2a69ffb6c0 Manual compaction at level-1 from '!scenes!Jr7lGxYk2RETlXRv' @ 72057594037927935 : 1 .. '!scenes.tokens.delta.items!Jr7lGxYk2RETlXRv.6urwC5SVcou6UOAG.CTg4yBE12iMee1RU.BYT1CrA37R3Og0nu' @ 0 : 0; will stop at (end)
|
||||||
8
packs-system/scenes/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/04/13-13:10:44.916572 7ff5813fc6c0 Recovering log #55
|
||||||
|
2026/04/13-13:10:44.960340 7ff5813fc6c0 Delete type=3 #53
|
||||||
|
2026/04/13-13:10:44.960396 7ff5813fc6c0 Delete type=0 #55
|
||||||
|
2026/04/13-13:10:47.857212 7ff580bfb6c0 Level-0 table #61: started
|
||||||
|
2026/04/13-13:10:47.857237 7ff580bfb6c0 Level-0 table #61: 0 bytes OK
|
||||||
|
2026/04/13-13:10:47.902098 7ff580bfb6c0 Delete type=0 #59
|
||||||
|
2026/04/13-13:10:47.973025 7ff580bfb6c0 Manual compaction at level-0 from '!scenes!Jr7lGxYk2RETlXRv' @ 72057594037927935 : 1 .. '!scenes.tokens.delta.items!Jr7lGxYk2RETlXRv.6urwC5SVcou6UOAG.CTg4yBE12iMee1RU.BYT1CrA37R3Og0nu' @ 0 : 0; will stop at (end)
|
||||||
|
2026/04/13-13:10:48.030475 7ff580bfb6c0 Manual compaction at level-1 from '!scenes!Jr7lGxYk2RETlXRv' @ 72057594037927935 : 1 .. '!scenes.tokens.delta.items!Jr7lGxYk2RETlXRv.6urwC5SVcou6UOAG.CTg4yBE12iMee1RU.BYT1CrA37R3Og0nu' @ 0 : 0; will stop at (end)
|
||||||
BIN
packs-system/scenes/MANIFEST-000062
Normal file
654
pretires_extracted.json
Normal file
@@ -0,0 +1,654 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Niels Bielke",
|
||||||
|
"concept": "L’ARTISTE MAUDIT",
|
||||||
|
"metier": "Acteur",
|
||||||
|
"age": "JOUEUR 38",
|
||||||
|
"origine": "Suède",
|
||||||
|
"faction": null,
|
||||||
|
"biographie": "Niels Bielke connut une carrière fulgurante en jouant sur les planches de tous les théâtres réputés \nde Suède. Lassé par son succès, il quitta la Terre pour conquérir la Lune.\nQuelle déception de perdre son statut de vedette ! En effet, la concurrence est rude à Célestopol, \noù la culture sous toutes ses formes est richement célébrée, notamment à travers les dépenses pha-\nraoniques du duc Nikolaï. Niels pensait pouvoir se tailler la part du lion, mais c’est son ego qui \nse retrouva taillé en pièce. Il s’obstina pendant plusieurs années, voyant toutes les portes se fermer \ndevant lui petit à petit. Sombrant dans le mépris de lui-même et l’alcool conjointement, Niels \nne connut son salut que grâce à l’intervention d’Ernest, qui sauva littéralement la vie de l’artiste \nmaudit sur le point de se jeter dans le sélénium.\nDepuis, Niels tente de payer sa dette en servant de son mieux comme agent au sein de l’équipe \ndu Lys blanc et en respectant sa parole de ne plus toucher à l’alcool. Niels porte les stigmates d’un \nhomme ayant commis des abus – en particulier un tic nerveux dont il n’arrive pas à se débarrasser. \nMalgré ce désagrément et une allure en apparence négligée, il conserve un charisme et une aura \ncertains. Niels approche de la quarantaine.",
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Suggestion",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’une Spécialisation d’Âme, Niels peut relancer les\n2d8 2 fois au cours d’un même scénario, mais doit conserver le\ndernier résultat.",
|
||||||
|
"narratif": "Niels est en mesure d’influer sur la prise de décision d’une per-\nsonne en lui parlant à voix haute et en la fixant dans les yeux. Cette\ndécision doit avoir un impact immédiat.\nExemples : Un garde choisit finalement de laisser passer Niels. Un\nprisonnier accepte de lui donner son nom, etc.\nNote : Ce pouvoir fonctionne également sur les automates\nsophistiqués de 4e et 5e générations.",
|
||||||
|
"exemples": null,
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"domaines": {
|
||||||
|
"ame": {
|
||||||
|
"resistance": 2,
|
||||||
|
"artifice": 2,
|
||||||
|
"attraction": 3,
|
||||||
|
"coercition": 0,
|
||||||
|
"faveur": 3
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"resistance": 2,
|
||||||
|
"echauffouree": 0,
|
||||||
|
"effacement": 0,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 2
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"resistance": 4,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 4,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 0
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"resistance": 2,
|
||||||
|
"instruction": 3,
|
||||||
|
"merveilleux_tech": 0,
|
||||||
|
"raisonnement": 2,
|
||||||
|
"traitement": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 2,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
{
|
||||||
|
"nom": "Charismatique",
|
||||||
|
"valeur": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Sensible",
|
||||||
|
"valeur": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"Agence Pinkerton": 0,
|
||||||
|
"-4 -3 -2 -1 0 1 2 3 4": 0,
|
||||||
|
"Police secrète du duc": 0,
|
||||||
|
"Okhrana": 0,
|
||||||
|
"Luna NovaTek": 0,
|
||||||
|
"Société théosophique OTO": 0,
|
||||||
|
"Syndicats clandestins": 0,
|
||||||
|
"Vorovskoy Mir": 0,
|
||||||
|
"Cour des merveilles": 0
|
||||||
|
},
|
||||||
|
"weapons": [],
|
||||||
|
"armures": [],
|
||||||
|
"equipment": [],
|
||||||
|
"descriptionPhysique": null,
|
||||||
|
"descriptionPsychologique": null,
|
||||||
|
"notes": "Armes/armures/équipement non présents dans le PDF. Relations de factions toutes à 0 (valeur par défaut, indicateurs visuels hors portée du PDF text)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Polyphème",
|
||||||
|
"concept": "LE CHRONIQUEUR",
|
||||||
|
"metier": "Assistant-archiviste",
|
||||||
|
"age": "JOUEUR Inconnu",
|
||||||
|
"origine": "Inconnue",
|
||||||
|
"faction": null,
|
||||||
|
"biographie": "Automate de 4e génération émancipé par Ernest après que le Français le retrouva dans un piteux \nétat au fond d’une impasse.\nL’attention du vétéran fut attirée par l’être artificiel alors qu’un groupe d’étudiants sortaient hilares \nde la ruelle sombre. L’automate avait manifestement été passé à tabac. Après plusieurs semaines \ndurant lesquelles personne n’en revendiqua la propriété, Ernest prit sur lui de payer ses réparations.\nMalgré sa remise en état de marche, l’automate semblait ne pas conserver de souvenir de sa « vie » \npassée. C’est ainsi qu’il entra au service d’Ernest en tant qu’assistant-archiviste au sein de l’agence \ndu Lys blanc. Baptisé Polyphème, un nom grec en référence à Ajax, l’automate le plus célèbre de \nla Cité, l’archiviste est désormais la propriété d’Ernest.\nDans les faits, l’automate est un membre à part entière de l’agence, parfaitement intégré au sein \nde son équipe. Malgré tout, depuis son agression, Polyphème se plaint de ne pas avoir le « bon \nvisage », ce dont il peut parfois faire une obsession.",
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Voyage astral",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’Appréciation, de Merveilleux technologique, de\nTraitement ou de Traque, Polyphème peut relancer les 2d8 2 fois au\ncours d’un même scénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "L’esprit de Polpyphème quitte son enveloppe corporelle et se déplace\nde 8 mètres par tour pendant 4 tours, dans n’importe quelle direction.\nDe cette manière, l’esprit est invisible et peut ignorer tous les obstacles.\nSes sens restent toutefois les mêmes.\nExemples : Polyphème accède aux toits d’une maison ou il peut aller\nen repérage dans la pièce voisine. Si un éboulis bloque le passage d’un\ntunnel, Polyphème peut voir sur quelle distance et s’il y a des survivants.",
|
||||||
|
"exemples": null,
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"domaines": {
|
||||||
|
"ame": {
|
||||||
|
"resistance": 0,
|
||||||
|
"artifice": 3,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 3
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"resistance": 2,
|
||||||
|
"echauffouree": 0,
|
||||||
|
"effacement": 2,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 0
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"resistance": 0,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 0,
|
||||||
|
"traque": 3
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"resistance": 2,
|
||||||
|
"instruction": 4,
|
||||||
|
"merveilleux_tech": 0,
|
||||||
|
"raisonnement": 3,
|
||||||
|
"traitement": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"vision": 2,
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 0
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
{
|
||||||
|
"nom": "Difficile à lire",
|
||||||
|
"valeur": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Étrangeté",
|
||||||
|
"valeur": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Mémoire eidétique",
|
||||||
|
"valeur": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Vision aiguisée",
|
||||||
|
"valeur": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"Agence Pinkerton": 0,
|
||||||
|
"-4 -3 -2 -1 0 1 2 3 4": 0,
|
||||||
|
"Police secrète du duc": 0,
|
||||||
|
"Okhrana": 0,
|
||||||
|
"Luna NovaTek": 0,
|
||||||
|
"Société théosophique OTO": 0,
|
||||||
|
"Syndicats clandestins": 0,
|
||||||
|
"Vorovskoy Mir": 0,
|
||||||
|
"Cour des merveilles": 0
|
||||||
|
},
|
||||||
|
"weapons": [],
|
||||||
|
"armures": [],
|
||||||
|
"equipment": [],
|
||||||
|
"descriptionPhysique": null,
|
||||||
|
"descriptionPsychologique": null,
|
||||||
|
"notes": "Armes/armures/équipement non présents dans le PDF. Relations de factions toutes à 0 (valeur par défaut, indicateurs visuels hors portée du PDF text)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sèdami Alassane",
|
||||||
|
"concept": "LA DIPLOMATE",
|
||||||
|
"metier": "Guerrière /",
|
||||||
|
"age": "JOUEUR 58",
|
||||||
|
"origine": "Dahomey",
|
||||||
|
"faction": null,
|
||||||
|
"biographie": "Formée au sein du redoutable bataillon des Mino, ces guerrières d’élite du royaume de Dahomey, Sèdami \ns’illustra au combat mais également en stratégie, ce qui lui permit rapidement de devenir officière parmi les \n« amazones du Dahomey », comme on les surnomme souvent.\nToutefois, son esprit militaire cohabitait toujours avec une certaine rêverie tournée vers la Lune \net sa ville utopique, Célestopol. Longtemps, elle espéra secrètement parcourir ses rues, obser-\nver ses canaux, ressentir son ambiance slave unique… Possédant une intelligence interpersonnelle \ntrès développée, Sèdami devint alors diplomate et fut à plusieurs reprises assignée à des missions \nprotocolaires sur le continent africain puis en Europe et au Moyen-Orient. Il y a cinq ans, elle eut l’occa-\nsion d’exaucer son rêve en tant que conseillère du roi Béhanzin au cours d’une visite de courtoisie au duc \nNikolaï, à Célestopol.\nTotalement subjuguée par la cité sélène et marquée dans son âme par le sélénium, Sèdami ne l’a plus jamais \nquittée, abandonnant son rôle de diplomate du Dahomey. Quoique son Afrique natale lui manque sou-\nvent, elle ne reviendra pas sur sa décision de vivre désormais sur la Lune.\nSèdami est une femme d’âge mûr, confiante en ses capacités, avec un esprit très ouvert.",
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Télépathie",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’Appréciation, d’Attraction, d’Échauffourée ou de\nFaveur, Sèdami peut relancer les 2d8 2 fois au cours d’un même\nscénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "Sèdami est capable de percevoir les pensées superficielles d’un tiers.\nDe cette manière, elle peut comprendre l’état émotionnel d’une per-\nsonne, voire capter une image ou un mot (à l’appréciation du narra-\nteur) dans son esprit, simplement en l’observant.\nExemples : Sèdami suspecte une tentative de meurtre et perçoit un flacon\nde cyanure dans l’esprit d’un domestique. Malgré un faciès contenu, elle\nsaisit qu’un magistrat est en fait terrorisé.\nNote : Ce pouvoir fonctionne également sur les automates\nsophistiqués de 4e et 5e générations.",
|
||||||
|
"exemples": null,
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"domaines": {
|
||||||
|
"ame": {
|
||||||
|
"resistance": 4,
|
||||||
|
"artifice": 1,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 3
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"resistance": 2,
|
||||||
|
"echauffouree": 3,
|
||||||
|
"effacement": 1,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 2
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"resistance": 0,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 1,
|
||||||
|
"traque": 2
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"resistance": 2,
|
||||||
|
"instruction": 2,
|
||||||
|
"merveilleux_tech": 0,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 1,
|
||||||
|
"fortune": 1,
|
||||||
|
"reve": 0,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
{
|
||||||
|
"nom": "Comportementaliste",
|
||||||
|
"valeur": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Stratège",
|
||||||
|
"valeur": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"Agence Pinkerton": 0,
|
||||||
|
"-4 -3 -2 -1 0 1 2 3 4": 0,
|
||||||
|
"Police secrète du duc": 0,
|
||||||
|
"Okhrana": 0,
|
||||||
|
"Luna NovaTek": 0,
|
||||||
|
"Société théosophique OTO": 0,
|
||||||
|
"Syndicats clandestins": 0,
|
||||||
|
"Vorovskoy Mir": 0,
|
||||||
|
"Cour des merveilles": 0
|
||||||
|
},
|
||||||
|
"weapons": [],
|
||||||
|
"armures": [],
|
||||||
|
"equipment": [],
|
||||||
|
"descriptionPhysique": null,
|
||||||
|
"descriptionPsychologique": null,
|
||||||
|
"notes": "Armes/armures/équipement non présents dans le PDF. Relations de factions toutes à 0 (valeur par défaut, indicateurs visuels hors portée du PDF text)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Bao Wang",
|
||||||
|
"concept": "L’ESCROC",
|
||||||
|
"metier": "Videur",
|
||||||
|
"age": "JOUEUR 32",
|
||||||
|
"origine": "Chine",
|
||||||
|
"faction": null,
|
||||||
|
"biographie": "Ancien employé du casino flottant La Libellule, où il officiait comme videur.\nAprès une adolescence de petite frappe dans les faubourgs de Shanghai, il parvint à s’introduire dans un \nobus-traversier pour rejoindre Célestopol, autant attiré par les perspectives offertes par la cité lunaire que \npar le besoin de se faire oublier des services de police de sa ville natale.\nProblème : Bao répugne à se battre et préfère largement la finesse à la violence. De son ancienne vie, il \nconserve un goût prononcé pour le jeu et la gent féminine. Par ailleurs, Bao a également un certain pen-\nchant pour les cabarets clandestins, dans lesquels il aime à se travestir à l’occasion. C’est dans l’un de ces \nlieux interlopes de la cité que le jeune chinois rencontra Ernest, le directeur de l’agence de détectives du Lys \nblanc. Le vétéran sut voir un certain potentiel chez Bao et les deux hommes sympathisèrent. Bao, passable-\nment désœuvré et fauché, accepta, sans trop y croire, d’intégrer l’agence en tant que détective.\nSon sens de la débrouillardise et son passé houleux parfois bien utile lui octroient des compétences uniques \net font désormais de lui un agent indispensable. Le jeune homme s’investit d’ailleurs sincèrement dans son \ntravail. Toujours tiré à quatre épingles, Bao est reconnu parmi ses coéquipiers comme le plus nonchalant \ndu groupe.",
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Entropie",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Bao peut relancer le dé de la Lune 1 fois au cours d’un même\nscénario et choisir de conserver le résultat qu’il préfère (cette faculté\nne fonctionne pas pour les tests de chance).",
|
||||||
|
"narratif": "Bao peut influer sur le hasard de manière mineure.\nExemples : En jouant au poker, Bao récupère une bonne main à la\ndistribution. Le feu passe au vert alors qu’il tourne au coin de la rue.",
|
||||||
|
"exemples": null,
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"domaines": {
|
||||||
|
"ame": {
|
||||||
|
"resistance": 2,
|
||||||
|
"artifice": 4,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"resistance": 4,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 4,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 0
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"resistance": 0,
|
||||||
|
"appreciation": 2,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 1,
|
||||||
|
"traque": 3
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"resistance": 0,
|
||||||
|
"instruction": 0,
|
||||||
|
"merveilleux_tech": 0,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 2,
|
||||||
|
"reve": 0,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
{
|
||||||
|
"nom": "Aime le jeu",
|
||||||
|
"valeur": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Belle gueule",
|
||||||
|
"valeur": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"Agence Pinkerton": 0,
|
||||||
|
"-4 -3 -2 -1 0 1 2 3 4": 0,
|
||||||
|
"Police secrète du duc": 0,
|
||||||
|
"Okhrana": 0,
|
||||||
|
"Luna NovaTek": 0,
|
||||||
|
"Société théosophique OTO": 0,
|
||||||
|
"Syndicats clandestins": 0,
|
||||||
|
"Vorovskoy Mir": 0,
|
||||||
|
"Cour des merveilles": 0
|
||||||
|
},
|
||||||
|
"weapons": [],
|
||||||
|
"armures": [],
|
||||||
|
"equipment": [],
|
||||||
|
"descriptionPhysique": null,
|
||||||
|
"descriptionPsychologique": null,
|
||||||
|
"notes": "Armes/armures/équipement non présents dans le PDF. Relations de factions toutes à 0 (valeur par défaut, indicateurs visuels hors portée du PDF text)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Elemiah Cowen",
|
||||||
|
"concept": "L’OUVRIER",
|
||||||
|
"metier": "Manutentionnaire",
|
||||||
|
"age": "JOUEUR 28",
|
||||||
|
"origine": "Célestopol",
|
||||||
|
"faction": null,
|
||||||
|
"biographie": "Elemiah et son frère Rubben travaillèrent toute leur vie comme des forçats dans les usines souterraines \nde Célestopol. Comme des dizaines de milliers d’ouvriers vivant dans des conditions très difficiles, ils \napprirent à se serrer les coudes de manière à soutenir leur communauté. Un jour, un des nombreux acci-\ndents quotidiens liés à un dysfonctionnement d’une machine emporta Rubben et priva Elemiah de deux \ndoigts de sa main gauche. Depuis, l’ouvrier survivant s’est promis de tout faire pour aider les miséreux \nde la ville. Il connaît donc bien la face cachée de Célestopol et en garde un goût amer. Dur à la tâche, \ndu genre taiseux, mais toujours le premier à proposer son aide et son soutien, cet ancien ouvrier d’une \ntrentaine d’années a le cœur tendre. Son torse large, ses sourcils broussailleux et sa moustache travaillée \nimpressionnent souvent. Mais il est surtout connu pour avoir un certain talent pour le tirage des cartes – il \ntombe d’ailleurs encore plus souvent juste depuis son accident –, et reste très apprécié de sa communauté, \nmême s’il travaille aujourd’hui comme manutentionnaire aux galeries Sabline.\nSubjugué par l’architecture démentielle de la cité lunaire et, surtout, par ses automates, il consacre désor-\nmais chaque minute de son temps libre à errer à travers la ville et à s’en émerveiller. Une manière pour lui \nd’expérimenter ce que son frère ne pourra jamais faire.",
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Tarot divinatoire",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’une Spécialisation de Cœur, Elemiah gagne la pos-\nsibilité de relancer les 2d8 2 fois au cours d’un même scénario,\nmais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "En tirant les cartes, Elemiah peut apprendre une information sur\nune personne concernant son passé, son présent ou son futur.\nL’information reste soumise à interprétation.\nExemples : Elemiah cherche à savoir où sera sa cible le lendemain. Il\nveut connaître l’histoire d’un voisin.",
|
||||||
|
"exemples": null,
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"domaines": {
|
||||||
|
"ame": {
|
||||||
|
"resistance": 0,
|
||||||
|
"artifice": 2,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"resistance": 2,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 0,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 4
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"resistance": 4,
|
||||||
|
"appreciation": 4,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 4,
|
||||||
|
"traque": 0
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"resistance": 0,
|
||||||
|
"instruction": 0,
|
||||||
|
"merveilleux_tech": 0,
|
||||||
|
"raisonnement": 1,
|
||||||
|
"traitement": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 1,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 1,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
{
|
||||||
|
"nom": "Digne de confiance",
|
||||||
|
"valeur": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Robuste",
|
||||||
|
"valeur": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"Agence Pinkerton": 0,
|
||||||
|
"-4 -3 -2 -1 0 1 2 3 4": 0,
|
||||||
|
"Police secrète du duc": 0,
|
||||||
|
"Okhrana": 0,
|
||||||
|
"Luna NovaTek": 0,
|
||||||
|
"Société théosophique OTO": 0,
|
||||||
|
"Syndicats clandestins": 0,
|
||||||
|
"Vorovskoy Mir": 0,
|
||||||
|
"Cour des merveilles": 0
|
||||||
|
},
|
||||||
|
"weapons": [],
|
||||||
|
"armures": [],
|
||||||
|
"equipment": [],
|
||||||
|
"descriptionPhysique": null,
|
||||||
|
"descriptionPsychologique": null,
|
||||||
|
"notes": "Armes/armures/équipement non présents dans le PDF. Relations de factions toutes à 0 (valeur par défaut, indicateurs visuels hors portée du PDF text)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nadeja Danilo",
|
||||||
|
"concept": "LA PILOTE",
|
||||||
|
"metier": "Pilote",
|
||||||
|
"age": "JOUEUR 35",
|
||||||
|
"origine": "Célestopol",
|
||||||
|
"faction": null,
|
||||||
|
"biographie": "Née à Célestopol de deux parents universitaires, Nadeja ne fut jamais une enfant comme les autres. \nIntrovertie, elle avait du mal à entrer en relation avec les autres. Intriguée par les nombreux livres occu-\npant l’espace familial, elle tourna surtout son esprit vers les étoiles, le lointain. Mars, Cérès, les confins du \nsystème solaire occupaient ses pensées comme une obsession, si bien qu’avec l’âge, elle choisit de devenir \npilote d’aéronef afin de pouvoir caresser l’espoir de rejoindre un jour ces destinations chères à son cœur. \nNadeja intégra l’académie de pilotes de la White Star Line et démontra des aptitudes presque hors norme, à \ntel point que les commandes des vaisseaux semblaient répondre à ses exigences sans qu’elle ait à intervenir. \nLucide, concentrée, apprenant vite, Nadeja décrocha son diplôme sans aucune difficulté et réalisa ainsi la \npremière étape de son rêve.\nC’était avant que la réalité – et la misogynie galopante dans le milieu majoritairement masculin des pilotes \nd’aéronef – ne la rattrape : Nadeja, pourtant bien plus compétente que la plupart de ses collègues, fut sou-\nvent reléguée au poste de copilote. Elle en garde une certaine rancœur et préfère, pour le moment, travailler \npour l’entreprise Columbia. De fait, elle peut paraître sérieuse de prime abord, certains diraient farouche. \nEn réalité, elle ne supporte pas qu’on lui manque de respect.",
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Télékinésie",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’une Spécialisation de Corps, Nadeja peut relancer\nles 2d8 2 fois au cours d’un même scénario, mais doit conserver le\ndernier résultat.",
|
||||||
|
"narratif": "Dans un rayon de 8 mètres, Nadeja peut déplacer un petit objet\nléger et sans attaches par la pensée sur 4 mètres (n’importe quelle\ndirection) pendant 2 tours.\nExemples : Nadeja peut déplacer une cuillère pour la faire tomber\nd’une table. Elle peut faire léviter un jeu de tarot.",
|
||||||
|
"exemples": null,
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"domaines": {
|
||||||
|
"ame": {
|
||||||
|
"resistance": 0,
|
||||||
|
"artifice": 0,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"resistance": 2,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 2,
|
||||||
|
"mobilite": 3,
|
||||||
|
"prouesse": 2
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"resistance": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 0
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"resistance": 2,
|
||||||
|
"instruction": 2,
|
||||||
|
"merveilleux_tech": 0,
|
||||||
|
"raisonnement": 2,
|
||||||
|
"traitement": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 1,
|
||||||
|
"vision": 1
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
{
|
||||||
|
"nom": "Résiliente",
|
||||||
|
"valeur": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Tête froide",
|
||||||
|
"valeur": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"Agence Pinkerton": 0,
|
||||||
|
"-4 -3 -2 -1 0 1 2 3 4": 0,
|
||||||
|
"Police secrète du duc": 0,
|
||||||
|
"Okhrana": 0,
|
||||||
|
"Luna NovaTek": 0,
|
||||||
|
"Société théosophique OTO": 0,
|
||||||
|
"Syndicats clandestins": 0,
|
||||||
|
"Vorovskoy Mir": 0,
|
||||||
|
"Cour des merveilles": 0
|
||||||
|
},
|
||||||
|
"weapons": [],
|
||||||
|
"armures": [],
|
||||||
|
"equipment": [],
|
||||||
|
"descriptionPhysique": null,
|
||||||
|
"descriptionPsychologique": null,
|
||||||
|
"notes": "Armes/armures/équipement non présents dans le PDF. Relations de factions toutes à 0 (valeur par défaut, indicateurs visuels hors portée du PDF text)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Maribel Vargas",
|
||||||
|
"concept": "LA SCIENTIFIQUE",
|
||||||
|
"metier": "Mécanicienne",
|
||||||
|
"age": "JOUEUR 25",
|
||||||
|
"origine": "Mexique",
|
||||||
|
"faction": null,
|
||||||
|
"biographie": "Ancienne mécanicienne ayant travaillé notamment sur les lignes de chemin de fer reliant Célestopol à la \nstation du Pôle Nord.\nMaribel Vargas fut longtemps bridée dans son évolution de carrière à cause de son origine mexicaine et de \nson genre. Or, malgré son jeune âge (vingt-cinq ans), Maribel est une experte dans l’ingénierie de pointe, \nspécialement dans l’utilisation du fameux sélénium occupant les canaux de la ville. Ses parents, aux revenus \nmodestes, firent beaucoup de sacrifices pour lui permettre d’étudier à l’université de Célestopol, dont elle \nest d’ailleurs sortie major de promotion. Pourtant, personne n’avait souhaité lui donner sa chance avant \nErnest, qui tenait à avoir dans son agence une personne capable de réparer Polyphème, mais aussi de com-\nprendre les complexités de la technologie lunaire.\nMaribel subit les moqueries amusées du reste de son équipe, car elle a tendance à parler seule, surtout \nquand elle répare ou examine un mécanisme, quel qu’il soit. Par ailleurs, c’est une véritable amatrice de \npeinture, qui connaît très bien les allées du musée des Beaux-Arts.",
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Communication avec les morts",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors du test d’une Spécialisation d’Esprit, Maribel peut relancer\nles 2d8 2 fois au cours d’un même scénario, mais doit conserver le\ndernier résultat.",
|
||||||
|
"narratif": "Maribel entre en contact avec l’esprit d’un défunt. Elle peut lui\nposer une question fermée (réponse par oui ou non).\nExemples : Maribel interroge la victime d’un meurtre. Elle consulte\nl’esprit d’un aïeul.",
|
||||||
|
"exemples": null,
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"domaines": {
|
||||||
|
"ame": {
|
||||||
|
"resistance": 0,
|
||||||
|
"artifice": 2,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 0,
|
||||||
|
"faveur": 2
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"resistance": 2,
|
||||||
|
"echauffouree": 0,
|
||||||
|
"effacement": 2,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 2
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"resistance": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 3,
|
||||||
|
"inspiration": 0,
|
||||||
|
"traque": 2
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"resistance": 2,
|
||||||
|
"instruction": 2,
|
||||||
|
"merveilleux_tech": 0,
|
||||||
|
"raisonnement": 2,
|
||||||
|
"traitement": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 2,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
{
|
||||||
|
"nom": "Déterminée",
|
||||||
|
"valeur": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Sagace",
|
||||||
|
"valeur": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"Agence Pinkerton": 0,
|
||||||
|
"-4 -3 -2 -1 0 1 2 3 4": 0,
|
||||||
|
"Police secrète du duc": 0,
|
||||||
|
"Okhrana": 0,
|
||||||
|
"Luna NovaTek": 0,
|
||||||
|
"Société théosophique OTO": 0,
|
||||||
|
"Syndicats clandestins": 0,
|
||||||
|
"Vorovskoy Mir": 0,
|
||||||
|
"Cour des merveilles": 0
|
||||||
|
},
|
||||||
|
"weapons": [],
|
||||||
|
"armures": [],
|
||||||
|
"equipment": [],
|
||||||
|
"descriptionPhysique": null,
|
||||||
|
"descriptionPsychologique": null,
|
||||||
|
"notes": "Armes/armures/équipement non présents dans le PDF. Relations de factions toutes à 0 (valeur par défaut, indicateurs visuels hors portée du PDF text)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Wiktoria Raźny",
|
||||||
|
"concept": "LA VÉTÉRANE",
|
||||||
|
"metier": "Soldate",
|
||||||
|
"age": "JOUEUR 39",
|
||||||
|
"origine": "Pologne",
|
||||||
|
"faction": null,
|
||||||
|
"biographie": "Wiktoria Raźny était soldate durant la Seconde Guerre de Crimée.\nCette Polonaise de presque quarante ans aujourd’hui prit les armes pour défendre sa patrie en tant que \nconscrite. Blessée à l’œil, qu’elle finit par perdre faute de soins adaptés, elle ne trouva pas le réconfort \ndans l’estime et la reconnaissance que son sacrifice aurait dû lui apporter. Au contraire, son handicap et \nsa dégaine dégingandée lui valurent un certain rejet de ses compatriotes, ce qui la poussa, par dégoût, à \ns’éloigner autant que possible de la Pologne. Et pour cela, quoi de mieux que d’aller sur la Lune ? Sur place, \nelle rencontra Ernest, avec qui elle eut une aventure. Leurs points communs étaient multiples, il faut dire. \nDevenus bons amis, ils commencèrent à travailler ensemble quand le vétéran recruta l’ancienne soldate \ndans son agence du Lys blanc.\nAvec l’âge et le recul, Wiktoria assume parfaitement son handicap et porte un œil de verre. Elle explore les \nsoirées mondaines qu’elle apprécie tant et durant lesquelles elle se fait fort de collecter nombre d’informa-\ntions utiles à l’agence, mêlant ainsi l’utile à l’agréable.",
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Illusion",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test de Coercition, d’Échauffourée, d’Effacement ou de\nTraque, Wiktoria peut relancer les 2d8 2 fois au cours d’un même\nscénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "Wiktoria peut générer une petite illusion mineure (visuelle, audi-\ntive, olfactive, etc., au choix) sans détail ou précision pendant\n1 minute.\nExemples : Wiktoria peut faire entendre le bruit d’un chat qui miaule\nou d’un livre qui tombe. Elle peut faire apparaître un éclat métallique\nou une ombre fugace. Elle peut créer une odeur de pluie ou de fumée.",
|
||||||
|
"exemples": null,
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"domaines": {
|
||||||
|
"ame": {
|
||||||
|
"resistance": 2,
|
||||||
|
"artifice": 0,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2
|
||||||
|
},
|
||||||
|
"corps": {
|
||||||
|
"resistance": 2,
|
||||||
|
"echauffouree": 4,
|
||||||
|
"effacement": 1,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 3
|
||||||
|
},
|
||||||
|
"coeur": {
|
||||||
|
"resistance": 0,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 2,
|
||||||
|
"traque": 3
|
||||||
|
},
|
||||||
|
"esprit": {
|
||||||
|
"resistance": 0,
|
||||||
|
"instruction": 1,
|
||||||
|
"merveilleux_tech": 0,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 2,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 0,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
{
|
||||||
|
"nom": "Affable",
|
||||||
|
"valeur": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Grande",
|
||||||
|
"valeur": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nom": "Expérience militaire",
|
||||||
|
"valeur": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"Agence Pinkerton": 0,
|
||||||
|
"-4 -3 -2 -1 0 1 2 3 4": 0,
|
||||||
|
"Police secrète du duc": 0,
|
||||||
|
"Okhrana": 0,
|
||||||
|
"Luna NovaTek": 0,
|
||||||
|
"Société théosophique OTO": 0,
|
||||||
|
"Syndicats clandestins": 0,
|
||||||
|
"Vorovskoy Mir": 0,
|
||||||
|
"Cour des merveilles": 0
|
||||||
|
},
|
||||||
|
"weapons": [],
|
||||||
|
"armures": [],
|
||||||
|
"equipment": [],
|
||||||
|
"descriptionPhysique": null,
|
||||||
|
"descriptionPsychologique": null,
|
||||||
|
"notes": "Armes/armures/équipement non présents dans le PDF. Relations de factions toutes à 0 (valeur par défaut, indicateurs visuels hors portée du PDF text)."
|
||||||
|
}
|
||||||
|
]
|
||||||
605
pretires_v2.json
Normal file
@@ -0,0 +1,605 @@
|
|||||||
|
{
|
||||||
|
"Niels Bielke": {
|
||||||
|
"age": 38,
|
||||||
|
"origine": "JOUEUR",
|
||||||
|
"metier": "Acteur MÉTIER",
|
||||||
|
"faction_label": "Suède ORIGINE Acteur MÉTIER",
|
||||||
|
"resistances": {
|
||||||
|
"ame": 2,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 4,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 2,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 2,
|
||||||
|
"attraction": 3,
|
||||||
|
"coercition": 0,
|
||||||
|
"faveur": 3,
|
||||||
|
"echauffouree": 0,
|
||||||
|
"effacement": 0,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 4,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 0,
|
||||||
|
"instruction": 3,
|
||||||
|
"merv_tech": 0,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 1
|
||||||
|
},
|
||||||
|
"raffinement": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Suggestion",
|
||||||
|
"niveau": 2
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Charismatique (2)",
|
||||||
|
"Sensible (1)",
|
||||||
|
"ATTRIBUTS"
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"_bio_text_raw": "NIELS BIELKE : L’ARTISTE MAUDIT\nNiels Bielke connut une carrière fulgurante en jouant sur les planches de tous les théâtres réputés \nde Suède. Lassé par son succès, il quitta la Terre pour conquérir la Lune.\nQuelle déception de perdre son statut de vedette ! En effet, la concurrence est rude à Célestopol, \noù la culture sous toutes ses formes est richement célébrée, notamment à travers les dépenses pha-\nraoniques du duc Nikolaï. Niels pensait pouvoir se tailler la part du lion, mais c’est son ego qui \nse retrouva taillé en pièce. Il s’obstina pendant plusieurs années, voyant toutes les portes se fermer \ndevant lui petit à petit. Sombrant dans le mépris de lui-même et l’alcool conjointement, Niels \nne connut son salut que grâce à l’intervention d’Ernest, qui sauva littéralement la vie de l’artiste \nmaudit sur le point de se jeter dans le sélénium.\nDepuis, Niels tente de payer sa dette en servant de son mieux comme agent au sein de l’équipe \ndu Lys blanc et en respectant sa parole de ne plus toucher à l’alcool. Niels porte les stigmates d’un \nhomme ayant commis des abus – en particulier un tic nerveux dont il n’arrive pas à se débarrasser. \nMalgré ce désagrément et une allure en apparence négligée, il conserve un charisme et une aura \ncertains. Niels approche de la quarantaine.\nANOMALIE\nSuggestion 2\nTechnique\nLors d’un test d’une Spécialisation d’Âme, Niels peut relancer les \n2d8 2 fois au cours d’un même scénario, mais doit conserver le \ndernier résultat.\nNarratif\nNiels est en mesure d’influer sur la prise de décision d’une per-\nsonne en lui parlant à voix haute et en la fixant dans les yeux. Cette \ndécision doit avoir un impact immédiat.\nExemples : Un garde choisit finalement de laisser passer Niels. Un \nprisonnier accepte de lui donner son nom, etc.\nNote : Ce pouvoir fonctionne également sur les automates \nsophistiqués de 4e et 5e générations.\n",
|
||||||
|
"_asp_text_raw": "BLESSURES\nASPECTS\nATTRIBUTS\nENTREGENT\nFORTUNE\nRÊVE\nVISION\nFACTIONS\n Agence Pinkerton\n Police secrète du duc\n Okhrana\n Luna NovaTek\n Société théosophique OTO\n Syndicats clandestins\n Vorovskoy Mir\n Cour des merveilles\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\nNIVEAU\nMALUS\nDURÉE\nAnodin\nAucun\n1 min\nNégligeable\nAucun\n1 min\nDérisoire\n−1\n10 min\nSuperficiel\n−1\n10 min\nLéger\n−2\n30 min\nModéré\n−2\n30 min\nGrave\n−3\nUne journée\nDramatique\nLe personnage est \nhors-fiction\nCharismatique (2)\nSensible (1)\n"
|
||||||
|
},
|
||||||
|
"Polyphème": {
|
||||||
|
"age": null,
|
||||||
|
"origine": "JOUEUR",
|
||||||
|
"metier": "Assistant-archiviste MÉTIER",
|
||||||
|
"faction_label": "Inconnue ORIGINE Assistant-archiviste MÉTIER",
|
||||||
|
"resistances": {
|
||||||
|
"ame": 0,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 0,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 0,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 3,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 3,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 2,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 0,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 0,
|
||||||
|
"traque": 3,
|
||||||
|
"instruction": 4,
|
||||||
|
"merv_tech": 3,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"raffinement": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "astral",
|
||||||
|
"niveau": 2
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Difficile à lire (2)",
|
||||||
|
"Étrangeté (2)",
|
||||||
|
"Mémoire eidétique (2)",
|
||||||
|
"Vision aiguisée (1)",
|
||||||
|
"ATTRIBUTS"
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"_bio_text_raw": "POLYPHÈME : LE CHRONIQUEUR\nAutomate de 4e génération émancipé par Ernest après que le Français le retrouva dans un piteux \nétat au fond d’une impasse.\nL’attention du vétéran fut attirée par l’être artificiel alors qu’un groupe d’étudiants sortaient hilares \nde la ruelle sombre. L’automate avait manifestement été passé à tabac. Après plusieurs semaines \ndurant lesquelles personne n’en revendiqua la propriété, Ernest prit sur lui de payer ses réparations.\nMalgré sa remise en état de marche, l’automate semblait ne pas conserver de souvenir de sa « vie » \npassée. C’est ainsi qu’il entra au service d’Ernest en tant qu’assistant-archiviste au sein de l’agence \ndu Lys blanc. Baptisé Polyphème, un nom grec en référence à Ajax, l’automate le plus célèbre de \nla Cité, l’archiviste est désormais la propriété d’Ernest.\nDans les faits, l’automate est un membre à part entière de l’agence, parfaitement intégré au sein \nde son équipe. Malgré tout, depuis son agression, Polyphème se plaint de ne pas avoir le « bon \nvisage », ce dont il peut parfois faire une obsession.\nANOMALIE\nVoyage astral 2\nTechnique\nLors d’un test d’Appréciation, de Merveilleux technologique, de \nTraitement ou de Traque, Polyphème peut relancer les 2d8 2 fois au \ncours d’un même scénario, mais doit conserver le dernier résultat.\nNarratif\nL’esprit de Polpyphème quitte son enveloppe corporelle et se déplace \nde 8 mètres par tour pendant 4 tours, dans n’importe quelle direction. \nDe cette manière, l’esprit est invisible et peut ignorer tous les obstacles. \nSes sens restent toutefois les mêmes.\nExemples : Polyphème accède aux toits d’une maison ou il peut aller \nen repérage dans la pièce voisine. Si un éboulis bloque le passage d’un \ntunnel, Polyphème peut voir sur quelle distance et s’il y a des survivants.\n \n",
|
||||||
|
"_asp_text_raw": " \nBLESSURES\nASPECTS\nATTRIBUTS\nENTREGENT\nFORTUNE\nRÊVE\nVISION\nFACTIONS\n Agence Pinkerton\n Police secrète du duc\n Okhrana\n Luna NovaTek\n Société théosophique OTO\n Syndicats clandestins\n Vorovskoy Mir\n Cour des merveilles\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\nNIVEAU\nMALUS\nDURÉE\nAnodin\nAucun\n1 min\nNégligeable\nAucun\n1 min\nDérisoire\n−1\n10 min\nSuperficiel\n−1\n10 min\nLéger\n−2\n30 min\nModéré\n−2\n30 min\nGrave\n−3\nUne journée\nDramatique\nLe personnage est \nhors-fiction\nDifficile à lire (2)\nÉtrangeté (2)\nMémoire eidétique (2)\nVision aiguisée (1)\n"
|
||||||
|
},
|
||||||
|
"Sèdami Alassane": {
|
||||||
|
"age": 58,
|
||||||
|
"origine": "JOUEUR",
|
||||||
|
"metier": "Guerrière / diplomate MÉTIER",
|
||||||
|
"faction_label": "Dahomey ORIGINE Guerrière / diplomate MÉTIER",
|
||||||
|
"resistances": {
|
||||||
|
"ame": 4,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 0,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 1,
|
||||||
|
"fortune": 1,
|
||||||
|
"reve": 0,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 1,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 5,
|
||||||
|
"faveur": 3,
|
||||||
|
"echauffouree": 3,
|
||||||
|
"effacement": 1,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 1,
|
||||||
|
"traque": 2,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 0,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Télépathie",
|
||||||
|
"niveau": 2
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Comportementaliste (1)",
|
||||||
|
"Stratège (2)",
|
||||||
|
"ATTRIBUTS"
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"_bio_text_raw": "SÈDAMI ALASSANE : LA DIPLOMATE\nFormée au sein du redoutable bataillon des Mino, ces guerrières d’élite du royaume de Dahomey, Sèdami \ns’illustra au combat mais également en stratégie, ce qui lui permit rapidement de devenir officière parmi les \n« amazones du Dahomey », comme on les surnomme souvent.\nToutefois, son esprit militaire cohabitait toujours avec une certaine rêverie tournée vers la Lune \net sa ville utopique, Célestopol. Longtemps, elle espéra secrètement parcourir ses rues, obser-\nver ses canaux, ressentir son ambiance slave unique… Possédant une intelligence interpersonnelle \ntrès développée, Sèdami devint alors diplomate et fut à plusieurs reprises assignée à des missions \nprotocolaires sur le continent africain puis en Europe et au Moyen-Orient. Il y a cinq ans, elle eut l’occa-\nsion d’exaucer son rêve en tant que conseillère du roi Béhanzin au cours d’une visite de courtoisie au duc \nNikolaï, à Célestopol.\nTotalement subjuguée par la cité sélène et marquée dans son âme par le sélénium, Sèdami ne l’a plus jamais \nquittée, abandonnant son rôle de diplomate du Dahomey. Quoique son Afrique natale lui manque sou-\nvent, elle ne reviendra pas sur sa décision de vivre désormais sur la Lune.\nSèdami est une femme d’âge mûr, confiante en ses capacités, avec un esprit très ouvert.\nANOMALIE\nTélépathie 2\nTechnique\nLors d’un test d’Appréciation, d’Attraction, d’Échauffourée ou de \nFaveur, Sèdami peut relancer les 2d8 2 fois au cours d’un même \nscénario, mais doit conserver le dernier résultat.\nNarratif\nSèdami est capable de percevoir les pensées superficielles d’un tiers. \nDe cette manière, elle peut comprendre l’état émotionnel d’une per-\nsonne, voire capter une image ou un mot (à l’appréciation du narra-\nteur) dans son esprit, simplement en l’observant.\nExemples : Sèdami suspecte une tentative de meurtre et perçoit un flacon \nde cyanure dans l’esprit d’un domestique. Malgré un faciès contenu, elle \nsaisit qu’un magistrat est en fait terrorisé.\nNote : Ce pouvoir fonctionne également sur les automates \nsophistiqués de 4e et 5e générations.\n",
|
||||||
|
"_asp_text_raw": " \nBLESSURES\nASPECTS\nATTRIBUTS\nENTREGENT\nFORTUNE\nRÊVE\nVISION\nFACTIONS\n Agence Pinkerton\n Police secrète du duc\n Okhrana\n Luna NovaTek\n Société théosophique OTO\n Syndicats clandestins\n Vorovskoy Mir\n Cour des merveilles\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\nNIVEAU\nMALUS\nDURÉE\nAnodin\nAucun\n1 min\nNégligeable\nAucun\n1 min\nDérisoire\n−1\n10 min\nSuperficiel\n−1\n10 min\nLéger\n−2\n30 min\nModéré\n−2\n30 min\nGrave\n−3\nUne journée\nDramatique\nLe personnage est \nhors-fiction\nComportementaliste (1)\nStratège (2)\n"
|
||||||
|
},
|
||||||
|
"Bao Wang": {
|
||||||
|
"age": 32,
|
||||||
|
"origine": "JOUEUR",
|
||||||
|
"metier": "Videur MÉTIER",
|
||||||
|
"faction_label": "Chine ORIGINE Videur MÉTIER",
|
||||||
|
"resistances": {
|
||||||
|
"ame": 2,
|
||||||
|
"corps": 4,
|
||||||
|
"coeur": 0,
|
||||||
|
"esprit": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 2,
|
||||||
|
"reve": 0,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 4,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 4,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 4,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 0,
|
||||||
|
"appreciation": 2,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 1,
|
||||||
|
"traque": 3,
|
||||||
|
"instruction": 0,
|
||||||
|
"merv_tech": 2,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Entropie",
|
||||||
|
"niveau": 2
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Aime le jeu (1)",
|
||||||
|
"Belle gueule (2)",
|
||||||
|
"ATTRIBUTS"
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"_bio_text_raw": "BAO WANG : L’ESCROC\nAncien employé du casino flottant La Libellule, où il officiait comme videur.\nAprès une adolescence de petite frappe dans les faubourgs de Shanghai, il parvint à s’introduire dans un \nobus-traversier pour rejoindre Célestopol, autant attiré par les perspectives offertes par la cité lunaire que \npar le besoin de se faire oublier des services de police de sa ville natale.\nProblème : Bao répugne à se battre et préfère largement la finesse à la violence. De son ancienne vie, il \nconserve un goût prononcé pour le jeu et la gent féminine. Par ailleurs, Bao a également un certain pen-\nchant pour les cabarets clandestins, dans lesquels il aime à se travestir à l’occasion. C’est dans l’un de ces \nlieux interlopes de la cité que le jeune chinois rencontra Ernest, le directeur de l’agence de détectives du Lys \nblanc. Le vétéran sut voir un certain potentiel chez Bao et les deux hommes sympathisèrent. Bao, passable-\nment désœuvré et fauché, accepta, sans trop y croire, d’intégrer l’agence en tant que détective.\nSon sens de la débrouillardise et son passé houleux parfois bien utile lui octroient des compétences uniques \net font désormais de lui un agent indispensable. Le jeune homme s’investit d’ailleurs sincèrement dans son \ntravail. Toujours tiré à quatre épingles, Bao est reconnu parmi ses coéquipiers comme le plus nonchalant \ndu groupe.\nANOMALIE\nEntropie 2\nTechnique\nBao peut relancer le dé de la Lune 1 fois au cours d’un même \nscénario et choisir de conserver le résultat qu’il préfère (cette faculté \nne fonctionne pas pour les tests de chance).\nNarratif\nBao peut influer sur le hasard de manière mineure.\nExemples : En jouant au poker, Bao récupère une bonne main à la \ndistribution. Le feu passe au vert alors qu’il tourne au coin de la rue.\n",
|
||||||
|
"_asp_text_raw": " \nBLESSURES\nASPECTS\nATTRIBUTS\nENTREGENT\nFORTUNE\nRÊVE\nVISION\nFACTIONS\n Agence Pinkerton\n Police secrète du duc\n Okhrana\n Luna NovaTek\n Société théosophique OTO\n Syndicats clandestins\n Vorovskoy Mir\n Cour des merveilles\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\nNIVEAU\nMALUS\nDURÉE\nAnodin\nAucun\n1 min\nNégligeable\nAucun\n1 min\nDérisoire\n−1\n10 min\nSuperficiel\n−1\n10 min\nLéger\n−2\n30 min\nModéré\n−2\n30 min\nGrave\n−3\nUne journée\nDramatique\nLe personnage est \nhors-fiction\nAime le jeu (1)\nBelle gueule (2)\n"
|
||||||
|
},
|
||||||
|
"Elemiah Cowen": {
|
||||||
|
"age": 28,
|
||||||
|
"origine": "JOUEUR",
|
||||||
|
"metier": "Manutentionnaire MÉTIER",
|
||||||
|
"faction_label": "Célestopol ORIGINE Manutentionnaire MÉTIER",
|
||||||
|
"resistances": {
|
||||||
|
"ame": 0,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 4,
|
||||||
|
"esprit": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 1,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 1,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 2,
|
||||||
|
"attraction": 1,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 0,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 4,
|
||||||
|
"appreciation": 4,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 4,
|
||||||
|
"traque": 0,
|
||||||
|
"instruction": 0,
|
||||||
|
"merv_tech": 1,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "divinatoire",
|
||||||
|
"niveau": 2
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Digne de confiance (2)",
|
||||||
|
"Robuste (1)",
|
||||||
|
"ATTRIBUTS"
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"_bio_text_raw": "ELEMIAH COWEN : L’OUVRIER\nElemiah et son frère Rubben travaillèrent toute leur vie comme des forçats dans les usines souterraines \nde Célestopol. Comme des dizaines de milliers d’ouvriers vivant dans des conditions très difficiles, ils \napprirent à se serrer les coudes de manière à soutenir leur communauté. Un jour, un des nombreux acci-\ndents quotidiens liés à un dysfonctionnement d’une machine emporta Rubben et priva Elemiah de deux \ndoigts de sa main gauche. Depuis, l’ouvrier survivant s’est promis de tout faire pour aider les miséreux \nde la ville. Il connaît donc bien la face cachée de Célestopol et en garde un goût amer. Dur à la tâche, \ndu genre taiseux, mais toujours le premier à proposer son aide et son soutien, cet ancien ouvrier d’une \ntrentaine d’années a le cœur tendre. Son torse large, ses sourcils broussailleux et sa moustache travaillée \nimpressionnent souvent. Mais il est surtout connu pour avoir un certain talent pour le tirage des cartes – il \ntombe d’ailleurs encore plus souvent juste depuis son accident –, et reste très apprécié de sa communauté, \nmême s’il travaille aujourd’hui comme manutentionnaire aux galeries Sabline.\nSubjugué par l’architecture démentielle de la cité lunaire et, surtout, par ses automates, il consacre désor-\nmais chaque minute de son temps libre à errer à travers la ville et à s’en émerveiller. Une manière pour lui \nd’expérimenter ce que son frère ne pourra jamais faire.\nANOMALIE\nTarot \ndivinatoire 2\nTechnique\nLors d’un test d’une Spécialisation de Cœur, Elemiah gagne la pos-\nsibilité de relancer les 2d8 2 fois au cours d’un même scénario, \nmais doit conserver le dernier résultat.\nNarratif\nEn tirant les cartes, Elemiah peut apprendre une information sur \nune personne concernant son passé, son présent ou son futur. \nL’information reste soumise à interprétation.\nExemples : Elemiah cherche à savoir où sera sa cible le lendemain. Il \nveut connaître l’histoire d’un voisin.\n \n",
|
||||||
|
"_asp_text_raw": " \nBLESSURES\nASPECTS\nATTRIBUTS\nENTREGENT\nFORTUNE\nRÊVE\nVISION\nFACTIONS\n Agence Pinkerton\n Police secrète du duc\n Okhrana\n Luna NovaTek\n Société théosophique OTO\n Syndicats clandestins\n Vorovskoy Mir\n Cour des merveilles\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\nNIVEAU\nMALUS\nDURÉE\nAnodin\nAucun\n1 min\nNégligeable\nAucun\n1 min\nDérisoire\n−1\n10 min\nSuperficiel\n−1\n10 min\nLéger\n−2\n30 min\nModéré\n−2\n30 min\nGrave\n−3\nUne journée\nDramatique\nLe personnage est \nhors-fiction\nDigne de confiance (2)\nRobuste (1)\n"
|
||||||
|
},
|
||||||
|
"Nadeja Danilo": {
|
||||||
|
"age": 35,
|
||||||
|
"origine": "JOUEUR",
|
||||||
|
"metier": "Pilote MÉTIER",
|
||||||
|
"faction_label": "Célestopol ORIGINE Pilote MÉTIER",
|
||||||
|
"resistances": {
|
||||||
|
"ame": 0,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 2,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 1,
|
||||||
|
"vision": 1
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 0,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 2,
|
||||||
|
"mobilite": 4,
|
||||||
|
"prouesse": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 0,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 2,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Télékinésie",
|
||||||
|
"niveau": 2
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Résiliente (1)",
|
||||||
|
"Tête froide (2)",
|
||||||
|
"ATTRIBUTS"
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"_bio_text_raw": "NADEJA DANILO : LA PILOTE\nNée à Célestopol de deux parents universitaires, Nadeja ne fut jamais une enfant comme les autres. \nIntrovertie, elle avait du mal à entrer en relation avec les autres. Intriguée par les nombreux livres occu-\npant l’espace familial, elle tourna surtout son esprit vers les étoiles, le lointain. Mars, Cérès, les confins du \nsystème solaire occupaient ses pensées comme une obsession, si bien qu’avec l’âge, elle choisit de devenir \npilote d’aéronef afin de pouvoir caresser l’espoir de rejoindre un jour ces destinations chères à son cœur. \nNadeja intégra l’académie de pilotes de la White Star Line et démontra des aptitudes presque hors norme, à \ntel point que les commandes des vaisseaux semblaient répondre à ses exigences sans qu’elle ait à intervenir. \nLucide, concentrée, apprenant vite, Nadeja décrocha son diplôme sans aucune difficulté et réalisa ainsi la \npremière étape de son rêve.\nC’était avant que la réalité – et la misogynie galopante dans le milieu majoritairement masculin des pilotes \nd’aéronef – ne la rattrape : Nadeja, pourtant bien plus compétente que la plupart de ses collègues, fut sou-\nvent reléguée au poste de copilote. Elle en garde une certaine rancœur et préfère, pour le moment, travailler \npour l’entreprise Columbia. De fait, elle peut paraître sérieuse de prime abord, certains diraient farouche. \nEn réalité, elle ne supporte pas qu’on lui manque de respect.\nANOMALIE\nTélékinésie 2\nTechnique\nLors d’un test d’une Spécialisation de Corps, Nadeja peut relancer \nles 2d8 2 fois au cours d’un même scénario, mais doit conserver le \ndernier résultat.\nNarratif\nDans un rayon de 8 mètres, Nadeja peut déplacer un petit objet \nléger et sans attaches par la pensée sur 4 mètres (n’importe quelle \ndirection) pendant 2 tours.\nExemples : Nadeja peut déplacer une cuillère pour la faire tomber \nd’une table. Elle peut faire léviter un jeu de tarot.\n \n",
|
||||||
|
"_asp_text_raw": " \nBLESSURES\nASPECTS\nATTRIBUTS\nENTREGENT\nFORTUNE\nRÊVE\nVISION\nFACTIONS\n Agence Pinkerton\n Police secrète du duc\n Okhrana\n Luna NovaTek\n Société théosophique OTO\n Syndicats clandestins\n Vorovskoy Mir\n Cour des merveilles\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\nNIVEAU\nMALUS\nDURÉE\nAnodin\nAucun\n1 min\nNégligeable\nAucun\n1 min\nDérisoire\n−1\n10 min\nSuperficiel\n−1\n10 min\nLéger\n−2\n30 min\nModéré\n−2\n30 min\nGrave\n−3\nUne journée\nDramatique\nLe personnage est \nhors-fiction\nRésiliente (1)\nTête froide (2)\n"
|
||||||
|
},
|
||||||
|
"Maribel Vargas": {
|
||||||
|
"age": 25,
|
||||||
|
"origine": "JOUEUR",
|
||||||
|
"metier": "Mécanicienne MÉTIER",
|
||||||
|
"faction_label": "Mexique ORIGINE Mécanicienne MÉTIER",
|
||||||
|
"resistances": {
|
||||||
|
"ame": 0,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 2,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 2,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 2,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 0,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 0,
|
||||||
|
"effacement": 2,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 3,
|
||||||
|
"inspiration": 0,
|
||||||
|
"traque": 2,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 4,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "morts",
|
||||||
|
"niveau": 2
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Déterminée (1)",
|
||||||
|
"Sagace (2)",
|
||||||
|
"ATTRIBUTS"
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"_bio_text_raw": "MARIBEL VARGAS : LA SCIENTIFIQUE\nAncienne mécanicienne ayant travaillé notamment sur les lignes de chemin de fer reliant Célestopol à la \nstation du Pôle Nord.\nMaribel Vargas fut longtemps bridée dans son évolution de carrière à cause de son origine mexicaine et de \nson genre. Or, malgré son jeune âge (vingt-cinq ans), Maribel est une experte dans l’ingénierie de pointe, \nspécialement dans l’utilisation du fameux sélénium occupant les canaux de la ville. Ses parents, aux revenus \nmodestes, firent beaucoup de sacrifices pour lui permettre d’étudier à l’université de Célestopol, dont elle \nest d’ailleurs sortie major de promotion. Pourtant, personne n’avait souhaité lui donner sa chance avant \nErnest, qui tenait à avoir dans son agence une personne capable de réparer Polyphème, mais aussi de com-\nprendre les complexités de la technologie lunaire.\nMaribel subit les moqueries amusées du reste de son équipe, car elle a tendance à parler seule, surtout \nquand elle répare ou examine un mécanisme, quel qu’il soit. Par ailleurs, c’est une véritable amatrice de \npeinture, qui connaît très bien les allées du musée des Beaux-Arts.\nANOMALIE\nCommunication \navec les morts 2\nTechnique\nLors du test d’une Spécialisation d’Esprit, Maribel peut relancer \nles 2d8 2 fois au cours d’un même scénario, mais doit conserver le \ndernier résultat.\nNarratif\nMaribel entre en contact avec l’esprit d’un défunt. Elle peut lui \nposer une question fermée (réponse par oui ou non).\nExemples : Maribel interroge la victime d’un meurtre. Elle consulte \nl’esprit d’un aïeul.\n",
|
||||||
|
"_asp_text_raw": " \nBLESSURES\nASPECTS\nATTRIBUTS\nENTREGENT\nFORTUNE\nRÊVE\nVISION\nFACTIONS\n Agence Pinkerton\n Police secrète du duc\n Okhrana\n Luna NovaTek\n Société théosophique OTO\n Syndicats clandestins\n Vorovskoy Mir\n Cour des merveilles\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\nNIVEAU\nMALUS\nDURÉE\nAnodin\nAucun\n1 min\nNégligeable\nAucun\n1 min\nDérisoire\n−1\n10 min\nSuperficiel\n−1\n10 min\nLéger\n−2\n30 min\nModéré\n−2\n30 min\nGrave\n−3\nUne journée\nDramatique\nLe personnage est \nhors-fiction\nDéterminée (1)\nSagace (2)\n"
|
||||||
|
},
|
||||||
|
"Wiktoria Razny": {
|
||||||
|
"age": 39,
|
||||||
|
"origine": "JOUEUR",
|
||||||
|
"metier": "Soldate MÉTIER",
|
||||||
|
"faction_label": "Pologne ORIGINE Soldate MÉTIER",
|
||||||
|
"resistances": {
|
||||||
|
"ame": 2,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 0,
|
||||||
|
"esprit": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 2,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 0,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 0,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 4,
|
||||||
|
"effacement": 1,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 3,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 2,
|
||||||
|
"traque": 3,
|
||||||
|
"instruction": 1,
|
||||||
|
"merv_tech": 1,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Illusion",
|
||||||
|
"niveau": 2
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Affable (1)",
|
||||||
|
"Grande (1)",
|
||||||
|
"Expérience militaire (1)",
|
||||||
|
"ATTRIBUTS"
|
||||||
|
],
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"_bio_text_raw": "WIKTORIA RAZNY : LA VÉTÉRANE\nWiktoria Raźny était soldate durant la Seconde Guerre de Crimée.\nCette Polonaise de presque quarante ans aujourd’hui prit les armes pour défendre sa patrie en tant que \nconscrite. Blessée à l’œil, qu’elle finit par perdre faute de soins adaptés, elle ne trouva pas le réconfort \ndans l’estime et la reconnaissance que son sacrifice aurait dû lui apporter. Au contraire, son handicap et \nsa dégaine dégingandée lui valurent un certain rejet de ses compatriotes, ce qui la poussa, par dégoût, à \ns’éloigner autant que possible de la Pologne. Et pour cela, quoi de mieux que d’aller sur la Lune ? Sur place, \nelle rencontra Ernest, avec qui elle eut une aventure. Leurs points communs étaient multiples, il faut dire. \nDevenus bons amis, ils commencèrent à travailler ensemble quand le vétéran recruta l’ancienne soldate \ndans son agence du Lys blanc.\nAvec l’âge et le recul, Wiktoria assume parfaitement son handicap et porte un œil de verre. Elle explore les \nsoirées mondaines qu’elle apprécie tant et durant lesquelles elle se fait fort de collecter nombre d’informa-\ntions utiles à l’agence, mêlant ainsi l’utile à l’agréable.\nANOMALIE\nIllusion 2 \nTechnique\nLors d’un test de Coercition, d’Échauffourée, d’Effacement ou de \nTraque, Wiktoria peut relancer les 2d8 2 fois au cours d’un même \nscénario, mais doit conserver le dernier résultat.\nNarratif\nWiktoria peut générer une petite illusion mineure (visuelle, audi-\ntive, olfactive, etc., au choix) sans détail ou précision pendant \n1 minute.\nExemples : Wiktoria peut faire entendre le bruit d’un chat qui miaule \nou d’un livre qui tombe. Elle peut faire apparaître un éclat métallique \nou une ombre fugace. Elle peut créer une odeur de pluie ou de fumée.\n",
|
||||||
|
"_asp_text_raw": " \nBLESSURES\nASPECTS\nATTRIBUTS\nENTREGENT\nFORTUNE\nRÊVE\nVISION\nFACTIONS\n Agence Pinkerton\n Police secrète du duc\n Okhrana\n Luna NovaTek\n Société théosophique OTO\n Syndicats clandestins\n Vorovskoy Mir\n Cour des merveilles\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\n-1\n0\n1\n2\n3\n-2\n4\n-3\n-4\nNIVEAU\nMALUS\nDURÉE\nAnodin\nAucun\n1 min\nNégligeable\nAucun\n1 min\nDérisoire\n−1\n10 min\nSuperficiel\n−1\n10 min\nLéger\n−2\n30 min\nModéré\n−2\n30 min\nGrave\n−3\nUne journée\nDramatique\nLe personnage est \nhors-fiction\nAffable (1)\nGrande (1)\nExpérience militaire (1)\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
669
pretires_v3.json
Normal file
@@ -0,0 +1,669 @@
|
|||||||
|
{
|
||||||
|
"Niels Bielke": {
|
||||||
|
"name": "Niels Bielke",
|
||||||
|
"concept": "L’ARTISTE MAUDIT",
|
||||||
|
"age": 38,
|
||||||
|
"origine": "Suède",
|
||||||
|
"metier": "Acteur",
|
||||||
|
"faction_appartenance": null,
|
||||||
|
"resistances": {
|
||||||
|
"ame": 2,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 4,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 2,
|
||||||
|
"vision": 2
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 2,
|
||||||
|
"attraction": 3,
|
||||||
|
"coercition": 0,
|
||||||
|
"faveur": 3,
|
||||||
|
"echauffouree": 0,
|
||||||
|
"effacement": 0,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 4,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 0,
|
||||||
|
"instruction": 3,
|
||||||
|
"merv_tech": 0,
|
||||||
|
"raisonnement": 2,
|
||||||
|
"traitement": 1
|
||||||
|
},
|
||||||
|
"raffinement_template": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Suggestion",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’une Spécialisation d’Âme, Niels peut relancer les 2d8 2 fois au cours d’un même scénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "Niels est en mesure d’influer sur la prise de décision d’une per- sonne en lui parlant à voix haute et en la fixant dans les yeux. Cette décision doit avoir un impact immédiat.",
|
||||||
|
"exemples": "Un garde choisit finalement de laisser passer Niels. Un prisonnier accepte de lui donner son nom, etc.",
|
||||||
|
"note": "Ce pouvoir fonctionne également sur les automates sophistiqués de 4e et 5e générations."
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Charismatique (2)",
|
||||||
|
"Sensible (1)"
|
||||||
|
],
|
||||||
|
"background": "NIELS BIELKE : L’ARTISTE MAUDIT Niels Bielke connut une carrière fulgurante en jouant sur les planches de tous les théâtres réputés de Suède. Lassé par son succès, il quitta la Terre pour conquérir la Lune. Quelle déception de perdre son statut de vedette ! En effet, la concurrence est rude à Célestopol, où la culture sous toutes ses formes est richement célébrée, notamment à travers les dépenses pha- raoniques du duc Nikolaï. Niels pensait pouvoir se tailler la part du lion, mais c’est son ego qui se retrouva taillé en pièce. Il s’obstina pendant plusieurs années, voyant toutes les portes se fermer devant lui petit à petit. Sombrant dans le mépris de lui-même et l’alcool conjointement, Niels ne connut son salut que grâce à l’intervention d’Ernest, qui sauva littéralement la vie de l’artiste maudit sur le point de se jeter dans le sélénium. Depuis, Niels tente de payer sa dette en servant de son mieux comme agent au sein de l’équipe du Lys blanc et en respectant sa parole de ne plus toucher à l’alcool. Niels porte les stigmates d’un homme ayant commis des abus – en particulier un tic nerveux dont il n’arrive pas à se débarrasser. Malgré ce désagrément et une allure en apparence négligée, il conserve un charisme et une aura certains. Niels approche de la quarantaine.",
|
||||||
|
"description_physique": null,
|
||||||
|
"description_psychologique": null,
|
||||||
|
"weapons": [],
|
||||||
|
"equipment": []
|
||||||
|
},
|
||||||
|
"Polyphème": {
|
||||||
|
"name": "Polyphème",
|
||||||
|
"concept": "LE CHRONIQUEUR",
|
||||||
|
"age": null,
|
||||||
|
"origine": "Inconnue",
|
||||||
|
"metier": "Assistant-archiviste",
|
||||||
|
"faction_appartenance": null,
|
||||||
|
"resistances": {
|
||||||
|
"ame": 0,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 0,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 0,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 3,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 3,
|
||||||
|
"echauffouree": 0,
|
||||||
|
"effacement": 2,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 0,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 0,
|
||||||
|
"traque": 3,
|
||||||
|
"instruction": 4,
|
||||||
|
"merv_tech": 3,
|
||||||
|
"raisonnement": 3,
|
||||||
|
"traitement": 0
|
||||||
|
},
|
||||||
|
"raffinement_template": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Voyage astral",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’Appréciation, de Merveilleux technologique, de Traitement ou de Traque, Polyphème peut relancer les 2d8 2 fois au cours d’un même scénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "L’esprit de Polpyphème quitte son enveloppe corporelle et se déplace de 8 mètres par tour pendant 4 tours, dans n’importe quelle direction. De cette manière, l’esprit est invisible et peut ignorer tous les obstacles. Ses sens restent toutefois les mêmes.",
|
||||||
|
"exemples": "Polyphème accède aux toits d’une maison ou il peut aller en repérage dans la pièce voisine. Si un éboulis bloque le passage d’un tunnel, Polyphème peut voir sur quelle distance et s’il y a des survivants.",
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Difficile à lire (2)",
|
||||||
|
"Étrangeté (2)",
|
||||||
|
"Mémoire eidétique (2)",
|
||||||
|
"Vision aiguisée (1)"
|
||||||
|
],
|
||||||
|
"background": "POLYPHÈME : LE CHRONIQUEUR Automate de 4e génération émancipé par Ernest après que le Français le retrouva dans un piteux état au fond d’une impasse. L’attention du vétéran fut attirée par l’être artificiel alors qu’un groupe d’étudiants sortaient hilares de la ruelle sombre. L’automate avait manifestement été passé à tabac. Après plusieurs semaines durant lesquelles personne n’en revendiqua la propriété, Ernest prit sur lui de payer ses réparations. Malgré sa remise en état de marche, l’automate semblait ne pas conserver de souvenir de sa « vie » passée. C’est ainsi qu’il entra au service d’Ernest en tant qu’assistant-archiviste au sein de l’agence du Lys blanc. Baptisé Polyphème, un nom grec en référence à Ajax, l’automate le plus célèbre de la Cité, l’archiviste est désormais la propriété d’Ernest. Dans les faits, l’automate est un membre à part entière de l’agence, parfaitement intégré au sein de son équipe. Malgré tout, depuis son agression, Polyphème se plaint de ne pas avoir le « bon visage », ce dont il peut parfois faire une obsession.",
|
||||||
|
"description_physique": null,
|
||||||
|
"description_psychologique": null,
|
||||||
|
"weapons": [],
|
||||||
|
"equipment": []
|
||||||
|
},
|
||||||
|
"Sèdami Alassane": {
|
||||||
|
"name": "Sèdami Alassane",
|
||||||
|
"concept": "LA DIPLOMATE",
|
||||||
|
"age": 58,
|
||||||
|
"origine": "Dahomey",
|
||||||
|
"metier": "Guerrière / diplomate",
|
||||||
|
"faction_appartenance": null,
|
||||||
|
"resistances": {
|
||||||
|
"ame": 4,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 0,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 1,
|
||||||
|
"fortune": 2,
|
||||||
|
"reve": 1,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 1,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 4,
|
||||||
|
"faveur": 3,
|
||||||
|
"echauffouree": 3,
|
||||||
|
"effacement": 1,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 1,
|
||||||
|
"traque": 2,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 0,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement_template": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Télépathie",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’Appréciation, d’Attraction, d’Échauffourée ou de Faveur, Sèdami peut relancer les 2d8 2 fois au cours d’un même scénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "Sèdami est capable de percevoir les pensées superficielles d’un tiers. De cette manière, elle peut comprendre l’état émotionnel d’une per- sonne, voire capter une image ou un mot (à l’appréciation du narra- teur) dans son esprit, simplement en l’observant.",
|
||||||
|
"exemples": "Sèdami suspecte une tentative de meurtre et perçoit un flacon de cyanure dans l’esprit d’un domestique. Malgré un faciès contenu, elle saisit qu’un magistrat est en fait terrorisé.",
|
||||||
|
"note": "Ce pouvoir fonctionne également sur les automates sophistiqués de 4e et 5e générations."
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Comportementaliste (1)",
|
||||||
|
"Stratège (2)"
|
||||||
|
],
|
||||||
|
"background": "SÈDAMI ALASSANE : LA DIPLOMATE Formée au sein du redoutable bataillon des Mino, ces guerrières d’élite du royaume de Dahomey, Sèdami s’illustra au combat mais également en stratégie, ce qui lui permit rapidement de devenir officière parmi les « amazones du Dahomey », comme on les surnomme souvent. Toutefois, son esprit militaire cohabitait toujours avec une certaine rêverie tournée vers la Lune et sa ville utopique, Célestopol. Longtemps, elle espéra secrètement parcourir ses rues, obser- ver ses canaux, ressentir son ambiance slave unique… Possédant une intelligence interpersonnelle très développée, Sèdami devint alors diplomate et fut à plusieurs reprises assignée à des missions protocolaires sur le continent africain puis en Europe et au Moyen-Orient. Il y a cinq ans, elle eut l’occa- sion d’exaucer son rêve en tant que conseillère du roi Béhanzin au cours d’une visite de courtoisie au duc Nikolaï, à Célestopol. Totalement subjuguée par la cité sélène et marquée dans son âme par le sélénium, Sèdami ne l’a plus jamais quittée, abandonnant son rôle de diplomate du Dahomey. Quoique son Afrique natale lui manque sou- vent, elle ne reviendra pas sur sa décision de vivre désormais sur la Lune. Sèdami est une femme d’âge mûr, confiante en ses capacités, avec un esprit très ouvert.",
|
||||||
|
"description_physique": null,
|
||||||
|
"description_psychologique": null,
|
||||||
|
"weapons": [],
|
||||||
|
"equipment": []
|
||||||
|
},
|
||||||
|
"Bao Wang": {
|
||||||
|
"name": "Bao Wang",
|
||||||
|
"concept": "L’ESCROC",
|
||||||
|
"age": 32,
|
||||||
|
"origine": "Chine",
|
||||||
|
"metier": "Videur",
|
||||||
|
"faction_appartenance": null,
|
||||||
|
"resistances": {
|
||||||
|
"ame": 2,
|
||||||
|
"corps": 4,
|
||||||
|
"coeur": 0,
|
||||||
|
"esprit": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 2,
|
||||||
|
"reve": 2,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 4,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 4,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 0,
|
||||||
|
"appreciation": 2,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 1,
|
||||||
|
"traque": 3,
|
||||||
|
"instruction": 0,
|
||||||
|
"merv_tech": 2,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement_template": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Entropie",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Bao peut relancer le dé de la Lune 1 fois au cours d’un même scénario et choisir de conserver le résultat qu’il préfère (cette faculté ne fonctionne pas pour les tests de chance).",
|
||||||
|
"narratif": "Bao peut influer sur le hasard de manière mineure.",
|
||||||
|
"exemples": "En jouant au poker, Bao récupère une bonne main à la distribution. Le feu passe au vert alors qu’il tourne au coin de la rue.",
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Aime le jeu (1)",
|
||||||
|
"Belle gueule (2)"
|
||||||
|
],
|
||||||
|
"background": "BAO WANG : L’ESCROC Ancien employé du casino flottant La Libellule, où il officiait comme videur. Après une adolescence de petite frappe dans les faubourgs de Shanghai, il parvint à s’introduire dans un obus-traversier pour rejoindre Célestopol, autant attiré par les perspectives offertes par la cité lunaire que par le besoin de se faire oublier des services de police de sa ville natale. Problème : Bao répugne à se battre et préfère largement la finesse à la violence. De son ancienne vie, il conserve un goût prononcé pour le jeu et la gent féminine. Par ailleurs, Bao a également un certain pen- chant pour les cabarets clandestins, dans lesquels il aime à se travestir à l’occasion. C’est dans l’un de ces lieux interlopes de la cité que le jeune chinois rencontra Ernest, le directeur de l’agence de détectives du Lys blanc. Le vétéran sut voir un certain potentiel chez Bao et les deux hommes sympathisèrent. Bao, passable- ment désœuvré et fauché, accepta, sans trop y croire, d’intégrer l’agence en tant que détective. Son sens de la débrouillardise et son passé houleux parfois bien utile lui octroient des compétences uniques et font désormais de lui un agent indispensable. Le jeune homme s’investit d’ailleurs sincèrement dans son travail. Toujours tiré à quatre épingles, Bao est reconnu parmi ses coéquipiers comme le plus nonchalant du groupe.",
|
||||||
|
"description_physique": null,
|
||||||
|
"description_psychologique": null,
|
||||||
|
"weapons": [],
|
||||||
|
"equipment": []
|
||||||
|
},
|
||||||
|
"Elemiah Cowen": {
|
||||||
|
"name": "Elemiah Cowen",
|
||||||
|
"concept": "L’OUVRIER",
|
||||||
|
"age": 28,
|
||||||
|
"origine": "Célestopol",
|
||||||
|
"metier": "Manutentionnaire",
|
||||||
|
"faction_appartenance": null,
|
||||||
|
"resistances": {
|
||||||
|
"ame": 0,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 4,
|
||||||
|
"esprit": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 1,
|
||||||
|
"fortune": 1,
|
||||||
|
"reve": 1,
|
||||||
|
"vision": 1
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 2,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 0,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 4,
|
||||||
|
"appreciation": 4,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 4,
|
||||||
|
"traque": 0,
|
||||||
|
"instruction": 0,
|
||||||
|
"merv_tech": 1,
|
||||||
|
"raisonnement": 1,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement_template": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Tarot divinatoire",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’une Spécialisation de Cœur, Elemiah gagne la pos- sibilité de relancer les 2d8 2 fois au cours d’un même scénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "En tirant les cartes, Elemiah peut apprendre une information sur une personne concernant son passé, son présent ou son futur. L’information reste soumise à interprétation.",
|
||||||
|
"exemples": "Elemiah cherche à savoir où sera sa cible le lendemain. Il veut connaître l’histoire d’un voisin.",
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Digne de confiance (2)",
|
||||||
|
"Robuste (1)"
|
||||||
|
],
|
||||||
|
"background": "ELEMIAH COWEN : L’OUVRIER Elemiah et son frère Rubben travaillèrent toute leur vie comme des forçats dans les usines souterraines de Célestopol. Comme des dizaines de milliers d’ouvriers vivant dans des conditions très difficiles, ils apprirent à se serrer les coudes de manière à soutenir leur communauté. Un jour, un des nombreux acci- dents quotidiens liés à un dysfonctionnement d’une machine emporta Rubben et priva Elemiah de deux doigts de sa main gauche. Depuis, l’ouvrier survivant s’est promis de tout faire pour aider les miséreux de la ville. Il connaît donc bien la face cachée de Célestopol et en garde un goût amer. Dur à la tâche, du genre taiseux, mais toujours le premier à proposer son aide et son soutien, cet ancien ouvrier d’une trentaine d’années a le cœur tendre. Son torse large, ses sourcils broussailleux et sa moustache travaillée impressionnent souvent. Mais il est surtout connu pour avoir un certain talent pour le tirage des cartes – il tombe d’ailleurs encore plus souvent juste depuis son accident –, et reste très apprécié de sa communauté, même s’il travaille aujourd’hui comme manutentionnaire aux galeries Sabline. Subjugué par l’architecture démentielle de la cité lunaire et, surtout, par ses automates, il consacre désor- mais chaque minute de son temps libre à errer à travers la ville et à s’en émerveiller. Une manière pour lui d’expérimenter ce que son frère ne pourra jamais faire.",
|
||||||
|
"description_physique": null,
|
||||||
|
"description_psychologique": null,
|
||||||
|
"weapons": [],
|
||||||
|
"equipment": []
|
||||||
|
},
|
||||||
|
"Nadeja Danilo": {
|
||||||
|
"name": "Nadeja Danilo",
|
||||||
|
"concept": "LA PILOTE",
|
||||||
|
"age": 35,
|
||||||
|
"origine": "Célestopol",
|
||||||
|
"metier": "Pilote",
|
||||||
|
"faction_appartenance": null,
|
||||||
|
"resistances": {
|
||||||
|
"ame": 0,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 2,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 1,
|
||||||
|
"vision": 2
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 0,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 2,
|
||||||
|
"effacement": 2,
|
||||||
|
"mobilite": 4,
|
||||||
|
"prouesse": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 0,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 2,
|
||||||
|
"raisonnement": 2,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement_template": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Télékinésie",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test d’une Spécialisation de Corps, Nadeja peut relancer les 2d8 2 fois au cours d’un même scénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "Dans un rayon de 8 mètres, Nadeja peut déplacer un petit objet léger et sans attaches par la pensée sur 4 mètres (n’importe quelle direction) pendant 2 tours.",
|
||||||
|
"exemples": "Nadeja peut déplacer une cuillère pour la faire tomber d’une table. Elle peut faire léviter un jeu de tarot.",
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Résiliente (1)",
|
||||||
|
"Tête froide (2)"
|
||||||
|
],
|
||||||
|
"background": "NADEJA DANILO : LA PILOTE Née à Célestopol de deux parents universitaires, Nadeja ne fut jamais une enfant comme les autres. Introvertie, elle avait du mal à entrer en relation avec les autres. Intriguée par les nombreux livres occu- pant l’espace familial, elle tourna surtout son esprit vers les étoiles, le lointain. Mars, Cérès, les confins du système solaire occupaient ses pensées comme une obsession, si bien qu’avec l’âge, elle choisit de devenir pilote d’aéronef afin de pouvoir caresser l’espoir de rejoindre un jour ces destinations chères à son cœur. Nadeja intégra l’académie de pilotes de la White Star Line et démontra des aptitudes presque hors norme, à tel point que les commandes des vaisseaux semblaient répondre à ses exigences sans qu’elle ait à intervenir. Lucide, concentrée, apprenant vite, Nadeja décrocha son diplôme sans aucune difficulté et réalisa ainsi la première étape de son rêve. C’était avant que la réalité – et la misogynie galopante dans le milieu majoritairement masculin des pilotes d’aéronef – ne la rattrape : Nadeja, pourtant bien plus compétente que la plupart de ses collègues, fut sou- vent reléguée au poste de copilote. Elle en garde une certaine rancœur et préfère, pour le moment, travailler pour l’entreprise Columbia. De fait, elle peut paraître sérieuse de prime abord, certains diraient farouche. En réalité, elle ne supporte pas qu’on lui manque de respect.",
|
||||||
|
"description_physique": null,
|
||||||
|
"description_psychologique": null,
|
||||||
|
"weapons": [],
|
||||||
|
"equipment": []
|
||||||
|
},
|
||||||
|
"Maribel Vargas": {
|
||||||
|
"name": "Maribel Vargas",
|
||||||
|
"concept": "LA SCIENTIFIQUE",
|
||||||
|
"age": 25,
|
||||||
|
"origine": "Mexique",
|
||||||
|
"metier": "Mécanicienne",
|
||||||
|
"faction_appartenance": null,
|
||||||
|
"resistances": {
|
||||||
|
"ame": 0,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 2,
|
||||||
|
"esprit": 2
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 0,
|
||||||
|
"fortune": 0,
|
||||||
|
"reve": 2,
|
||||||
|
"vision": 2
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 2,
|
||||||
|
"attraction": 0,
|
||||||
|
"coercition": 0,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 0,
|
||||||
|
"effacement": 2,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 2,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 3,
|
||||||
|
"inspiration": 0,
|
||||||
|
"traque": 2,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 4,
|
||||||
|
"raisonnement": 2,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement_template": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Communication avec les morts",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors du test d’une Spécialisation d’Esprit, Maribel peut relancer les 2d8 2 fois au cours d’un même scénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "Maribel entre en contact avec l’esprit d’un défunt. Elle peut lui poser une question fermée (réponse par oui ou non).",
|
||||||
|
"exemples": "Maribel interroge la victime d’un meurtre. Elle consulte l’esprit d’un aïeul.",
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Déterminée (1)",
|
||||||
|
"Sagace (2)"
|
||||||
|
],
|
||||||
|
"background": "MARIBEL VARGAS : LA SCIENTIFIQUE Ancienne mécanicienne ayant travaillé notamment sur les lignes de chemin de fer reliant Célestopol à la station du Pôle Nord. Maribel Vargas fut longtemps bridée dans son évolution de carrière à cause de son origine mexicaine et de son genre. Or, malgré son jeune âge (vingt-cinq ans), Maribel est une experte dans l’ingénierie de pointe, spécialement dans l’utilisation du fameux sélénium occupant les canaux de la ville. Ses parents, aux revenus modestes, firent beaucoup de sacrifices pour lui permettre d’étudier à l’université de Célestopol, dont elle est d’ailleurs sortie major de promotion. Pourtant, personne n’avait souhaité lui donner sa chance avant Ernest, qui tenait à avoir dans son agence une personne capable de réparer Polyphème, mais aussi de com- prendre les complexités de la technologie lunaire. Maribel subit les moqueries amusées du reste de son équipe, car elle a tendance à parler seule, surtout quand elle répare ou examine un mécanisme, quel qu’il soit. Par ailleurs, c’est une véritable amatrice de peinture, qui connaît très bien les allées du musée des Beaux-Arts.",
|
||||||
|
"description_physique": null,
|
||||||
|
"description_psychologique": null,
|
||||||
|
"weapons": [],
|
||||||
|
"equipment": []
|
||||||
|
},
|
||||||
|
"Wiktoria Razny": {
|
||||||
|
"name": "Wiktoria Razny",
|
||||||
|
"concept": "LA VÉTÉRANE",
|
||||||
|
"age": 39,
|
||||||
|
"origine": "Pologne",
|
||||||
|
"metier": "Soldate",
|
||||||
|
"faction_appartenance": null,
|
||||||
|
"resistances": {
|
||||||
|
"ame": 2,
|
||||||
|
"corps": 2,
|
||||||
|
"coeur": 0,
|
||||||
|
"esprit": 0
|
||||||
|
},
|
||||||
|
"attributs": {
|
||||||
|
"entregent": 2,
|
||||||
|
"fortune": 2,
|
||||||
|
"reve": 0,
|
||||||
|
"vision": 0
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"artifice": 0,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 2,
|
||||||
|
"faveur": 2,
|
||||||
|
"echauffouree": 4,
|
||||||
|
"effacement": 1,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 3,
|
||||||
|
"appreciation": 3,
|
||||||
|
"arts": 0,
|
||||||
|
"inspiration": 2,
|
||||||
|
"traque": 3,
|
||||||
|
"instruction": 1,
|
||||||
|
"merv_tech": 1,
|
||||||
|
"raisonnement": 0,
|
||||||
|
"traitement": 2
|
||||||
|
},
|
||||||
|
"raffinement_template": {
|
||||||
|
"artifice": 5,
|
||||||
|
"attraction": 2,
|
||||||
|
"coercition": 3,
|
||||||
|
"faveur": 6,
|
||||||
|
"echauffouree": 6,
|
||||||
|
"effacement": 3,
|
||||||
|
"mobilite": 2,
|
||||||
|
"prouesse": 5,
|
||||||
|
"appreciation": 6,
|
||||||
|
"arts": 2,
|
||||||
|
"inspiration": 3,
|
||||||
|
"traque": 5,
|
||||||
|
"instruction": 2,
|
||||||
|
"merv_tech": 6,
|
||||||
|
"raisonnement": 5,
|
||||||
|
"traitement": 3
|
||||||
|
},
|
||||||
|
"factions": {
|
||||||
|
"pinkerton": 0,
|
||||||
|
"police_duc": 0,
|
||||||
|
"okhrana": 0,
|
||||||
|
"luna_novatek": 0,
|
||||||
|
"oto": 0,
|
||||||
|
"syndicats": 0,
|
||||||
|
"vorovskoy": 0,
|
||||||
|
"cour_merveilles": 0
|
||||||
|
},
|
||||||
|
"anomalie": {
|
||||||
|
"nom": "Illusion",
|
||||||
|
"niveau": 2,
|
||||||
|
"technique": "Lors d’un test de Coercition, d’Échauffourée, d’Effacement ou de Traque, Wiktoria peut relancer les 2d8 2 fois au cours d’un même scénario, mais doit conserver le dernier résultat.",
|
||||||
|
"narratif": "Wiktoria peut générer une petite illusion mineure (visuelle, audi- tive, olfactive, etc., au choix) sans détail ou précision pendant 1 minute.",
|
||||||
|
"exemples": "Wiktoria peut faire entendre le bruit d’un chat qui miaule ou d’un livre qui tombe. Elle peut faire apparaître un éclat métallique ou une ombre fugace. Elle peut créer une odeur de pluie ou de fumée.",
|
||||||
|
"note": null
|
||||||
|
},
|
||||||
|
"aspects": [
|
||||||
|
"Affable (1)",
|
||||||
|
"Grande (1)",
|
||||||
|
"Expérience militaire (1)"
|
||||||
|
],
|
||||||
|
"background": "WIKTORIA RAZNY : LA VÉTÉRANE Wiktoria Raźny était soldate durant la Seconde Guerre de Crimée. Cette Polonaise de presque quarante ans aujourd’hui prit les armes pour défendre sa patrie en tant que conscrite. Blessée à l’œil, qu’elle finit par perdre faute de soins adaptés, elle ne trouva pas le réconfort dans l’estime et la reconnaissance que son sacrifice aurait dû lui apporter. Au contraire, son handicap et sa dégaine dégingandée lui valurent un certain rejet de ses compatriotes, ce qui la poussa, par dégoût, à s’éloigner autant que possible de la Pologne. Et pour cela, quoi de mieux que d’aller sur la Lune ? Sur place, elle rencontra Ernest, avec qui elle eut une aventure. Leurs points communs étaient multiples, il faut dire. Devenus bons amis, ils commencèrent à travailler ensemble quand le vétéran recruta l’ancienne soldate dans son agence du Lys blanc. Avec l’âge et le recul, Wiktoria assume parfaitement son handicap et porte un œil de verre. Elle explore les soirées mondaines qu’elle apprécie tant et durant lesquelles elle se fait fort de collecter nombre d’informa- tions utiles à l’agence, mêlant ainsi l’utile à l’agréable.",
|
||||||
|
"description_physique": null,
|
||||||
|
"description_psychologique": null,
|
||||||
|
"weapons": [],
|
||||||
|
"equipment": []
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
renders/aspects-page-12.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/aspects-page-16.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/aspects-page-20.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/aspects-page-24.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/aspects-page-28.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/aspects-page-32.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/aspects-page-4.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/aspects-page-8.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/debug_ENTREGENT.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
renders/debug_FORTUNE.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
renders/debug_REVE.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
renders/debug_VISION.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
renders/debug_aspects_left.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
renders/debug_attribut.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
renders/debug_attribut_enlarged.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
renders/page-11.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/page-15.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/page-19.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/page-23.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
renders/page-27.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
renders/page-3.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |