Import de personnages du précédent système
Release Creation / build (release) Successful in 1m21s

This commit is contained in:
2026-04-27 22:29:49 +02:00
parent 58478d56ea
commit 8123b53f75
32 changed files with 3846 additions and 84 deletions
+368
View File
@@ -0,0 +1,368 @@
#!/usr/bin/env python3
"""
convert-old-system.py
Convertit les fichiers JSON d'acteurs exportés de l'ancien système Célestopol 1922
(system id: celestopol1922) vers le format du nouveau système (fvtt-celestopol).
Usage:
python3 convert-old-system.py <fichier.json> [fichier2.json ...]
Le fichier converti est écrit à côté du fichier source sous le nom :
<nom>-converted.json
"""
import json
import os
import re
import sys
import uuid
# ─── Mapping des types d'anomalies (préfixe "CEL1922.opt." → clé nouveau système) ─────
ANOMALY_TYPE_MAP = {
"none": "none",
"entropie": "entropie",
"communicationaveclesmorts": "communicationaveclesmorts",
"illusion": "illusion",
"suggestion": "suggestion",
"tarotdivinatoire": "tarotdivinatoire",
"telekinesie": "telekinesie",
"telepathie": "telepathie",
"voyageastral": "voyageastral",
}
# Clé anomaly vide → "none"
VALID_ANOMALY_TYPES = set(ANOMALY_TYPE_MAP.values())
# ─── Domaines (stats) ──────────────────────────────────────────────────────────────────
STATS = ["ame", "corps", "coeur", "esprit"]
# Compétences par domaine (dans l'ordre du nouveau système)
SKILLS = {
"ame": ["artifice", "attraction", "coercition", "faveur"],
"corps": ["echauffouree", "effacement", "mobilite", "prouesse"],
"coeur": ["appreciation", "arts", "inspiration", "traque"],
"esprit": ["instruction", "mtechnologique", "raisonnement", "traitement"],
}
def make_id():
"""Génère un identifiant Foundry-compatible (16 chars hex)."""
return uuid.uuid4().hex[:16]
def strip_prefix(label: str) -> str:
"""Extrait la clé depuis 'CEL1922.opt.<clé>'."""
return label.split(".")[-1]
def resolve_anomaly_type(old_system: dict) -> str:
"""
Résout le type d'anomalie depuis l'ancien système.
old_system.anomaly est un int (index dans anomalytypes[]).
"""
anomalytypes = old_system.get("skill", {}).get("anomalytypes", [])
idx = old_system.get("anomaly", 0)
try:
idx = int(idx)
except (ValueError, TypeError):
idx = 0
if idx == 0 or not anomalytypes:
return "none"
if idx < len(anomalytypes):
raw = strip_prefix(anomalytypes[idx])
return ANOMALY_TYPE_MAP.get(raw, "none")
return "none"
def resolve_anomaly_type_from_name(item_name: str) -> str:
"""
Infère le type d'anomalie depuis le nom de l'item (ex: 'Entropie 1''entropie').
Fallback si le mapping par index est insuffisant.
"""
# Normalise : minuscules, retire accents, retire espaces et chiffres
name_clean = item_name.lower().strip()
name_clean = re.sub(r"\s*\d+\s*$", "", name_clean).strip() # retire le niveau en fin
name_clean = re.sub(r"\s+", "", name_clean) # retire tous les espaces
# Normalisation des accents courants
replacements = [
("é", "e"), ("è", "e"), ("ê", "e"), ("ë", "e"),
("à", "a"), ("â", "a"), ("ä", "a"),
("î", "i"), ("ï", "i"),
("ô", "o"), ("ö", "o"),
("û", "u"), ("ù", "u"), ("ü", "u"),
("ç", "c"),
]
for src, dst in replacements:
name_clean = name_clean.replace(src, dst)
for key in ANOMALY_TYPE_MAP:
if key == "none":
continue
if key in name_clean or name_clean in key:
return key
return "none"
def convert_stats_character(old_skill: dict, warnings: list) -> dict:
"""Convertit system.skill (ancien) → system.stats (nouveau) pour un PJ."""
stats = {}
for stat in STATS:
old_stat = old_skill.get(stat, {})
new_stat = {
"label": stat,
"res": int(old_stat.get("res", 0) or 0),
}
for skill in SKILLS[stat]:
old_sk = old_stat.get(skill, {})
val = old_sk.get("value", 0)
try:
val = int(val) if val is not None else 0
except (ValueError, TypeError):
warnings.append(f"Valeur de compétence invalide pour {stat}.{skill}: {val!r} → 0")
val = 0
new_stat[skill] = {"label": skill, "value": val}
stats[stat] = new_stat
return stats
def convert_stats_npc(old_skill: dict, warnings: list) -> dict:
"""Convertit system.skill (ancien) → system.stats (nouveau) pour un PNJ."""
stats = {}
for stat in STATS:
old_stat = old_skill.get(stat, {})
res = int(old_stat.get("res", 0) or 0)
stats[stat] = {
"label": stat,
"res": res,
"actuel": res, # actuel = res par défaut
}
return stats
def convert_attributs(old_attributs: dict) -> dict:
"""Convertit les attributs du vieux format plat vers SchemaField {value, max}."""
return {
"entregent": {
"value": int(old_attributs.get("entregent", 0) or 0),
"max": int(old_attributs.get("entregentmax", 0) or 0),
},
"fortune": {
"value": int(old_attributs.get("fortune", 0) or 0),
"max": int(old_attributs.get("fortunemax", 0) or 0),
},
"reve": {
"value": int(old_attributs.get("reve", 0) or 0),
"max": int(old_attributs.get("revemax", 0) or 0),
},
"vision": {
"value": int(old_attributs.get("vision", 0) or 0),
"max": int(old_attributs.get("visionmax", 0) or 0),
},
}
def convert_item(item: dict, warnings: list) -> dict | None:
"""
Convertit un item de l'ancien format vers le nouveau.
Retourne None si l'item doit être ignoré (type inconnu sans données utiles).
"""
old_type = item.get("type", "")
old_sys = item.get("system", {})
name = item.get("name", "Sans nom")
new_item = {
"_id": make_id(),
"name": name,
"img": item.get("img", "icons/svg/item-bag.svg"),
"effects": [],
"flags": {},
"sort": 0,
}
if old_type == "anomaly":
level = 0
try:
level = int(old_sys.get("value", 1) or 1)
except (ValueError, TypeError):
level = 1
level = max(1, min(4, level))
# Résolution du subtype : l'ancien système le stocke mal ("weapon"), on l'infère du nom
subtype = resolve_anomaly_type_from_name(name)
if subtype == "none":
warnings.append(f"Impossible de déterminer le type d'anomalie depuis '{name}', 'none' utilisé")
new_item["type"] = "anomaly"
new_item["system"] = {
"subtype": subtype,
"level": level,
"usesRemaining": level,
"technique": old_sys.get("technique", "") or "",
"narratif": old_sys.get("narratif", "") or "",
}
elif old_type == "aspect":
valeur = 0
try:
valeur = int(old_sys.get("value", 0) or 0)
except (ValueError, TypeError):
valeur = 0
new_item["type"] = "aspect"
new_item["system"] = {
"valeur": valeur,
"description": old_sys.get("narratif", "") or old_sys.get("technique", "") or "",
}
elif old_type == "item":
old_subtype = (old_sys.get("subtype") or "").lower().strip()
damage = old_sys.get("damage", "") or ""
protect = old_sys.get("protection", "") or ""
if old_subtype == "weapon" or (damage and not protect):
new_item["type"] = "weapon"
new_item["system"] = {
"type": "melee",
"degats": "0",
"portee": "contact",
"equipped": False,
"description": old_sys.get("technique", "") or old_sys.get("narratif", "") or "",
}
if damage:
warnings.append(f"Item '{name}' : valeur damage='{damage}' non convertie automatiquement, à saisir manuellement")
elif old_subtype == "armor" or (protect and not damage):
new_item["type"] = "armure"
new_item["system"] = {
"protection": 1,
"malus": 1,
"equipped": False,
"description": old_sys.get("technique", "") or old_sys.get("narratif", "") or "",
}
if protect:
warnings.append(f"Item '{name}' : valeur protection='{protect}' non convertie automatiquement, à saisir manuellement")
else:
# Équipement générique
new_item["type"] = "equipment"
new_item["system"] = {
"description": old_sys.get("technique", "") or old_sys.get("narratif", "") or "",
}
else:
warnings.append(f"Type d'item inconnu '{old_type}' pour '{name}', ignoré")
return None
return new_item
def convert_actor(old: dict) -> tuple[dict, list[str]]:
"""Convertit un acteur complet. Retourne (new_actor, warnings)."""
warnings = []
actor_type = old.get("type", "character")
old_sys = old.get("system", {})
old_skill = old_sys.get("skill", {})
new_sys = {}
# ── Champs communs ──────────────────────────────────────────────────────────────
new_sys["concept"] = old_sys.get("concept", "") or ""
new_sys["description"] = old_sys.get("description", "") or "" # gardé dans metier/concept
new_sys["metier"] = old_sys.get("metier", "") or old_sys.get("concept", "") or ""
new_sys["faction"] = old_sys.get("faction", "") or ""
# ── Blessures / Destin / Spleen ─────────────────────────────────────────────────
new_sys["blessures"] = {"lvl": int(old_sys.get("blessures", {}).get("lvl", 0) or 0)}
new_sys["destin"] = {"lvl": int(old_sys.get("destin", {}).get("lvl", 0) or 0)}
new_sys["spleen"] = {"lvl": int(old_sys.get("spleen", {}).get("lvl", 0) or 0)}
# ── Stats ───────────────────────────────────────────────────────────────────────
if actor_type == "character":
new_sys["stats"] = convert_stats_character(old_skill, warnings)
# Anomalie : type depuis l'index, niveau depuis anomalyval
anomaly_type = resolve_anomaly_type(old_sys)
anomaly_val = int(old_sys.get("anomalyval", 0) or 0)
new_sys["anomaly"] = {"type": anomaly_type, "value": anomaly_val}
# Attributs
new_sys["attributs"] = convert_attributs(old_sys.get("attributs", {}))
# Biographie → historique
new_sys["historique"] = old_sys.get("description", "") or ""
new_sys["descriptionPhysique"] = ""
new_sys["descriptionPsychologique"] = ""
new_sys["initiative"] = 0
# Factions (vide)
new_sys["factions"] = old_sys.get("factions", {})
elif actor_type == "npc":
new_sys["stats"] = convert_stats_npc(old_skill, warnings)
new_sys["npcType"] = old_sys.get("npcType", "standard") or "standard"
new_sys["historique"] = old_sys.get("description", "") or ""
# ── Items ───────────────────────────────────────────────────────────────────────
new_items = []
for item in old.get("items", []):
converted = convert_item(item, warnings)
if converted:
new_items.append(converted)
new_actor = {
"_id": make_id(),
"name": old.get("name", "Personnage sans nom"),
"type": actor_type,
"img": old.get("img", "icons/svg/mystery-man.svg"),
"system": new_sys,
"items": new_items,
"effects": [],
"folder": None,
"flags": {},
"prototypeToken": old.get("prototypeToken", {}),
}
return new_actor, warnings
def convert_file(path: str) -> None:
"""Lit path, convertit, écrit <base>-converted.json."""
print(f"\n{'' * 60}")
print(f"Traitement : {path}")
with open(path, "r", encoding="utf-8") as f:
old = json.load(f)
new_actor, warnings = convert_actor(old)
for w in warnings:
print(f"{w}")
base, _ = os.path.splitext(path)
out_path = f"{base}-converted.json"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(new_actor, f, ensure_ascii=False, indent=2)
print(f" ✓ Écrit : {out_path}")
print(f" type={new_actor['type']} items={len(new_actor['items'])} avertissements={len(warnings)}")
def main():
if len(sys.argv) < 2:
print("Usage: python3 convert-old-system.py <fichier.json> [fichier2.json ...]")
sys.exit(1)
for path in sys.argv[1:]:
if not os.path.isfile(path):
print(f"Fichier introuvable : {path}", file=sys.stderr)
continue
convert_file(path)
print(f"\n{'' * 60}")
print("Conversion terminée.")
if __name__ == "__main__":
main()
@@ -0,0 +1,143 @@
{
"_id": "e9e583fc41454c90",
"name": "Ajax",
"type": "npc",
"img": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/celestopol/ajax.Avatar.webp?1719437847752",
"system": {
"concept": "",
"description": "",
"metier": "",
"faction": "",
"blessures": {
"lvl": 0
},
"destin": {
"lvl": 0
},
"spleen": {
"lvl": 0
},
"stats": {
"ame": {
"label": "ame",
"res": 0,
"actuel": 0
},
"corps": {
"label": "corps",
"res": 0,
"actuel": 0
},
"coeur": {
"label": "coeur",
"res": 0,
"actuel": 0
},
"esprit": {
"label": "esprit",
"res": 0,
"actuel": 0
}
},
"npcType": "standard",
"historique": ""
},
"items": [],
"effects": [],
"folder": null,
"flags": {},
"prototypeToken": {
"name": "Ajax",
"displayName": 0,
"actorLink": false,
"appendNumber": false,
"prependAdjective": false,
"texture": {
"src": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/celestopol/ajax.Token.webp?1719437847752",
"scaleX": 1,
"scaleY": 1,
"offsetX": 0,
"offsetY": 0,
"rotation": 0,
"anchorX": 0.5,
"anchorY": 0.5,
"fit": "contain",
"tint": "#ffffff",
"alphaThreshold": 0.75
},
"width": 1,
"height": 1,
"lockRotation": false,
"rotation": 0,
"alpha": 1,
"disposition": -1,
"displayBars": 0,
"bar1": {
"attribute": null
},
"bar2": {
"attribute": null
},
"light": {
"alpha": 0.5,
"angle": 360,
"bright": 0,
"coloration": 1,
"dim": 0,
"attenuation": 0.5,
"luminosity": 0.5,
"saturation": 0,
"contrast": 0,
"shadows": 0,
"animation": {
"type": null,
"speed": 5,
"intensity": 5,
"reverse": false
},
"darkness": {
"min": 0,
"max": 1
},
"negative": false,
"priority": 0,
"color": null
},
"sight": {
"enabled": true,
"range": 30,
"angle": 360,
"visionMode": "basic",
"color": null,
"attenuation": 0.1,
"brightness": 0,
"saturation": 0,
"contrast": 0
},
"detectionModes": [],
"flags": {},
"randomImg": false,
"occludable": {
"radius": 0
},
"ring": {
"enabled": false,
"colors": {
"ring": null,
"background": null
},
"effects": 1,
"subject": {
"scale": 1,
"texture": null
}
},
"turnMarker": {
"mode": 1,
"animation": null,
"src": null,
"disposition": false
},
"movementAction": null
}
}
@@ -0,0 +1,459 @@
{
"folder": "fbRXVyFLLyOpuiTd",
"name": "Ajax",
"type": "npc",
"img": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/celestopol/ajax.Avatar.webp?1719437847752",
"system": {
"concept": "",
"anomaly": "0",
"anomalyval": 0,
"anomalymax": "0",
"initiative": 0,
"description": "",
"prefs": {
"typeofthrow": {
"check": true,
"choice": "0"
}
},
"prompt": {
"typeofthrow": {
"check": true,
"choice": "0"
},
"configure": {
"numberofdice": 0,
"aspect": 0,
"bonus": 0,
"bonusauspiciousdice": 0,
"typeofthrow": 0,
"aspectskill": 0,
"bonusmalusskill": 0,
"aspectspeciality": 0,
"rolldifficulty": 0,
"bonusmalusspeciality": 0
}
},
"skill": {
"skilltypes": [
"CEL1922.opt.ame",
"CEL1922.opt.attraction",
"CEL1922.opt.artifice",
"CEL1922.opt.coercition",
"CEL1922.opt.faveur",
"CEL1922.opt.corps",
"CEL1922.opt.echauffouree",
"CEL1922.opt.effacement",
"CEL1922.opt.prouesse",
"CEL1922.opt.mobilite",
"CEL1922.opt.coeur",
"CEL1922.opt.appreciation",
"CEL1922.opt.arts",
"CEL1922.opt.inspiration",
"CEL1922.opt.traque",
"CEL1922.opt.esprit",
"CEL1922.opt.instruction",
"CEL1922.opt.mtechnologique",
"CEL1922.opt.raisonnement",
"CEL1922.opt.traitement"
],
"moondicetypes": [
"CEL1922.opt.nouvellelune",
"CEL1922.opt.premiercroissant",
"CEL1922.opt.premierquartier",
"CEL1922.opt.lunegibbeuse",
"CEL1922.opt.lunevoutee",
"CEL1922.opt.derniercroissant",
"CEL1922.opt.dernierquartier",
"CEL1922.opt.pleinelune"
],
"woundstypes": [
"CEL1922.opt.none",
"CEL1922.opt.anodin",
"CEL1922.opt.derisoire",
"CEL1922.opt.negligeable",
"CEL1922.opt.superficiel",
"CEL1922.opt.leger",
"CEL1922.opt.modere",
"CEL1922.opt.grave",
"CEL1922.opt.dramatique"
],
"woundsmalus": [
0,
0,
0,
-1,
-1,
-2,
-2,
-3,
-999
],
"woundsrecup": [
"CEL1922.opt.none",
"CEL1922.1 minute",
"CEL1922.1 minute",
"CEL1922.10 minutes",
"CEL1922.10 minutes",
"CEL1922.30 minutes",
"CEL1922.30 minutes",
"CEL1922.1 day",
"CEL1922.Out of Fiction"
],
"destinytypes": [
"CEL1922.opt.none",
"CEL1922.opt.libel1",
"CEL1922.opt.libel2",
"CEL1922.opt.libel3",
"CEL1922.opt.libel4",
"CEL1922.opt.libel5",
"CEL1922.opt.libel6",
"CEL1922.opt.libel7",
"CEL1922.opt.libel8"
],
"spleentypes": [
"CEL1922.opt.none",
"CEL1922.opt.libel1",
"CEL1922.opt.libel2",
"CEL1922.opt.libel3",
"CEL1922.opt.libel4",
"CEL1922.opt.libel5",
"CEL1922.opt.libel6",
"CEL1922.opt.libel7",
"CEL1922.opt.libel8"
],
"anomalytypes": [
"CEL1922.opt.none",
"CEL1922.opt.entropie",
"CEL1922.opt.communicationaveclesmorts",
"CEL1922.opt.illusion",
"CEL1922.opt.suggestion",
"CEL1922.opt.tarotdivinatoire",
"CEL1922.opt.telekinesie",
"CEL1922.opt.telepathie",
"CEL1922.opt.voyageastral"
],
"ame": {
"res": 0,
"actuel": 0,
"artifice": {
"value": 0
},
"attraction": {
"value": 0
},
"coercition": {
"value": 0
},
"faveur": {
"value": 0
}
},
"corps": {
"res": 0,
"actuel": 0,
"echauffouree": {
"value": 0
},
"effacement": {
"value": 0
},
"prouesse": {
"value": 0
},
"mobilite": {
"value": 0
}
},
"coeur": {
"res": 0,
"actuel": 0,
"appreciation": {
"value": 0
},
"arts": {
"value": 0
},
"inspiration": {
"value": 0
},
"traque": {
"value": 0
}
},
"esprit": {
"res": 0,
"actuel": 0,
"instruction": {
"value": 0
},
"mtechnologique": {
"value": 0
},
"raisonnement": {
"value": 0
},
"traitement": {
"value": 0
}
},
"aspecttypes": [
"CEL1922.opt.none",
"CEL1922.opt.aspect1",
"CEL1922.opt.aspect2",
"CEL1922.opt.aspect3",
"CEL1922.opt.aspect4",
"CEL1922.opt.aspect5",
"CEL1922.opt.aspect6",
"CEL1922.opt.aspect7",
"CEL1922.opt.aspect8"
]
},
"blessures": {
"lvl": 0,
"blessure_1": {
"check": false,
"bonus": 0,
"malus": 0
},
"blessure_2": {
"check": false,
"bonus": 0,
"malus": 0
},
"blessure_3": {
"check": false,
"bonus": 0,
"malus": -1
},
"blessure_4": {
"check": false,
"bonus": 0,
"malus": -1
},
"blessure_5": {
"check": false,
"bonus": 0,
"malus": -2
},
"blessure_6": {
"check": false,
"bonus": 0,
"malus": -2
},
"blessure_7": {
"check": false,
"bonus": 0,
"malus": -3
},
"blessure_8": {
"check": false,
"bonus": 0,
"malus": -999
}
},
"destin": {
"lvl": 0,
"destin_1": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_2": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_3": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_4": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_5": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_6": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_7": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_8": {
"check": false,
"bonus": 0,
"malus": 0
}
},
"spleen": {
"lvl": 0,
"spleen_1": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_2": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_3": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_4": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_5": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_6": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_7": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_8": {
"check": false,
"bonus": 0,
"malus": 0
}
}
},
"prototypeToken": {
"name": "Ajax",
"displayName": 0,
"actorLink": false,
"appendNumber": false,
"prependAdjective": false,
"texture": {
"src": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/celestopol/ajax.Token.webp?1719437847752",
"scaleX": 1,
"scaleY": 1,
"offsetX": 0,
"offsetY": 0,
"rotation": 0,
"anchorX": 0.5,
"anchorY": 0.5,
"fit": "contain",
"tint": "#ffffff",
"alphaThreshold": 0.75
},
"width": 1,
"height": 1,
"lockRotation": false,
"rotation": 0,
"alpha": 1,
"disposition": -1,
"displayBars": 0,
"bar1": {
"attribute": null
},
"bar2": {
"attribute": null
},
"light": {
"alpha": 0.5,
"angle": 360,
"bright": 0,
"coloration": 1,
"dim": 0,
"attenuation": 0.5,
"luminosity": 0.5,
"saturation": 0,
"contrast": 0,
"shadows": 0,
"animation": {
"type": null,
"speed": 5,
"intensity": 5,
"reverse": false
},
"darkness": {
"min": 0,
"max": 1
},
"negative": false,
"priority": 0,
"color": null
},
"sight": {
"enabled": true,
"range": 30,
"angle": 360,
"visionMode": "basic",
"color": null,
"attenuation": 0.1,
"brightness": 0,
"saturation": 0,
"contrast": 0
},
"detectionModes": [],
"flags": {},
"randomImg": false,
"occludable": {
"radius": 0
},
"ring": {
"enabled": false,
"colors": {
"ring": null,
"background": null
},
"effects": 1,
"subject": {
"scale": 1,
"texture": null
}
},
"turnMarker": {
"mode": 1,
"animation": null,
"src": null,
"disposition": false
},
"movementAction": null
},
"items": [],
"effects": [],
"flags": {},
"_stats": {
"systemId": "celestopol1922",
"systemVersion": "0.0.7bêta",
"coreVersion": "13.348",
"createdTime": 1719437808862,
"modifiedTime": 1719437944369,
"lastModifiedBy": "6VFjkRpqiseDFIh9",
"compendiumSource": null,
"duplicateSource": null,
"exportSource": {
"worldId": "celestopol-1922",
"uuid": "Actor.QcGcXhb9FvHCb3uf",
"coreVersion": "13.351",
"systemId": "celestopol1922",
"systemVersion": "1.2.0"
}
},
"ownership": {
"default": 0
}
}
@@ -0,0 +1,296 @@
{
"_id": "04f6df7b17f747c6",
"name": "BAO WANG",
"type": "character",
"img": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/systems/celestopol1922/images/pj/bao_wang.png?1719410935795",
"system": {
"concept": "LESCROC",
"description": "<h1>BAO WANG : LESCROC </h1><p></p><p><span class=\"fontstyle0\">Ancien employé du casino flottant la Libellule, Bao Wang officiait comme videur.</span></p><p></p><p><span class=\"fontstyle0\">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.</span></p><p></p><p><span class=\"fontstyle0\">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 gente féminine. Par ailleurs, Bao a également un certain penchant 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 a rencontré Ernest. Ils ont sympathisé et le vétéran a su voir un certain potentiel chez Bao. Ce dernier, passablement désœuvré et fauché, accepta sans trop y croire dintégrer lagence du Lys blanc.</span></p><p></p><p><span class=\"fontstyle0\">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.</span> <br style=\"font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;orphans:2;text-align:-webkit-auto;text-indent:0px;text-transform:none;white-space:normal;widows:2;word-spacing:0px;-webkit-text-size-adjust:auto;-webkit-text-stroke-width:0px\" /></p>",
"metier": "LESCROC",
"faction": "",
"blessures": {
"lvl": 0
},
"destin": {
"lvl": 0
},
"spleen": {
"lvl": 0
},
"stats": {
"ame": {
"label": "ame",
"res": 2,
"artifice": {
"label": "artifice",
"value": 4
},
"attraction": {
"label": "attraction",
"value": 2
},
"coercition": {
"label": "coercition",
"value": 2
},
"faveur": {
"label": "faveur",
"value": 2
}
},
"corps": {
"label": "corps",
"res": 4,
"echauffouree": {
"label": "echauffouree",
"value": 0
},
"effacement": {
"label": "effacement",
"value": 4
},
"mobilite": {
"label": "mobilite",
"value": 2
},
"prouesse": {
"label": "prouesse",
"value": 2
}
},
"coeur": {
"label": "coeur",
"res": 0,
"appreciation": {
"label": "appreciation",
"value": 2
},
"arts": {
"label": "arts",
"value": 0
},
"inspiration": {
"label": "inspiration",
"value": 1
},
"traque": {
"label": "traque",
"value": 3
}
},
"esprit": {
"label": "esprit",
"res": 0,
"instruction": {
"label": "instruction",
"value": 1
},
"mtechnologique": {
"label": "mtechnologique",
"value": 2
},
"raisonnement": {
"label": "raisonnement",
"value": 1
},
"traitement": {
"label": "traitement",
"value": 2
}
}
},
"anomaly": {
"type": "entropie",
"value": 1
},
"attributs": {
"entregent": {
"value": 0,
"max": 0
},
"fortune": {
"value": 0,
"max": 0
},
"reve": {
"value": 0,
"max": 0
},
"vision": {
"value": 0,
"max": 0
}
},
"historique": "<h1>BAO WANG : LESCROC </h1><p></p><p><span class=\"fontstyle0\">Ancien employé du casino flottant la Libellule, Bao Wang officiait comme videur.</span></p><p></p><p><span class=\"fontstyle0\">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.</span></p><p></p><p><span class=\"fontstyle0\">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 gente féminine. Par ailleurs, Bao a également un certain penchant 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 a rencontré Ernest. Ils ont sympathisé et le vétéran a su voir un certain potentiel chez Bao. Ce dernier, passablement désœuvré et fauché, accepta sans trop y croire dintégrer lagence du Lys blanc.</span></p><p></p><p><span class=\"fontstyle0\">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.</span> <br style=\"font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;orphans:2;text-align:-webkit-auto;text-indent:0px;text-transform:none;white-space:normal;widows:2;word-spacing:0px;-webkit-text-size-adjust:auto;-webkit-text-stroke-width:0px\" /></p>",
"descriptionPhysique": "",
"descriptionPsychologique": "",
"initiative": 0,
"factions": {
"pinkerton": 0,
"police": 0,
"okhrana": 0,
"lunanovatek": 0,
"oto": 0,
"syndicats": 0,
"vorovskoymir": 0,
"cour": 0,
"perso": 0,
"libel": "",
"perso2": 0,
"libel2": ""
}
},
"items": [
{
"_id": "23315ed101e248c8",
"name": "Entropie 1",
"img": "https://assets.forge-vtt.com/bazaar/core/icons/svg/item-bag.svg",
"effects": [],
"flags": {},
"sort": 0,
"type": "anomaly",
"system": {
"subtype": "entropie",
"level": 1,
"usesRemaining": 1,
"technique": "<p><span class=\"fontstyle0\">Bao peut relancer le dé de la Lune une fois lors dun même scénario et choisit de conserver le résultat quil préfère (cette faculté ne fonctionne pas sur les tests de chance).</span></p>",
"narratif": "<p><span class=\"fontstyle0\">Bao peut influer sur le hasard de manière mineure. </span><span class=\"fontstyle2\">Exemple : <em>avoir une bonne main en jouant au poker à la distribution, que le feu soit au vert en tournant au coin de la rue, etc.</em></span></p>"
}
},
{
"_id": "4edb4b80b9514a7b",
"name": "Belle gueule 2",
"img": "https://assets.forge-vtt.com/bazaar/core/icons/svg/item-bag.svg",
"effects": [],
"flags": {},
"sort": 0,
"type": "aspect",
"system": {
"valeur": 2,
"description": ""
}
},
{
"_id": "153b0a094ca4462e",
"name": "Aime le jeu 1",
"img": "https://assets.forge-vtt.com/bazaar/core/icons/svg/item-bag.svg",
"effects": [],
"flags": {},
"sort": 0,
"type": "aspect",
"system": {
"valeur": 1,
"description": ""
}
},
{
"_id": "cbdc76f66bd041a0",
"name": "Nouvel Équipement",
"img": "https://assets.forge-vtt.com/bazaar/core/icons/svg/item-bag.svg",
"effects": [],
"flags": {},
"sort": 0,
"type": "equipment",
"system": {
"description": ""
}
}
],
"effects": [],
"folder": null,
"flags": {},
"prototypeToken": {
"name": "BAO WANG",
"displayName": 0,
"actorLink": false,
"appendNumber": false,
"prependAdjective": false,
"texture": {
"src": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/celestopol/bao_wang.Token.webp?1719410935795",
"scaleX": 1,
"scaleY": 1,
"offsetX": 0,
"offsetY": 0,
"rotation": 0,
"tint": "#ffffff",
"anchorX": 0.5,
"anchorY": 0.5,
"fit": "contain",
"alphaThreshold": 0.75
},
"width": 1,
"height": 1,
"lockRotation": false,
"rotation": 0,
"alpha": 1,
"disposition": -1,
"displayBars": 0,
"bar1": {
"attribute": null
},
"bar2": {
"attribute": null
},
"light": {
"alpha": 0.5,
"angle": 360,
"bright": 0,
"coloration": 1,
"dim": 0,
"attenuation": 0.5,
"luminosity": 0.5,
"saturation": 0,
"contrast": 0,
"shadows": 0,
"animation": {
"type": null,
"speed": 5,
"intensity": 5,
"reverse": false
},
"darkness": {
"min": 0,
"max": 1
},
"color": null,
"negative": false,
"priority": 0
},
"sight": {
"enabled": false,
"range": 0,
"angle": 360,
"visionMode": "basic",
"attenuation": 0.1,
"brightness": 0,
"saturation": 0,
"contrast": 0,
"color": null
},
"detectionModes": [],
"flags": {},
"randomImg": false,
"occludable": {
"radius": 0
},
"ring": {
"enabled": false,
"colors": {
"ring": null,
"background": null
},
"effects": 1,
"subject": {
"scale": 1,
"texture": null
}
},
"turnMarker": {
"mode": 1,
"animation": null,
"src": null,
"disposition": false
},
"movementAction": null
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,143 @@
{
"_id": "fabe178df844475b",
"name": "Citoyen",
"type": "npc",
"img": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/celestopol/citoyen.Avatar.webp?1719438184882",
"system": {
"concept": "",
"description": "",
"metier": "",
"faction": "",
"blessures": {
"lvl": 0
},
"destin": {
"lvl": 0
},
"spleen": {
"lvl": 0
},
"stats": {
"ame": {
"label": "ame",
"res": 0,
"actuel": 0
},
"corps": {
"label": "corps",
"res": 0,
"actuel": 0
},
"coeur": {
"label": "coeur",
"res": 0,
"actuel": 0
},
"esprit": {
"label": "esprit",
"res": 0,
"actuel": 0
}
},
"npcType": "standard",
"historique": ""
},
"items": [],
"effects": [],
"folder": null,
"flags": {},
"prototypeToken": {
"name": "Citoyen",
"displayName": 0,
"actorLink": false,
"appendNumber": false,
"prependAdjective": false,
"texture": {
"src": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/celestopol/citoyen.Token.webp?1719438184882",
"scaleX": 1,
"scaleY": 1,
"offsetX": 0,
"offsetY": 0,
"rotation": 0,
"anchorX": 0.5,
"anchorY": 0.5,
"fit": "contain",
"tint": "#ffffff",
"alphaThreshold": 0.75
},
"width": 1,
"height": 1,
"lockRotation": false,
"rotation": 0,
"alpha": 1,
"disposition": -1,
"displayBars": 0,
"bar1": {
"attribute": null
},
"bar2": {
"attribute": null
},
"light": {
"alpha": 0.5,
"angle": 360,
"bright": 0,
"coloration": 1,
"dim": 0,
"attenuation": 0.5,
"luminosity": 0.5,
"saturation": 0,
"contrast": 0,
"shadows": 0,
"animation": {
"type": null,
"speed": 5,
"intensity": 5,
"reverse": false
},
"darkness": {
"min": 0,
"max": 1
},
"negative": false,
"priority": 0,
"color": null
},
"sight": {
"enabled": true,
"range": 30,
"angle": 360,
"visionMode": "basic",
"color": null,
"attenuation": 0.1,
"brightness": 0,
"saturation": 0,
"contrast": 0
},
"detectionModes": [],
"flags": {},
"randomImg": false,
"occludable": {
"radius": 0
},
"ring": {
"enabled": false,
"colors": {
"ring": null,
"background": null
},
"effects": 1,
"subject": {
"scale": 1,
"texture": null
}
},
"turnMarker": {
"mode": 1,
"animation": null,
"src": null,
"disposition": false
},
"movementAction": null
}
}
@@ -0,0 +1,459 @@
{
"folder": "5aDs3Gbi2cHy35X9",
"name": "Citoyen",
"type": "npc",
"img": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/celestopol/citoyen.Avatar.webp?1719438184882",
"system": {
"concept": "",
"anomaly": "0",
"anomalyval": 0,
"anomalymax": "0",
"initiative": 0,
"description": "",
"prefs": {
"typeofthrow": {
"check": true,
"choice": "0"
}
},
"prompt": {
"typeofthrow": {
"check": true,
"choice": "0"
},
"configure": {
"numberofdice": 0,
"aspect": 0,
"bonus": 0,
"bonusauspiciousdice": 0,
"typeofthrow": 0,
"aspectskill": 0,
"bonusmalusskill": 0,
"aspectspeciality": 0,
"rolldifficulty": 0,
"bonusmalusspeciality": 0
}
},
"skill": {
"skilltypes": [
"CEL1922.opt.ame",
"CEL1922.opt.attraction",
"CEL1922.opt.artifice",
"CEL1922.opt.coercition",
"CEL1922.opt.faveur",
"CEL1922.opt.corps",
"CEL1922.opt.echauffouree",
"CEL1922.opt.effacement",
"CEL1922.opt.prouesse",
"CEL1922.opt.mobilite",
"CEL1922.opt.coeur",
"CEL1922.opt.appreciation",
"CEL1922.opt.arts",
"CEL1922.opt.inspiration",
"CEL1922.opt.traque",
"CEL1922.opt.esprit",
"CEL1922.opt.instruction",
"CEL1922.opt.mtechnologique",
"CEL1922.opt.raisonnement",
"CEL1922.opt.traitement"
],
"moondicetypes": [
"CEL1922.opt.nouvellelune",
"CEL1922.opt.premiercroissant",
"CEL1922.opt.premierquartier",
"CEL1922.opt.lunegibbeuse",
"CEL1922.opt.lunevoutee",
"CEL1922.opt.derniercroissant",
"CEL1922.opt.dernierquartier",
"CEL1922.opt.pleinelune"
],
"woundstypes": [
"CEL1922.opt.none",
"CEL1922.opt.anodin",
"CEL1922.opt.derisoire",
"CEL1922.opt.negligeable",
"CEL1922.opt.superficiel",
"CEL1922.opt.leger",
"CEL1922.opt.modere",
"CEL1922.opt.grave",
"CEL1922.opt.dramatique"
],
"woundsmalus": [
0,
0,
0,
-1,
-1,
-2,
-2,
-3,
-999
],
"woundsrecup": [
"CEL1922.opt.none",
"CEL1922.1 minute",
"CEL1922.1 minute",
"CEL1922.10 minutes",
"CEL1922.10 minutes",
"CEL1922.30 minutes",
"CEL1922.30 minutes",
"CEL1922.1 day",
"CEL1922.Out of Fiction"
],
"destinytypes": [
"CEL1922.opt.none",
"CEL1922.opt.libel1",
"CEL1922.opt.libel2",
"CEL1922.opt.libel3",
"CEL1922.opt.libel4",
"CEL1922.opt.libel5",
"CEL1922.opt.libel6",
"CEL1922.opt.libel7",
"CEL1922.opt.libel8"
],
"spleentypes": [
"CEL1922.opt.none",
"CEL1922.opt.libel1",
"CEL1922.opt.libel2",
"CEL1922.opt.libel3",
"CEL1922.opt.libel4",
"CEL1922.opt.libel5",
"CEL1922.opt.libel6",
"CEL1922.opt.libel7",
"CEL1922.opt.libel8"
],
"anomalytypes": [
"CEL1922.opt.none",
"CEL1922.opt.entropie",
"CEL1922.opt.communicationaveclesmorts",
"CEL1922.opt.illusion",
"CEL1922.opt.suggestion",
"CEL1922.opt.tarotdivinatoire",
"CEL1922.opt.telekinesie",
"CEL1922.opt.telepathie",
"CEL1922.opt.voyageastral"
],
"ame": {
"res": 0,
"actuel": 0,
"artifice": {
"value": 0
},
"attraction": {
"value": 0
},
"coercition": {
"value": 0
},
"faveur": {
"value": 0
}
},
"corps": {
"res": 0,
"actuel": 0,
"echauffouree": {
"value": 0
},
"effacement": {
"value": 0
},
"prouesse": {
"value": 0
},
"mobilite": {
"value": 0
}
},
"coeur": {
"res": 0,
"actuel": 0,
"appreciation": {
"value": 0
},
"arts": {
"value": 0
},
"inspiration": {
"value": 0
},
"traque": {
"value": 0
}
},
"esprit": {
"res": 0,
"actuel": 0,
"instruction": {
"value": 0
},
"mtechnologique": {
"value": 0
},
"raisonnement": {
"value": 0
},
"traitement": {
"value": 0
}
},
"aspecttypes": [
"CEL1922.opt.none",
"CEL1922.opt.aspect1",
"CEL1922.opt.aspect2",
"CEL1922.opt.aspect3",
"CEL1922.opt.aspect4",
"CEL1922.opt.aspect5",
"CEL1922.opt.aspect6",
"CEL1922.opt.aspect7",
"CEL1922.opt.aspect8"
]
},
"blessures": {
"lvl": 0,
"blessure_1": {
"check": false,
"bonus": 0,
"malus": 0
},
"blessure_2": {
"check": false,
"bonus": 0,
"malus": 0
},
"blessure_3": {
"check": false,
"bonus": 0,
"malus": -1
},
"blessure_4": {
"check": false,
"bonus": 0,
"malus": -1
},
"blessure_5": {
"check": false,
"bonus": 0,
"malus": -2
},
"blessure_6": {
"check": false,
"bonus": 0,
"malus": -2
},
"blessure_7": {
"check": false,
"bonus": 0,
"malus": -3
},
"blessure_8": {
"check": false,
"bonus": 0,
"malus": -999
}
},
"destin": {
"lvl": 0,
"destin_1": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_2": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_3": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_4": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_5": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_6": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_7": {
"check": false,
"bonus": 0,
"malus": 0
},
"destin_8": {
"check": false,
"bonus": 0,
"malus": 0
}
},
"spleen": {
"lvl": 0,
"spleen_1": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_2": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_3": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_4": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_5": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_6": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_7": {
"check": false,
"bonus": 0,
"malus": 0
},
"spleen_8": {
"check": false,
"bonus": 0,
"malus": 0
}
}
},
"prototypeToken": {
"name": "Citoyen",
"displayName": 0,
"actorLink": false,
"appendNumber": false,
"prependAdjective": false,
"texture": {
"src": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/celestopol/citoyen.Token.webp?1719438184882",
"scaleX": 1,
"scaleY": 1,
"offsetX": 0,
"offsetY": 0,
"rotation": 0,
"anchorX": 0.5,
"anchorY": 0.5,
"fit": "contain",
"tint": "#ffffff",
"alphaThreshold": 0.75
},
"width": 1,
"height": 1,
"lockRotation": false,
"rotation": 0,
"alpha": 1,
"disposition": -1,
"displayBars": 0,
"bar1": {
"attribute": null
},
"bar2": {
"attribute": null
},
"light": {
"alpha": 0.5,
"angle": 360,
"bright": 0,
"coloration": 1,
"dim": 0,
"attenuation": 0.5,
"luminosity": 0.5,
"saturation": 0,
"contrast": 0,
"shadows": 0,
"animation": {
"type": null,
"speed": 5,
"intensity": 5,
"reverse": false
},
"darkness": {
"min": 0,
"max": 1
},
"negative": false,
"priority": 0,
"color": null
},
"sight": {
"enabled": true,
"range": 30,
"angle": 360,
"visionMode": "basic",
"color": null,
"attenuation": 0.1,
"brightness": 0,
"saturation": 0,
"contrast": 0
},
"detectionModes": [],
"flags": {},
"randomImg": false,
"occludable": {
"radius": 0
},
"ring": {
"enabled": false,
"colors": {
"ring": null,
"background": null
},
"effects": 1,
"subject": {
"scale": 1,
"texture": null
}
},
"turnMarker": {
"mode": 1,
"animation": null,
"src": null,
"disposition": false
},
"movementAction": null
},
"items": [],
"effects": [],
"flags": {},
"_stats": {
"systemId": "celestopol1922",
"systemVersion": "0.0.7bêta",
"coreVersion": "13.348",
"createdTime": 1719438039685,
"modifiedTime": 1719438184895,
"lastModifiedBy": "6VFjkRpqiseDFIh9",
"compendiumSource": null,
"duplicateSource": null,
"exportSource": {
"worldId": "celestopol-1922",
"uuid": "Actor.H4qPWYC8kbacuBfm",
"coreVersion": "13.351",
"systemId": "celestopol1922",
"systemVersion": "1.2.0"
}
},
"ownership": {
"default": 0
}
}