16 Commits

Author SHA1 Message Date
uberwald a99eeaccba Corrections sur font, again v3
Release Creation / build (release) Successful in 1m35s
2026-04-27 17:40:59 +02:00
uberwald 2ab380786f Correction sur O dans police de titre, step2
Release Creation / build (release) Successful in 1m12s
2026-04-27 06:39:11 +02:00
uberwald c3ce628e24 Ameliorations CSS et font diverses
Release Creation / build (release) Successful in 1m25s
2026-04-26 22:44:23 +02:00
uberwald 8e5fb9aca1 Correction sur jauge de destin
Release Creation / build (release) Successful in 1m26s
2026-04-16 22:26:44 +02:00
uberwald 1b2a74969d Update READM.md 2026-04-16 08:39:52 +02:00
uberwald 98a6a41078 Ajout README.md 2026-04-15 11:53:52 +02:00
uberwald c73136b3c9 Ajout README.md 2026-04-15 11:41:44 +02:00
uberwald 49996104ce Roll and styles update
Release Creation / build (release) Failing after 1m17s
2026-04-15 02:16:51 +02:00
uberwald b3cf0b0aa1 Corrections après tests de combat
Release Creation / build (release) Failing after 1m34s
2026-04-14 18:59:09 +02:00
uberwald 63c0153860 FIx v13/v14
Release Creation / build (release) Failing after 1m17s
2026-04-14 00:56:25 +02:00
uberwald 8bfbdedf43 Upgrade compat 2026-04-13 17:33:06 +02:00
uberwald 6d2fca9fc2 Cleanup 2026-04-13 16:34:38 +02:00
uberwald b26ce2f114 Diverses corrections autour du combat 2026-04-13 16:23:31 +02:00
uberwald 8b75a15e3f Diverses corrections autour du combat
Release Creation / build (release) Failing after 1m5s
2026-04-13 15:55:40 +02:00
uberwald d69144f506 Corrections diverses autout du combat 2026-04-13 14:19:24 +02:00
uberwald 44cc07db73 Portraits et corrections sur valeurs des PNJ
Release Creation / build (release) Failing after 1m24s
2026-04-12 11:52:17 +02:00
123 changed files with 6558 additions and 700 deletions
+1 -1
View File
@@ -68,4 +68,4 @@ jobs:
manifest: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/latest/system.json' manifest: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/latest/system.json'
notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-celestopol-${{github.event.release.tag_name}}.zip' notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-celestopol-${{github.event.release.tag_name}}.zip'
compatibility-minimum: '13' compatibility-minimum: '13'
compatibility-verified: '13' compatibility-verified: '14'
-87
View File
@@ -1,87 +0,0 @@
# Copilot Instructions — fvtt-celestopol
## Project Overview
This is a **Foundry VTT system** for **Célestopol 1922**, a French tabletop RPG set in an art-deco 1922 universe. The project targets FoundryVTT v13+ and is developed in French.
The reference rulebooks are in `__regles/` (gitignored):
- *Célestopol 1922 Livre de base* — core rulebook
- *Célestopol 1922 Fiches de prêts à jouer* — pre-generated character sheets
## Architecture
This system uses **FoundryVTT v13 DataModels + ApplicationV2** — NOT the legacy template.json / AppV1 approach.
```
fvtt-celestopol.mjs # Main entry point (Hooks.once("init"))
module/
config/system.mjs # All game constants (SYSTEM export)
models/ # TypeDataModel subclasses (character, npc, items)
documents/ # Actor, Item, ChatMessage, Roll wrappers
applications/sheets/ # AppV2 sheets (HandlebarsApplicationMixin)
lang/fr.json # French i18n (key prefix: CELESTOPOL.*)
styles/ # LESS source files
css/ # Compiled CSS (via gulp)
templates/ # Handlebars (.hbs) templates
assets/fonts/ # CopaseticNF art-deco font
assets/ui/ # Background images
assets/icons/ # Item icons
packs-system/ # Source files for compendium packs
```
## DataModels (no template.json)
- Extend `foundry.abstract.TypeDataModel`
- Schema in `static defineSchema()` using `foundry.data.fields.*`
- `prepareDerivedData()` for computed values
- Files: `module/models/character.mjs`, `npc.mjs`, `items.mjs`
## ApplicationV2 / Sheets
- Actor sheets: `HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2)`
- Item sheets: `HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2)`
- `static DEFAULT_OPTIONS` for config; `static PARTS` for templates
- `_prepareContext()` for base context; `_preparePartContext(partId, context)` for per-tab
- Edit/Play mode toggle via `_sheetMode` + `isPlayMode`/`isEditMode` getters
- Actions: `static #onXxx(event, target)` private static methods in `DEFAULT_OPTIONS.actions`
- `form: { submitOnChange: true }` enables live saving
## Roll Mechanics
- Pool of d6 dice: `nbDice = max(1, skillValue + woundMalus)`
- Formula: `{n}d6 [+ moonBonus + modifier]`
- Moon phase bonus: Nouvelle Lune=0, Croissants=+1, Gibbeuse=+2, Pleine Lune=+3
- Compare total vs difficulty threshold (normal=7)
- Wound malus: levels 1-2=0, 3-4=-1, 5-6=-2, 7=-3, 8=-999 (out)
- DialogV2 for roll configuration: `foundry.applications.api.DialogV2.wait(...)`
## Game Data (4 stats × 4 skills)
- **Âme**: Artifice, Attraction, Coercition, Faveur
- **Corps**: Échauffourée, Effacement, Mobilité, Prouesse
- **Cœur**: Appréciation, Arts, Inspiration, Traque
- **Esprit**: Instruction, Merveilleux technologique, Raisonnement, Traitement
**Tracks**: Blessures (8 niveaux), Destin (8), Spleen (8)
**Anomalies**: 9 types (none + 8)
**Factions**: 8 standard + 2 custom
## Build
```bash
npm install # Install dev deps
npx gulp css # Compile LESS → css/fvtt-celestopol.css (once)
npx gulp # Compile + watch
```
## Visual Style
- Font: **CopaseticNF** (Regular + Bold, in `assets/fonts/`) — art-deco style
- Header bg color: `rgb(12, 76, 12)` (dark green) with orange text (`#e07b00`)
- Sheet header texture: `assets/ui/fond_cadrille.jpg`
- CSS variables: `--cel-green`, `--cel-orange`, `--cel-font-title`, etc.
## Language
All in-game text, labels, and code comments should be in **French**. Code identifiers may be English. All i18n keys use the `CELESTOPOL.*` prefix (see `lang/fr.json`).
+1
View File
@@ -13,3 +13,4 @@ css/*.css
# Règles (PDFs privés) # Règles (PDFs privés)
__regles/ __regles/
*.pdf *.pdf
.github/
+84
View File
@@ -0,0 +1,84 @@
# Célestopol 1922 — Système FoundryVTT
Système [Foundry VTT](https://foundryvtt.com) pour **Célestopol 1922**, le jeu de rôle d'[Antre Monde Éditions](https://antremonde.fr).
Copyright 2025-2026 Antre Monde Editions All rights reserved
Celestopol 1922 is a game written by Emmanuel Chastellière and Julien Arnaud. The authors retain their moral rights to this work in both print and digital formats.
This system for FoundryVTT has been approved and authorized by Antre-Monde Edition
---
## ✨ Fonctionnalités
### Personnages joueurs (PJ)
- **4 attributs** (Âme, Corps, Cœur, Esprit) × **4 compétences** chacun (16 compétences au total)
- Gestion des **Blessures** (8 niveaux avec malus automatique aux jets)
- Suivi du **Destin** et du **Spleen** (jauges à 8 crans)
- **Anomalies** : 9 types, déclenchées sur certains résultats de dé
- **Factions** : 8 factions standard + 2 personnalisées, avec aspects liés
- **Équipement** : armes, armures, équipements génériques
- Onglets dédiés : Principal · Compétences · Factions · Équipement · Combat · Biographie
### Personnages non-joueurs (PNJ)
- Fiche simplifiée : compétences, blessures, équipement, biographie
### Items
- **Arme** : mêlée ou à distance, modificateurs de portée
- **Armure** : protection et malus éventuels
- **Aspect de faction** : lié à une faction, bonus / usage limité
- **Anomalie** : type, usages, description
- **Équipement** générique
### Mécanique de jet de désde démonstration
- **Pool de d6** : `max(1, compétence + malus_blessures)` dés
- **Bonus Phase de Lune** : automatique selon la phase en cours (0 à +3)
- **Modificateurs** : difficulté et bonus libres via DialogV2
- Seuil de succès standard : **7**
- Résultats spéciaux : **Brio** (+destin), **Contrecoup** (destin), **Triomphe** (+anomalie/spleen), **Catastrophe** (anomalie/+spleen)
### Dé de la Lune
- Jet de dé de lune indépendant (menu contextuel du token)
- 8 faces : Nouvelle Lune, Premiers Croissants, Dernier Croissant, Gibbeuses ×2, Pleine Lune, Lune de Feu, Lune Noire
- Sur certains résultats : **choix de contrepartie** via pop-up (perte d'anomalie, spleen, ou échec catastrophique)
- Carte de résultat avec boutons d'application directe
### Combat
- **Initiative Célestopol** : jet d'initiative avec dé spécial, insertion dans le tracker
- Attaque avec calcul automatique dommages / protection
- Chat de combat avec récapitulatif des dégâts et blessures appliqués
### Compendiums inclus
| Pack | Contenu |
| ------------ | ------------------------------ |
| Anomalies | Les 8 anomalies du jeu de base |
| Prétirés | 8 personnages prêts à jouer |
| Aides de jeu | Aide du système |
| Scènes | Scènes utiles |
## 🎨 Charte graphique
- Police de titre : **CopaseticNF** (Regular + Bold)
- Vert sombre en-tête : `#0c4c0c`
- Orange texte : `#e07b00`
- Fond parchemin : `#f0e8d4`
- Texture en-tête : `assets/ui/fond_cadrille.jpg`
---
## 👤 Auteur
**LeRatierBretonnien / Uberwald**
- Discord : `LeRatierBretonnien`
- Site : [uberwald.me](https://www.uberwald.me)
---
## 📜 Licence
Copyright 2025 Antre Monde Editions All rights reserved
Celestopol 1922 is a game written by Emmanuel Chastellière and Julien Arnaud. The authors retain their moral rights to this work in both print and digital formats.
This system for FoundryVTT has been approved and authorized by Antre-Monde Edition.
+228
View 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": "Lescroc",
"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 lagence 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 quil préfère.</p><p>Cette capacité ne sapplique 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
View 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": "Louvrier",
"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 dune trentaine dannées, Elemiah impressionne par son torse large, ses sourcils broussailleux, sa moustache travaillée et labsence de deux doigts à la main gauche.</p>",
"descriptionPsychologique": "<p>Après la mort de son frère Rubben dans un accident industriel, Elemiah sest juré de soutenir les miséreux de Célestopol.</p><p>Taiseux mais profondément solidaire, il s’émerveille désormais de larchitecture 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
View 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 lallure vive et appliquée de celles qui pensent plus vite quelles ne parlent.</p>",
"descriptionPsychologique": "<p>Mécanicienne brillante et major de promotion de luniversité de Célestopol, Maribel sest spécialisée dans lingé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 dErnest, 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
View 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 dabord limage dune femme sérieuse et farouche, toujours parfaitement concentrée.</p>",
"descriptionPsychologique": "<p>Née à Célestopol dans une famille duniversitaires, Nadeja sest 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
View 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": "LArtiste 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 lalcool.</p><p>Sauvé par Ernest alors quil é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 à lalcool.</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
View 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 dune impasse, Polyphème a été réparé puis intégré à lagence 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 labsence 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
View 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 dun esprit très ouvert.</p>",
"descriptionPsychologique": "<p>Ancienne officière des Mino du Dahomey, Sèdami sest illustrée au combat comme en stratégie avant de devenir diplomate.</p><p>Fascinée par Célestopol lors dune 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
View 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 dErnest 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": {}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

+154
View 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")
+210 -12
View File
@@ -39,7 +39,9 @@ import {
} from "./module/applications/_module.mjs" } from "./module/applications/_module.mjs"
const DAMAGE_APPLICATION_FLAG = "damageApplication" const DAMAGE_APPLICATION_FLAG = "damageApplication"
const MOON_EFFECT_FLAG = "moonEffectApplied"
const FACTION_ASPECT_STATE_SETTING = "factionAspectState" const FACTION_ASPECT_STATE_SETTING = "factionAspectState"
const PREGENS_IMPORTED_SETTING = "pregensImported"
const WELCOME_SCENE_IMPORTED_SETTING = "welcomeSceneImported" const WELCOME_SCENE_IMPORTED_SETTING = "welcomeSceneImported"
/* ─── Init hook ──────────────────────────────────────────────────────────── */ /* ─── Init hook ──────────────────────────────────────────────────────────── */
@@ -172,6 +174,9 @@ Hooks.once("ready", async () => {
if (foundry.utils.hasProperty(changed, `flags.${SYSTEM_ID}.${DAMAGE_APPLICATION_FLAG}`)) { if (foundry.utils.hasProperty(changed, `flags.${SYSTEM_ID}.${DAMAGE_APPLICATION_FLAG}`)) {
_updateRenderedChatMessageState(message) _updateRenderedChatMessageState(message)
} }
if (foundry.utils.hasProperty(changed, `flags.${SYSTEM_ID}.${MOON_EFFECT_FLAG}`)) {
_updateRenderedMoonEffectState(message)
}
}) })
_activateExistingChatCards() _activateExistingChatCards()
@@ -179,7 +184,9 @@ Hooks.once("ready", async () => {
if (game.user.isGM) { if (game.user.isGM) {
_migrateObsoleteItems() _migrateObsoleteItems()
_migrateIntegerTracks() _migrateIntegerTracks()
_setupAnomaliesFolder() _migrateBiographyFields()
await _setupAnomaliesFolder()
await _setupPregensFolder()
await _setupWelcomeScene() await _setupWelcomeScene()
} }
@@ -271,6 +278,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() {
@@ -347,6 +379,12 @@ 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, { game.settings.register(SYSTEM_ID, FACTION_ASPECT_STATE_SETTING, {
scope: "world", scope: "world",
config: false, config: false,
@@ -428,6 +466,34 @@ async function _setupWelcomeScene() {
await game.settings.set(SYSTEM_ID, WELCOME_SCENE_IMPORTED_SETTING, true) 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 ───────────────────────────────────────────────────── */
function _preloadTemplates() { function _preloadTemplates() {
@@ -486,8 +552,9 @@ function _activateExistingChatCards() {
document.querySelectorAll(".message[data-message-id]").forEach(messageEl => { document.querySelectorAll(".message[data-message-id]").forEach(messageEl => {
const messageId = messageEl.dataset.messageId const messageId = messageEl.dataset.messageId
const message = game.messages.get(messageId) const message = game.messages.get(messageId)
const root = messageEl.querySelector(".celestopol.chat-roll") if (!message) return
if (!message || !root) return const root = messageEl.querySelector(".celestopol.chat-roll, .celestopol-roll.moon-standalone-card")
if (!root) return
_activateChatCardListeners(message, root) _activateChatCardListeners(message, root)
}) })
} }
@@ -497,12 +564,19 @@ function _activateChatCardListeners(message, html) {
if (!root) return if (!root) return
_renderWeaponDamageState(message, root) _renderWeaponDamageState(message, root)
_renderMoonEffectState(message, root)
root.querySelectorAll('[data-action="apply-weapon-damage"]').forEach(button => { root.querySelectorAll('[data-action="apply-weapon-damage"]').forEach(button => {
if (button.dataset.bound === "true") return if (button.dataset.bound === "true") return
button.dataset.bound = "true" button.dataset.bound = "true"
button.addEventListener("click", event => _onApplyWeaponDamageClick(event, message)) button.addEventListener("click", event => _onApplyWeaponDamageClick(event, message))
}) })
root.querySelectorAll('[data-action="apply-moon-effect"]').forEach(button => {
if (button.dataset.bound === "true") return
button.dataset.bound = "true"
button.addEventListener("click", event => _onApplyMoonEffectClick(event, message))
})
} }
async function _onApplyWeaponDamageClick(event, message) { async function _onApplyWeaponDamageClick(event, message) {
@@ -510,8 +584,10 @@ async function _onApplyWeaponDamageClick(event, message) {
const button = event.currentTarget const button = event.currentTarget
const card = button.closest(".celestopol.chat-roll") const card = button.closest(".celestopol.chat-roll")
const select = button.closest(".weapon-damage-actions")?.querySelector('select[name="targetActorId"]') const select = button.closest(".weapon-damage-actions")?.querySelector('select[name="targetActorUuid"]')
const actorId = button.dataset.actorId || select?.value || "" 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 incomingWounds = Number.parseInt(button.dataset.incomingWounds ?? "", 10)
const currentState = _getDamageApplicationState(message) const currentState = _getDamageApplicationState(message)
@@ -520,7 +596,7 @@ async function _onApplyWeaponDamageClick(event, message) {
return return
} }
if (!actorId) { if (!actorUuid && !actorId) {
ui.notifications.warn(game.i18n.localize("CELESTOPOL.Combat.selectCharacterFirst")) ui.notifications.warn(game.i18n.localize("CELESTOPOL.Combat.selectCharacterFirst"))
return return
} }
@@ -531,14 +607,15 @@ async function _onApplyWeaponDamageClick(event, message) {
button.disabled = true button.disabled = true
await _requestWeaponDamageApplication({ await _requestWeaponDamageApplication({
actorId, actorId,
actorUuid,
incomingWounds, incomingWounds,
chatMessageId: message?.id ?? null, chatMessageId: message?.id ?? null,
}) })
} }
async function _requestWeaponDamageApplication({ actorId, incomingWounds, chatMessageId = null }) { async function _requestWeaponDamageApplication({ actorId, actorUuid = null, incomingWounds, chatMessageId = null }) {
if (game.user.isGM) { if (game.user.isGM) {
return _applyWeaponDamage({ actorId, incomingWounds, chatMessageId }) return _applyWeaponDamage({ actorId, actorUuid, incomingWounds, chatMessageId })
} }
if (!game.socket) return if (!game.socket) return
@@ -546,6 +623,7 @@ async function _requestWeaponDamageApplication({ actorId, incomingWounds, chatMe
game.socket.emit(`system.${SYSTEM_ID}`, { game.socket.emit(`system.${SYSTEM_ID}`, {
type: "applyWeaponDamage", type: "applyWeaponDamage",
actorId, actorId,
actorUuid,
incomingWounds, incomingWounds,
chatMessageId, chatMessageId,
}) })
@@ -583,7 +661,7 @@ function _setDamageStatus(root, { text, cssClass = "" }) {
function _renderPendingWeaponDamageState(root) { function _renderPendingWeaponDamageState(root) {
const button = root.querySelector('[data-action="apply-weapon-damage"]') const button = root.querySelector('[data-action="apply-weapon-damage"]')
const select = root.querySelector('select[name="targetActorId"]') const select = root.querySelector('select[name="targetActorUuid"]')
if (button) { if (button) {
button.disabled = true button.disabled = true
button.textContent = game.i18n.localize("CELESTOPOL.Combat.damageApplying") button.textContent = game.i18n.localize("CELESTOPOL.Combat.damageApplying")
@@ -597,7 +675,7 @@ function _renderPendingWeaponDamageState(root) {
function _renderWeaponDamageState(message, root) { function _renderWeaponDamageState(message, root) {
const button = root.querySelector('[data-action="apply-weapon-damage"]') const button = root.querySelector('[data-action="apply-weapon-damage"]')
const select = root.querySelector('select[name="targetActorId"]') const select = root.querySelector('select[name="targetActorUuid"]')
const state = _getDamageApplicationState(message) const state = _getDamageApplicationState(message)
if (!state?.applied) { if (!state?.applied) {
@@ -639,8 +717,8 @@ async function _markChatMessageDamageApplied(chatMessageId, data) {
_updateRenderedChatMessageState(message) _updateRenderedChatMessageState(message)
} }
async function _applyWeaponDamage({ actorId, incomingWounds, chatMessageId = null }) { async function _applyWeaponDamage({ actorId, actorUuid = null, incomingWounds, chatMessageId = null }) {
const actor = game.actors.get(actorId) const actor = await CelestopolRoll.resolveActor({ actorId, actorUuid })
if (!actor) return null if (!actor) return null
const message = chatMessageId ? game.messages.get(chatMessageId) : null const message = chatMessageId ? game.messages.get(chatMessageId) : null
@@ -680,6 +758,126 @@ async function _applyWeaponDamage({ actorId, incomingWounds, chatMessageId = nul
return { actorName: actor.name, appliedWounds, armorProtection } return { actorName: actor.name, appliedWounds, armorProtection }
} }
/* ─── Dé de la Lune — contreparties ─────────────────────────────────────── */
function _getMoonEffectState(message) {
return message?.getFlag(SYSTEM_ID, MOON_EFFECT_FLAG) ?? null
}
function _renderMoonEffectState(message, root) {
const state = _getMoonEffectState(message)
const actionsDiv = root.querySelector(".moon-effect-actions")
if (!actionsDiv) return
if (!state?.applied) return
const NEGATIVE_EFFECTS = new Set(["lose-anomaly", "gain-spleen", "lose-destin"])
// Désactiver tous les boutons et afficher le statut
actionsDiv.querySelectorAll(".moon-effect-btn").forEach(btn => { btn.disabled = true })
let statusEl = actionsDiv.querySelector(".moon-effect-applied-status")
if (!statusEl) {
statusEl = document.createElement("span")
actionsDiv.append(statusEl)
}
statusEl.className = "moon-effect-applied-status" + (NEGATIVE_EFFECTS.has(state.effect) ? " is-negative" : "")
statusEl.textContent = state.effectLabel
? game.i18n.format("CELESTOPOL.Moon.effectApplied", { label: state.effectLabel })
: game.i18n.localize("CELESTOPOL.Moon.effectApplied")
}
function _updateRenderedMoonEffectState(message) {
const msgEl = document.querySelector(`.message[data-message-id="${message.id}"]`)
if (!msgEl) return
const root = msgEl.querySelector(".celestopol.chat-roll, .celestopol-roll.moon-standalone-card")
if (!root) return
_renderMoonEffectState(message, root)
}
async function _onApplyMoonEffectClick(event, message) {
event.preventDefault()
const state = _getMoonEffectState(message)
if (state?.applied) return
const button = event.currentTarget
const effect = button.dataset.effect
const actionsDiv = button.closest(".moon-effect-actions")
const actorId = actionsDiv?.dataset.moonActorId ?? ""
const actorUuid = actionsDiv?.dataset.moonActorUuid ?? ""
// Désactiver immédiatement pour éviter les double-clics
actionsDiv?.querySelectorAll(".moon-effect-btn").forEach(btn => { btn.disabled = true })
const actor = await CelestopolRoll.resolveActor({ actorId, actorUuid })
if (!actor) {
ui.notifications.warn(game.i18n.localize("CELESTOPOL.Moon.actorNotFound"))
actionsDiv?.querySelectorAll(".moon-effect-btn").forEach(btn => { btn.disabled = false })
return
}
const effectLabel = await _applyMoonEffect(actor, effect)
if (effectLabel === null) {
actionsDiv?.querySelectorAll(".moon-effect-btn").forEach(btn => { btn.disabled = false })
return
}
await message.setFlag(SYSTEM_ID, MOON_EFFECT_FLAG, { applied: true, effect, effectLabel })
}
async function _applyMoonEffect(actor, effect) {
const i18n = key => game.i18n.localize(`CELESTOPOL.Moon.${key}`)
const anomaly = actor.items.find(i => i.type === "anomaly")
switch (effect) {
case "regain-anomaly": {
if (!anomaly) { ui.notifications.warn(i18n("noAnomaly")); return null }
const max = anomaly.system?.level ?? 0
const cur = anomaly.system?.usesRemaining ?? 0
if (cur >= max) { ui.notifications.warn(i18n("anomalyFull")); return null }
await anomaly.update({ "system.usesRemaining": Math.min(max, cur + 1) })
ui.notifications.info(game.i18n.format("CELESTOPOL.Moon.effectApplied", { label: i18n("effectRegainAnomaly") }))
return i18n("effectRegainAnomaly")
}
case "lose-spleen": {
const cur = actor.system?.spleen?.lvl ?? 0
if (cur <= 0) { ui.notifications.warn(i18n("spleenEmpty")); return null }
await actor.update({ "system.spleen.lvl": Math.max(0, cur - 1) })
ui.notifications.info(game.i18n.format("CELESTOPOL.Moon.effectApplied", { label: i18n("effectLoseSpleen") }))
return i18n("effectLoseSpleen")
}
case "gain-destin": {
const cur = actor.system?.destin?.lvl ?? 0
await actor.update({ "system.destin.lvl": Math.min(8, cur + 2) })
ui.notifications.info(game.i18n.format("CELESTOPOL.Moon.effectApplied", { label: i18n("effectGainDestin") }))
return i18n("effectGainDestin")
}
case "lose-destin": {
const cur = actor.system?.destin?.lvl ?? 0
await actor.update({ "system.destin.lvl": Math.max(0, cur - 2) })
ui.notifications.info(game.i18n.format("CELESTOPOL.Moon.effectApplied", { label: i18n("effectLoseDestin") }))
return i18n("effectLoseDestin")
}
case "lose-anomaly": {
if (!anomaly) { ui.notifications.warn(i18n("noAnomaly")); return null }
const cur = anomaly.system?.usesRemaining ?? 0
if (cur <= 0) { ui.notifications.warn(i18n("anomalyEmpty")); return null }
await anomaly.update({ "system.usesRemaining": Math.max(0, cur - 1) })
ui.notifications.info(game.i18n.format("CELESTOPOL.Moon.effectApplied", { label: i18n("effectLoseAnomaly") }))
return i18n("effectLoseAnomaly")
}
case "gain-spleen": {
const cur = actor.system?.spleen?.lvl ?? 0
if (cur >= 8) { ui.notifications.warn(i18n("spleenFull")); return null }
await actor.update({ "system.spleen.lvl": Math.min(8, cur + 1) })
ui.notifications.info(game.i18n.format("CELESTOPOL.Moon.effectApplied", { label: i18n("effectGainSpleen") }))
return i18n("effectGainSpleen")
}
default:
return null
}
}
function _getDefaultFactionAspectState() { function _getDefaultFactionAspectState() {
return { return {
pointsMax: 8, pointsMax: 8,
+59 -7
View File
@@ -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 len-tête de la fiche.",
"portraitImageEmpty": "Aucun portrait biographique distinct nest encore renseigné.",
"portraitImageMissing": "Aucun portrait biographique distinct nest 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",
@@ -50,7 +58,25 @@
"instruction": "Instruction", "instruction": "Instruction",
"mtechnologique": "Merveilleux technologique", "mtechnologique": "Merveilleux technologique",
"raisonnement": "Raisonnement", "raisonnement": "Raisonnement",
"traitement": "Traitement" "traitement": "Traitement",
"tooltip": {
"artifice": "Art du badinage et de la conversation subtile. Permet de se glisser dans une discussion pour glaner des renseignements ou noyer des informations mensongères. Favorise la manipulation et l'incitation plutôt que la contrainte.",
"attraction": "Capacité à orienter les émotions d'un interlocuteur. Sert à plaire, séduire, mais aussi à passer inaperçu lors d'une soirée mondaine.",
"coercition": "Art d'intimider par le regard ou les mots. Couvre également la capacité à donner des ordres dans un contexte militaire ou hiérarchique.",
"faveur": "Rapport de force oral : convaincre, persuader, négocier. S'appuie sur un discours argumentatif pour imposer son point de vue ou emporter l'adhésion.",
"echauffouree": "Techniques de combat à mains nues, avec une arme improvisée, une arme blanche ou des armes à feu et à distance.",
"effacement": "Capacité à être discret et à ne pas se faire repérer. Couvre le camouflage et l'escamotage. Requiert souvent de demeurer immobile.",
"mobilite": "Réflexes et vivacité. Sert pour la conduite, le pilotage, l'équitation, et représente l'initiative d'un protagoniste.",
"prouesse": "Efforts sportifs et physiques : escalade, natation, sprint, force brute et endurance.",
"appreciation": "Observer son environnement de façon exhaustive. Permet de repérer des intrus, des pièges, des guets-apens et des indices.",
"arts": "Domaines artistiques au sens large : musique, littérature, théâtre, prestidigitation. Permet de captiver un auditoire ou de créer des œuvres.",
"inspiration": "Intuition et inconscient : écouter sa petite voix intérieure sans démarche logique préalable. Permet aussi de lire le comportement d'une personne.",
"traque": "Filer un individu ou un groupe, pister et s'orienter. Ouvre également vers les techniques de survie.",
"instruction": "Savoirs encyclopédiques : sciences humaines, sciences dures, sciences sociales et linguistique. Chaque tranche de 2 points permet de maîtriser une langue supplémentaire.",
"mtechnologique": "Technologies extraordinaires de Célestopol : mécanique, automates, ingénierie au sélénium, horlogerie, serrurerie et artisanat.",
"raisonnement": "Capacités cognitives : déduction froide, logique pragmatique, analyse des faits. Utile pour percevoir des liens dans une enquête.",
"traitement": "Techniques médicales et thérapeutiques : premiers secours, chirurgie, pharmacopée, psychanalyse et psychothérapie."
}
}, },
"Anomaly": { "Anomaly": {
"type": "Type d'anomalie", "type": "Type d'anomalie",
@@ -194,19 +220,23 @@
"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": "Attaque réussie — cible touchée", "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é", "targetCharacterLabel": "Personnage visé",
@@ -240,6 +270,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",
@@ -337,7 +368,22 @@
"mauvaiseFortune": "🔴 Mauvaise Fortune", "mauvaiseFortune": "🔴 Mauvaise Fortune",
"chanceInterpret": "Chance", "chanceInterpret": "Chance",
"narrativeInterpret": "Narratif", "narrativeInterpret": "Narratif",
"quantiteHint": "Valeur" "quantiteHint": "Valeur",
"applyChoose": "Choisissez la contrepartie :",
"effectNarrativeOnly": "(Narratif — sans effet mécanique)",
"effectRegainAnomaly": "🌟 Regain d'1 Anomalie",
"effectLoseSpleen": "💚 Perte d'1 Spleen",
"effectGainDestin": "⭐ Gain de 2 Destin",
"effectLoseDestin": "⭐ Perte de 2 Destin",
"effectLoseAnomaly": "❌ Perte d'1 Anomalie",
"effectGainSpleen": "💔 Gain d'1 Spleen",
"effectApplied": "Contrepartie appliquée : {label}",
"noAnomaly": "Ce personnage n'a pas d'anomalie",
"anomalyFull": "L'anomalie est déjà au maximum d'utilisations",
"anomalyEmpty": "L'anomalie n'a plus d'utilisation disponible",
"spleenEmpty": "Le Spleen est déjà à 0",
"spleenFull": "Le Spleen est déjà au maximum",
"actorNotFound": "Personnage introuvable"
}, },
"Difficulty": { "Difficulty": {
"unknown": "Aucun seuil", "unknown": "Aucun seuil",
@@ -413,6 +459,10 @@
"helpFallback": "Célestopol 1922 — Aides de jeu", "helpFallback": "Célestopol 1922 — Aides de jeu",
"bookLinkLabel": "Voir le livre de base sur le site dAntre-Monde Éditions" "bookLinkLabel": "Voir le livre de base sur le site dAntre-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})"
}, },
@@ -432,7 +482,9 @@
"rangeLongue": "Longue portée", "rangeLongue": "Longue portée",
"type": "Type", "type": "Type",
"typeMelee": "Mêlée", "typeMelee": "Mêlée",
"typeDistance": "Distance" "typeDistance": "Distance",
"equip": "Équiper",
"unequip": "Retirer"
}, },
"Armure": { "Armure": {
"protection": "Protection", "protection": "Protection",
@@ -34,15 +34,19 @@ 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,
toggleArmure: CelestopolActorSheet.#onToggleArmure, toggleArmure: CelestopolActorSheet.#onToggleArmure,
toggleWeapon: CelestopolActorSheet.#onToggleWeapon,
}, },
} }
@@ -136,6 +140,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
@@ -167,6 +200,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
@@ -195,6 +238,13 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
if (item?.type === "armure") await item.update({ "system.equipped": !item.system.equipped }) if (item?.type === "armure") await item.update({ "system.equipped": !item.system.equipped })
} }
static async #onToggleWeapon(_event, target) {
const uuid = target.closest('[data-item-uuid]')?.dataset.itemUuid
if (!uuid) return
const item = await fromUuid(uuid)
if (item?.type === "weapon") await item.update({ "system.equipped": !item.system.equipped })
}
static #onFactionLevel(_event, target) { static #onFactionLevel(_event, target) {
if (!this.isEditable) return if (!this.isEditable) return
const factionId = target.dataset.faction const factionId = target.dataset.faction
+12 -2
View File
@@ -42,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" },
} }
@@ -53,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" },
} }
@@ -156,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
} }
+7
View File
@@ -35,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" },
} }
@@ -45,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" },
} }
@@ -106,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 }
) )
+16 -16
View File
@@ -31,28 +31,28 @@ export const STATS = {
/** Domaines groupées par attribut. */ /** Domaines groupées par attribut. */
export const SKILLS = { export const SKILLS = {
ame: { ame: {
artifice: { id: "artifice", label: "CELESTOPOL.Skill.artifice", stat: "ame", resThreshold: 5 }, artifice: { id: "artifice", label: "CELESTOPOL.Skill.artifice", tooltip: "CELESTOPOL.Skill.tooltip.artifice", stat: "ame", resThreshold: 5 },
attraction: { id: "attraction", label: "CELESTOPOL.Skill.attraction", stat: "ame", resThreshold: 2 }, attraction: { id: "attraction", label: "CELESTOPOL.Skill.attraction", tooltip: "CELESTOPOL.Skill.tooltip.attraction", stat: "ame", resThreshold: 2 },
coercition: { id: "coercition", label: "CELESTOPOL.Skill.coercition", stat: "ame", resThreshold: 3 }, coercition: { id: "coercition", label: "CELESTOPOL.Skill.coercition", tooltip: "CELESTOPOL.Skill.tooltip.coercition", stat: "ame", resThreshold: 3 },
faveur: { id: "faveur", label: "CELESTOPOL.Skill.faveur", stat: "ame", resThreshold: 6 }, faveur: { id: "faveur", label: "CELESTOPOL.Skill.faveur", tooltip: "CELESTOPOL.Skill.tooltip.faveur", stat: "ame", resThreshold: 6 },
}, },
corps: { corps: {
echauffouree: { id: "echauffouree", label: "CELESTOPOL.Skill.echauffouree", stat: "corps", resThreshold: 6 }, echauffouree: { id: "echauffouree", label: "CELESTOPOL.Skill.echauffouree", tooltip: "CELESTOPOL.Skill.tooltip.echauffouree", stat: "corps", resThreshold: 6 },
effacement: { id: "effacement", label: "CELESTOPOL.Skill.effacement", stat: "corps", resThreshold: 3 }, effacement: { id: "effacement", label: "CELESTOPOL.Skill.effacement", tooltip: "CELESTOPOL.Skill.tooltip.effacement", stat: "corps", resThreshold: 3 },
mobilite: { id: "mobilite", label: "CELESTOPOL.Skill.mobilite", stat: "corps", resThreshold: 2 }, mobilite: { id: "mobilite", label: "CELESTOPOL.Skill.mobilite", tooltip: "CELESTOPOL.Skill.tooltip.mobilite", stat: "corps", resThreshold: 2 },
prouesse: { id: "prouesse", label: "CELESTOPOL.Skill.prouesse", stat: "corps", resThreshold: 5 }, prouesse: { id: "prouesse", label: "CELESTOPOL.Skill.prouesse", tooltip: "CELESTOPOL.Skill.tooltip.prouesse", stat: "corps", resThreshold: 5 },
}, },
coeur: { coeur: {
appreciation: { id: "appreciation", label: "CELESTOPOL.Skill.appreciation", stat: "coeur", resThreshold: 6 }, appreciation: { id: "appreciation", label: "CELESTOPOL.Skill.appreciation", tooltip: "CELESTOPOL.Skill.tooltip.appreciation", stat: "coeur", resThreshold: 6 },
arts: { id: "arts", label: "CELESTOPOL.Skill.arts", stat: "coeur", resThreshold: 2 }, arts: { id: "arts", label: "CELESTOPOL.Skill.arts", tooltip: "CELESTOPOL.Skill.tooltip.arts", stat: "coeur", resThreshold: 2 },
inspiration: { id: "inspiration", label: "CELESTOPOL.Skill.inspiration", stat: "coeur", resThreshold: 3 }, inspiration: { id: "inspiration", label: "CELESTOPOL.Skill.inspiration", tooltip: "CELESTOPOL.Skill.tooltip.inspiration", stat: "coeur", resThreshold: 3 },
traque: { id: "traque", label: "CELESTOPOL.Skill.traque", stat: "coeur", resThreshold: 5 }, traque: { id: "traque", label: "CELESTOPOL.Skill.traque", tooltip: "CELESTOPOL.Skill.tooltip.traque", stat: "coeur", resThreshold: 5 },
}, },
esprit: { esprit: {
instruction: { id: "instruction", label: "CELESTOPOL.Skill.instruction", stat: "esprit", resThreshold: 2 }, instruction: { id: "instruction", label: "CELESTOPOL.Skill.instruction", tooltip: "CELESTOPOL.Skill.tooltip.instruction", stat: "esprit", resThreshold: 2 },
mtechnologique: { id: "mtechnologique", label: "CELESTOPOL.Skill.mtechnologique", stat: "esprit", resThreshold: 6 }, mtechnologique: { id: "mtechnologique", label: "CELESTOPOL.Skill.mtechnologique", tooltip: "CELESTOPOL.Skill.tooltip.mtechnologique", stat: "esprit", resThreshold: 6 },
raisonnement: { id: "raisonnement", label: "CELESTOPOL.Skill.raisonnement", stat: "esprit", resThreshold: 5 }, raisonnement: { id: "raisonnement", label: "CELESTOPOL.Skill.raisonnement", tooltip: "CELESTOPOL.Skill.tooltip.raisonnement", stat: "esprit", resThreshold: 5 },
traitement: { id: "traitement", label: "CELESTOPOL.Skill.traitement", stat: "esprit", resThreshold: 3 }, traitement: { id: "traitement", label: "CELESTOPOL.Skill.traitement", tooltip: "CELESTOPOL.Skill.tooltip.traitement", stat: "esprit", resThreshold: 3 },
}, },
} }
+107 -18
View File
@@ -78,6 +78,22 @@ export class CelestopolRoll extends Roll {
.reduce((sum, a) => sum + Math.abs(a.system.protection ?? a.system.malus ?? 0), 0) .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
@@ -298,11 +314,16 @@ export class CelestopolRoll extends Roll {
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 selectedCombatTargetId = typeof rollContext.targetSelect === "string" ? rollContext.targetSelect : "" const selectedCombatTargetRef = typeof rollContext.targetSelect === "string" ? rollContext.targetSelect : ""
const selectedCombatTarget = selectedCombatTargetId const selectedCombatTarget = selectedCombatTargetRef
? availableTargets.find(t => t.id === selectedCombatTargetId) ?? null ? availableTargets.find(t => t.uuid === selectedCombatTargetRef || t.id === selectedCombatTargetRef) ?? null
: null : null
const resolvedWeaponName = (isRangedDefense && selectedCombatTarget?.weaponName) ? selectedCombatTarget.weaponName : weaponName
const resolvedWeaponDegats = (isRangedDefense && selectedCombatTarget?.weaponDegats) ? selectedCombatTarget.weaponDegats : weaponDegats
// Dégâts de l'arme adverse en cas d'échec (arme équipée du PNJ ciblé en mêlée, arme distance en esquive)
const incomingWeaponDegats = selectedCombatTarget?.weaponDegats ?? resolvedWeaponDegats
const targetActorId = selectedCombatTarget?.id || "" const targetActorId = selectedCombatTarget?.id || ""
const targetActorUuid = selectedCombatTarget?.uuid || ""
const targetActorName = selectedCombatTarget?.name || "" 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
@@ -352,9 +373,11 @@ export class CelestopolRoll extends Roll {
isCombat, isCombat,
isRangedDefense, isRangedDefense,
weaponType, weaponType,
weaponName, weaponName: resolvedWeaponName,
weaponDegats, weaponDegats: resolvedWeaponDegats,
incomingWeaponDegats,
targetActorId, targetActorId,
targetActorUuid,
targetActorName, targetActorName,
availableTargets, availableTargets,
rangedMod: effectiveRangedMod, rangedMod: effectiveRangedMod,
@@ -363,7 +386,7 @@ export class CelestopolRoll extends Roll {
puiserRessources: effectivePuiser, puiserRessources: effectivePuiser,
nbDice: (!isResistance && useFortune) ? 1 : nbDice, nbDice: (!isResistance && useFortune) ? 1 : nbDice,
formula, formula,
rollMode: rollContext.visibility ?? "publicroll", rollMode: rollContext.visibility ?? "public",
rollMoonDie: effectiveMoon, rollMoonDie: effectiveMoon,
moonDieResult, moonDieResult,
moonFace, moonFace,
@@ -375,7 +398,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) {
@@ -384,16 +410,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 (PJ uniquement) 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?.type === "character" && roll.options.resultType === "failure") {
const nextLvl = (actor.system.blessures.lvl ?? 0) + 1 const incomingWounds = this.getIncomingWounds(roll.options.incomingWeaponDegats ?? 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) {
@@ -506,12 +541,16 @@ export class CelestopolRoll extends Roll {
const incomingWounds = isWeaponHit ? this.constructor.getIncomingWounds(weaponDegats) : null const incomingWounds = isWeaponHit ? this.constructor.getIncomingWounds(weaponDegats) : null
const hasVariableDamage = isWeaponHit && incomingWounds === null const hasVariableDamage = isWeaponHit && incomingWounds === null
const targetActorId = this.options.targetActorId ?? "" const targetActorId = this.options.targetActorId ?? ""
const targetActorUuid = this.options.targetActorUuid ?? ""
const targetActorName = this.options.targetActorName ?? "" const targetActorName = this.options.targetActorName ?? ""
const availableTargets = (this.options.availableTargets ?? []).map(target => ({ const availableTargets = (this.options.availableTargets ?? []).map(target => ({
...target, ...target,
selected: target.id === targetActorId, selected: target.uuid === targetActorUuid || target.id === targetActorId,
})) }))
const selectedTargetActor = targetActorId ? game.actors.get(targetActorId) : null const selectedTargetActor = await this.constructor.resolveActor({
actorUuid: targetActorUuid,
actorId: targetActorId,
})
const selectedTargetProtection = selectedTargetActor const selectedTargetProtection = selectedTargetActor
? this.constructor.getActorArmorProtection(selectedTargetActor) ? this.constructor.getActorArmorProtection(selectedTargetActor)
: null : null
@@ -519,6 +558,13 @@ export class CelestopolRoll extends Roll {
? Math.max(0, incomingWounds - selectedTargetProtection) ? Math.max(0, incomingWounds - selectedTargetProtection)
: null : null
// Type de l'acteur qui lance le jet (character | npc)
const rollingActor = await this.constructor.resolveActor({
actorUuid: this.options.actorUuid ?? null,
actorId: this.options.actorId ?? null,
})
const actorType = rollingActor?.type ?? this.options.actorType ?? 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
? `${game.i18n.localize("CELESTOPOL.Combat.corpsPnj")} : ${threshold}` ? `${game.i18n.localize("CELESTOPOL.Combat.corpsPnj")} : ${threshold}`
@@ -564,11 +610,14 @@ export class CelestopolRoll extends Roll {
woundLabel, woundLabel,
isResistance: this.options.isResistance ?? false, isResistance: this.options.isResistance ?? false,
isCombat: this.options.isCombat ?? false, isCombat: this.options.isCombat ?? false,
actorType,
isNpcAttack: actorType === "npc",
weaponName: this.options.weaponName ?? null, weaponName: this.options.weaponName ?? null,
weaponDegats, 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, hasDamageSummary: isWeaponHit,
@@ -577,6 +626,7 @@ export class CelestopolRoll extends Roll {
hasVariableDamage, hasVariableDamage,
canApplyWeaponDamage: incomingWounds !== null, canApplyWeaponDamage: incomingWounds !== null,
targetActorId, targetActorId,
targetActorUuid,
targetActorName, targetActorName,
selectedTargetProtection, selectedTargetProtection,
selectedTargetAppliedWounds, selectedTargetAppliedWounds,
@@ -589,20 +639,55 @@ export class CelestopolRoll extends Roll {
moonResultClass: moonResultType?.cssClass ?? "", moonResultClass: moonResultType?.cssClass ?? "",
moonResultLabel: moonResultType ? game.i18n.localize(moonResultType.label) : "", moonResultLabel: moonResultType ? game.i18n.localize(moonResultType.label) : "",
moonResultDesc: moonResultType ? game.i18n.localize(moonResultType.desc) : "", moonResultDesc: moonResultType ? game.i18n.localize(moonResultType.desc) : "",
moonResultTypeId: moonResultType?.id ?? null,
moonActorId: (actorType === "character") ? (this.options.actorId ?? null) : null,
moonActorUuid: (actorType === "character") ? (this.options.actorUuid ?? null) : null,
moonActorIsCharacter: actorType === "character",
isPrivate, isPrivate,
tooltip: isPrivate ? "" : await this.getTooltip(), tooltip: isPrivate ? "" : await this.getTooltip(),
} }
} }
/** @override */ /** @override */
async toMessage(messageData = {}, { rollMode, create = true } = {}) { async toMessage(messageData = {}, { messageMode, rollMode, create = true } = {}) {
const modernToLegacyRollMode = {
public: CONST.DICE_ROLL_MODES.PUBLIC,
gm: CONST.DICE_ROLL_MODES.PRIVATE,
blind: CONST.DICE_ROLL_MODES.BLIND,
self: CONST.DICE_ROLL_MODES.SELF,
ic: CONST.DICE_ROLL_MODES.PUBLIC,
}
let effectiveRollMode = rollMode ?? messageMode ?? game.settings.get("core", "rollMode") ?? CONST.DICE_ROLL_MODES.PUBLIC
effectiveRollMode = modernToLegacyRollMode[effectiveRollMode] ?? effectiveRollMode
if (!Object.values(CONST.DICE_ROLL_MODES).includes(effectiveRollMode)) {
effectiveRollMode = game.settings.get("core", "rollMode") ?? CONST.DICE_ROLL_MODES.PUBLIC
}
if (!this._evaluated) await this.evaluate({ allowInteractive: effectiveRollMode !== CONST.DICE_ROLL_MODES.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: effectiveRollMode !== CONST.DICE_ROLL_MODES.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)
ChatMessage.applyRollMode(chatData, effectiveRollMode)
if (create) return ChatMessage.create(chatData)
return chatData
} }
/** /**
@@ -617,6 +702,7 @@ export class CelestopolRoll extends Roll {
const resultType = face ? SYSTEM.MOON_RESULT_TYPES[face.result] ?? null : null const resultType = face ? SYSTEM.MOON_RESULT_TYPES[face.result] ?? null : null
const isGoodFortune = result <= 4 const isGoodFortune = result <= 4
const actorIsCharacter = actor?.type === "character"
const templateData = { const templateData = {
result, result,
moonFaceSymbol: face?.symbol ?? "", moonFaceSymbol: face?.symbol ?? "",
@@ -624,8 +710,12 @@ export class CelestopolRoll extends Roll {
moonResultLabel: resultType ? game.i18n.localize(resultType.label) : "", moonResultLabel: resultType ? game.i18n.localize(resultType.label) : "",
moonResultDesc: resultType ? game.i18n.localize(resultType.desc) : "", moonResultDesc: resultType ? game.i18n.localize(resultType.desc) : "",
moonResultClass: resultType?.cssClass ?? "", moonResultClass: resultType?.cssClass ?? "",
moonResultTypeId: resultType?.id ?? null,
isGoodFortune, isGoodFortune,
actorName: actor?.name ?? null, actorName: actor?.name ?? null,
moonActorIsCharacter: actorIsCharacter,
moonActorId: actorIsCharacter ? (actor.id ?? null) : null,
moonActorUuid: actorIsCharacter ? (actor.uuid ?? null) : null,
} }
const content = await foundry.applications.handlebars.renderTemplate( const content = await foundry.applications.handlebars.renderTemplate(
@@ -640,8 +730,7 @@ export class CelestopolRoll extends Roll {
await ChatMessage.create({ await ChatMessage.create({
content, content,
speaker, speaker,
rolls: [roll], style: CONST.CHAT_MESSAGE_STYLES.OTHER,
style: CONST.CHAT_MESSAGE_STYLES?.ROLL ?? 5,
}) })
} }
} }
+109 -11
View File
@@ -129,6 +129,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
@@ -216,6 +218,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,
@@ -228,7 +231,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
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,
destGaugeFull: this.destin.lvl > 0, destGaugeFull: this.destin.lvl >= 8,
fortuneValue: this.attributs.fortune.value, fortuneValue: this.attributs.fortune.value,
}) })
} }
@@ -247,6 +250,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,
@@ -268,21 +272,46 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
/** /**
* Collecte les cibles de combat sur la scène active. * Collecte les cibles de combat sur la scène active.
* Pour un PJ attaquant, seules les cibles PNJ présentes sur la scène sont proposées. * 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] - Filtrer sur les PNJ ayant une arme à distance équipée
* @param {boolean} [options.fallbackToAll=false] - Revenir à tous les PNJ si aucune cible trouvée
* @param {boolean} [options.includeMeleeWeapon=false] - Inclure l'arme de mêlée équipée du PNJ (dégâts adverses)
* @returns {Array<{id:string, uuid:string, name:string, corps:number, weaponName?:string, weaponDegats?:string}>}
*/ */
_getCombatTargets() { _getCombatTargets({ onlyRanged = false, fallbackToAll = false, includeMeleeWeapon = false } = {}) {
const toEntry = actor => ({ const getEquippedWeapon = (actor, type) =>
actor.itemTypes?.weapon?.find(item => item.system.type === type && item.system.equipped) ?? null
const toEntry = actor => {
const entry = {
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,
}) }
if (onlyRanged) {
const weapon = getEquippedWeapon(actor, "distance")
if (weapon) {
entry.weaponName = weapon.name
entry.weaponDegats = weapon.system.degats
}
} else if (includeMeleeWeapon) {
const weapon = getEquippedWeapon(actor, "melee")
entry.weaponDegats = weapon ? weapon.system.degats : "0"
}
return entry
}
const sceneTokens = canvas?.scene?.isView ? (canvas.tokens?.placeables ?? []) : [] const sceneTokens = canvas?.scene?.isView ? (canvas.tokens?.placeables ?? []) : []
return [...new Map(sceneTokens const targets = [...new Map(sceneTokens
.filter(t => t.actor?.type === "npc" && t.actor.id !== this.parent.id) .filter(t => t.actor?.type === "npc" && t.actor.id !== this.parent.id)
.filter(t => !onlyRanged || getEquippedWeapon(t.actor, "distance"))
.map(t => { .map(t => {
const actor = t.actor const actor = t.actor
return [actor.id, toEntry(actor)] return [actor.uuid, toEntry(actor)]
})).values()] })).values()]
if (!targets.length && onlyRanged && fallbackToAll) return this._getCombatTargets()
return targets
} }
/** /**
@@ -302,6 +331,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",
@@ -313,14 +343,47 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
armorMalus: this.getArmorMalusForRoll("corps", "echauffouree"), 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 >= 8,
fortuneValue: this.attributs.fortune.value, fortuneValue: this.attributs.fortune.value,
isCombat: true, isCombat: true,
isRangedDefense: false, isRangedDefense: false,
weaponType: item.system.type, weaponType: item.system.type,
weaponName: item.name, weaponName: item.name,
weaponDegats: item.system.degats, weaponDegats: item.system.degats,
availableTargets: this._getCombatTargets(), availableTargets: this._getCombatTargets({ includeMeleeWeapon: item.system.type === "melee" }),
})
}
/**
* Lance une attaque à mains nues (Échauffourée sans arme).
*/
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 >= 8,
fortuneValue: this.attributs.fortune.value,
isCombat: true,
isRangedDefense: false,
weaponType: "melee",
weaponName: game.i18n.localize("CELESTOPOL.Combat.unarmedAttack"),
weaponDegats: "0",
availableTargets: this._getCombatTargets({ includeMeleeWeapon: true }),
}) })
} }
@@ -340,6 +403,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",
@@ -351,14 +415,48 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
armorMalus: this.getArmorMalusForRoll("corps", "mobilite"), 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 >= 8,
fortuneValue: this.attributs.fortune.value, fortuneValue: this.attributs.fortune.value,
isCombat: true, isCombat: true,
isRangedDefense: true, isRangedDefense: true,
weaponType: "distance", weaponType: "distance",
weaponName: item.name, weaponName: item.name,
weaponDegats: "0", weaponDegats: "0",
availableTargets: this._getCombatTargets(), availableTargets: this._getCombatTargets({ onlyRanged: true, fallbackToAll: true }),
})
}
/**
* 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 >= 8,
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
View File
@@ -85,6 +85,7 @@ export class CelestopolWeapon extends foundry.abstract.TypeDataModel {
choices: Object.keys(SYSTEM.WEAPON_DAMAGE_TYPES) }), choices: Object.keys(SYSTEM.WEAPON_DAMAGE_TYPES) }),
portee: new fields.StringField({ required: true, nullable: false, initial: "contact", portee: new fields.StringField({ required: true, nullable: false, initial: "contact",
choices: Object.keys(SYSTEM.WEAPON_RANGE_TYPES) }), choices: Object.keys(SYSTEM.WEAPON_RANGE_TYPES) }),
equipped: new fields.BooleanField({ initial: false }),
description: new fields.HTMLField({ required: true, textSearch: true }), description: new fields.HTMLField({ required: true, textSearch: true }),
} }
} }
+94 -1
View File
@@ -34,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 }),
}) })
@@ -51,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
@@ -111,6 +112,7 @@ 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,
@@ -126,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(),
})
}
} }
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000022 MANIFEST-000089
+7 -8
View File
@@ -1,8 +1,7 @@
2026/04/11-15:27:57.620400 7ff3bebfd6c0 Recovering log #20 2026/04/27-17:34:57.971923 7f57a57ee6c0 Recovering log #86
2026/04/11-15:27:57.630743 7ff3bebfd6c0 Delete type=3 #18 2026/04/27-17:34:57.982802 7f57a57ee6c0 Delete type=3 #84
2026/04/11-15:27:57.630810 7ff3bebfd6c0 Delete type=0 #20 2026/04/27-17:34:57.982897 7f57a57ee6c0 Delete type=0 #86
2026/04/11-15:29:26.022154 7ff3bdbfb6c0 Level-0 table #25: started 2026/04/27-17:40:33.923146 7f57977fe6c0 Level-0 table #92: started
2026/04/11-15:29:26.022184 7ff3bdbfb6c0 Level-0 table #25: 0 bytes OK 2026/04/27-17:40:33.923179 7f57977fe6c0 Level-0 table #92: 0 bytes OK
2026/04/11-15:29:26.028372 7ff3bdbfb6c0 Delete type=0 #23 2026/04/27-17:40:33.960133 7f57977fe6c0 Delete type=0 #90
2026/04/11-15:29:26.034985 7ff3bdbfb6c0 Manual compaction at level-0 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end) 2026/04/27-17:40:34.016282 7f57977fe6c0 Manual compaction at level-0 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end)
2026/04/11-15:29:26.054681 7ff3bdbfb6c0 Manual compaction at level-1 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end)
+11 -8
View File
@@ -1,8 +1,11 @@
2026/04/11-15:27:20.564248 7f20edbfe6c0 Recovering log #16 2026/04/26-21:30:37.725370 7f57a5fef6c0 Delete type=3 #1
2026/04/11-15:27:20.573933 7f20edbfe6c0 Delete type=3 #14 2026/04/26-22:44:03.500717 7f57977fe6c0 Level-0 table #87: started
2026/04/11-15:27:20.573986 7f20edbfe6c0 Delete type=0 #16 2026/04/26-22:44:03.500753 7f57977fe6c0 Level-0 table #87: 0 bytes OK
2026/04/11-15:27:31.323603 7f1e4ffff6c0 Level-0 table #21: started 2026/04/26-22:44:03.506825 7f57977fe6c0 Delete type=0 #85
2026/04/11-15:27:31.323630 7f1e4ffff6c0 Level-0 table #21: 0 bytes OK 2026/04/26-22:44:03.534971 7f57977fe6c0 Manual compaction at level-0 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 1 : 1
2026/04/11-15:27:31.330503 7f1e4ffff6c0 Delete type=0 #19 2026/04/26-22:44:03.534983 7f57977fe6c0 Compacting 1@0 + 0@1 files
2026/04/11-15:27:31.336782 7f1e4ffff6c0 Manual compaction at level-0 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end) 2026/04/26-22:44:03.538403 7f57977fe6c0 Generated table #88@0: 6 keys, 5441 bytes
2026/04/11-15:27:31.347179 7f1e4ffff6c0 Manual compaction at level-1 from '!journal!eNYstmPK0mMmVJYC' @ 72057594037927935 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end) 2026/04/26-22:44:03.538450 7f57977fe6c0 Compacted 1@0 + 0@1 files => 5441 bytes
2026/04/26-22:44:03.544821 7f57977fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/04/26-22:44:03.545059 7f57977fe6c0 Delete type=2 #5
2026/04/26-22:44:03.555755 7f57977fe6c0 Manual compaction at level-0 from '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 1 : 1 .. '!journal.pages!eNYstmPK0mMmVJYC.r9h1ggd3G9hiqYJX' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000058 MANIFEST-000147
+14 -15
View File
@@ -1,15 +1,14 @@
2026/04/11-15:27:57.608051 7ff3bf3fe6c0 Recovering log #55 2026/04/27-17:34:57.942157 7f5797fff6c0 Recovering log #144
2026/04/11-15:27:57.617618 7ff3bf3fe6c0 Delete type=3 #53 2026/04/27-17:34:57.953303 7f5797fff6c0 Delete type=3 #142
2026/04/11-15:27:57.617668 7ff3bf3fe6c0 Delete type=0 #55 2026/04/27-17:34:57.953395 7f5797fff6c0 Delete type=0 #144
2026/04/11-15:29:26.012403 7ff3bdbfb6c0 Level-0 table #61: started 2026/04/27-17:40:33.795334 7f57977fe6c0 Level-0 table #150: started
2026/04/11-15:29:26.015819 7ff3bdbfb6c0 Level-0 table #61: 3524 bytes OK 2026/04/27-17:40:33.811935 7f57977fe6c0 Level-0 table #150: 3524 bytes OK
2026/04/11-15:29:26.022024 7ff3bdbfb6c0 Delete type=0 #59 2026/04/27-17:40:33.849036 7f57977fe6c0 Delete type=0 #148
2026/04/11-15:29:26.034975 7ff3bdbfb6c0 Manual compaction at level-0 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end) 2026/04/27-17:40:33.960338 7f57977fe6c0 Manual compaction at level-0 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 125 : 1
2026/04/11-15:29:26.044411 7ff3bdbfb6c0 Manual compaction at level-1 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 53 : 1 2026/04/27-17:40:33.960357 7f57977fe6c0 Compacting 1@0 + 1@1 files
2026/04/11-15:29:26.044420 7ff3bdbfb6c0 Compacting 1@1 + 1@2 files 2026/04/27-17:40:33.978560 7f57977fe6c0 Generated table #151@0: 9 keys, 6617 bytes
2026/04/11-15:29:26.047715 7ff3bdbfb6c0 Generated table #62@1: 9 keys, 6617 bytes 2026/04/27-17:40:33.978605 7f57977fe6c0 Compacted 1@0 + 1@1 files => 6617 bytes
2026/04/11-15:29:26.047736 7ff3bdbfb6c0 Compacted 1@1 + 1@2 files => 6617 bytes 2026/04/27-17:40:34.015833 7f57977fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/04/11-15:29:26.054365 7ff3bdbfb6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/04/27-17:40:34.015983 7f57977fe6c0 Delete type=2 #146
2026/04/11-15:29:26.054474 7ff3bdbfb6c0 Delete type=2 #57 2026/04/27-17:40:34.016171 7f57977fe6c0 Delete type=2 #150
2026/04/11-15:29:26.054607 7ff3bdbfb6c0 Delete type=2 #61 2026/04/27-17:40:34.016294 7f57977fe6c0 Manual compaction at level-0 from '!items!null' @ 125 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/04/11-15:29:26.064047 7ff3bdbfb6c0 Manual compaction at level-1 from '!items!null' @ 53 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
+12 -15
View File
@@ -1,15 +1,12 @@
2026/04/11-15:27:20.551914 7f20ee3ff6c0 Recovering log #50 2026/04/26-21:30:37.676414 7f57a4fed6c0 Delete type=3 #1
2026/04/11-15:27:20.562310 7f20ee3ff6c0 Delete type=3 #48 2026/04/26-22:44:03.490255 7f57977fe6c0 Level-0 table #145: started
2026/04/11-15:27:20.562396 7f20ee3ff6c0 Delete type=0 #50 2026/04/26-22:44:03.493705 7f57977fe6c0 Level-0 table #145: 3524 bytes OK
2026/04/11-15:27:31.313674 7f1e4ffff6c0 Level-0 table #56: started 2026/04/26-22:44:03.500560 7f57977fe6c0 Delete type=0 #143
2026/04/11-15:27:31.317287 7f1e4ffff6c0 Level-0 table #56: 3524 bytes OK 2026/04/26-22:44:03.524721 7f57977fe6c0 Manual compaction at level-0 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 121 : 1
2026/04/11-15:27:31.323440 7f1e4ffff6c0 Delete type=0 #54 2026/04/26-22:44:03.524743 7f57977fe6c0 Compacting 2@0 + 0@1 files
2026/04/11-15:27:31.336770 7f1e4ffff6c0 Manual compaction at level-0 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end) 2026/04/26-22:44:03.528390 7f57977fe6c0 Generated table #146@0: 9 keys, 6617 bytes
2026/04/11-15:27:31.336813 7f1e4ffff6c0 Manual compaction at level-1 from '!items!anomCommMorts001' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 49 : 1 2026/04/26-22:44:03.528448 7f57977fe6c0 Compacted 2@0 + 0@1 files => 6617 bytes
2026/04/11-15:27:31.336819 7f1e4ffff6c0 Compacting 1@1 + 1@2 files 2026/04/26-22:44:03.534535 7f57977fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/04/11-15:27:31.340181 7f1e4ffff6c0 Generated table #57@1: 9 keys, 6617 bytes 2026/04/26-22:44:03.534696 7f57977fe6c0 Delete type=2 #140
2026/04/11-15:27:31.340207 7f1e4ffff6c0 Compacted 1@1 + 1@2 files => 6617 bytes 2026/04/26-22:44:03.534864 7f57977fe6c0 Delete type=2 #145
2026/04/11-15:27:31.346883 7f1e4ffff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/04/26-22:44:03.555737 7f57977fe6c0 Manual compaction at level-0 from '!items!null' @ 121 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/04/11-15:27:31.346998 7f1e4ffff6c0 Delete type=2 #52
2026/04/11-15:27:31.347109 7f1e4ffff6c0 Delete type=2 #56
2026/04/11-15:27:31.353403 7f1e4ffff6c0 Manual compaction at level-1 from '!items!null' @ 49 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
Binary file not shown.
View File
+1
View File
@@ -0,0 +1 @@
MANIFEST-000050
View File
+7
View File
@@ -0,0 +1,7 @@
2026/04/27-17:34:57.956196 7f57a57ee6c0 Recovering log #47
2026/04/27-17:34:57.967031 7f57a57ee6c0 Delete type=3 #45
2026/04/27-17:34:57.967119 7f57a57ee6c0 Delete type=0 #47
2026/04/27-17:40:33.886478 7f57977fe6c0 Level-0 table #53: started
2026/04/27-17:40:33.886512 7f57977fe6c0 Level-0 table #53: 0 bytes OK
2026/04/27-17:40:33.922981 7f57977fe6c0 Delete type=0 #51
2026/04/27-17:40:34.016271 7f57977fe6c0 Manual compaction at level-0 from '!actors!6RZ6IzJUHm4dB5Ut' @ 72057594037927935 : 1 .. '!folders!MbFQgPdF6Gtbj5AU' @ 0 : 0; will stop at (end)
+11
View File
@@ -0,0 +1,11 @@
2026/04/26-21:30:37.703085 7f57a4fed6c0 Delete type=3 #1
2026/04/26-22:44:03.483917 7f57977fe6c0 Level-0 table #48: started
2026/04/26-22:44:03.483998 7f57977fe6c0 Level-0 table #48: 0 bytes OK
2026/04/26-22:44:03.490106 7f57977fe6c0 Delete type=0 #46
2026/04/26-22:44:03.513451 7f57977fe6c0 Manual compaction at level-0 from '!actors!6RZ6IzJUHm4dB5Ut' @ 72057594037927935 : 1 .. '!folders!MbFQgPdF6Gtbj5AU' @ 0 : 0; will stop at '!folders!MbFQgPdF6Gtbj5AU' @ 37 : 1
2026/04/26-22:44:03.513463 7f57977fe6c0 Compacting 1@0 + 0@1 files
2026/04/26-22:44:03.517217 7f57977fe6c0 Generated table #49@0: 36 keys, 35733 bytes
2026/04/26-22:44:03.517247 7f57977fe6c0 Compacted 1@0 + 0@1 files => 35733 bytes
2026/04/26-22:44:03.523835 7f57977fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/04/26-22:44:03.523973 7f57977fe6c0 Delete type=2 #18
2026/04/26-22:44:03.555713 7f57977fe6c0 Manual compaction at level-0 from '!folders!MbFQgPdF6Gtbj5AU' @ 37 : 1 .. '!folders!MbFQgPdF6Gtbj5AU' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
Binary file not shown.
View File
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000015 MANIFEST-000092
+7 -15
View File
@@ -1,15 +1,7 @@
2026/04/11-15:27:57.633319 7ff3bf3fe6c0 Recovering log #13 2026/04/27-17:34:57.986104 7f5797fff6c0 Recovering log #89
2026/04/11-15:27:57.644646 7ff3bf3fe6c0 Delete type=3 #11 2026/04/27-17:34:57.996510 7f5797fff6c0 Delete type=3 #87
2026/04/11-15:27:57.644712 7ff3bf3fe6c0 Delete type=0 #13 2026/04/27-17:34:57.996596 7f5797fff6c0 Delete type=0 #89
2026/04/11-15:29:26.001386 7ff3bdbfb6c0 Level-0 table #18: started 2026/04/27-17:40:33.849202 7f57977fe6c0 Level-0 table #95: started
2026/04/11-15:29:26.005135 7ff3bdbfb6c0 Level-0 table #18: 3095 bytes OK 2026/04/27-17:40:33.849252 7f57977fe6c0 Level-0 table #95: 0 bytes OK
2026/04/11-15:29:26.012219 7ff3bdbfb6c0 Delete type=0 #16 2026/04/27-17:40:33.886306 7f57977fe6c0 Delete type=0 #93
2026/04/11-15:29:26.034958 7ff3bdbfb6c0 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/27-17:40:34.016257 7f57977fe6c0 Manual compaction at level-0 from '!scenes!0iGCRqkdJKjmmbl4' @ 72057594037927935 : 1 .. '!scenes.levels!X3XJg7raEXtOFOtj.defaultLevel0000' @ 0 : 0; will stop at (end)
2026/04/11-15:29:26.035011 7ff3bdbfb6c0 Manual compaction at level-1 from '!scenes!Jr7lGxYk2RETlXRv' @ 72057594037927935 : 1 .. '!scenes.tokens.delta.items!Jr7lGxYk2RETlXRv.6urwC5SVcou6UOAG.CTg4yBE12iMee1RU.BYT1CrA37R3Og0nu' @ 0 : 0; will stop at '!scenes.tokens.delta.items!Jr7lGxYk2RETlXRv.6urwC5SVcou6UOAG.CTg4yBE12iMee1RU.BYT1CrA37R3Og0nu' @ 31 : 1
2026/04/11-15:29:26.035018 7ff3bdbfb6c0 Compacting 1@1 + 1@2 files
2026/04/11-15:29:26.038199 7ff3bdbfb6c0 Generated table #19@1: 7 keys, 3095 bytes
2026/04/11-15:29:26.038215 7ff3bdbfb6c0 Compacted 1@1 + 1@2 files => 3095 bytes
2026/04/11-15:29:26.044152 7ff3bdbfb6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/11-15:29:26.044236 7ff3bdbfb6c0 Delete type=2 #10
2026/04/11-15:29:26.044341 7ff3bdbfb6c0 Delete type=2 #18
2026/04/11-15:29:26.064030 7ff3bdbfb6c0 Manual compaction at level-1 from '!scenes.tokens.delta.items!Jr7lGxYk2RETlXRv.6urwC5SVcou6UOAG.CTg4yBE12iMee1RU.BYT1CrA37R3Og0nu' @ 31 : 1 .. '!scenes.tokens.delta.items!Jr7lGxYk2RETlXRv.6urwC5SVcou6UOAG.CTg4yBE12iMee1RU.BYT1CrA37R3Og0nu' @ 0 : 0; will stop at (end)
+11 -8
View File
@@ -1,8 +1,11 @@
2026/04/11-15:27:20.576678 7f20ecbfc6c0 Recovering log #8 2026/04/26-21:30:37.754039 7f5797fff6c0 Delete type=3 #1
2026/04/11-15:27:20.586717 7f20ecbfc6c0 Delete type=3 #6 2026/04/26-22:44:03.507044 7f57977fe6c0 Level-0 table #90: started
2026/04/11-15:27:20.586780 7f20ecbfc6c0 Delete type=0 #8 2026/04/26-22:44:03.507095 7f57977fe6c0 Level-0 table #90: 0 bytes OK
2026/04/11-15:27:31.330627 7f1e4ffff6c0 Level-0 table #14: started 2026/04/26-22:44:03.513200 7f57977fe6c0 Delete type=0 #88
2026/04/11-15:27:31.330649 7f1e4ffff6c0 Level-0 table #14: 0 bytes OK 2026/04/26-22:44:03.545324 7f57977fe6c0 Manual compaction at level-0 from '!scenes!0iGCRqkdJKjmmbl4' @ 72057594037927935 : 1 .. '!scenes.levels!X3XJg7raEXtOFOtj.defaultLevel0000' @ 0 : 0; will stop at '!scenes.levels!X3XJg7raEXtOFOtj.defaultLevel0000' @ 61 : 1
2026/04/11-15:27:31.336642 7f1e4ffff6c0 Delete type=0 #12 2026/04/26-22:44:03.545342 7f57977fe6c0 Compacting 1@0 + 0@1 files
2026/04/11-15:27:31.336792 7f1e4ffff6c0 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/26-22:44:03.549127 7f57977fe6c0 Generated table #91@0: 4 keys, 1746 bytes
2026/04/11-15:27:31.347189 7f1e4ffff6c0 Manual compaction at level-1 from '!scenes!Jr7lGxYk2RETlXRv' @ 72057594037927935 : 1 .. '!scenes.tokens.delta.items!Jr7lGxYk2RETlXRv.6urwC5SVcou6UOAG.CTg4yBE12iMee1RU.BYT1CrA37R3Og0nu' @ 0 : 0; will stop at (end) 2026/04/26-22:44:03.549142 7f57977fe6c0 Compacted 1@0 + 0@1 files => 1746 bytes
2026/04/26-22:44:03.555374 7f57977fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/04/26-22:44:03.555539 7f57977fe6c0 Delete type=2 #85
2026/04/26-22:44:03.555771 7f57977fe6c0 Manual compaction at level-0 from '!scenes.levels!X3XJg7raEXtOFOtj.defaultLevel0000' @ 61 : 1 .. '!scenes.levels!X3XJg7raEXtOFOtj.defaultLevel0000' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
+654
View File
@@ -0,0 +1,654 @@
[
{
"name": "Niels Bielke",
"concept": "LARTISTE 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 cest son ego qui \nse retrouva taillé en pièce. Il sobstina pendant plusieurs années, voyant toutes les portes se fermer \ndevant lui petit à petit. Sombrant dans le mépris de lui-même et lalcool conjointement, Niels \nne connut son salut que grâce à lintervention dErnest, qui sauva littéralement la vie de lartiste \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 à lalcool. Niels porte les stigmates dun \nhomme ayant commis des abus  en particulier un tic nerveux dont il narrive 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 dun test dune Spécialisation d’Âme, Niels peut relancer les\n2d8 2 fois au cours dun même scénario, mais doit conserver le\ndernier résultat.",
"narratif": "Niels est en mesure dinfluer sur la prise de décision dune 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 dune impasse.\nLattention du vétéran fut attirée par l’être artificiel alors quun groupe d’étudiants sortaient hilares \nde la ruelle sombre. Lautomate avait manifestement été passé à tabac. Après plusieurs semaines \ndurant lesquelles personne nen revendiqua la propriété, Ernest prit sur lui de payer ses réparations.\nMalgré sa remise en état de marche, lautomate semblait ne pas conserver de souvenir de sa « vie » \npassée. Cest ainsi quil entra au service dErnest en tant quassistant-archiviste au sein de lagence \ndu Lys blanc. Baptisé Polyphème, un nom grec en référence à Ajax, lautomate le plus célèbre de \nla Cité, larchiviste est désormais la propriété dErnest.\nDans les faits, lautomate est un membre à part entière de lagence, 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 dun test dAppréciation, de Merveilleux technologique, de\nTraitement ou de Traque, Polyphème peut relancer les 2d8 2 fois au\ncours dun même scénario, mais doit conserver le dernier résultat.",
"narratif": "Lesprit de Polpyphème quitte son enveloppe corporelle et se déplace\nde 8 mètres par tour pendant 4 tours, dans nimporte quelle direction.\nDe cette manière, lesprit est invisible et peut ignorer tous les obstacles.\nSes sens restent toutefois les mêmes.\nExemples : Polyphème accède aux toits dune maison ou il peut aller\nen repérage dans la pièce voisine. Si un éboulis bloque le passage dun\ntunnel, Polyphème peut voir sur quelle distance et sil 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 \nsillustra 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 locca-\nsion dexaucer son rêve en tant que conseillère du roi Béhanzin au cours dune 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 la 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 dun test dAppréciation, dAttraction, d’Échauffourée ou de\nFaveur, Sèdami peut relancer les 2d8 2 fois au cours dun même\nscénario, mais doit conserver le dernier résultat.",
"narratif": "Sèdami est capable de percevoir les pensées superficielles dun tiers.\nDe cette manière, elle peut comprendre l’état émotionnel dune per-\nsonne, voire capter une image ou un mot (à lappréciation du narra-\nteur) dans son esprit, simplement en lobservant.\nExemples : Sèdami suspecte une tentative de meurtre et perçoit un flacon\nde cyanure dans lesprit dun domestique. Malgré un faciès contenu, elle\nsaisit quun 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": "LESCROC",
"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 à sintroduire 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 à loccasion. Cest dans lun de ces \nlieux interlopes de la cité que le jeune chinois rencontra Ernest, le directeur de lagence 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, dintégrer lagence 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 sinvestit dailleurs 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 dun même\nscénario et choisir de conserver le résultat quil 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 quil 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": "LOUVRIER",
"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 douvriers 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 dune machine emporta Rubben et priva Elemiah de deux \ndoigts de sa main gauche. Depuis, louvrier survivant sest 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 dune \ntrentaine danné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 dailleurs encore plus souvent juste depuis son accident , et reste très apprécié de sa communauté, \nmême sil travaille aujourdhui comme manutentionnaire aux galeries Sabline.\nSubjugué par larchitecture 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 à sen émerveiller. Une manière pour lui \ndexpérimenter ce que son frère ne pourra jamais faire.",
"anomalie": {
"nom": "Tarot divinatoire",
"niveau": 2,
"technique": "Lors dun test dune Spécialisation de Cœur, Elemiah gagne la pos-\nsibilité de relancer les 2d8 2 fois au cours dun 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.\nLinformation reste soumise à interprétation.\nExemples : Elemiah cherche à savoir où sera sa cible le lendemain. Il\nveut connaître lhistoire dun 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 lespace 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 quavec l’âge, elle choisit de devenir \npilote daéronef afin de pouvoir caresser lespoir de rejoindre un jour ces destinations chères à son cœur. \nNadeja intégra lacadé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 quelle 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 \ndaé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 lentreprise Columbia. De fait, elle peut paraître sérieuse de prime abord, certains diraient farouche. \nEn réalité, elle ne supporte pas quon lui manque de respect.",
"anomalie": {
"nom": "Télékinésie",
"niveau": 2,
"technique": "Lors dun test dune Spécialisation de Corps, Nadeja peut relancer\nles 2d8 2 fois au cours dun 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 (nimporte quelle\ndirection) pendant 2 tours.\nExemples : Nadeja peut déplacer une cuillère pour la faire tomber\ndune 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 lingénierie de pointe, \nspécialement dans lutilisation 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 à luniversité de Célestopol, dont elle \nest dailleurs sortie major de promotion. Pourtant, personne navait 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 quil soit. Par ailleurs, cest 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 dune Spécialisation dEsprit, Maribel peut relancer\nles 2d8 2 fois au cours dun même scénario, mais doit conserver le\ndernier résultat.",
"narratif": "Maribel entre en contact avec lesprit dun défunt. Elle peut lui\nposer une question fermée (réponse par oui ou non).\nExemples : Maribel interroge la victime dun meurtre. Elle consulte\nlesprit dun 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 aujourdhui prit les armes pour défendre sa patrie en tant que \nconscrite. Blessée à l’œil, quelle finit par perdre faute de soins adaptés, elle ne trouva pas le réconfort \ndans lestime 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 daller 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 lancienne 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 quelle apprécie tant et durant lesquelles elle se fait fort de collecter nombre dinforma-\ntions utiles à lagence, mêlant ainsi lutile à lagréable.",
"anomalie": {
"nom": "Illusion",
"niveau": 2,
"technique": "Lors dun test de Coercition, d’Échauffourée, dEffacement ou de\nTraque, Wiktoria peut relancer les 2d8 2 fois au cours dun 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 dun chat qui miaule\nou dun 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
View 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 : LARTISTE 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 cest son ego qui \nse retrouva taillé en pièce. Il sobstina pendant plusieurs années, voyant toutes les portes se fermer \ndevant lui petit à petit. Sombrant dans le mépris de lui-même et lalcool conjointement, Niels \nne connut son salut que grâce à lintervention dErnest, qui sauva littéralement la vie de lartiste \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 à lalcool. Niels porte les stigmates dun \nhomme ayant commis des abus  en particulier un tic nerveux dont il narrive 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 dun test dune Spécialisation d’Âme, Niels peut relancer les \n2d8 2 fois au cours dun même scénario, mais doit conserver le \ndernier résultat.\nNarratif\nNiels est en mesure dinfluer sur la prise de décision dune 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\n1\n10 min\nSuperficiel\n1\n10 min\nLéger\n2\n30 min\nModéré\n2\n30 min\nGrave\n3\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 dune impasse.\nLattention du vétéran fut attirée par l’être artificiel alors quun groupe d’étudiants sortaient hilares \nde la ruelle sombre. Lautomate avait manifestement été passé à tabac. Après plusieurs semaines \ndurant lesquelles personne nen revendiqua la propriété, Ernest prit sur lui de payer ses réparations.\nMalgré sa remise en état de marche, lautomate semblait ne pas conserver de souvenir de sa « vie » \npassée. Cest ainsi quil entra au service dErnest en tant quassistant-archiviste au sein de lagence \ndu Lys blanc. Baptisé Polyphème, un nom grec en référence à Ajax, lautomate le plus célèbre de \nla Cité, larchiviste est désormais la propriété dErnest.\nDans les faits, lautomate est un membre à part entière de lagence, 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 dun test dAppréciation, de Merveilleux technologique, de \nTraitement ou de Traque, Polyphème peut relancer les 2d8 2 fois au \ncours dun même scénario, mais doit conserver le dernier résultat.\nNarratif\nLesprit de Polpyphème quitte son enveloppe corporelle et se déplace \nde 8 mètres par tour pendant 4 tours, dans nimporte quelle direction. \nDe cette manière, lesprit est invisible et peut ignorer tous les obstacles. \nSes sens restent toutefois les mêmes.\nExemples : Polyphème accède aux toits dune maison ou il peut aller \nen repérage dans la pièce voisine. Si un éboulis bloque le passage dun \ntunnel, Polyphème peut voir sur quelle distance et sil 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\n1\n10 min\nSuperficiel\n1\n10 min\nLéger\n2\n30 min\nModéré\n2\n30 min\nGrave\n3\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 \nsillustra 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 locca-\nsion dexaucer son rêve en tant que conseillère du roi Béhanzin au cours dune 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 la 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 dun test dAppréciation, dAttraction, d’Échauffourée ou de \nFaveur, Sèdami peut relancer les 2d8 2 fois au cours dun même \nscénario, mais doit conserver le dernier résultat.\nNarratif\nSèdami est capable de percevoir les pensées superficielles dun tiers. \nDe cette manière, elle peut comprendre l’état émotionnel dune per-\nsonne, voire capter une image ou un mot (à lappréciation du narra-\nteur) dans son esprit, simplement en lobservant.\nExemples : Sèdami suspecte une tentative de meurtre et perçoit un flacon \nde cyanure dans lesprit dun domestique. Malgré un faciès contenu, elle \nsaisit quun 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\n1\n10 min\nSuperficiel\n1\n10 min\nLéger\n2\n30 min\nModéré\n2\n30 min\nGrave\n3\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 : LESCROC\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 à sintroduire 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 à loccasion. Cest dans lun de ces \nlieux interlopes de la cité que le jeune chinois rencontra Ernest, le directeur de lagence 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, dintégrer lagence 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 sinvestit dailleurs 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 dun même \nscénario et choisir de conserver le résultat quil 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 quil 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\n1\n10 min\nSuperficiel\n1\n10 min\nLéger\n2\n30 min\nModéré\n2\n30 min\nGrave\n3\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 : LOUVRIER\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 douvriers 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 dune machine emporta Rubben et priva Elemiah de deux \ndoigts de sa main gauche. Depuis, louvrier survivant sest 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 dune \ntrentaine danné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 dailleurs encore plus souvent juste depuis son accident , et reste très apprécié de sa communauté, \nmême sil travaille aujourdhui comme manutentionnaire aux galeries Sabline.\nSubjugué par larchitecture 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 à sen émerveiller. Une manière pour lui \ndexpérimenter ce que son frère ne pourra jamais faire.\nANOMALIE\nTarot \ndivinatoire 2\nTechnique\nLors dun test dune Spécialisation de Cœur, Elemiah gagne la pos-\nsibilité de relancer les 2d8 2 fois au cours dun 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. \nLinformation reste soumise à interprétation.\nExemples : Elemiah cherche à savoir où sera sa cible le lendemain. Il \nveut connaître lhistoire dun 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\n1\n10 min\nSuperficiel\n1\n10 min\nLéger\n2\n30 min\nModéré\n2\n30 min\nGrave\n3\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 lespace 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 quavec l’âge, elle choisit de devenir \npilote daéronef afin de pouvoir caresser lespoir de rejoindre un jour ces destinations chères à son cœur. \nNadeja intégra lacadé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 quelle 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 \ndaé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 lentreprise Columbia. De fait, elle peut paraître sérieuse de prime abord, certains diraient farouche. \nEn réalité, elle ne supporte pas quon lui manque de respect.\nANOMALIE\nTélékinésie 2\nTechnique\nLors dun test dune Spécialisation de Corps, Nadeja peut relancer \nles 2d8 2 fois au cours dun 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 (nimporte quelle \ndirection) pendant 2 tours.\nExemples : Nadeja peut déplacer une cuillère pour la faire tomber \ndune 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\n1\n10 min\nSuperficiel\n1\n10 min\nLéger\n2\n30 min\nModéré\n2\n30 min\nGrave\n3\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 lingénierie de pointe, \nspécialement dans lutilisation 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 à luniversité de Célestopol, dont elle \nest dailleurs sortie major de promotion. Pourtant, personne navait 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 quil soit. Par ailleurs, cest 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 dune Spécialisation dEsprit, Maribel peut relancer \nles 2d8 2 fois au cours dun même scénario, mais doit conserver le \ndernier résultat.\nNarratif\nMaribel entre en contact avec lesprit dun défunt. Elle peut lui \nposer une question fermée (réponse par oui ou non).\nExemples : Maribel interroge la victime dun meurtre. Elle consulte \nlesprit dun 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\n1\n10 min\nSuperficiel\n1\n10 min\nLéger\n2\n30 min\nModéré\n2\n30 min\nGrave\n3\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 aujourdhui prit les armes pour défendre sa patrie en tant que \nconscrite. Blessée à l’œil, quelle finit par perdre faute de soins adaptés, elle ne trouva pas le réconfort \ndans lestime 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 daller 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 lancienne 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 quelle apprécie tant et durant lesquelles elle se fait fort de collecter nombre dinforma-\ntions utiles à lagence, mêlant ainsi lutile à lagréable.\nANOMALIE\nIllusion 2 \nTechnique\nLors dun test de Coercition, d’Échauffourée, dEffacement ou de \nTraque, Wiktoria peut relancer les 2d8 2 fois au cours dun 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 dun chat qui miaule \nou dun 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\n1\n10 min\nSuperficiel\n1\n10 min\nLéger\n2\n30 min\nModéré\n2\n30 min\nGrave\n3\nUne journée\nDramatique\nLe personnage est \nhors-fiction\nAffable (1)\nGrande (1)\nExpérience militaire (1)\n"
}
}
+669
View File
@@ -0,0 +1,669 @@
{
"Niels Bielke": {
"name": "Niels Bielke",
"concept": "LARTISTE 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 dun test dune Spécialisation d’Âme, Niels peut relancer les 2d8 2 fois au cours dun même scénario, mais doit conserver le dernier résultat.",
"narratif": "Niels est en mesure dinfluer sur la prise de décision dune 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 : LARTISTE 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 cest son ego qui se retrouva taillé en pièce. Il sobstina pendant plusieurs années, voyant toutes les portes se fermer devant lui petit à petit. Sombrant dans le mépris de lui-même et lalcool conjointement, Niels ne connut son salut que grâce à lintervention dErnest, qui sauva littéralement la vie de lartiste 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 à lalcool. Niels porte les stigmates dun homme ayant commis des abus en particulier un tic nerveux dont il narrive 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 dun test dAppréciation, de Merveilleux technologique, de Traitement ou de Traque, Polyphème peut relancer les 2d8 2 fois au cours dun même scénario, mais doit conserver le dernier résultat.",
"narratif": "Lesprit de Polpyphème quitte son enveloppe corporelle et se déplace de 8 mètres par tour pendant 4 tours, dans nimporte quelle direction. De cette manière, lesprit est invisible et peut ignorer tous les obstacles. Ses sens restent toutefois les mêmes.",
"exemples": "Polyphème accède aux toits dune maison ou il peut aller en repérage dans la pièce voisine. Si un éboulis bloque le passage dun tunnel, Polyphème peut voir sur quelle distance et sil 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 dune impasse. Lattention du vétéran fut attirée par l’être artificiel alors quun groupe d’étudiants sortaient hilares de la ruelle sombre. Lautomate avait manifestement été passé à tabac. Après plusieurs semaines durant lesquelles personne nen revendiqua la propriété, Ernest prit sur lui de payer ses réparations. Malgré sa remise en état de marche, lautomate semblait ne pas conserver de souvenir de sa « vie » passée. Cest ainsi quil entra au service dErnest en tant quassistant-archiviste au sein de lagence du Lys blanc. Baptisé Polyphème, un nom grec en référence à Ajax, lautomate le plus célèbre de la Cité, larchiviste est désormais la propriété dErnest. Dans les faits, lautomate est un membre à part entière de lagence, 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 dun test dAppréciation, dAttraction, d’Échauffourée ou de Faveur, Sèdami peut relancer les 2d8 2 fois au cours dun même scénario, mais doit conserver le dernier résultat.",
"narratif": "Sèdami est capable de percevoir les pensées superficielles dun tiers. De cette manière, elle peut comprendre l’état émotionnel dune per- sonne, voire capter une image ou un mot (à lappréciation du narra- teur) dans son esprit, simplement en lobservant.",
"exemples": "Sèdami suspecte une tentative de meurtre et perçoit un flacon de cyanure dans lesprit dun domestique. Malgré un faciès contenu, elle saisit quun 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 sillustra 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 locca- sion dexaucer son rêve en tant que conseillère du roi Béhanzin au cours dune 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 la 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": "LESCROC",
"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 dun même scénario et choisir de conserver le résultat quil 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 quil tourne au coin de la rue.",
"note": null
},
"aspects": [
"Aime le jeu (1)",
"Belle gueule (2)"
],
"background": "BAO WANG : LESCROC 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 à sintroduire 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 à loccasion. Cest dans lun de ces lieux interlopes de la cité que le jeune chinois rencontra Ernest, le directeur de lagence 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, dintégrer lagence 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 sinvestit dailleurs 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": "LOUVRIER",
"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 dun test dune Spécialisation de Cœur, Elemiah gagne la pos- sibilité de relancer les 2d8 2 fois au cours dun 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. Linformation reste soumise à interprétation.",
"exemples": "Elemiah cherche à savoir où sera sa cible le lendemain. Il veut connaître lhistoire dun voisin.",
"note": null
},
"aspects": [
"Digne de confiance (2)",
"Robuste (1)"
],
"background": "ELEMIAH COWEN : LOUVRIER 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 douvriers 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 dune machine emporta Rubben et priva Elemiah de deux doigts de sa main gauche. Depuis, louvrier survivant sest 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 dune trentaine danné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 dailleurs encore plus souvent juste depuis son accident , et reste très apprécié de sa communauté, même sil travaille aujourdhui comme manutentionnaire aux galeries Sabline. Subjugué par larchitecture 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 à sen émerveiller. Une manière pour lui dexpé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 dun test dune Spécialisation de Corps, Nadeja peut relancer les 2d8 2 fois au cours dun 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 (nimporte quelle direction) pendant 2 tours.",
"exemples": "Nadeja peut déplacer une cuillère pour la faire tomber dune 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 lespace 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 quavec l’âge, elle choisit de devenir pilote daéronef afin de pouvoir caresser lespoir de rejoindre un jour ces destinations chères à son cœur. Nadeja intégra lacadé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 quelle 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 daé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 lentreprise Columbia. De fait, elle peut paraître sérieuse de prime abord, certains diraient farouche. En réalité, elle ne supporte pas quon 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 dune Spécialisation dEsprit, Maribel peut relancer les 2d8 2 fois au cours dun même scénario, mais doit conserver le dernier résultat.",
"narratif": "Maribel entre en contact avec lesprit dun défunt. Elle peut lui poser une question fermée (réponse par oui ou non).",
"exemples": "Maribel interroge la victime dun meurtre. Elle consulte lesprit dun 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 lingénierie de pointe, spécialement dans lutilisation 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 à luniversité de Célestopol, dont elle est dailleurs sortie major de promotion. Pourtant, personne navait 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 quil soit. Par ailleurs, cest 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 dun test de Coercition, d’Échauffourée, dEffacement ou de Traque, Wiktoria peut relancer les 2d8 2 fois au cours dun 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 dun chat qui miaule ou dun 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 aujourdhui prit les armes pour défendre sa patrie en tant que conscrite. Blessée à l’œil, quelle finit par perdre faute de soins adaptés, elle ne trouva pas le réconfort dans lestime 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 daller 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 lancienne 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 quelle apprécie tant et durant lesquelles elle se fait fort de collecter nombre dinforma- tions utiles à lagence, mêlant ainsi lutile à lagréable.",
"description_physique": null,
"description_psychologique": null,
"weapons": [],
"equipment": []
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Some files were not shown because too many files have changed in this diff Show More