7 Commits

Author SHA1 Message Date
uberwald a9c70c004d Actualiser README.md 2026-05-12 00:56:07 +02:00
uberwald 5c5ef7b0e4 Cleanup code
Release Creation / build (release) Successful in 1m23s
2026-05-12 00:37:16 +02:00
uberwald 503ee9395a Cleanup code 2026-05-12 00:34:47 +02:00
uberwald 389517a774 Ready to deploy
Release Creation / build (release) Successful in 1m17s
2026-05-11 22:24:37 +02:00
uberwald 9617005a5c Finalisation du système
Release Creation / build (release) Successful in 1m24s
2026-05-06 22:37:40 +02:00
uberwald 73a3381d2a Import des persos du système précédent 2026-05-06 21:31:03 +02:00
uberwald fbfb265570 Sync compendiums 2026-05-06 20:27:40 +02:00
109 changed files with 4541 additions and 1051 deletions
+10 -10
View File
@@ -66,13 +66,13 @@ jobs:
system.json system.json
api_key: "${{secrets.ALLOW_PUSH_RELEASE}}" api_key: "${{secrets.ALLOW_PUSH_RELEASE}}"
#- name: Publish to Foundry server - name: Publish to Foundry server
# uses: https://github.com/djlechuck/foundryvtt-publish-package-action@v1 uses: https://github.com/djlechuck/foundryvtt-publish-package-action@v1
# with: with:
# #token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }} token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }}
# id: 'fvtt-chroniques-de-l-etrange' id: "fvtt-chroniques-de-l-etrange"
# version: ${{github.event.release.tag_name}} version: ${{github.event.release.tag_name}}
# manifest: 'https://www.uberwald.me/gitea/public/fvtt-chroniques-de-l-etrange/releases/download/latest/system.json' manifest: "https://www.uberwald.me/gitea/uberwald/fvtt-chroniques-de-l-etrange/releases/download/latest/system.json"
# notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-chroniques-de-l-etrange.zip' notes: "https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-chroniques-de-l-etrange.zip"
# compatibility-minimum: '13' compatibility-minimum: "14"
# compatibility-verified: '13' compatibility-verified: "14"
+3 -2
View File
@@ -12,10 +12,11 @@ Vous pouvez retrouver le nom de leurs auteurs respectifs sur ce site.
version 1.2 ou toute version ultérieure publiée par la Free Software Foundation, version 1.2 ou toute version ultérieure publiée par la Free Software Foundation,
https://commons.wikimedia.org/wiki/File:Hong_Kong_18_Districts_Blank_Map.svg https://commons.wikimedia.org/wiki/File:Hong_Kong_18_Districts_Blank_Map.svg
- 'loksyu_roue_d_initiative.jpg' est une création originale de 'Darkwin'. - 'loksyu_roue_d_initiative.jpg' est une création originale de 'Darkwin'.
- L'organisation du reste des images provient du système produit par MysteryMan (merci à lui) - La version 14.02 contenait des icones produites par David R.D. 'Mystery Man From Outerspace' Bercovici (merci à lui)
- Les versions suivantes contiennent des icones produites par LeRatierBretonnien
Code Author : Code Author :
- Developed by LeRatierBretonnien - Developed by LeRatierBretonnien / www.uberwald.me
Code LICENCE : Code LICENCE :
C BY-NC-SA 4.0 C BY-NC-SA 4.0
+3 -2
View File
@@ -6,7 +6,8 @@ Copyright 2025-2026 Antre Monde Editions All rights reserved
Chroniques de l'ETrange is a game written by Romain d'Huissier and Cédric Lameire. The authors retain their moral rights to this work in both print and digital formats. Chroniques de l'ETrange is a game written by Romain d'Huissier and Cédric Lameire. 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 This system for FoundryVTT has been approved and authorized by Antre-Monde Edition.
Ce système s'inspire d'un système précédent "chroniquesdeletrange" développé par David R.D. 'Mystery Man From Outerspace' Bercovici et Christophe 'Kristov / Qaw' Laudon. Le code a été intégralément réécrit pour être compatible avec la version 14 de FoundryVTT, et pour intégrer le maximum de mécaniques de jeu des Chroniques de l'Etrange, et n'a donc aucun lien de code avec le système précédent.
--- ---
+268
View File
@@ -4188,3 +4188,271 @@ ol.item-list li.item .item-controls a.item-control:hover {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
/* ===================================================================
Migration App
=================================================================== */
.cde-migration-app .window-content {
padding: 0;
overflow-y: auto;
}
.cde-migration-body {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
}
/* Drop zone */
.cde-migration-drop-zone {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 28px 20px;
border: 2px dashed #1a2436;
border-radius: 8px;
background: rgba(13, 21, 32, 0.6);
text-align: center;
transition: border-color 0.15s, background 0.15s;
cursor: pointer;
}
.cde-migration-drop-zone.is-dragover {
border-color: #4a9eff;
background: rgba(74, 158, 255, 0.15);
}
.cde-migration-drop-icon {
font-size: 36px;
color: #4a9eff;
opacity: 0.7;
}
.cde-migration-drop-hint {
margin: 0;
font-size: 12px;
color: #7d94b8;
}
.cde-migration-file-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border: 1px solid #4a9eff;
border-radius: 4px;
color: #4a9eff;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: background 0.15s;
}
.cde-migration-file-btn:hover {
background: rgba(74, 158, 255, 0.2);
}
/* Preview section */
.cde-migration-preview {
border: 1px solid #1a2436;
border-radius: 6px;
overflow: hidden;
}
.cde-migration-preview-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: rgba(13, 21, 32, 0.8);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #7d94b8;
}
.cde-migration-clear-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border: 1px solid #1a2436;
border-radius: 4px;
font-size: 11px;
color: #7d94b8;
background: none;
cursor: pointer;
transition: color 0.15s, border-color 0.15s;
}
.cde-migration-clear-btn:hover {
color: #e04444;
border-color: #e04444;
}
/* Preview table */
.cde-migration-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
.cde-migration-table th {
padding: 5px 8px;
background: rgba(13, 21, 32, 0.9);
color: #7d94b8;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
text-align: left;
border-bottom: 1px solid #1a2436;
}
.cde-migration-table td {
padding: 5px 8px;
border-bottom: 1px solid rgba(26, 36, 54, 0.4);
vertical-align: middle;
}
.cde-migration-table tr:last-child td {
border-bottom: none;
}
.cde-migration-thumb {
width: 28px;
height: 28px;
border-radius: 3px;
object-fit: cover;
}
.cde-migration-name {
font-weight: 600;
color: #e2e8f4;
}
.cde-migration-items-count {
text-align: center;
color: #7d94b8;
}
.cde-migration-srcfile {
font-size: 10px;
color: #7d94b8;
max-width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Type badge */
.cde-migration-type-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 10px;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
}
.cde-migration-type-badge.cde-migration-type-character {
background: rgba(74, 158, 255, 0.2);
color: #4a9eff;
border: 1px solid rgba(74, 158, 255, 0.4);
}
.cde-migration-type-badge.cde-migration-type-npc {
background: rgba(156, 77, 204, 0.2);
color: #c97ae0;
border: 1px solid rgba(156, 77, 204, 0.4);
}
/* Errors */
.cde-migration-errors {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.cde-migration-errors li {
display: flex;
align-items: flex-start;
gap: 6px;
padding: 6px 10px;
border: 1px solid rgba(224, 68, 68, 0.6);
border-radius: 4px;
background: rgba(224, 68, 68, 0.1);
color: #e07070;
font-size: 11px;
}
.cde-migration-errors li i {
margin-top: 2px;
flex-shrink: 0;
}
/* Bottom action bar */
.cde-migration-actions {
display: flex;
justify-content: center;
padding-top: 4px;
}
.cde-migration-import-btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 9px 24px;
border: none;
border-radius: 6px;
background: #4a9eff;
color: #fff;
font-size: 13px;
font-weight: 700;
cursor: pointer;
transition: filter 0.15s;
}
.cde-migration-import-btn:hover {
filter: brightness(1.15);
}
.cde-migration-hint {
margin: 0;
font-size: 12px;
color: #7d94b8;
text-align: center;
}
.cde-welcome-message {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: #101622;
border: 1px solid #263853;
border-radius: 8px;
text-align: center;
}
.cde-welcome-logo {
width: 120px;
height: auto;
object-fit: contain;
filter: drop-shadow(0 0 8px rgba(74, 158, 255, 0.4));
}
.cde-welcome-title {
margin: 0;
font-size: 15px;
font-weight: 700;
color: #4a9eff;
text-shadow: 0 0 8px rgba(74, 158, 255, 0.5);
}
.cde-welcome-links {
margin: 0;
font-size: 12px;
color: #7d94b8;
}
.cde-welcome-links a {
color: #00d4d4;
text-decoration: none;
border-bottom: 1px solid rgba(0, 212, 212, 0.4);
}
.cde-welcome-links a:hover {
color: #fff;
border-bottom-color: #fff;
}
.cde-welcome-help-btn {
display: inline-flex;
align-items: center;
gap: 6px;
margin-top: 4px;
padding: 7px 18px;
background: #4a9eff;
border: none;
border-radius: 6px;
color: #fff;
font-size: 12px;
font-weight: 700;
cursor: pointer;
transition: filter 0.15s;
}
.cde-welcome-help-btn:hover {
filter: brightness(1.2);
}
+310
View File
@@ -4344,3 +4344,313 @@ ol.item-list {
from { transform-origin: var(--fx) var(--fy); transform: rotate(0deg); } from { transform-origin: var(--fx) var(--fy); transform: rotate(0deg); }
to { transform-origin: var(--fx) var(--fy); transform: rotate(360deg); } to { transform-origin: var(--fx) var(--fy); transform: rotate(360deg); }
} }
/* ===================================================================
Migration App
=================================================================== */
.cde-migration-app {
.window-content {
padding: 0;
overflow-y: auto;
}
}
.cde-migration-body {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
}
/* Drop zone */
.cde-migration-drop-zone {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 28px 20px;
border: 2px dashed @cde-border;
border-radius: 8px;
background: fadeout(@cde-surface2, 40%);
text-align: center;
transition: border-color 0.15s, background 0.15s;
cursor: pointer;
&.is-dragover {
border-color: @cde-spell;
background: fadeout(@cde-spell, 85%);
}
}
.cde-migration-drop-icon {
font-size: 36px;
color: @cde-spell;
opacity: 0.7;
}
.cde-migration-drop-hint {
margin: 0;
font-size: 12px;
color: @cde-muted;
}
.cde-migration-file-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border: 1px solid @cde-spell;
border-radius: 4px;
color: @cde-spell;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: background 0.15s;
&:hover {
background: fadeout(@cde-spell, 80%);
}
}
/* Preview section */
.cde-migration-preview {
border: 1px solid @cde-border;
border-radius: 6px;
overflow: hidden;
}
.cde-migration-preview-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: fadeout(@cde-surface2, 20%);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: @cde-muted;
}
.cde-migration-clear-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border: 1px solid @cde-border;
border-radius: 4px;
font-size: 11px;
color: @cde-muted;
background: none;
cursor: pointer;
transition: color 0.15s, border-color 0.15s;
&:hover {
color: #e04444;
border-color: #e04444;
}
}
/* Preview table */
.cde-migration-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
th {
padding: 5px 8px;
background: fadeout(@cde-surface2, 10%);
color: @cde-muted;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
text-align: left;
border-bottom: 1px solid @cde-border;
}
td {
padding: 5px 8px;
border-bottom: 1px solid fadeout(@cde-border, 60%);
vertical-align: middle;
}
tr:last-child td {
border-bottom: none;
}
}
.cde-migration-thumb {
width: 28px;
height: 28px;
border-radius: 3px;
object-fit: cover;
}
.cde-migration-name {
font-weight: 600;
color: @cde-text;
}
.cde-migration-items-count {
text-align: center;
color: @cde-muted;
}
.cde-migration-srcfile {
font-size: 10px;
color: @cde-muted;
max-width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Type badge */
.cde-migration-type-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 10px;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
&.cde-migration-type-character {
background: fadeout(@cde-spell, 80%);
color: @cde-spell;
border: 1px solid fadeout(@cde-spell, 60%);
}
&.cde-migration-type-npc {
background: fadeout(#9c4dcc, 80%);
color: #c97ae0;
border: 1px solid fadeout(#9c4dcc, 60%);
}
}
/* Errors */
.cde-migration-errors {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 4px;
li {
display: flex;
align-items: flex-start;
gap: 6px;
padding: 6px 10px;
border: 1px solid fadeout(#e04444, 40%);
border-radius: 4px;
background: fadeout(#e04444, 90%);
color: #e07070;
font-size: 11px;
i { margin-top: 2px; flex-shrink: 0; }
}
}
/* Bottom action bar */
.cde-migration-actions {
display: flex;
justify-content: center;
padding-top: 4px;
}
.cde-migration-import-btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 9px 24px;
border: none;
border-radius: 6px;
background: @cde-spell;
color: #fff;
font-size: 13px;
font-weight: 700;
cursor: pointer;
transition: filter 0.15s;
&:hover {
filter: brightness(1.15);
}
}
.cde-migration-hint {
margin: 0;
font-size: 12px;
color: @cde-muted;
text-align: center;
}
// ============================================================
// Welcome message
// ============================================================
.cde-welcome-message {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: @cde-surface;
border: 1px solid @cde-border-hi;
border-radius: 8px;
text-align: center;
}
.cde-welcome-logo {
width: 120px;
height: auto;
object-fit: contain;
filter: drop-shadow(0 0 8px fade(@cde-spell, 40%));
}
.cde-welcome-title {
margin: 0;
font-size: 15px;
font-weight: 700;
color: @cde-spell;
text-shadow: 0 0 8px fade(@cde-spell, 50%);
}
.cde-welcome-links {
margin: 0;
font-size: 12px;
color: @cde-muted;
a {
color: @cde-item;
text-decoration: none;
border-bottom: 1px solid fade(@cde-item, 40%);
&:hover {
color: #fff;
border-bottom-color: #fff;
}
}
}
.cde-welcome-help-btn {
display: inline-flex;
align-items: center;
gap: 6px;
margin-top: 4px;
padding: 7px 18px;
background: @cde-spell;
border: none;
border-radius: 6px;
color: #fff;
font-size: 12px;
font-weight: 700;
cursor: pointer;
transition: filter 0.15s;
&:hover {
filter: brightness(1.2);
}
}
+536 -55
View File
@@ -124,7 +124,6 @@ var TEMPLATE_PARTIALS = [
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-treasures.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-treasures.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-items.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-items.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-kungfus.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-kungfus.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-spells.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-supernaturals.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-supernaturals.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-spells.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-spells.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-kungfus.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-kungfus.html",
@@ -134,8 +133,456 @@ var TEMPLATE_PARTIALS = [
"systems/fvtt-chroniques-de-l-etrange/templates/apps/cde-wheel-app.html" "systems/fvtt-chroniques-de-l-etrange/templates/apps/cde-wheel-app.html"
]; ];
// src/migration/migrator.js
var ELEMENT_LABEL_TO_KEY = {
"m\xE9tal": "metal",
"metal": "metal",
"eau": "eau",
"terre": "terre",
"feu": "feu",
"bois": "bois"
};
function elementKey(label = "") {
return ELEMENT_LABEL_TO_KEY[label.toLowerCase().trim()] ?? "metal";
}
function heiKey(label = "") {
const l = label.toLowerCase().trim();
if (l === "yin/yang" || l === "yinyang") return "yinyang";
if (l === "yang") return "yang";
return "yin";
}
var SPECIALITY_TO_DISCIPLINE = {
// internalcinnabar
"essence": "internalcinnabar",
"esprit": "internalcinnabar",
"mind": "internalcinnabar",
"purification": "internalcinnabar",
"manipulation": "internalcinnabar",
"aura": "internalcinnabar",
// alchemy
"acupuncture": "alchemy",
"\xE9lixirs": "alchemy",
"elixirs": "alchemy",
"poisons": "alchemy",
"arsenal": "alchemy",
"potions": "alchemy",
// masteryoftheway
"mal\xE9diction": "masteryoftheway",
"malediction": "masteryoftheway",
"transfiguration": "masteryoftheway",
"n\xE9cromancie": "masteryoftheway",
"necromancie": "masteryoftheway",
"contr\xF4le climatique": "masteryoftheway",
"controle climatique": "masteryoftheway",
"magie d'or": "masteryoftheway",
"magie dor": "masteryoftheway",
// exorcism
"invocation": "exorcism",
"pistage": "exorcism",
"tra\xE7age": "exorcism",
"tracage": "exorcism",
"protection": "exorcism",
"ch\xE2timent": "exorcism",
"chatiment": "exorcism",
"domination": "exorcism",
// geomancy
"neutralisation": "geomancy",
"divination": "geomancy",
"pri\xE8re terrestre": "geomancy",
"priere terrestre": "geomancy",
"pri\xE8re c\xE9leste": "geomancy",
"priere celeste": "geomancy",
"g\xE9omancie": "geomancy",
"geomancie": "geomancy",
"feng shui": "geomancy",
"fungseoi": "geomancy"
};
function inferDiscipline(specialityName = "", itemName = "") {
const key = specialityName.toLowerCase().trim();
if (SPECIALITY_TO_DISCIPLINE[key]) return SPECIALITY_TO_DISCIPLINE[key];
const name = itemName.toLowerCase();
if (name.includes("exorcis")) return "exorcism";
if (name.includes("g\xE9omanci") || name.includes("geomanci")) return "geomancy";
if (name.includes("alchimi")) return "alchemy";
if (name.includes("cinnabre") || name.includes("interne")) return "internalcinnabar";
if (name.includes("ma\xEEtrise") || name.includes("maitrise") || name.includes("tao")) return "masteryoftheway";
return "internalcinnabar";
}
function mapActivation(oldActivation = "") {
const s = oldActivation.toLowerCase();
if (s.includes("inflig\xE9s") || s.includes("infliges")) return "damage-inflicted";
if (s.includes("re\xE7us") || s.includes("recus")) return "damage-received";
if (s.includes("r\xE9action") || s.includes("reaction")) return "reaction";
if (s.includes("d\xE9s-fastes") || s.includes("des-fastes") || s.includes("fastes")) return "dice";
if (s.includes("aide")) return "action-aid";
if (s.includes("attaque") && s.includes("d\xE9fense")) return "action-attack-defense";
if (s.includes("attaque") && s.includes("defense")) return "action-attack-defense";
if (s.includes("attaque")) return "action-attack";
if (s.includes("d\xE9fense") || s.includes("defense")) return "action-defense";
return "action-attack";
}
var DEFAULT_ACTOR_IMG = "icons/svg/mystery-man.svg";
var DEFAULT_ITEM_IMG = "icons/svg/item-bag.svg";
function migrateEquipmentItem(oldItem) {
const s = oldItem.system ?? {};
return {
name: oldItem.name,
type: "item",
img: oldItem.img || DEFAULT_ITEM_IMG,
system: {
reference: s.reference ?? "",
description: s.description ?? "",
quantity: Number(s.quantity ?? 1),
weight: Number(s.weight ?? 0),
notes: s.notes ?? ""
}
};
}
function migrateKungfuItem(oldItem) {
const s = oldItem.system ?? {};
const techs = s.techniques ?? {};
const migratedTechs = {};
for (const key of ["technique1", "technique2", "technique3"]) {
const t = techs[key] ?? {};
migratedTechs[key] = {
check: Boolean(t.check),
name: t.name ?? "",
activation: mapActivation(t.activation ?? ""),
technique: t.technique ?? ""
};
}
return {
name: oldItem.name,
type: "kungfu",
img: oldItem.img || DEFAULT_ITEM_IMG,
system: {
reference: s.reference ?? "",
description: s.description ?? "",
orientation: s.orientation || "yin",
aspect: s.aspect || "metal",
skill: s.skill || "kungfu",
speciality: s.speciality ?? "",
style: s.style ?? "",
techniques: migratedTechs,
notes: s.notes ?? ""
}
};
}
function migrateSpellItem(oldItem) {
const s = oldItem.system ?? {};
return {
name: oldItem.name,
type: "spell",
img: oldItem.img || DEFAULT_ITEM_IMG,
system: {
reference: s.reference ?? "",
description: s.description ?? "",
specialityname: s.specialityname ?? "",
associatedelement: elementKey(s.associatedelement ?? ""),
heiType: heiKey(s.hei ?? ""),
heiCost: Number(s.heiCost ?? 0),
difficulty: Number(s.difficulty ?? 0),
realizationtimeritual: s.realizationtimeritual ?? "",
realizationtimeaccelerated: s.realizationtimeaccelerated ?? "",
flashback: s.flashback ?? "",
components: s.components ?? "",
effects: s.effects ?? "",
examples: s.examples ?? "",
notes: s.notes ?? "",
discipline: inferDiscipline(s.specialityname ?? "", oldItem.name ?? "")
}
};
}
function migrateSupernaturalItem(oldItem) {
const s = oldItem.system ?? {};
const nestedRef = s.supernatural?.reference ?? "";
return {
name: oldItem.name,
type: "supernatural",
img: oldItem.img || DEFAULT_ITEM_IMG,
system: {
reference: s.reference || nestedRef,
description: s.description ?? "",
notes: s.notes ?? "",
heiType: "yin",
heiCost: 0,
trigger: "",
effects: ""
}
};
}
function migrateItem(oldItem) {
switch (oldItem.type) {
case "item":
return migrateEquipmentItem(oldItem);
case "kungfu":
return migrateKungfuItem(oldItem);
case "spell":
return migrateSpellItem(oldItem);
case "supernatural":
return migrateSupernaturalItem(oldItem);
default:
return migrateEquipmentItem({ ...oldItem, type: "item" });
}
}
function migrateCharacter(old) {
const s = old.system ?? {};
const aspect = {};
for (const [k, v] of Object.entries(s.aspect ?? {})) {
aspect[k] = { chinese: v.chinese ?? "", label: v.label ?? "", value: Number(v.value ?? 0) };
}
const skills = {};
for (const [k, v] of Object.entries(s.skills ?? {})) {
skills[k] = { label: v.label ?? "", specialities: v.specialities ?? "", value: Number(v.value ?? 0) };
}
const resources = {};
for (const [k, v] of Object.entries(s.resources ?? {})) {
resources[k] = { label: v.label ?? "", specialities: v.specialities ?? "", value: Number(v.value ?? 0), debt: Boolean(v.debt) };
}
const component = {};
for (const [k, v] of Object.entries(s.component ?? {})) {
component[k] = { value: v.value ?? "" };
}
const MAGIC_SPECIALITIES = {
internalcinnabar: ["essence", "mind", "purification", "manipulation", "aura"],
alchemy: ["acupuncture", "elixirs", "poisons", "arsenal", "potions"],
masteryoftheway: ["curse", "transfiguration", "necromancy", "climatecontrol", "goldenmagic"],
exorcism: ["invocation", "tracking", "protection", "punishment", "domination"],
geomancy: ["neutralization", "divination", "earthlyprayer", "heavenlyprayer", "fungseoi"]
};
const magics = {};
for (const [school, specs] of Object.entries(MAGIC_SPECIALITIES)) {
const om = s.magics?.[school] ?? {};
const speciality = {};
for (const spec of specs) {
speciality[spec] = { check: Boolean(om.speciality?.[spec]?.check) };
}
magics[school] = { visible: Boolean(om.visible), value: Number(om.value ?? 0), speciality };
}
const tt = s.threetreasures ?? {};
const threetreasures = {
heiyang: { value: Number(tt.heiyang?.value ?? 0), max: Number(tt.heiyang?.max ?? 0) },
heiyin: { value: Number(tt.heiyin?.value ?? 0), max: Number(tt.heiyin?.max ?? 0) },
dicelevel: {
level0d: {
san: { value: Number(tt.dicelevel?.level0d?.san?.value ?? 0), max: Number(tt.dicelevel?.level0d?.san?.max ?? 0) },
zing: { value: Number(tt.dicelevel?.level0d?.zing?.value ?? 0), max: Number(tt.dicelevel?.level0d?.zing?.max ?? 0) }
},
level1d: {
san: { value: Number(tt.dicelevel?.level1d?.san?.value ?? 0), max: Number(tt.dicelevel?.level1d?.san?.max ?? 0) },
zing: { value: Number(tt.dicelevel?.level1d?.zing?.value ?? 0), max: Number(tt.dicelevel?.level1d?.zing?.max ?? 0) }
},
level2d: {
san: { value: Number(tt.dicelevel?.level2d?.san?.value ?? 0), max: Number(tt.dicelevel?.level2d?.san?.max ?? 0) },
zing: { value: Number(tt.dicelevel?.level2d?.zing?.value ?? 0), max: Number(tt.dicelevel?.level2d?.zing?.max ?? 0) }
}
}
};
const description = s.description || s.biography || "";
return {
name: old.name,
type: "character",
img: old.img || DEFAULT_ACTOR_IMG,
system: {
concept: s.concept ?? "",
guardian: parseInt(s.guardian ?? "0") || 0,
initiative: Number(s.initiative ?? 1),
anti_initiative: Number(s.anti_initiative ?? 24),
description,
aspect,
skills,
resources,
component,
magics,
threetreasures,
experience: {
value: Number(s.experience?.value ?? 0),
max: Number(s.experience?.max ?? 0),
min: Number(s.experience?.min ?? 0)
}
},
items: (old.items ?? []).map(migrateItem)
};
}
function migrateNpc(old) {
const s = old.system ?? {};
const aptitudes = {};
for (const [k, v] of Object.entries(s.aptitudes ?? {})) {
aptitudes[k] = { value: Number(v.value ?? 0), speciality: v.speciality ?? "" };
}
return {
name: old.name,
type: "npc",
img: old.img || DEFAULT_ACTOR_IMG,
system: {
type: s.type ?? "",
// Old system had separate `levelofthreat`/`powerofnuisance` as numbers
// and string copies `threat`/`nuisance` — use the numeric fields
threat: Number(s.levelofthreat ?? s.threat ?? 0),
nuisance: Number(s.powerofnuisance ?? s.nuisance ?? 0),
initiative: Number(s.initiative ?? 1),
anti_initiative: Number(s.anti_initiative ?? 24),
aptitudes,
vitality: {
value: Number(s.vitality?.value ?? 0),
calcul: Number(s.vitality?.calcul ?? 0),
note: s.vitality?.note ?? ""
},
hei: {
value: Number(s.hei?.value ?? 0),
calcul: Number(s.hei?.calcul ?? 0),
note: s.hei?.note ?? ""
},
description: s.description ?? ""
},
items: (old.items ?? []).map(migrateItem)
};
}
function migrateActor(oldJson) {
switch (oldJson.type) {
case "character":
return migrateCharacter(oldJson);
case "npc":
return migrateNpc(oldJson);
default:
throw new Error(`Unknown actor type "${oldJson.type}" in "${oldJson.name}"`);
}
}
function parseLegacyJson(jsonText) {
const parsed = JSON.parse(jsonText);
if (typeof parsed !== "object" || parsed === null) {
throw new Error("Le fichier JSON doit contenir un objet acteur ou un tableau d'acteurs");
}
const actors = Array.isArray(parsed) ? parsed : [parsed];
return actors.map(migrateActor);
}
// src/ui/apps/migration-app.js
var MIGRATION_TEMPLATE = "systems/fvtt-chroniques-de-l-etrange/templates/apps/cde-migration-app.html";
var CDEMigrationApp = class _CDEMigrationApp extends foundry.applications.api.HandlebarsApplicationMixin(
foundry.applications.api.ApplicationV2
) {
static DEFAULT_OPTIONS = {
id: "cde-migration-app",
classes: ["cde-migration-app"],
tag: "div",
window: {
title: "CDE.MigrationTitle",
icon: "fas fa-file-import",
resizable: false
},
position: { width: 560, height: "auto" },
actions: {
clearFiles: _CDEMigrationApp.#clearFiles,
doImport: _CDEMigrationApp.#doImport
}
};
static PARTS = {
form: { template: MIGRATION_TEMPLATE }
};
/** @type {Array<{name: string, type: string, img: string, system: object, items: object[], _srcFile: string}>} */
#pending = [];
/** @type {string[]} - error messages per file */
#errors = [];
async _prepareContext(options) {
return {
pending: this.#pending,
errors: this.#errors,
hasPending: this.#pending.length > 0,
hasErrors: this.#errors.length > 0,
count: this.#pending.length
};
}
/** After render, wire up the file input. */
_onRender(context, options) {
super._onRender(context, options);
const input = this.element.querySelector(".cde-migration-file-input");
input?.addEventListener("change", this.#onFileChange.bind(this));
const dropZone = this.element.querySelector(".cde-migration-drop-zone");
if (dropZone) {
dropZone.addEventListener("dragover", (e) => {
e.preventDefault();
dropZone.classList.add("is-dragover");
});
dropZone.addEventListener("dragleave", () => dropZone.classList.remove("is-dragover"));
dropZone.addEventListener("drop", (e) => {
e.preventDefault();
dropZone.classList.remove("is-dragover");
this.#processFiles(Array.from(e.dataTransfer.files));
});
}
}
async #onFileChange(event) {
const files = Array.from(event.target.files ?? []);
event.target.value = "";
await this.#processFiles(files);
}
async #processFiles(files) {
for (const file of files) {
if (!file.name.endsWith(".json")) {
this.#errors.push(game.i18n.format("CDE.MigrationErrorNotJson", { file: file.name }));
continue;
}
try {
const text = await file.text();
const actors = parseLegacyJson(text);
for (const actor of actors) {
actor._srcFile = file.name;
if (!this.#pending.some((p) => p.name === actor.name)) {
this.#pending.push(actor);
}
}
} catch (err) {
this.#errors.push(game.i18n.format("CDE.MigrationErrorParse", { file: file.name, error: err.message }));
}
}
this.render();
}
static async #clearFiles() {
this.#pending = [];
this.#errors = [];
this.render();
}
static async #doImport() {
if (!this.#pending.length) return;
const created = [];
const failed = [];
for (const data of this.#pending) {
try {
const { _srcFile, ...actorData } = data;
const actor = await Actor.create(actorData);
created.push(actor.name);
} catch (err) {
failed.push(`${data.name}: ${err.message}`);
console.error(`CHRONIQUESDELETRANGE | Migration failed for "${data.name}":`, err);
}
}
this.#pending = [];
this.#errors = failed;
this.render();
if (created.length) {
ui.notifications.info(
game.i18n.format("CDE.MigrationSuccess", { count: created.length, names: created.join(", ") })
);
}
if (failed.length) {
ui.notifications.warn(
game.i18n.format("CDE.MigrationPartialError", { count: failed.length })
);
}
}
};
// src/config/settings.js // src/config/settings.js
function registerSettings() { function registerSettings() {
game.settings.registerMenu(SYSTEM_ID, "migrationTool", {
name: "CDE.MigrationTitle",
label: "CDE.MigrationMenuLabel",
hint: "CDE.MigrationMenuHint",
icon: "fas fa-file-import",
type: CDEMigrationApp,
restricted: true
});
game.settings.register(SYSTEM_ID, "loksyuData", { game.settings.register(SYSTEM_ID, "loksyuData", {
scope: "world", scope: "world",
config: false, config: false,
@@ -154,9 +601,36 @@ function registerSettings() {
type: Number, type: Number,
default: 0 default: 0
}); });
game.settings.register(SYSTEM_ID, "welcomeSceneLoaded", {
scope: "world",
config: false,
type: Boolean,
default: false
});
} }
async function migrateIfNeeded() { async function migrateIfNeeded() {
} }
async function loadWelcomeSceneIfNeeded() {
if (!game.user.isGM) return;
if (game.settings.get(SYSTEM_ID, "welcomeSceneLoaded")) return;
try {
const pack = game.packs.get(`${SYSTEM_ID}.cde-scenes`);
if (!pack) return;
const index = await pack.getIndex();
const entry = index.find((e) => e.name === "Accueil");
if (!entry) return;
const existing = game.scenes.find((s) => s.name === "Accueil");
let scene = existing;
if (!scene) {
const doc = await pack.getDocument(entry._id);
[scene] = await Scene.createDocuments([doc.toObject()]);
}
await game.settings.set(SYSTEM_ID, "welcomeSceneLoaded", true);
await scene.activate();
} catch (err) {
console.error("CHRONIQUESDELETRANGE | loadWelcomeSceneIfNeeded failed:", err);
}
}
// src/config/localize.js // src/config/localize.js
function preLocalizeConfig() { function preLocalizeConfig() {
@@ -351,6 +825,9 @@ var CharacterDataModel = class extends foundry.abstract.TypeDataModel {
}; };
return schema; return schema;
} }
prepareDerivedData() {
this.anti_initiative = 25 - (this.initiative ?? 0);
}
}; };
// src/data/actors/npc.js // src/data/actors/npc.js
@@ -396,6 +873,11 @@ var NpcDataModel = class extends foundry.abstract.TypeDataModel {
}) })
}; };
} }
prepareDerivedData() {
this.anti_initiative = 25 - (this.initiative ?? 0);
this.vitality.calcul = (this.aptitudes.physical.value ?? 0) * 4;
this.hei.calcul = (this.aptitudes.spiritual.value ?? 0) * 4;
}
}; };
// src/data/items/item.js // src/data/items/item.js
@@ -593,58 +1075,25 @@ var CDEMessage = class extends ChatMessage {
this.#enrichChatCard(html); this.#enrichChatCard(html);
return html; return html;
} }
getAssociatedActor() {
if (this.speaker.scene && this.speaker.token) {
const scene = game.scenes.get(this.speaker.scene);
const token = scene?.tokens.get(this.speaker.token);
if (token) return token.actor;
}
return game.actors.get(this.speaker.actor);
}
#enrichChatCard(html) { #enrichChatCard(html) {
const actor = this.getAssociatedActor(); const tokenDoc = this.speaker.scene && this.speaker.token ? game.scenes.get(this.speaker.scene)?.tokens.get(this.speaker.token) : null;
let img; const actor = tokenDoc?.actor ?? game.actors.get(this.speaker.actor) ?? null;
let nameText; const [img, nameText] = this.isContentVisible ? [actor?.img ?? this.author.avatar, this.alias] : [this.author.avatar, this.author.name];
if (this.isContentVisible) { const avatarImg = Object.assign(document.createElement("img"), { src: img, alt: nameText });
img = actor?.img ?? this.author.avatar; const avatar = Object.assign(document.createElement("a"), { className: "avatar" });
nameText = this.alias;
} else {
img = this.author.avatar;
nameText = this.author.name;
}
const avatar = document.createElement("a");
avatar.classList.add("avatar");
if (actor) avatar.dataset.uuid = actor.uuid; if (actor) avatar.dataset.uuid = actor.uuid;
const avatarImg = document.createElement("img");
Object.assign(avatarImg, { src: img, alt: nameText });
avatar.append(avatarImg); avatar.append(avatarImg);
const name = document.createElement("span"); const title = Object.assign(document.createElement("span"), { className: "title", textContent: nameText });
name.classList.add("name-stacked"); const name = Object.assign(document.createElement("span"), { className: "name-stacked" });
const title = document.createElement("span");
title.classList.add("title");
title.append(nameText);
name.append(title); name.append(title);
const sender = html.querySelector(".message-sender"); html.querySelector(".message-sender")?.replaceChildren(avatar, name);
sender?.replaceChildren(avatar, name);
} }
}; };
// src/documents/actor.js // src/documents/actor.js
var CDEActor = class extends Actor { var CDEActor = class extends Actor {
getRollData() { getRollData() {
const data = this.toObject(false).system; return this.toObject(false).system;
return data;
}
prepareBaseData() {
super.prepareBaseData();
if (this.type === ACTOR_TYPES.character) {
this.system.anti_initiative = 25 - (this.system.initiative ?? 0);
}
if (this.type === ACTOR_TYPES.npc) {
this.system.vitality.calcul = (this.system.aptitudes.physical.value ?? 0) * 4;
this.system.hei.calcul = (this.system.aptitudes.spiritual.value ?? 0) * 4;
this.system.anti_initiative = 25 - (this.system.initiative ?? 0);
}
} }
}; };
@@ -721,15 +1170,6 @@ function registerDice() {
function registerHandlebarsHelpers() { function registerHandlebarsHelpers() {
const { Handlebars } = globalThis; const { Handlebars } = globalThis;
if (!Handlebars) return; if (!Handlebars) return;
Handlebars.registerHelper("select", function(selected, options) {
const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected));
const rgx = new RegExp(` value=["']${escapedValue}["']`);
const html = options.fn(this);
return html.replace(rgx, "$& selected");
});
Handlebars.registerHelper("getMagicBackground", function(magic) {
return game.i18n.localize(MAGICS[magic]?.background ?? "");
});
Handlebars.registerHelper("getMagicLabel", function(magic) { Handlebars.registerHelper("getMagicLabel", function(magic) {
return game.i18n.localize(MAGICS[magic]?.label ?? ""); return game.i18n.localize(MAGICS[magic]?.label ?? "");
}); });
@@ -742,9 +1182,6 @@ function registerHandlebarsHelpers() {
Handlebars.registerHelper("getMagicSpecialityClassIcon", function(magic, speciality) { Handlebars.registerHelper("getMagicSpecialityClassIcon", function(magic, speciality) {
return MAGICS[magic]?.speciality?.[speciality]?.classicon ?? ""; return MAGICS[magic]?.speciality?.[speciality]?.classicon ?? "";
}); });
Handlebars.registerHelper("getMagicSpecialityIcon", function(magic, speciality) {
return MAGICS[magic]?.speciality?.[speciality]?.icon ?? "";
});
Handlebars.registerHelper("getMagicSpecialityElementIcon", function(magic, speciality) { Handlebars.registerHelper("getMagicSpecialityElementIcon", function(magic, speciality) {
return MAGICS[magic]?.speciality?.[speciality]?.elementicon ?? ""; return MAGICS[magic]?.speciality?.[speciality]?.elementicon ?? "";
}); });
@@ -2570,6 +3007,47 @@ function refreshAllRollActions() {
}); });
} }
// src/ui/apps/welcome.js
var HELP_JOURNAL_UUID = `Compendium.${SYSTEM_ID}.cde-help.JournalEntry.CDEGuideMain0001`;
async function showWelcomeMessage() {
const logo = `systems/${SYSTEM_ID}/images/logo_jeu.webp`;
const content = `
<div class="cde-welcome-message">
<img class="cde-welcome-logo" src="${logo}" alt="Les Chroniques de l'\xC9trange" />
<h2 class="cde-welcome-title">Les Chroniques de l'\xC9trange</h2>
<p class="cde-welcome-links">
Un jeu de r\xF4le \xE9dit\xE9 par
<a href="https://antre-monde.com/les-chroniques-de-letrengae/" target="_blank" rel="noopener">Antre-Monde \xC9ditions</a>
</p>
<p class="cde-welcome-links">
Syst\xE8me FoundryVTT r\xE9alis\xE9 par
<a href="https://www.uberwald.me" target="_blank" rel="noopener">LeRatierBretonnien</a>
</p>
<button type="button" class="cde-welcome-help-btn" data-action="open-cde-help">
<i class="fas fa-book-open"></i>
${game.i18n.localize("CDE.WelcomeOpenHelp")}
</button>
</div>`;
await ChatMessage.create({
content,
speaker: { alias: "Les Chroniques de l'\xC9trange" },
flags: { [SYSTEM_ID]: { welcome: true } }
});
}
function injectWelcomeActions(_message, html) {
const el = html instanceof HTMLElement ? html : html[0] ?? html;
const btn = el?.querySelector?.("[data-action='open-cde-help']");
if (!btn) return;
btn.addEventListener("click", async () => {
try {
const doc = await fromUuid(HELP_JOURNAL_UUID);
doc?.sheet?.render(true);
} catch {
game.packs.get(`${SYSTEM_ID}.cde-help`)?.render(true);
}
});
}
// src/system.js // src/system.js
Hooks.once("i18nInit", preLocalizeConfig); Hooks.once("i18nInit", preLocalizeConfig);
Hooks.once("init", async () => { Hooks.once("init", async () => {
@@ -2655,7 +3133,9 @@ Hooks.once("init", async () => {
}); });
Hooks.once("ready", async () => { Hooks.once("ready", async () => {
await migrateIfNeeded(); await migrateIfNeeded();
await loadWelcomeSceneIfNeeded();
CDEWheelApp.registerHooks(); CDEWheelApp.registerHooks();
if (game.user.isGM) showWelcomeMessage();
}); });
Hooks.on("renderChatLog", (_app, html) => { Hooks.on("renderChatLog", (_app, html) => {
const el = html instanceof HTMLElement ? html : html[0] ?? html; const el = html instanceof HTMLElement ? html : html[0] ?? html;
@@ -2685,6 +3165,7 @@ Hooks.on("renderChatLog", (_app, html) => {
}); });
Hooks.on("renderChatMessageHTML", (message, html) => { Hooks.on("renderChatMessageHTML", (message, html) => {
injectRollActions(message, html); injectRollActions(message, html);
if (message.flags?.[SYSTEM_ID]?.welcome) injectWelcomeActions(message, html);
}); });
Hooks.on("updateSetting", (setting) => { Hooks.on("updateSetting", (setting) => {
if (!setting.key) return; if (!setting.key) return;
+3 -3
View File
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

+22 -50
View File
@@ -1,58 +1,30 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="ar-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="ar-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#4ecdc4" stop-opacity="0.3"/> <stop offset="0%" stop-color="#00e5ff" stop-opacity="0.35"/>
<stop offset="100%" stop-color="#4ecdc4" stop-opacity="0"/> <stop offset="100%" stop-color="#00e5ff" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#ar-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#ar-glow)"/>
<!-- Plastron style écaille de dragon — rangées d'écailles imbriquées -->
<!-- Bouclier circulaire extérieur --> <!-- Rangée 1 -->
<circle cx="50" cy="50" r="36" fill="#4ecdc4" fill-opacity="0.08" stroke="#4ecdc4" stroke-width="2.5"/> <path d="M42 24 Q50 18 58 24 Q50 32 42 24Z" fill="#00e5ff" fill-opacity="0.22" stroke="#00e5ff" stroke-width="1.6"/>
<!-- Anneau décoratif intérieur --> <!-- Rangée 2 -->
<circle cx="50" cy="50" r="30" fill="none" stroke="#4ecdc4" stroke-width="1" opacity="0.5"/> <path d="M30 36 Q38 28 46 36 Q38 44 30 36Z" fill="#00e5ff" fill-opacity="0.22" stroke="#00e5ff" stroke-width="1.6"/>
<path d="M54 36 Q62 28 70 36 Q62 44 54 36Z" fill="#00e5ff" fill-opacity="0.22" stroke="#00e5ff" stroke-width="1.6"/>
<!-- 8 rivets décoratifs sur le bord extérieur du bouclier --> <!-- Rangée 3 -->
<circle cx="50" cy="15" r="2" fill="#4ecdc4" opacity="0.8"/> <path d="M22 50 Q30 42 38 50 Q30 58 22 50Z" fill="#00e5ff" fill-opacity="0.22" stroke="#00e5ff" stroke-width="1.6"/>
<circle cx="75" cy="25" r="2" fill="#4ecdc4" opacity="0.8"/> <path d="M42 50 Q50 42 58 50 Q50 58 42 50Z" fill="#00e5ff" fill-opacity="0.22" stroke="#00e5ff" stroke-width="1.6"/>
<circle cx="85" cy="50" r="2" fill="#4ecdc4" opacity="0.8"/> <path d="M62 50 Q70 42 78 50 Q70 58 62 50Z" fill="#00e5ff" fill-opacity="0.22" stroke="#00e5ff" stroke-width="1.6"/>
<circle cx="75" cy="75" r="2" fill="#4ecdc4" opacity="0.8"/> <!-- Rangée 4 -->
<circle cx="50" cy="85" r="2" fill="#4ecdc4" opacity="0.8"/> <path d="M30 64 Q38 56 46 64 Q38 72 30 64Z" fill="#00e5ff" fill-opacity="0.22" stroke="#00e5ff" stroke-width="1.6"/>
<circle cx="25" cy="75" r="2" fill="#4ecdc4" opacity="0.8"/> <path d="M54 64 Q62 56 70 64 Q62 72 54 64Z" fill="#00e5ff" fill-opacity="0.22" stroke="#00e5ff" stroke-width="1.6"/>
<circle cx="15" cy="50" r="2" fill="#4ecdc4" opacity="0.8"/> <!-- Rangée 5 -->
<circle cx="25" cy="25" r="2" fill="#4ecdc4" opacity="0.8"/> <path d="M42 77 Q50 70 58 77 Q50 84 42 77Z" fill="#00e5ff" fill-opacity="0.22" stroke="#00e5ff" stroke-width="1.6"/>
<!-- Reflets -->
<!-- Tête de tigre stylisée --> <circle cx="50" cy="24" r="1.5" fill="#00e5ff" opacity="0.9"/>
<!-- Front / dessus de la tête --> <circle cx="38" cy="36" r="1.5" fill="#00e5ff" opacity="0.9"/>
<path d="M34 40 Q50 30 66 40 Q68 48 64 54 Q58 62 50 64 Q42 62 36 54 Q32 48 34 40 Z" <circle cx="62" cy="36" r="1.5" fill="#00e5ff" opacity="0.9"/>
fill="#4ecdc4" fill-opacity="0.15" stroke="#4ecdc4" stroke-width="2"/> <circle cx="50" cy="50" r="1.5" fill="#00e5ff" opacity="0.9"/>
<!-- Oreilles de tigre -->
<path d="M34 40 Q28 30 32 24 Q36 32 40 38" fill="#4ecdc4" fill-opacity="0.2" stroke="#4ecdc4" stroke-width="1.5"/>
<path d="M66 40 Q72 30 68 24 Q64 32 60 38" fill="#4ecdc4" fill-opacity="0.2" stroke="#4ecdc4" stroke-width="1.5"/>
<!-- Yeux du tigre -->
<ellipse cx="43" cy="46" rx="5" ry="4" fill="none" stroke="#4ecdc4" stroke-width="1.5"/>
<ellipse cx="57" cy="46" rx="5" ry="4" fill="none" stroke="#4ecdc4" stroke-width="1.5"/>
<ellipse cx="43" cy="46" rx="2" ry="3" fill="#4ecdc4" fill-opacity="0.5"/>
<ellipse cx="57" cy="46" rx="2" ry="3" fill="#4ecdc4" fill-opacity="0.5"/>
<!-- Nez du tigre -->
<path d="M47 52 Q50 50 53 52 Q50 55 47 52 Z" fill="#4ecdc4" fill-opacity="0.6" stroke="#4ecdc4" stroke-width="1"/>
<!-- Moustaches -->
<line x1="32" y1="53" x2="45" y2="53" stroke="#4ecdc4" stroke-width="1" opacity="0.7"/>
<line x1="55" y1="53" x2="68" y2="53" stroke="#4ecdc4" stroke-width="1" opacity="0.7"/>
<line x1="33" y1="57" x2="45" y2="55" stroke="#4ecdc4" stroke-width="1" opacity="0.7"/>
<line x1="55" y1="55" x2="67" y2="57" stroke="#4ecdc4" stroke-width="1" opacity="0.7"/>
<!-- Rayures de tigre sur le front -->
<path d="M46 36 Q48 40 47 44" fill="none" stroke="#4ecdc4" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
<path d="M50 34 Q50 38 50 42" fill="none" stroke="#4ecdc4" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
<path d="M54 36 Q52 40 53 44" fill="none" stroke="#4ecdc4" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
<!-- Crocs en bas -->
<path d="M46 60 L44 68 L46 64 L48 70 L50 63 L52 70 L54 64 L56 68 L54 60"
fill="none" stroke="#4ecdc4" stroke-width="1.5" stroke-linejoin="round" opacity="0.8"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+19 -46
View File
@@ -1,53 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="ig-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="ig-glow" cx="50%" cy="60%" r="50%">
<stop offset="0%" stop-color="#98c379" stop-opacity="0.3"/> <stop offset="0%" stop-color="#39ff14" stop-opacity="0.35"/>
<stop offset="100%" stop-color="#98c379" stop-opacity="0"/> <stop offset="100%" stop-color="#39ff14" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#ig-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#ig-glow)"/>
<!-- Mortier de pharmacopée chinoise -->
<!-- Pilon (pestle) — en haut à droite --> <path d="M28 62 Q28 76 50 76 Q72 76 72 62 L68 48 Q68 44 50 44 Q32 44 32 48 Z"
<line x1="62" y1="20" x2="52" y2="40" stroke="#98c379" stroke-width="3.5" stroke-linecap="round"/> fill="#39ff14" fill-opacity="0.15" stroke="#39ff14" stroke-width="2"/>
<!-- Tête du pilon (plus épaisse) --> <!-- Pilon -->
<ellipse cx="63" cy="19" rx="4" ry="3" fill="#98c379" fill-opacity="0.4" stroke="#98c379" stroke-width="1.5" transform="rotate(-30 63 19)"/> <line x1="50" y1="22" x2="50" y2="54" stroke="#39ff14" stroke-width="3" stroke-linecap="round"/>
<ellipse cx="50" cy="54" rx="7" ry="4" fill="#39ff14" fill-opacity="0.3" stroke="#39ff14" stroke-width="1.8"/>
<!-- Mortier (bol) --> <!-- Vapeur montante -->
<!-- Corps extérieur du mortier --> <path d="M38 42 Q34 36 38 30 Q42 24 38 18" fill="none" stroke="#39ff14" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
<path d="M22 54 Q22 76 50 80 Q78 76 78 54 Z" <path d="M50 40 Q46 34 50 28 Q54 22 50 16" fill="none" stroke="#39ff14" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
fill="#98c379" fill-opacity="0.12" stroke="#98c379" stroke-width="2.5"/> <path d="M62 42 Q58 36 62 30 Q66 24 62 18" fill="none" stroke="#39ff14" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
<!-- Bord supérieur du mortier (ellipse) --> <!-- Petits cristaux/herbes dans le mortier -->
<ellipse cx="50" cy="54" rx="28" ry="8" fill="#98c379" fill-opacity="0.1" stroke="#98c379" stroke-width="2"/> <circle cx="44" cy="58" r="2" fill="#39ff14" opacity="0.7"/>
<!-- Contenu / intérieur sombre --> <circle cx="52" cy="60" r="1.5" fill="#39ff14" opacity="0.7"/>
<ellipse cx="50" cy="56" rx="22" ry="6" fill="#101622" fill-opacity="0.6"/> <circle cx="59" cy="57" r="2" fill="#39ff14" opacity="0.7"/>
<!-- Surface du contenu (herbes) --> <!-- Bande décorative -->
<ellipse cx="50" cy="55" rx="18" ry="4" fill="#98c379" fill-opacity="0.2" stroke="#98c379" stroke-width="0.8" opacity="0.6"/> <path d="M32 48 Q50 52 68 48" fill="none" stroke="#39ff14" stroke-width="1" opacity="0.5"/>
<!-- Ornements du mortier (gravures) -->
<!-- Motif vague (eau) sur le corps du mortier -->
<path d="M26 64 Q32 60 38 64 Q44 68 50 64 Q56 60 62 64 Q68 68 74 64"
fill="none" stroke="#98c379" stroke-width="1" opacity="0.5"/>
<!-- Fleur de lotus au-dessus du mortier -->
<!-- Pétales (6) -->
<path d="M50 44 Q46 36 42 30 Q46 34 50 32 Q54 34 58 30 Q54 36 50 44"
fill="#98c379" fill-opacity="0.2" stroke="#98c379" stroke-width="1.5"/>
<path d="M38 48 Q30 44 26 38 Q32 40 36 36 Q40 32 40 38 Q40 44 38 48"
fill="#98c379" fill-opacity="0.2" stroke="#98c379" stroke-width="1.5"/>
<path d="M62 48 Q70 44 74 38 Q68 40 64 36 Q60 32 60 38 Q60 44 62 48"
fill="#98c379" fill-opacity="0.2" stroke="#98c379" stroke-width="1.5"/>
<!-- Coeur du lotus -->
<circle cx="50" cy="46" r="4" fill="#98c379" fill-opacity="0.4" stroke="#98c379" stroke-width="1.5"/>
<circle cx="50" cy="46" r="1.5" fill="#98c379"/>
<!-- Vapeurs alchimiques -->
<path d="M36 52 Q32 46 36 40 Q34 44 38 46 Q36 48 38 52"
fill="none" stroke="#98c379" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
<path d="M50 52 Q48 44 52 38 Q50 44 54 46 Q52 48 52 52"
fill="none" stroke="#98c379" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
<path d="M64 52 Q68 46 64 40 Q66 44 62 46 Q64 48 62 52"
fill="none" stroke="#98c379" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

+19 -51
View File
@@ -1,59 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="it-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="it-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#00d4d4" stop-opacity="0.3"/> <stop offset="0%" stop-color="#ffd700" stop-opacity="0.4"/>
<stop offset="100%" stop-color="#00d4d4" stop-opacity="0"/> <stop offset="100%" stop-color="#ffd700" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#it-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#it-glow)"/>
<!-- Disque de jade bi (圓璧) — cercle avec trou central -->
<!-- Coffret laqué (corps) --> <circle cx="50" cy="50" r="32" fill="#ffd700" fill-opacity="0.12" stroke="#ffd700" stroke-width="2.5"/>
<!-- Couvercle --> <circle cx="50" cy="50" r="22" fill="#101622" stroke="#ffd700" stroke-width="1.5"/>
<rect x="20" y="26" width="60" height="20" rx="3" fill="#00d4d4" fill-opacity="0.12" stroke="#00d4d4" stroke-width="2.5"/> <circle cx="50" cy="50" r="14" fill="#ffd700" fill-opacity="0.08" stroke="#ffd700" stroke-width="1" opacity="0.6"/>
<!-- Corps du coffret --> <!-- Motifs décoratifs — 8 trigrammes stylisés sur le pourtour -->
<rect x="18" y="46" width="64" height="32" rx="3" fill="#00d4d4" fill-opacity="0.08" stroke="#00d4d4" stroke-width="2.5"/> <circle cx="50" cy="50" r="27" fill="none" stroke="#ffd700" stroke-width="0.8" stroke-dasharray="3 4.2" opacity="0.5"/>
<!-- Encoches décoratives aux 8 directions -->
<!-- Ligne de séparation couvercle/corps --> <line x1="50" y1="18" x2="50" y2="23" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="18" y1="46" x2="82" y2="46" stroke="#00d4d4" stroke-width="1.5" opacity="0.7"/> <line x1="50" y1="77" x2="50" y2="82" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="18" y1="50" x2="23" y2="50" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<!-- Ornements du couvercle --> <line x1="77" y1="50" x2="82" y2="50" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<!-- Motif de losanges (style laque chinoise) --> <line x1="27.6" y1="27.6" x2="31.1" y2="31.1" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<path d="M36 36 L50 28 L64 36 L50 44 Z" fill="none" stroke="#00d4d4" stroke-width="1" opacity="0.5"/> <line x1="68.9" y1="68.9" x2="72.4" y2="72.4" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<!-- Lignes décoratives côtés couvercle --> <line x1="72.4" y1="27.6" x2="68.9" y2="31.1" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="22" y1="30" x2="22" y2="42" stroke="#00d4d4" stroke-width="1" opacity="0.4"/> <line x1="27.6" y1="72.4" x2="31.1" y2="68.9" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="78" y1="30" x2="78" y2="42" stroke="#00d4d4" stroke-width="1" opacity="0.4"/> <!-- Reflet jade -->
<path d="M36 30 Q42 28 46 32" fill="none" stroke="#ffd700" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
<!-- Serrure yin-yang au centre du corps du coffret -->
<!-- Cercle de la serrure -->
<circle cx="50" cy="60" r="9" fill="#101622" stroke="#00d4d4" stroke-width="2"/>
<!-- Yin-yang simplifié dans la serrure -->
<!-- Moitié yang (gauche, claire) -->
<path d="M50 51 A9 9 0 0 0 50 69 A4.5 4.5 0 0 0 50 60 A4.5 4.5 0 0 1 50 51"
fill="#00d4d4" fill-opacity="0.4"/>
<!-- Petits cercles du yin-yang -->
<circle cx="50" cy="55.5" r="2" fill="#101622"/>
<circle cx="50" cy="64.5" r="2" fill="#00d4d4" fill-opacity="0.6"/>
<!-- Anneau de la serrure (trou de clé) -->
<rect x="48" y="66" width="4" height="5" rx="1" fill="#00d4d4" fill-opacity="0.5"/>
<!-- Ferrures du coffret (coins) -->
<!-- Coins du corps -->
<path d="M18 46 L18 54 L24 54" fill="none" stroke="#00d4d4" stroke-width="1.5" stroke-linecap="round"/>
<path d="M82 46 L82 54 L76 54" fill="none" stroke="#00d4d4" stroke-width="1.5" stroke-linecap="round"/>
<path d="M18 78 L18 70 L24 70" fill="none" stroke="#00d4d4" stroke-width="1.5" stroke-linecap="round"/>
<path d="M82 78 L82 70 L76 70" fill="none" stroke="#00d4d4" stroke-width="1.5" stroke-linecap="round"/>
<!-- Charnières du coffret (sur le côté droit) -->
<rect x="76" y="40" width="8" height="5" rx="2" fill="#00d4d4" fill-opacity="0.3" stroke="#00d4d4" stroke-width="1"/>
<rect x="76" y="50" width="8" height="5" rx="2" fill="#00d4d4" fill-opacity="0.3" stroke="#00d4d4" stroke-width="1"/>
<!-- Motif décoratif sur le bas du coffret -->
<path d="M26 72 Q50 68 74 72" fill="none" stroke="#00d4d4" stroke-width="1" stroke-dasharray="3 2" opacity="0.4"/>
<!-- Petits ornements latéraux sur le corps -->
<line x1="22" y1="56" x2="22" y2="68" stroke="#00d4d4" stroke-width="1" stroke-dasharray="2 2" opacity="0.4"/>
<line x1="78" y1="56" x2="78" y2="68" stroke="#00d4d4" stroke-width="1" stroke-dasharray="2 2" opacity="0.4"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

+24 -33
View File
@@ -1,38 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<!-- Fond circulaire -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#kg-glow)" fill-opacity="0.12"/>
<defs> <defs>
<radialGradient id="kg-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="kf-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#ff3d5a" stop-opacity="0.4"/> <stop offset="0%" stop-color="#ff2244" stop-opacity="0.4"/>
<stop offset="100%" stop-color="#ff3d5a" stop-opacity="0"/> <stop offset="100%" stop-color="#ff2244" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<!-- Poing stylisé (vue de face, doigts repliés) --> <circle cx="50" cy="50" r="48" fill="url(#kf-glow)"/>
<!-- Paume / base --> <!-- Silhouette grue — posture sur une jambe, ailes déployées -->
<rect x="32" y="52" width="36" height="20" rx="4" fill="#ff3d5a" fill-opacity="0.18" stroke="#ff3d5a" stroke-width="2"/> <!-- Tête -->
<!-- Doigts repliés — 4 rangées --> <circle cx="50" cy="18" r="5" fill="#ff2244" fill-opacity="0.2" stroke="#ff2244" stroke-width="2"/>
<rect x="33" y="38" width="8" height="16" rx="3" fill="#ff3d5a" fill-opacity="0.18" stroke="#ff3d5a" stroke-width="2"/> <!-- Corps -->
<rect x="43" y="35" width="8" height="19" rx="3" fill="#ff3d5a" fill-opacity="0.18" stroke="#ff3d5a" stroke-width="2"/> <line x1="50" y1="23" x2="50" y2="52" stroke="#ff2244" stroke-width="2.5" stroke-linecap="round"/>
<rect x="53" y="36" width="8" height="18" rx="3" fill="#ff3d5a" fill-opacity="0.18" stroke="#ff3d5a" stroke-width="2"/> <!-- Bras gauche déployé vers le haut -->
<rect x="63" y="40" width="6" height="14" rx="3" fill="#ff3d5a" fill-opacity="0.18" stroke="#ff3d5a" stroke-width="2"/> <path d="M50 33 Q38 26 24 28" fill="none" stroke="#ff2244" stroke-width="2.5" stroke-linecap="round"/>
<!-- Pouce --> <!-- Bras droit vers le bas en attaque -->
<path d="M32 62 Q24 60 25 54 Q26 50 32 52" fill="#ff3d5a" fill-opacity="0.18" stroke="#ff3d5a" stroke-width="2" stroke-linejoin="round"/> <path d="M50 33 Q62 36 72 30" fill="none" stroke="#ff2244" stroke-width="2.5" stroke-linecap="round"/>
<!-- Jambe d'appui -->
<!-- Éclairs de qi (3 rayons) --> <line x1="50" y1="52" x2="50" y2="78" stroke="#ff2244" stroke-width="2.5" stroke-linecap="round"/>
<!-- Éclair gauche-haut --> <path d="M50 78 Q45 82 40 80" fill="none" stroke="#ff2244" stroke-width="2.5" stroke-linecap="round"/>
<polyline points="24,28 19,20 25,22 20,13" fill="none" stroke="#ff3d5a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity="0.9"/> <!-- Jambe levée -->
<!-- Éclair centre-haut --> <path d="M50 52 Q60 58 66 52 Q70 46 72 50" fill="none" stroke="#ff2244" stroke-width="2.5" stroke-linecap="round"/>
<polyline points="50,30 47,20 52,23 49,13" fill="none" stroke="#ff3d5a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity="0.9"/> <!-- Énergie chi — spirales autour du personnage -->
<!-- Éclair droite-haut --> <path d="M20 45 Q18 38 24 34 Q30 30 28 24" fill="none" stroke="#ff2244" stroke-width="1.2" stroke-linecap="round" opacity="0.5"/>
<polyline points="74,32 80,22 74,24 79,14" fill="none" stroke="#ff3d5a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity="0.9"/> <path d="M78 40 Q82 46 78 52 Q74 58 78 64" fill="none" stroke="#ff2244" stroke-width="1.2" stroke-linecap="round" opacity="0.5"/>
<circle cx="24" cy="28" r="2" fill="#ff2244" opacity="0.8"/>
<!-- Petites étincelles --> <circle cx="72" cy="30" r="2" fill="#ff2244" opacity="0.8"/>
<circle cx="22" cy="35" r="1.5" fill="#ff3d5a" opacity="0.8"/>
<circle cx="78" cy="37" r="1.5" fill="#ff3d5a" opacity="0.8"/>
<circle cx="50" cy="26" r="1.5" fill="#ff3d5a" opacity="0.8"/>
<!-- Ligne de force sous le poing -->
<path d="M28 73 Q50 80 72 73" fill="none" stroke="#ff3d5a" stroke-width="1.5" stroke-dasharray="3 2" opacity="0.5"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

+26 -35
View File
@@ -1,43 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="dm-glow" cx="50%" cy="45%" r="50%"> <radialGradient id="dm-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#cc2222" stop-opacity="0.5"/> <stop offset="0%" stop-color="#cc0000" stop-opacity="0.45"/>
<stop offset="100%" stop-color="#cc2222" stop-opacity="0"/> <stop offset="100%" stop-color="#cc0000" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#dm-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#dm-glow)"/>
<!-- Masque d'opéra cantonais — démon -->
<!-- Flammes infernales en arrière-plan --> <!-- Visage -->
<path d="M30 78 Q28 65 34 56 Q30 62 32 52 Q36 42 42 48 Q38 36 46 28 Q48 42 44 50 Q50 38 54 30 Q56 44 50 54 Q56 46 62 40 Q62 54 56 62 Q62 56 68 52 Q66 64 62 72 Q56 66 54 72 Q50 80 48 72 Q44 64 40 72 Q36 80 30 78 Z" <ellipse cx="50" cy="50" rx="26" ry="30" fill="#cc0000" fill-opacity="0.18" stroke="#cc0000" stroke-width="2"/>
fill="#cc2222" fill-opacity="0.18" stroke="#cc2222" stroke-width="1" stroke-linejoin="round" opacity="0.7"/> <!-- Motif front — marques tribales -->
<path d="M36 36 Q42 30 50 32 Q58 30 64 36" fill="none" stroke="#cc0000" stroke-width="2" stroke-linecap="round"/>
<!-- Visage de démon - contour de tête --> <line x1="50" y1="22" x2="50" y2="32" stroke="#cc0000" stroke-width="2" stroke-linecap="round"/>
<ellipse cx="50" cy="52" rx="20" ry="22" fill="#cc2222" fill-opacity="0.12" stroke="#cc2222" stroke-width="2"/> <!-- Cornes -->
<path d="M36 28 Q30 16 26 12" fill="none" stroke="#cc0000" stroke-width="2.5" stroke-linecap="round"/>
<!-- Deux cornes --> <path d="M64 28 Q70 16 74 12" fill="none" stroke="#cc0000" stroke-width="2.5" stroke-linecap="round"/>
<path d="M36 35 Q32 20 38 16 Q40 24 38 30" fill="#cc2222" fill-opacity="0.7" stroke="#cc2222" stroke-width="1.5" stroke-linejoin="round"/> <!-- Yeux en amande — regard furieux -->
<path d="M64 35 Q68 20 62 16 Q60 24 62 30" fill="#cc2222" fill-opacity="0.7" stroke="#cc2222" stroke-width="1.5" stroke-linejoin="round"/> <path d="M34 46 Q38 42 42 46 Q38 50 34 46Z" fill="#cc0000" fill-opacity="0.5" stroke="#cc0000" stroke-width="1.5"/>
<path d="M58 46 Q62 42 66 46 Q62 50 58 46Z" fill="#cc0000" fill-opacity="0.5" stroke="#cc0000" stroke-width="1.5"/>
<!-- Yeux (forme de flamme) --> <circle cx="38" cy="46" r="1.5" fill="#cc0000"/>
<path d="M40 50 Q44 44 48 50 Q44 56 40 50 Z" fill="#cc2222" opacity="0.9"/> <circle cx="62" cy="46" r="1.5" fill="#cc0000"/>
<path d="M52 50 Q56 44 60 50 Q56 56 52 50 Z" fill="#cc2222" opacity="0.9"/> <!-- Nez -->
<!-- Pupilles --> <path d="M46 52 Q50 56 54 52" fill="none" stroke="#cc0000" stroke-width="1.5" stroke-linecap="round"/>
<ellipse cx="44" cy="50" rx="1.5" ry="2.5" fill="#101622"/>
<ellipse cx="56" cy="50" rx="1.5" ry="2.5" fill="#101622"/>
<!-- Nez aplati (démon) -->
<path d="M47 58 Q50 60 53 58" fill="none" stroke="#cc2222" stroke-width="2" stroke-linecap="round" opacity="0.7"/>
<!-- Bouche avec crocs --> <!-- Bouche avec crocs -->
<path d="M38 66 Q50 74 62 66" fill="none" stroke="#cc2222" stroke-width="2" stroke-linecap="round" opacity="0.8"/> <path d="M34 62 Q42 70 50 68 Q58 70 66 62" fill="none" stroke="#cc0000" stroke-width="2" stroke-linecap="round"/>
<!-- Crocs --> <line x1="44" y1="65" x2="44" y2="72" stroke="#cc0000" stroke-width="1.5" stroke-linecap="round"/>
<path d="M43 67 L42 73 L45 68" fill="#cc2222" opacity="0.7"/> <line x1="50" y1="68" x2="50" y2="75" stroke="#cc0000" stroke-width="1.5" stroke-linecap="round"/>
<path d="M57 67 L58 73 L55 68" fill="#cc2222" opacity="0.7"/> <line x1="56" y1="65" x2="56" y2="72" stroke="#cc0000" stroke-width="1.5" stroke-linecap="round"/>
<!-- Moustaches -->
<!-- Sourcils menaçants --> <path d="M36 56 Q28 52 22 54" fill="none" stroke="#cc0000" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
<path d="M38 44 Q44 40 48 43" fill="none" stroke="#cc2222" stroke-width="2.5" stroke-linecap="round" opacity="0.8"/> <path d="M64 56 Q72 52 78 54" fill="none" stroke="#cc0000" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
<path d="M52 43 Q56 40 62 44" fill="none" stroke="#cc2222" stroke-width="2.5" stroke-linecap="round" opacity="0.8"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

+31 -38
View File
@@ -1,44 +1,37 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="dv-glow" cx="50%" cy="40%" r="55%"> <radialGradient id="dieu-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#ddaa00" stop-opacity="0.5"/> <stop offset="0%" stop-color="#ffd700" stop-opacity="0.5"/>
<stop offset="100%" stop-color="#ddaa00" stop-opacity="0"/> <stop offset="100%" stop-color="#ffd700" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#dv-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#dieu-glow)"/>
<!-- Soleil impérial céleste -->
<!-- Rayons célestes (halo) --> <!-- Rayons -->
<line x1="50" y1="10" x2="50" y2="18" stroke="#ddaa00" stroke-width="2" opacity="0.6"/> <line x1="50" y1="14" x2="50" y2="24" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="68" y1="15" x2="64" y2="22" stroke="#ddaa00" stroke-width="2" opacity="0.6"/> <line x1="50" y1="76" x2="50" y2="86" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="80" y1="28" x2="74" y2="32" stroke="#ddaa00" stroke-width="2" opacity="0.6"/> <line x1="14" y1="50" x2="24" y2="50" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="32" y1="15" x2="36" y2="22" stroke="#ddaa00" stroke-width="2" opacity="0.6"/> <line x1="76" y1="50" x2="86" y2="50" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="20" y1="28" x2="26" y2="32" stroke="#ddaa00" stroke-width="2" opacity="0.6"/> <line x1="24.8" y1="24.8" x2="31.9" y2="31.9" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="86" y1="44" x2="80" y2="46" stroke="#ddaa00" stroke-width="1.5" opacity="0.5"/> <line x1="68.1" y1="68.1" x2="75.2" y2="75.2" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="14" y1="44" x2="20" y2="46" stroke="#ddaa00" stroke-width="1.5" opacity="0.5"/> <line x1="75.2" y1="24.8" x2="68.1" y2="31.9" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<line x1="24.8" y1="75.2" x2="31.9" y2="68.1" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/>
<!-- Halo circulaire doré --> <!-- Rayons diagonaux secondaires -->
<circle cx="50" cy="36" r="14" fill="none" stroke="#ddaa00" stroke-width="2.5" opacity="0.8"/> <line x1="29.6" y1="18.8" x2="33.8" y2="26.8" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<circle cx="50" cy="36" r="16" fill="none" stroke="#ddaa00" stroke-width="0.8" opacity="0.4" stroke-dasharray="3,4"/> <line x1="70.4" y1="18.8" x2="66.2" y2="26.8" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<line x1="18.8" y1="29.6" x2="26.8" y2="33.8" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<!-- Lotus (5 pétales) --> <line x1="81.2" y1="29.6" x2="73.2" y2="33.8" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<ellipse cx="50" cy="72" rx="8" ry="4" fill="#ddaa00" fill-opacity="0.6" transform="rotate(0,50,72)"/> <line x1="18.8" y1="70.4" x2="26.8" y2="66.2" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<ellipse cx="50" cy="72" rx="8" ry="4" fill="#ddaa00" fill-opacity="0.4" transform="rotate(36,50,72)"/> <line x1="81.2" y1="70.4" x2="73.2" y2="66.2" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<ellipse cx="50" cy="72" rx="8" ry="4" fill="#ddaa00" fill-opacity="0.4" transform="rotate(72,50,72)"/> <line x1="29.6" y1="81.2" x2="33.8" y2="73.2" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<ellipse cx="50" cy="72" rx="8" ry="4" fill="#ddaa00" fill-opacity="0.4" transform="rotate(108,50,72)"/> <line x1="70.4" y1="81.2" x2="66.2" y2="73.2" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<ellipse cx="50" cy="72" rx="8" ry="4" fill="#ddaa00" fill-opacity="0.4" transform="rotate(144,50,72)"/> <!-- Cercle central solaire -->
<!-- Cœur du lotus --> <circle cx="50" cy="50" r="16" fill="#ffd700" fill-opacity="0.2" stroke="#ffd700" stroke-width="2.5"/>
<circle cx="50" cy="72" r="4" fill="#ddaa00" fill-opacity="0.8"/> <!-- Nuages célestes stylisés en dessous -->
<path d="M32 58 Q36 52 42 56 Q46 50 50 54 Q54 50 58 56 Q64 52 68 58" fill="none" stroke="#ffd700" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
<!-- Silhouette divine — corps lumineux --> <!-- Idéogramme stylisé au centre (天) -->
<ellipse cx="50" cy="36" rx="8" ry="10" fill="#ddaa00" fill-opacity="0.25"/> <line x1="44" y1="45" x2="56" y2="45" stroke="#ffd700" stroke-width="1.8" stroke-linecap="round"/>
<!-- Corps stylisé (robe longue) --> <line x1="50" y1="42" x2="50" y2="58" stroke="#ffd700" stroke-width="1.8" stroke-linecap="round"/>
<path d="M44 44 Q40 58 42 72 Q50 68 58 72 Q60 58 56 44 Z" <line x1="42" y1="58" x2="58" y2="58" stroke="#ffd700" stroke-width="1.8" stroke-linecap="round"/>
fill="#ddaa00" fill-opacity="0.18" stroke="#ddaa00" stroke-width="1.5"/>
<!-- Mains en prière -->
<path d="M44 54 Q38 52 36 56 Q38 60 44 58" fill="#ddaa00" fill-opacity="0.3" stroke="#ddaa00" stroke-width="1.2"/>
<path d="M56 54 Q62 52 64 56 Q62 60 56 58" fill="#ddaa00" fill-opacity="0.3" stroke="#ddaa00" stroke-width="1.2"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

+28 -33
View File
@@ -1,41 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="ea-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="ea-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#e8a030" stop-opacity="0.4"/> <stop offset="0%" stop-color="#ff8c00" stop-opacity="0.4"/>
<stop offset="100%" stop-color="#e8a030" stop-opacity="0"/> <stop offset="100%" stop-color="#ff8c00" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#ea-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#ea-glow)"/>
<!-- Tête de renard (esprit renard — huli jing) -->
<!-- Anneau extérieur --> <!-- Museau triangulaire -->
<circle cx="50" cy="50" r="42" fill="none" stroke="#e8a030" stroke-width="1.2" opacity="0.4"/> <path d="M28 68 Q50 30 72 68 Q62 80 50 78 Q38 80 28 68Z"
fill="#ff8c00" fill-opacity="0.18" stroke="#ff8c00" stroke-width="2"/>
<!-- Yin-Yang stylisé (transformation) --> <!-- Oreilles pointues -->
<!-- Demi-cercle yang (clair) --> <path d="M28 68 Q22 48 30 30 Q36 44 40 52Z" fill="#ff8c00" fill-opacity="0.25" stroke="#ff8c00" stroke-width="2"/>
<path d="M50 18 A32 32 0 0 1 50 82 A16 16 0 0 0 50 50 A16 16 0 0 1 50 18 Z" <path d="M72 68 Q78 48 70 30 Q64 44 60 52Z" fill="#ff8c00" fill-opacity="0.25" stroke="#ff8c00" stroke-width="2"/>
fill="#e8a030" fill-opacity="0.25" stroke="#e8a030" stroke-width="1.5"/> <!-- Intérieur oreilles -->
<!-- Demi-cercle yin (sombre) --> <path d="M30 62 Q27 50 33 36 Q36 46 39 52Z" fill="#ff8c00" fill-opacity="0.4" stroke="none"/>
<path d="M50 18 A32 32 0 0 0 50 82 A16 16 0 0 1 50 50 A16 16 0 0 0 50 18 Z" <path d="M70 62 Q73 50 67 36 Q64 46 61 52Z" fill="#ff8c00" fill-opacity="0.4" stroke="none"/>
fill="#e8a030" fill-opacity="0.05" stroke="#e8a030" stroke-width="1.5"/> <!-- Yeux en amande -->
<!-- Petits cercles yin-yang --> <path d="M36 58 Q42 52 46 58 Q42 62 36 58Z" fill="#ff8c00" fill-opacity="0.6" stroke="#ff8c00" stroke-width="1.5"/>
<circle cx="50" cy="34" r="6" fill="#e8a030" fill-opacity="0.6"/> <path d="M54 58 Q58 52 64 58 Q58 62 54 58Z" fill="#ff8c00" fill-opacity="0.6" stroke="#ff8c00" stroke-width="1.5"/>
<circle cx="50" cy="66" r="6" fill="#e8a030" fill-opacity="0.15" stroke="#e8a030" stroke-width="1.2"/> <circle cx="41" cy="57" r="1.5" fill="#ff8c00"/>
<circle cx="59" cy="57" r="1.5" fill="#ff8c00"/>
<!-- Patte d'animal (5 coussinets) --> <!-- Nez -->
<!-- Coussinet principal (paume) --> <path d="M46 66 Q50 70 54 66 Q50 64 46 66Z" fill="#ff8c00" opacity="0.7"/>
<ellipse cx="50" cy="55" rx="9" ry="7" fill="#e8a030" fill-opacity="0.7"/> <!-- Moustaches -->
<!-- 4 orteils --> <line x1="50" y1="67" x2="50" y2="74" stroke="#ff8c00" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
<ellipse cx="40" cy="44" rx="4" ry="3.5" fill="#e8a030" fill-opacity="0.7" transform="rotate(-15,40,44)"/> <line x1="34" y1="68" x2="46" y2="68" stroke="#ff8c00" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
<ellipse cx="45" cy="41" rx="4" ry="3.5" fill="#e8a030" fill-opacity="0.7" transform="rotate(-5,45,41)"/> <line x1="54" y1="68" x2="66" y2="68" stroke="#ff8c00" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
<ellipse cx="55" cy="41" rx="4" ry="3.5" fill="#e8a030" fill-opacity="0.7" transform="rotate(5,55,41)"/> <line x1="32" y1="72" x2="46" y2="70" stroke="#ff8c00" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
<ellipse cx="60" cy="44" rx="4" ry="3.5" fill="#e8a030" fill-opacity="0.7" transform="rotate(15,60,44)"/> <line x1="54" y1="70" x2="68" y2="72" stroke="#ff8c00" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
<!-- Marques spirituelles sur le front -->
<!-- Griffes stylisées --> <circle cx="50" cy="46" r="3" fill="none" stroke="#ff8c00" stroke-width="1.5" opacity="0.7"/>
<path d="M38 42 Q35 38 34 34" fill="none" stroke="#e8a030" stroke-width="1.8" stroke-linecap="round" opacity="0.8"/> <line x1="50" y1="34" x2="50" y2="43" stroke="#ff8c00" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
<path d="M43 39 Q42 35 42 31" fill="none" stroke="#e8a030" stroke-width="1.8" stroke-linecap="round" opacity="0.8"/>
<path d="M57 39 Q58 35 58 31" fill="none" stroke="#e8a030" stroke-width="1.8" stroke-linecap="round" opacity="0.8"/>
<path d="M62 42 Q65 38 66 34" fill="none" stroke="#e8a030" stroke-width="1.8" stroke-linecap="round" opacity="0.8"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

+27 -35
View File
@@ -1,41 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="gh-glow" cx="50%" cy="40%" r="55%"> <radialGradient id="fa-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#88ccee" stop-opacity="0.45"/> <stop offset="0%" stop-color="#88aaff" stop-opacity="0.4"/>
<stop offset="100%" stop-color="#88ccee" stop-opacity="0"/> <stop offset="100%" stop-color="#88aaff" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#gh-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#fa-glow)"/>
<!-- Lanterne en papier avec visage fantôme -->
<!-- Silhouette fantôme — tête arrondie --> <!-- Corde du haut -->
<path d="M34 44 Q34 24 50 22 Q66 24 66 44 L66 68 Q62 64 58 68 Q55 72 52 68 Q49 64 50 68 Q47 72 44 68 Q40 64 36 68 Q34 64 34 68 Z" <line x1="50" y1="14" x2="50" y2="22" stroke="#88aaff" stroke-width="1.8" stroke-linecap="round"/>
fill="#88ccee" fill-opacity="0.15" stroke="#88ccee" stroke-width="2" stroke-linejoin="round"/> <line x1="44" y1="22" x2="56" y2="22" stroke="#88aaff" stroke-width="1.8" stroke-linecap="round"/>
<!-- Corps de la lanterne -->
<!-- Yeux fantôme — vides, inquiétants --> <path d="M36 22 Q24 38 24 54 Q24 70 36 76 Q50 84 64 76 Q76 70 76 54 Q76 38 64 22 Z"
<ellipse cx="43" cy="42" rx="4" ry="5" fill="#101622" stroke="#88ccee" stroke-width="1.5" opacity="0.9"/> fill="#88aaff" fill-opacity="0.12" stroke="#88aaff" stroke-width="2"/>
<ellipse cx="57" cy="42" rx="4" ry="5" fill="#101622" stroke="#88ccee" stroke-width="1.5" opacity="0.9"/> <!-- Côtes de lanterne -->
<!-- Lueurs dans les yeux --> <path d="M26 36 Q50 32 74 36" fill="none" stroke="#88aaff" stroke-width="1" opacity="0.5"/>
<ellipse cx="43" cy="42" rx="1.5" ry="2" fill="#88ccee" opacity="0.5"/> <path d="M24 50 Q50 46 76 50" fill="none" stroke="#88aaff" stroke-width="1" opacity="0.5"/>
<ellipse cx="57" cy="42" rx="1.5" ry="2" fill="#88ccee" opacity="0.5"/> <path d="M26 64 Q50 60 74 64" fill="none" stroke="#88aaff" stroke-width="1" opacity="0.5"/>
<!-- Visage fantomatique -->
<!-- Kanji 鬼 stylisé en filigrane (simplifié) --> <!-- Yeux creux -->
<!-- Traits horizontaux et verticaux évoquant le caractère --> <ellipse cx="40" cy="48" rx="5" ry="6" fill="#88aaff" fill-opacity="0.35" stroke="#88aaff" stroke-width="1.5"/>
<line x1="40" y1="53" x2="60" y2="53" stroke="#88ccee" stroke-width="1.5" opacity="0.5"/> <ellipse cx="60" cy="48" rx="5" ry="6" fill="#88aaff" fill-opacity="0.35" stroke="#88aaff" stroke-width="1.5"/>
<line x1="50" y1="53" x2="50" y2="62" stroke="#88ccee" stroke-width="1.5" opacity="0.5"/> <!-- Bouche tordue -->
<path d="M42 58 Q50 55 58 58" fill="none" stroke="#88ccee" stroke-width="1.5" opacity="0.5"/> <path d="M36 60 Q42 66 50 62 Q58 66 64 60" fill="none" stroke="#88aaff" stroke-width="2" stroke-linecap="round"/>
<!-- Franges du bas de la lanterne -->
<!-- Traînée vaporeuse en bas --> <line x1="40" y1="76" x2="38" y2="86" stroke="#88aaff" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<path d="M38 68 Q36 75 38 82 Q40 88 44 84 Q46 78 48 84 Q50 88 52 84 Q54 78 56 84 Q58 88 62 82 Q64 75 62 68" <line x1="50" y1="78" x2="50" y2="88" stroke="#88aaff" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
fill="#88ccee" fill-opacity="0.08" stroke="#88ccee" stroke-width="1.2" stroke-dasharray="3,3" opacity="0.6"/> <line x1="60" y1="76" x2="62" y2="86" stroke="#88aaff" stroke-width="1.2" stroke-linecap="round" opacity="0.6"/>
<!-- Lueur intérieure -->
<!-- Chaînes (lien au monde des vivants) — deux petits maillons --> <ellipse cx="50" cy="52" rx="12" ry="14" fill="#88aaff" fill-opacity="0.08"/>
<circle cx="32" cy="52" r="3" fill="none" stroke="#88ccee" stroke-width="1.5" opacity="0.5"/>
<circle cx="32" cy="58" r="3" fill="none" stroke="#88ccee" stroke-width="1.5" opacity="0.5"/>
<line x1="32" y1="49" x2="32" y2="55" stroke="#88ccee" stroke-width="1.5" opacity="0.5"/>
<circle cx="68" cy="52" r="3" fill="none" stroke="#88ccee" stroke-width="1.5" opacity="0.5"/>
<circle cx="68" cy="58" r="3" fill="none" stroke="#88ccee" stroke-width="1.5" opacity="0.5"/>
<line x1="68" y1="49" x2="68" y2="55" stroke="#88ccee" stroke-width="1.5" opacity="0.5"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

+30 -39
View File
@@ -1,47 +1,38 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="jg-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="jg-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#22cc88" stop-opacity="0.4"/> <stop offset="0%" stop-color="#cc44ff" stop-opacity="0.45"/>
<stop offset="100%" stop-color="#22cc88" stop-opacity="0"/> <stop offset="100%" stop-color="#cc44ff" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#jg-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#jg-glow)"/>
<!-- Jiugwaai — créature de rue à multiples yeux -->
<!-- Cercle de transformation mystique --> <!-- Silhouette bossue -->
<circle cx="50" cy="50" r="38" fill="none" stroke="#22cc88" stroke-width="1.2" opacity="0.35" stroke-dasharray="4,3"/> <path d="M24 72 Q22 56 28 44 Q34 32 50 28 Q66 32 72 44 Q78 56 76 72 Q66 78 50 80 Q34 78 24 72Z"
fill="#cc44ff" fill-opacity="0.12" stroke="#cc44ff" stroke-width="2"/>
<!-- Silhouette multi-forme (créature hybride) --> <!-- Multiples yeux disposés irrégulièrement -->
<!-- Corps central — humanoïde --> <ellipse cx="36" cy="42" rx="5" ry="4" fill="#cc44ff" fill-opacity="0.4" stroke="#cc44ff" stroke-width="1.5"/>
<ellipse cx="50" cy="52" rx="12" ry="15" fill="#22cc88" fill-opacity="0.15" stroke="#22cc88" stroke-width="1.8"/> <circle cx="36" cy="42" r="2" fill="#cc44ff"/>
<ellipse cx="52" cy="36" rx="4" ry="5" fill="#cc44ff" fill-opacity="0.4" stroke="#cc44ff" stroke-width="1.5"/>
<!-- Tête avec cornes/oreilles animales --> <circle cx="52" cy="36" r="2" fill="#cc44ff"/>
<circle cx="50" cy="35" r="9" fill="#22cc88" fill-opacity="0.15" stroke="#22cc88" stroke-width="1.8"/> <ellipse cx="65" cy="44" rx="5" ry="4" fill="#cc44ff" fill-opacity="0.4" stroke="#cc44ff" stroke-width="1.5"/>
<!-- Oreilles pointues (animal) --> <circle cx="65" cy="44" r="2" fill="#cc44ff"/>
<path d="M41 30 L38 20 L45 28" fill="#22cc88" fill-opacity="0.5" stroke="#22cc88" stroke-width="1.5" stroke-linejoin="round"/> <ellipse cx="42" cy="56" rx="4" ry="4" fill="#cc44ff" fill-opacity="0.4" stroke="#cc44ff" stroke-width="1.5"/>
<path d="M59 30 L62 20 L55 28" fill="#22cc88" fill-opacity="0.5" stroke="#22cc88" stroke-width="1.5" stroke-linejoin="round"/> <circle cx="42" cy="56" r="2" fill="#cc44ff"/>
<ellipse cx="60" cy="54" rx="4" ry="5" fill="#cc44ff" fill-opacity="0.4" stroke="#cc44ff" stroke-width="1.5"/>
<!-- Yeux (regard surnaturel) --> <circle cx="60" cy="54" r="2" fill="#cc44ff"/>
<ellipse cx="46" cy="35" rx="3" ry="2" fill="#22cc88" opacity="0.8"/> <!-- Petit oeil supplémentaire -->
<ellipse cx="54" cy="35" rx="3" ry="2" fill="#22cc88" opacity="0.8"/> <circle cx="50" cy="48" r="3" fill="#cc44ff" fill-opacity="0.3" stroke="#cc44ff" stroke-width="1.2"/>
<circle cx="50" cy="48" r="1.5" fill="#cc44ff" opacity="0.8"/>
<!-- Queue spiralée --> <!-- Membres difformes -->
<path d="M62 60 Q72 58 74 65 Q76 72 68 74 Q62 74 62 68" <path d="M24 72 Q16 68 12 78" fill="none" stroke="#cc44ff" stroke-width="2" stroke-linecap="round" opacity="0.7"/>
fill="none" stroke="#22cc88" stroke-width="2" stroke-linecap="round" opacity="0.7"/> <path d="M76 72 Q84 68 88 78" fill="none" stroke="#cc44ff" stroke-width="2" stroke-linecap="round" opacity="0.7"/>
<path d="M30 46 Q18 44 14 50" fill="none" stroke="#cc44ff" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
<!-- Tentacule / membre supplémentaire gauche --> <path d="M70 46 Q82 44 86 50" fill="none" stroke="#cc44ff" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
<path d="M38 55 Q26 52 22 60 Q20 68 28 66" <!-- Bouche dentée -->
fill="none" stroke="#22cc88" stroke-width="2" stroke-linecap="round" opacity="0.6"/> <path d="M36 66 Q44 72 50 70 Q56 72 64 66" fill="none" stroke="#cc44ff" stroke-width="2" stroke-linecap="round"/>
<line x1="42" y1="68" x2="40" y2="74" stroke="#cc44ff" stroke-width="1.5" stroke-linecap="round"/>
<!-- Écailles / motif sur le corps --> <line x1="50" y1="70" x2="50" y2="76" stroke="#cc44ff" stroke-width="1.5" stroke-linecap="round"/>
<path d="M44 46 Q50 42 56 46 Q52 50 50 48 Q48 50 44 46 Z" <line x1="58" y1="68" x2="60" y2="74" stroke="#cc44ff" stroke-width="1.5" stroke-linecap="round"/>
fill="#22cc88" fill-opacity="0.3" stroke="#22cc88" stroke-width="1"/>
<path d="M44 52 Q50 48 56 52 Q52 56 50 54 Q48 56 44 52 Z"
fill="#22cc88" fill-opacity="0.3" stroke="#22cc88" stroke-width="1"/>
<!-- Kanji 怪 (étrange) stylisé — simplifié -->
<text x="50" y="81" text-anchor="middle" font-size="11" font-family="serif"
fill="#22cc88" opacity="0.55"></text>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

+29 -34
View File
@@ -1,41 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="mt-glow" cx="50%" cy="40%" r="50%"> <radialGradient id="mo-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#6688aa" stop-opacity="0.35"/> <stop offset="0%" stop-color="#4ecdc4" stop-opacity="0.35"/>
<stop offset="100%" stop-color="#6688aa" stop-opacity="0"/> <stop offset="100%" stop-color="#4ecdc4" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#mt-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#mo-glow)"/>
<!-- Silhouette homme en trench-coat — style triades HK -->
<!-- Anneau ésotérique (cercle de protection) -->
<circle cx="50" cy="50" r="40" fill="none" stroke="#6688aa" stroke-width="1.2" opacity="0.4"/>
<circle cx="50" cy="50" r="38" fill="none" stroke="#6688aa" stroke-width="0.6" opacity="0.25" stroke-dasharray="5,4"/>
<!-- Silhouette humaine -->
<!-- Tête --> <!-- Tête -->
<circle cx="50" cy="30" r="9" fill="#6688aa" fill-opacity="0.25" stroke="#6688aa" stroke-width="1.8"/> <circle cx="50" cy="22" r="8" fill="#4ecdc4" fill-opacity="0.2" stroke="#4ecdc4" stroke-width="2"/>
<!-- Corps --> <!-- Cou -->
<path d="M40 42 L36 66 L42 66 L45 56 L50 60 L55 56 L58 66 L64 66 L60 42 Z" <line x1="50" y1="30" x2="50" y2="36" stroke="#4ecdc4" stroke-width="3" stroke-linecap="round"/>
fill="#6688aa" fill-opacity="0.2" stroke="#6688aa" stroke-width="1.8" stroke-linejoin="round"/> <!-- Corps trench-coat -->
<!-- Bras gauche --> <path d="M32 36 Q32 54 34 68 Q42 72 50 72 Q58 72 66 68 Q68 54 68 36 Q60 32 50 32 Q40 32 32 36Z"
<path d="M40 44 Q30 52 28 60" fill="none" stroke="#6688aa" stroke-width="2" stroke-linecap="round"/> fill="#4ecdc4" fill-opacity="0.12" stroke="#4ecdc4" stroke-width="2"/>
<!-- Bras droit (levé, tenant un talisman) --> <!-- Col relevé -->
<path d="M60 44 Q70 48 72 42" fill="none" stroke="#6688aa" stroke-width="2" stroke-linecap="round"/> <path d="M36 36 Q42 30 50 32 Q58 30 64 36" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round"/>
<!-- Revers du manteau -->
<!-- Talisman / ofuda (papier de prière) --> <path d="M36 36 Q44 44 48 68" fill="none" stroke="#4ecdc4" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
<rect x="68" y="32" width="10" height="14" rx="1" <path d="M64 36 Q56 44 52 68" fill="none" stroke="#4ecdc4" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
fill="#6688aa" fill-opacity="0.2" stroke="#6688aa" stroke-width="1.5"/> <!-- Bras gauche — main dans la poche -->
<!-- Lignes du talisman --> <path d="M32 36 Q22 44 22 56 Q24 60 28 58" fill="none" stroke="#4ecdc4" stroke-width="2.5" stroke-linecap="round"/>
<line x1="70" y1="36" x2="76" y2="36" stroke="#6688aa" stroke-width="1" opacity="0.7"/> <!-- Bras droit — bras le long du corps -->
<line x1="70" y1="39" x2="76" y2="39" stroke="#6688aa" stroke-width="1" opacity="0.7"/> <path d="M68 36 Q78 44 78 56 Q76 60 72 58" fill="none" stroke="#4ecdc4" stroke-width="2.5" stroke-linecap="round"/>
<line x1="70" y1="42" x2="76" y2="42" stroke="#6688aa" stroke-width="1" opacity="0.7"/> <!-- Jambes -->
<line x1="44" y1="72" x2="42" y2="88" stroke="#4ecdc4" stroke-width="2.5" stroke-linecap="round"/>
<!-- Symbole occulte sur la poitrine (trigramme Pa Kua simplifié) --> <line x1="56" y1="72" x2="58" y2="88" stroke="#4ecdc4" stroke-width="2.5" stroke-linecap="round"/>
<line x1="45" y1="46" x2="55" y2="46" stroke="#6688aa" stroke-width="1.5" opacity="0.6"/> <!-- Chaussures -->
<line x1="45" y1="49" x2="55" y2="49" stroke="#6688aa" stroke-width="1.5" opacity="0.6"/> <path d="M42 88 Q38 90 34 88" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round"/>
<line x1="45" y1="52" x2="50" y2="52" stroke="#6688aa" stroke-width="1.5" opacity="0.6"/> <path d="M58 88 Q62 90 66 88" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round"/>
<line x1="52" y1="52" x2="55" y2="52" stroke="#6688aa" stroke-width="1.5" opacity="0.6"/> <!-- Cigarette à la bouche -->
<line x1="52" y1="21" x2="58" y2="19" stroke="#4ecdc4" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
<circle cx="59" cy="18" r="1.5" fill="#4ecdc4" opacity="0.8"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

+23 -50
View File
@@ -1,58 +1,31 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="sh-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="sh-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#cc44ff" stop-opacity="0.35"/> <stop offset="0%" stop-color="#00ff9f" stop-opacity="0.45"/>
<stop offset="100%" stop-color="#cc44ff" stop-opacity="0"/> <stop offset="100%" stop-color="#00ff9f" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#sh-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#sh-glow)"/>
<!-- Trois souffles (三氣) — trois spirales en trinity -->
<!-- Disque Bi (玉璧) — anneau de jade --> <!-- Spirale 1 — haut gauche -->
<!-- Anneau extérieur --> <path d="M36 38 Q28 30 30 22 Q34 14 42 18 Q50 22 48 30 Q46 36 38 38 Q32 38 30 34"
<circle cx="50" cy="50" r="32" fill="#cc44ff" fill-opacity="0.12" stroke="#cc44ff" stroke-width="2.5"/> fill="none" stroke="#00ff9f" stroke-width="2.2" stroke-linecap="round"/>
<!-- Trou central du bi --> <circle cx="30" cy="34" r="2.5" fill="#00ff9f" opacity="0.8"/>
<circle cx="50" cy="50" r="14" fill="#101622" stroke="#cc44ff" stroke-width="2"/> <!-- Spirale 2 — haut droite -->
<path d="M64 38 Q72 30 70 22 Q66 14 58 18 Q50 22 52 30 Q54 36 62 38 Q68 38 70 34"
<!-- Gravures décoratives sur le disque (surface du bi) --> fill="none" stroke="#00ff9f" stroke-width="2.2" stroke-linecap="round"/>
<!-- Spirales de grain (yun wen) — motif traditionnel des bi --> <circle cx="70" cy="34" r="2.5" fill="#00ff9f" opacity="0.8"/>
<circle cx="50" cy="50" r="23" fill="none" stroke="#cc44ff" stroke-width="0.8" stroke-dasharray="2.5 2" opacity="0.5"/> <!-- Spirale 3 — bas centre -->
<circle cx="50" cy="50" r="19" fill="none" stroke="#cc44ff" stroke-width="0.8" stroke-dasharray="1.5 3" opacity="0.4"/> <path d="M50 72 Q42 76 36 72 Q30 64 36 58 Q42 52 50 56 Q58 60 58 68 Q56 76 50 78"
fill="none" stroke="#00ff9f" stroke-width="2.2" stroke-linecap="round"/>
<!-- Petits motifs en spirale sur la surface du bi (8 grains) --> <circle cx="50" cy="78" r="2.5" fill="#00ff9f" opacity="0.8"/>
<path d="M50 22 Q52 20 54 22 Q52 24 50 22" fill="none" stroke="#cc44ff" stroke-width="1" opacity="0.7"/> <!-- Connexions entre les trois spirales -->
<path d="M68 32 Q70 30 72 32 Q70 34 68 32" fill="none" stroke="#cc44ff" stroke-width="1" opacity="0.7"/> <line x1="36" y1="38" x2="42" y2="48" stroke="#00ff9f" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<path d="M78 50 Q80 48 82 50 Q80 52 78 50" fill="none" stroke="#cc44ff" stroke-width="1" opacity="0.7"/> <line x1="64" y1="38" x2="58" y2="48" stroke="#00ff9f" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<path d="M68 68 Q70 66 72 68 Q70 70 68 68" fill="none" stroke="#cc44ff" stroke-width="1" opacity="0.7"/> <line x1="42" y1="48" x2="58" y2="48" stroke="#00ff9f" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<path d="M50 78 Q52 76 54 78 Q52 80 50 78" fill="none" stroke="#cc44ff" stroke-width="1" opacity="0.7"/> <line x1="50" y1="48" x2="50" y2="56" stroke="#00ff9f" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<path d="M28 68 Q30 66 32 68 Q30 70 28 68" fill="none" stroke="#cc44ff" stroke-width="1" opacity="0.7"/> <!-- Centre lumineux -->
<path d="M18 50 Q20 48 22 50 Q20 52 18 50" fill="none" stroke="#cc44ff" stroke-width="1" opacity="0.7"/> <circle cx="50" cy="48" r="5" fill="#00ff9f" fill-opacity="0.25" stroke="#00ff9f" stroke-width="1.5"/>
<path d="M28 32 Q30 30 32 32 Q30 34 28 32" fill="none" stroke="#cc44ff" stroke-width="1" opacity="0.7"/> <circle cx="50" cy="48" r="2" fill="#00ff9f" opacity="0.9"/>
<!-- 3 Perles (San = 3 en cantonais) flottant autour du bi -->
<!-- Perle 1 — haut -->
<circle cx="50" cy="10" r="5" fill="#cc44ff" fill-opacity="0.25" stroke="#cc44ff" stroke-width="1.8"/>
<circle cx="48" cy="8" r="1.5" fill="#cc44ff" fill-opacity="0.6"/>
<!-- Fil de perle haut -->
<line x1="50" y1="15" x2="50" y2="18" stroke="#cc44ff" stroke-width="1" stroke-dasharray="2 1" opacity="0.6"/>
<!-- Perle 2 — bas-gauche -->
<circle cx="26" cy="84" r="5" fill="#cc44ff" fill-opacity="0.25" stroke="#cc44ff" stroke-width="1.8"/>
<circle cx="24" cy="82" r="1.5" fill="#cc44ff" fill-opacity="0.6"/>
<!-- Fil perle bas-gauche -->
<line x1="30" y1="80" x2="34" y2="76" stroke="#cc44ff" stroke-width="1" stroke-dasharray="2 1" opacity="0.6"/>
<!-- Perle 3 — bas-droit -->
<circle cx="74" cy="84" r="5" fill="#cc44ff" fill-opacity="0.25" stroke="#cc44ff" stroke-width="1.8"/>
<circle cx="72" cy="82" r="1.5" fill="#cc44ff" fill-opacity="0.6"/>
<!-- Fil perle bas-droit -->
<line x1="70" y1="80" x2="66" y2="76" stroke="#cc44ff" stroke-width="1" stroke-dasharray="2 1" opacity="0.6"/>
<!-- Runes dans le trou central -->
<!-- Symbole 三 (3 traits) -->
<line x1="44" y1="46" x2="56" y2="46" stroke="#cc44ff" stroke-width="1.5" opacity="0.8"/>
<line x1="43" y1="50" x2="57" y2="50" stroke="#cc44ff" stroke-width="1.5" opacity="0.8"/>
<line x1="44" y1="54" x2="56" y2="54" stroke="#cc44ff" stroke-width="1.5" opacity="0.8"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

+24 -54
View File
@@ -1,62 +1,32 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="sp-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="sp-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#4a9eff" stop-opacity="0.35"/> <stop offset="0%" stop-color="#ffe033" stop-opacity="0.45"/>
<stop offset="100%" stop-color="#4a9eff" stop-opacity="0"/> <stop offset="100%" stop-color="#ffe033" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#sp-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#sp-glow)"/>
<!-- Papier talisman (符) qui vole -->
<!-- Octogone extérieur (bagua) --> <!-- Papier incliné -->
<polygon points="50,18 68,24 79,40 79,60 68,76 50,82 32,76 21,60 21,40 32,24" <rect x="30" y="20" width="40" height="58" rx="3" ry="3"
fill="none" stroke="#4a9eff" stroke-width="1.8" opacity="0.7"/> fill="#ffe033" fill-opacity="0.12" stroke="#ffe033" stroke-width="2"
transform="rotate(-12 50 50)"/>
<!-- 8 trigrammes sur les côtés (petites lignes stylisées) --> <!-- Lignes d'écriture de talisman -->
<!-- Chaque trigramme = 3 lignes, positionnées sur les 8 côtés de l'octogone --> <line x1="36" y1="30" x2="62" y2="26" stroke="#ffe033" stroke-width="1.8" stroke-linecap="round" opacity="0.8" transform="rotate(-12 50 50)"/>
<!-- Nord --> <line x1="34" y1="38" x2="64" y2="34" stroke="#ffe033" stroke-width="1.4" stroke-linecap="round" opacity="0.6" transform="rotate(-12 50 50)"/>
<line x1="46" y1="13" x2="54" y2="13" stroke="#4a9eff" stroke-width="1.5"/> <line x1="33" y1="46" x2="63" y2="42" stroke="#ffe033" stroke-width="1.4" stroke-linecap="round" opacity="0.6" transform="rotate(-12 50 50)"/>
<line x1="46" y1="16" x2="54" y2="16" stroke="#4a9eff" stroke-width="1.5"/> <!-- Grand caractère fu (符) au centre -->
<line x1="46" y1="19" x2="54" y2="19" stroke="#4a9eff" stroke-width="1.5"/> <text x="50" y="58" text-anchor="middle" font-size="26" font-family="serif"
<!-- Nord-Est --> fill="#ffe033" fill-opacity="0.7" transform="rotate(-12 50 50)"></text>
<line x1="66" y1="18" x2="72" y2="22" stroke="#4a9eff" stroke-width="1.5"/> <!-- Lignes de sceau en bas -->
<line x1="64" y1="21" x2="70" y2="25" stroke="#4a9eff" stroke-width="1.5"/> <line x1="34" y1="66" x2="64" y2="62" stroke="#ffe033" stroke-width="1.8" stroke-linecap="round" opacity="0.8" transform="rotate(-12 50 50)"/>
<line x1="62" y1="24" x2="66" y2="27" stroke="#4a9eff" stroke-width="1.5"/> <!-- Énergie qui s'échappe du papier -->
<!-- Est --> <path d="M72 22 Q80 16 82 22 Q76 26 72 22Z" fill="#ffe033" fill-opacity="0.5"/>
<line x1="81" y1="46" x2="87" y2="46" stroke="#4a9eff" stroke-width="1.5"/> <path d="M76 32 Q84 28 86 34 Q80 36 76 32Z" fill="#ffe033" fill-opacity="0.4"/>
<line x1="81" y1="50" x2="87" y2="50" stroke="#4a9eff" stroke-width="1.5"/> <path d="M22 60 Q14 56 14 62 Q20 64 22 60Z" fill="#ffe033" fill-opacity="0.4"/>
<line x1="83" y1="54" x2="87" y2="54" stroke="#4a9eff" stroke-width="1.5"/> <!-- Petites étincelles -->
<!-- Sud-Est --> <circle cx="80" cy="26" r="1.5" fill="#ffe033" opacity="0.9"/>
<line x1="66" y1="78" x2="72" y2="74" stroke="#4a9eff" stroke-width="1.5"/> <circle cx="18" cy="62" r="1.5" fill="#ffe033" opacity="0.9"/>
<line x1="64" y1="75" x2="70" y2="71" stroke="#4a9eff" stroke-width="1.5"/> <circle cx="74" cy="40" r="1" fill="#ffe033" opacity="0.7"/>
<line x1="62" y1="72" x2="68" y2="69" stroke="#4a9eff" stroke-width="1.5"/>
<!-- Sud -->
<line x1="46" y1="87" x2="54" y2="87" stroke="#4a9eff" stroke-width="1.5"/>
<line x1="46" y1="84" x2="54" y2="84" stroke="#4a9eff" stroke-width="1.5"/>
<line x1="46" y1="81" x2="54" y2="81" stroke="#4a9eff" stroke-width="1.5"/>
<!-- Sud-Ouest -->
<line x1="34" y1="78" x2="28" y2="74" stroke="#4a9eff" stroke-width="1.5"/>
<line x1="36" y1="75" x2="30" y2="71" stroke="#4a9eff" stroke-width="1.5"/>
<line x1="38" y1="72" x2="32" y2="69" stroke="#4a9eff" stroke-width="1.5"/>
<!-- Ouest -->
<line x1="19" y1="46" x2="13" y2="46" stroke="#4a9eff" stroke-width="1.5"/>
<line x1="19" y1="50" x2="13" y2="50" stroke="#4a9eff" stroke-width="1.5"/>
<line x1="17" y1="54" x2="13" y2="54" stroke="#4a9eff" stroke-width="1.5"/>
<!-- Nord-Ouest -->
<line x1="34" y1="18" x2="28" y2="22" stroke="#4a9eff" stroke-width="1.5"/>
<line x1="36" y1="21" x2="30" y2="25" stroke="#4a9eff" stroke-width="1.5"/>
<line x1="38" y1="24" x2="34" y2="27" stroke="#4a9eff" stroke-width="1.5"/>
<!-- Cercle intérieur -->
<circle cx="50" cy="50" r="22" fill="none" stroke="#4a9eff" stroke-width="1.5" opacity="0.6"/>
<!-- Spirale de cinnabre (qi) — double spirale -->
<path d="M50 50 Q54 42 50 38 Q44 34 40 40 Q36 48 42 54 Q50 62 60 56 Q68 48 62 38 Q54 28 42 32"
fill="none" stroke="#4a9eff" stroke-width="2" stroke-linecap="round" opacity="0.9"/>
<!-- Point central (cinnabre) -->
<circle cx="50" cy="50" r="4" fill="#4a9eff" fill-opacity="0.5" stroke="#4a9eff" stroke-width="1.5"/>
<circle cx="50" cy="50" r="1.5" fill="#4a9eff"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

+21 -41
View File
@@ -1,49 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="sn-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="sn-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#cc44ff" stop-opacity="0.4"/> <stop offset="0%" stop-color="#ff44cc" stop-opacity="0.5"/>
<stop offset="100%" stop-color="#cc44ff" stop-opacity="0"/> <stop offset="100%" stop-color="#ff44cc" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#sn-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#sn-glow)"/>
<!-- Vortex de qi — spirale d'énergie -->
<!-- Flammes surnaturelles (gauche) --> <!-- Spirale externe -->
<path d="M22 72 Q20 60 26 52 Q22 56 24 46 Q28 38 32 42 Q28 32 36 26 Q38 38 34 44 Q40 36 42 28 Q46 40 40 50 Q46 44 48 36 Q52 48 46 56 Q50 50 54 44 Q56 56 50 64 Q54 58 58 52 Q60 62 56 70 Q52 68 50 72" <path d="M50 16 Q72 18 80 36 Q88 54 74 68 Q60 82 42 78 Q24 74 18 58 Q12 42 24 28 Q36 14 52 16"
fill="#cc44ff" fill-opacity="0.2" stroke="#cc44ff" stroke-width="1.5" stroke-linejoin="round" opacity="0.8"/> fill="none" stroke="#ff44cc" stroke-width="2.2" stroke-linecap="round"/>
<!-- Spirale intermédiaire -->
<!-- Contour de l'œil (grand) --> <path d="M50 26 Q64 28 70 42 Q76 56 66 66 Q56 76 42 70 Q28 64 28 50 Q28 36 40 30 Q52 24 62 28"
<path d="M20 50 Q35 30 50 30 Q65 30 80 50 Q65 70 50 70 Q35 70 20 50 Z" fill="none" stroke="#ff44cc" stroke-width="2" stroke-linecap="round" opacity="0.75"/>
fill="none" stroke="#cc44ff" stroke-width="2" opacity="0.9"/> <!-- Spirale interne -->
<!-- Remplissage doux de l'œil --> <path d="M50 36 Q58 36 62 44 Q66 52 60 60 Q54 66 46 62 Q38 58 38 50 Q38 42 44 38 Q50 34 56 38"
<path d="M25 50 Q38 36 50 36 Q62 36 75 50 Q62 64 50 64 Q38 64 25 50 Z" fill="none" stroke="#ff44cc" stroke-width="1.8" stroke-linecap="round" opacity="0.6"/>
fill="#cc44ff" fill-opacity="0.08"/> <!-- Noyau central -->
<circle cx="50" cy="50" r="8" fill="#ff44cc" fill-opacity="0.2" stroke="#ff44cc" stroke-width="2"/>
<!-- Iris de l'œil --> <circle cx="50" cy="50" r="3" fill="#ff44cc" opacity="0.9"/>
<circle cx="50" cy="50" r="10" fill="none" stroke="#cc44ff" stroke-width="2" opacity="0.8"/> <!-- Particules d'énergie en orbite -->
<!-- Pupille verticale (reptilienne / surnaturelle) --> <circle cx="50" cy="16" r="2.5" fill="#ff44cc" opacity="0.8"/>
<ellipse cx="50" cy="50" rx="3" ry="8" fill="#cc44ff" fill-opacity="0.6"/> <circle cx="80" cy="36" r="2" fill="#ff44cc" opacity="0.7"/>
<!-- Reflet --> <circle cx="74" cy="68" r="2.5" fill="#ff44cc" opacity="0.8"/>
<circle cx="47" cy="46" r="2" fill="#cc44ff" fill-opacity="0.5"/> <circle cx="18" cy="58" r="2" fill="#ff44cc" opacity="0.7"/>
<circle cx="24" cy="28" r="2" fill="#ff44cc" opacity="0.7"/>
<!-- Flammes supérieures (au-dessus de l'œil) -->
<path d="M38 30 Q36 22 42 18 Q40 26 46 22 Q44 28 50 24 Q48 30 54 26 Q52 32 58 28 Q56 34 62 30"
fill="none" stroke="#cc44ff" stroke-width="1.8" stroke-linecap="round" opacity="0.9"/>
<!-- Lignes de rayonnement autour de l'œil -->
<line x1="50" y1="24" x2="50" y2="18" stroke="#cc44ff" stroke-width="1.5" opacity="0.6"/>
<line x1="50" y1="76" x2="50" y2="82" stroke="#cc44ff" stroke-width="1.5" opacity="0.6"/>
<line x1="14" y1="50" x2="8" y2="50" stroke="#cc44ff" stroke-width="1.5" opacity="0.6"/>
<line x1="86" y1="50" x2="92" y2="50" stroke="#cc44ff" stroke-width="1.5" opacity="0.6"/>
<!-- Diagonaux -->
<line x1="27" y1="27" x2="22" y2="22" stroke="#cc44ff" stroke-width="1.2" opacity="0.5"/>
<line x1="73" y1="27" x2="78" y2="22" stroke="#cc44ff" stroke-width="1.2" opacity="0.5"/>
<line x1="27" y1="73" x2="22" y2="78" stroke="#cc44ff" stroke-width="1.2" opacity="0.5"/>
<line x1="73" y1="73" x2="78" y2="78" stroke="#cc44ff" stroke-width="1.2" opacity="0.5"/>
<!-- Spirales surnaturelles aux coins -->
<path d="M16 22 Q20 16 26 20 Q22 24 18 22" fill="none" stroke="#cc44ff" stroke-width="1.2" opacity="0.6"/>
<path d="M84 22 Q80 16 74 20 Q78 24 82 22" fill="none" stroke="#cc44ff" stroke-width="1.2" opacity="0.6"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

+27 -37
View File
@@ -1,46 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs> <defs>
<radialGradient id="wp-glow" cx="50%" cy="50%" r="50%"> <radialGradient id="wp-glow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#ff6b35" stop-opacity="0.3"/> <stop offset="0%" stop-color="#ff6b35" stop-opacity="0.4"/>
<stop offset="100%" stop-color="#ff6b35" stop-opacity="0"/> <stop offset="100%" stop-color="#ff6b35" stop-opacity="0"/>
</radialGradient> </radialGradient>
</defs> </defs>
<!-- Fond -->
<circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/> <circle cx="50" cy="50" r="48" fill="#101622" stroke="#1a2436" stroke-width="2"/>
<circle cx="50" cy="50" r="48" fill="url(#wp-glow)"/> <circle cx="50" cy="50" r="48" fill="url(#wp-glow)"/>
<!-- Deux couteaux papillon (wing chun) croisés -->
<!-- Dao (sabre courbé) — de bas-gauche à haut-droit --> <!-- Couteau 1 — orientation ↗ -->
<!-- Lame du dao (légèrement courbée) --> <!-- Lame gauche -->
<path d="M22 78 Q30 60 45 45 Q58 32 72 22" <path d="M24 72 Q28 60 36 50 Q44 40 56 28 L58 32 Q48 44 40 54 Q32 64 28 76 Z"
fill="none" stroke="#ff6b35" stroke-width="3.5" stroke-linecap="round"/> fill="#ff6b35" fill-opacity="0.25" stroke="#ff6b35" stroke-width="1.8"/>
<!-- Dos de la lame (trait intérieur plus fin) --> <!-- Garde en D couteau 1 -->
<path d="M25 76 Q33 58 47 44 Q59 33 72 24" <path d="M36 50 Q32 46 28 48 Q26 52 30 54 Q34 56 36 50Z"
fill="none" stroke="#ff6b35" stroke-width="1" stroke-linecap="round" opacity="0.5"/> fill="#ff6b35" fill-opacity="0.4" stroke="#ff6b35" stroke-width="1.5"/>
<!-- Brillance de la lame --> <!-- Manche couteau 1 -->
<path d="M28 73 Q38 56 52 42" <line x1="28" y1="76" x2="20" y2="84" stroke="#ff6b35" stroke-width="3.5" stroke-linecap="round"/>
fill="none" stroke="#ff6b35" stroke-width="0.8" stroke-linecap="round" opacity="0.8" stroke-dasharray="2 3"/> <!-- Couteau 2 — orientation ↖ croisé -->
<!-- Garde du dao (petite croix ornementée) --> <!-- Lame droite -->
<path d="M36 66 Q38 60 46 56 Q50 54 56 58 Q50 62 44 68 Z" <path d="M76 72 Q72 60 64 50 Q56 40 44 28 L42 32 Q52 44 60 54 Q68 64 72 76 Z"
fill="#ff6b35" fill-opacity="0.3" stroke="#ff6b35" stroke-width="1.5"/> fill="#ff6b35" fill-opacity="0.25" stroke="#ff6b35" stroke-width="1.8"/>
<!-- Poignée du dao --> <!-- Garde en D couteau 2 -->
<line x1="22" y1="78" x2="15" y2="86" stroke="#ff6b35" stroke-width="4" stroke-linecap="round" opacity="0.7"/> <path d="M64 50 Q68 46 72 48 Q74 52 70 54 Q66 56 64 50Z"
<rect x="13" y="84" width="6" height="4" rx="2" fill="#ff6b35" fill-opacity="0.5" stroke="#ff6b35" stroke-width="1" transform="rotate(-45 16 86)"/> fill="#ff6b35" fill-opacity="0.4" stroke="#ff6b35" stroke-width="1.5"/>
<!-- Manche couteau 2 -->
<!-- Lance — de bas-droit à haut-gauche --> <line x1="72" y1="76" x2="80" y2="84" stroke="#ff6b35" stroke-width="3.5" stroke-linecap="round"/>
<!-- Hampe de la lance --> <!-- Reflets sur les lames -->
<line x1="78" y1="78" x2="22" y2="22" stroke="#ff6b35" stroke-width="2" stroke-linecap="round" opacity="0.7"/> <line x1="42" y1="44" x2="46" y2="40" stroke="#ff6b35" stroke-width="1" stroke-linecap="round" opacity="0.7"/>
<!-- Pointe de la lance (haut-gauche) --> <line x1="58" y1="44" x2="54" y2="40" stroke="#ff6b35" stroke-width="1" stroke-linecap="round" opacity="0.7"/>
<path d="M22 22 L16 14 L20 20 L14 18 Z" fill="#ff6b35" stroke="#ff6b35" stroke-width="1" stroke-linejoin="round"/> <!-- Étincelle au croisement -->
<!-- Ornement milieu de hampe --> <circle cx="50" cy="50" r="3" fill="#ff6b35" opacity="0.9"/>
<path d="M44 44 Q48 40 52 44 Q48 48 44 44 Z" fill="#ff6b35" fill-opacity="0.4" stroke="#ff6b35" stroke-width="1"/> <line x1="50" y1="44" x2="50" y2="56" stroke="#ff6b35" stroke-width="1" stroke-linecap="round" opacity="0.6"/>
<!-- Embout de la lance (bas-droit) --> <line x1="44" y1="50" x2="56" y2="50" stroke="#ff6b35" stroke-width="1" stroke-linecap="round" opacity="0.6"/>
<path d="M78 78 L84 85 L80 80 L86 82 Z" fill="#ff6b35" stroke="#ff6b35" stroke-width="1" stroke-linejoin="round" opacity="0.7"/>
<!-- Petites étincelles au croisement -->
<circle cx="50" cy="50" r="3" fill="#ff6b35" fill-opacity="0.3" stroke="#ff6b35" stroke-width="1.5"/>
<line x1="50" y1="44" x2="50" y2="40" stroke="#ff6b35" stroke-width="1" opacity="0.7"/>
<line x1="56" y1="50" x2="60" y2="50" stroke="#ff6b35" stroke-width="1" opacity="0.7"/>
<line x1="44" y1="50" x2="40" y2="50" stroke="#ff6b35" stroke-width="1" opacity="0.7"/>
<line x1="50" y1="56" x2="50" y2="60" stroke="#ff6b35" stroke-width="1" opacity="0.7"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1024 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 986 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 KiB

+19 -1
View File
@@ -131,6 +131,23 @@
"CDE.InitiativeNPCSpeciality": "Première action (Aptitude) que vous escomptez effectuer", "CDE.InitiativeNPCSpeciality": "Première action (Aptitude) que vous escomptez effectuer",
"CDE.InitiativeRoll": "Jet d'initiative", "CDE.InitiativeRoll": "Jet d'initiative",
"CDE.InitiativeSpeciality": "Première action (Compétence) que vous escomptez effectuer", "CDE.InitiativeSpeciality": "Première action (Compétence) que vous escomptez effectuer",
"CDE.MigrationTitle": "Migration depuis l'ancien système",
"CDE.MigrationMenuLabel": "Importer des personnages",
"CDE.MigrationMenuHint": "Importer des fiches de personnage depuis l'ancien système CDE",
"CDE.MigrationHint": "Glissez-déposez des fichiers JSON ou cliquez pour les sélectionner.",
"CDE.MigrationDropHint": "Déposez vos fichiers JSON ici",
"CDE.MigrationChooseFiles": "Choisir des fichiers",
"CDE.MigrationPreviewTitle": "Personnages à importer",
"CDE.MigrationClear": "Vider",
"CDE.MigrationColName": "Nom",
"CDE.MigrationColType": "Type",
"CDE.MigrationColItems": "Objets",
"CDE.MigrationColFile": "Fichier source",
"CDE.MigrationImport": "Importer",
"CDE.MigrationSuccess": "{count} personnage(s) importé(s) : {names}",
"CDE.MigrationPartialError": "{count} personnage(s) n'ont pas pu être importés.",
"CDE.MigrationErrorNotJson": "Le fichier « {file} » n'est pas un fichier JSON.",
"CDE.MigrationErrorParse": "Erreur lors de la lecture de « {file} » : {error}",
"CDE.InitiativeWheel": "Roue d'Initiative", "CDE.InitiativeWheel": "Roue d'Initiative",
"CDE.InitiativeWheelOpen": "Ouvrir la Roue d'Initiative", "CDE.InitiativeWheelOpen": "Ouvrir la Roue d'Initiative",
"CDE.InitiativeWheelHint": "Roue d'initiative Les Chroniques de l'Étrange", "CDE.InitiativeWheelHint": "Roue d'initiative Les Chroniques de l'Étrange",
@@ -415,5 +432,6 @@
"CDE.TotalDamage": "Dommages", "CDE.TotalDamage": "Dommages",
"CDE.WeaponRoll": "Jet d'arme", "CDE.WeaponRoll": "Jet d'arme",
"CDE.RangePenalty": "Pénalité de portée", "CDE.RangePenalty": "Pénalité de portée",
"CDE.SuccessTimesDamage": "succès × dégâts de base" "CDE.SuccessTimesDamage": "succès × dégâts de base",
"CDE.WelcomeOpenHelp": "Ouvrir l'aide en ligne"
} }
+293
View File
@@ -0,0 +1,293 @@
{
"_id": "CDEGuideMain0001",
"_key": "!journal!CDEGuideMain0001",
"name": "Guide du Système CDE",
"pages": [
{
"_id": "CDEHelpP01Intro",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP01Intro",
"name": "Bienvenue dans CDE",
"type": "text",
"text": {
"content": "\n<h1>Bienvenue dans les Chroniques de l'Étrange</h1>\n<p>Ce guide vous présente l'interface du système FoundryVTT pour <em>Chroniques de l'Étrange</em> (CDE), jeu de rôle d'enquête et d'action surnaturelle dans le Hong Kong contemporain, édité par Antre-Monde Éditions.</p>\n<h2>Structure du système</h2>\n<ul>\n<li><strong>Fiches de personnage (Fat Si)</strong> — Héros joueurs avec cinq aspects Wu Xing, compétences, Trois Trésors et équipement.</li>\n<li><strong>Fiches de PNJ</strong> — Créatures, dieux, fantômes et humains à l'usage du MJ.</li>\n<li><strong>Compendiums</strong> — Arts martiaux, sortilèges, équipements, PNJs, capacités surnaturelles, ingrédients, San Hei.</li>\n<li><strong>Outils de MJ</strong> — Roue d'initiative, compteurs Loksyu/Tin Ji, outil de migration.</li>\n</ul>\n<p>Naviguez dans ce journal via les onglets de page pour découvrir chaque aspect du système.</p>\n",
"format": 1,
"markdown": ""
},
"sort": 100000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
},
{
"_id": "CDEHelpP02WuXin",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP02WuXin",
"name": "Le Cycle Wu Xing",
"type": "text",
"text": {
"content": "\n<h1>Le Cycle Wu Xing (五行)</h1>\n<p>Le Wu Xing est le cœur du système de résolution. Cinq aspects — <strong>Métal (㊎)</strong>, <strong>Eau (㊌)</strong>, <strong>Terre (㊏)</strong>, <strong>Feu (㊋)</strong> et <strong>Bois (㊍)</strong> — définissent les capacités d'un personnage.</p>\n<h2>Faces des d10 et résultats</h2>\n<p>Chaque aspect est associé à deux faces du d10. Lors d'un jet, vous déclarez l'aspect utilisé avant de lancer vos dés. Chaque dé donne un résultat :</p>\n<ul>\n<li><strong>Succès</strong> — avancez vers votre objectif.</li>\n<li><strong>Dés-fastes (吉)</strong> — résultats favorables supplémentaires.</li>\n<li><strong>Dés-néfastes (凶)</strong> — complications.</li>\n<li><strong>Loksyu (落穗)</strong> — alimentent le compteur mondial de chance collective.</li>\n<li><strong>Tin Ji (天機)</strong> — alimentent le compteur de destin.</li>\n</ul>\n<h2>Correspondances</h2>\n<table>\n<thead><tr><th>Aspect</th><th>Faces d10</th><th>Caractère</th></tr></thead>\n<tbody>\n<tr><td>㊎ Métal</td><td>1 &amp; 6</td><td>Agressif, passionné, combatif</td></tr>\n<tr><td>㊌ Eau</td><td>2 &amp; 7</td><td>Souple, appliqué, adaptable</td></tr>\n<tr><td>㊏ Terre</td><td>3 &amp; 8</td><td>Obstiné, résilient, endurant</td></tr>\n<tr><td>㊋ Feu</td><td>4 &amp; 9</td><td>Chaleureux, créatif, empathique</td></tr>\n<tr><td>㊍ Bois</td><td>5 &amp; 10</td><td>Intuitif, observateur, instinctif</td></tr>\n</tbody>\n</table>\n<p><em>La valeur d'un aspect (de 1 à 5) détermine le nombre de dés que vous lancez.</em></p>\n",
"format": 1,
"markdown": ""
},
"sort": 200000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
},
{
"_id": "CDEHelpP03Sheet",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP03Sheet",
"name": "La Fiche de Personnage",
"type": "text",
"text": {
"content": "\n<h1>La Fiche de Personnage (Fat Si)</h1>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/character-sheet-nghang.png\" alt=\"Fiche de personnage — onglet Ng Hang\"/><figcaption>Onglet Ng Hang : les cinq aspects Wu Xing</figcaption></figure>\n<p>La fiche de personnage se décompose en sept onglets :</p>\n<ol>\n<li><strong>Description</strong> — Biographie, concept, gardien céleste.</li>\n<li><strong>Ng Hang</strong> — Les cinq aspects Wu Xing (valeur 15). Cliquez sur l'image du dé pour lancer.</li>\n<li><strong>Compétences</strong> — Les compétences générales et ressources.</li>\n<li><strong>Trois Trésors</strong> — Hei-Yang, Hei-Yin et les niveaux de dés.</li>\n<li><strong>Magies</strong> — Les cinq écoles de magie et leurs sortilèges.</li>\n<li><strong>Kung Fu</strong> — Arts martiaux possédés.</li>\n<li><strong>Équipement</strong> — Objets portés.</li>\n</ol>\n<h2>En-tête de fiche</h2>\n<p>En haut de la fiche se trouvent le <strong>concept du personnage</strong>, son <strong>gardien céleste</strong> (aspect dominant) et la zone d'<strong>initiative</strong> avec les boutons ±.</p>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/character-sheet-skills.png\" alt=\"Fiche de personnage — onglet Compétences\"/><figcaption>Onglet Compétences : compétences et ressources</figcaption></figure>\n",
"format": 1,
"markdown": ""
},
"sort": 300000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
},
{
"_id": "CDEHelpP04Treas",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP04Treas",
"name": "Les Trois Trésors",
"type": "text",
"text": {
"content": "\n<h1>Les Trois Trésors (三寶)</h1>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/character-sheet-treasures.png\" alt=\"Fiche de personnage — onglet Trois Trésors\"/><figcaption>Onglet Trois Trésors : Hei-Yang, Hei-Yin et niveaux de dés</figcaption></figure>\n<p>Les Trois Trésors représentent les réserves d'énergie vitale du personnage :</p>\n<h2>Hei-Yang (陽氣) et Hei-Yin (陰氣)</h2>\n<p>Ce sont les deux jauges de vitalité. Le Hei-Yang représente l'énergie active, le Hei-Yin l'énergie passive. Ensemble, ils forment le <strong>token attribute</strong> visible sur la carte.</p>\n<h2>Niveaux de dés</h2>\n<p>Les niveaux de dés (d4 → d6 → d8 → d10 → d12) reflètent la progression du personnage dans un aspect. Chaque niveau de dé confère un bonus ou un avantage supplémentaire.</p>\n<h2>Blessures</h2>\n<p>Les blessures s'accumulent et imposent des malus croissants :</p>\n<ul>\n<li>Blessé : 1 dé à tous les jets</li>\n<li>Gravement blessé : 2 dés</li>\n<li>État critique : 3 dés</li>\n</ul>\n",
"format": 1,
"markdown": ""
},
"sort": 400000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
},
{
"_id": "CDEHelpP05Magic",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP05Magic",
"name": "Magie",
"type": "text",
"text": {
"content": "\n<h1>Magie</h1>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/character-sheet-magics.png\" alt=\"Fiche de personnage — onglet Magies\"/><figcaption>Onglet Magies : les cinq écoles et leurs sortilèges</figcaption></figure>\n<p>CDE dispose de cinq écoles de magie, chacune divisée en cinq spécialités :</p>\n<ul>\n<li><strong>Cinabre Interne</strong> (内丹) — magie du souffle et du corps.</li>\n<li><strong>Alchimie</strong> (外丹) — préparations, potions et talismans matériels.</li>\n<li><strong>Maîtrise du Tao</strong> (道術) — maîtrise des principes cosmiques.</li>\n<li><strong>Exorcisme</strong> (驅魔) — combat contre les entités surnaturelles.</li>\n<li><strong>Géomancie</strong> (風水) — magie des lieux et de l'environnement.</li>\n</ul>\n<h2>Utiliser un sortilège</h2>\n<ol>\n<li>Cliquez sur l'icône dé du sort dans l'onglet Magies.</li>\n<li>Un dialog apparaît avec l'aspect associé, le coût en Hei et le nombre de dés.</li>\n<li>Validez pour effectuer le jet.</li>\n</ol>\n<p>Les sortilèges sont importés depuis le compendium <em>Sortilèges</em> et glissés sur la fiche.</p>\n",
"format": 1,
"markdown": ""
},
"sort": 500000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
},
{
"_id": "CDEHelpP06KungF",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP06KungF",
"name": "Arts Martiaux",
"type": "text",
"text": {
"content": "\n<h1>Arts Martiaux (武術)</h1>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/character-sheet-kungfu.png\" alt=\"Fiche de personnage — onglet Kung Fu\"/><figcaption>Onglet Kung Fu : arts martiaux possédés</figcaption></figure>\n<p>Les arts martiaux représentent les techniques de combat du personnage. Chaque art martial possède :</p>\n<ul>\n<li><strong>Un mode d'activation</strong> : passif, action d'attaque, réaction, etc.</li>\n<li><strong>Une description</strong> des effets en jeu.</li>\n</ul>\n<h2>Importer un art martial</h2>\n<p>Ouvrez le compendium <em>Arts Martiaux</em> et faites glisser une technique sur la fiche du personnage. Elle apparaît alors dans l'onglet Kung Fu.</p>\n<h2>Types d'activation</h2>\n<table>\n<thead><tr><th>Type</th><th>Déclencheur</th></tr></thead>\n<tbody>\n<tr><td>Passif (dés)</td><td>Toujours actif</td></tr>\n<tr><td>Action d'attaque</td><td>Lors d'une attaque</td></tr>\n<tr><td>Action de défense</td><td>Lors d'une défense</td></tr>\n<tr><td>Réaction</td><td>En réponse à un événement</td></tr>\n<tr><td>Dégâts infligés</td><td>Quand vous blessez</td></tr>\n</tbody>\n</table>\n",
"format": 1,
"markdown": ""
},
"sort": 600000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
},
{
"_id": "CDEHelpP07Items",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP07Items",
"name": "Équipement & Inventaire",
"type": "text",
"text": {
"content": "\n<h1>Équipement &amp; Inventaire</h1>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/character-sheet-items.png\" alt=\"Fiche de personnage — onglet Équipement\"/><figcaption>Onglet Équipement : objets portés</figcaption></figure>\n<p>L'onglet Équipement liste tout ce que porte le personnage. Les objets sont classés en plusieurs catégories :</p>\n<ul>\n<li><strong>Armes</strong> — avec dégâts, distance et type.</li>\n<li><strong>Protections</strong> — armures et protections spirituelles.</li>\n<li><strong>San Hei (三氣)</strong> — objets magiques à charges.</li>\n<li><strong>Ingrédients</strong> — matériaux pour l'alchimie.</li>\n<li><strong>Équipement générique</strong> — tout autre objet.</li>\n</ul>\n<h2>Ajouter un objet</h2>\n<p>Faites glisser un objet depuis un compendium (Armes, Protections, San Hei, Ingrédients, Équipements) ou créez-en un avec le bouton <em>Créer</em> correspondant.</p>\n<p>Cliquez sur l'image d'un objet pour ouvrir sa fiche détaillée.</p>\n",
"format": 1,
"markdown": ""
},
"sort": 700000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
},
{
"_id": "CDEHelpP08NPCSH",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP08NPCSH",
"name": "Les PNJ",
"type": "text",
"text": {
"content": "\n<h1>Les Personnages Non-Joueurs (PNJ)</h1>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/npc-sheet.png\" alt=\"Fiche de PNJ\"/><figcaption>Fiche PNJ : type, nuisance, menace et aptitudes</figcaption></figure>\n<p>Les PNJ ont une fiche simplifiée par rapport aux personnages joueurs.</p>\n<h2>Caractéristiques</h2>\n<ul>\n<li><strong>Type de créature</strong> : Mortel, Démon, Esprit, Esprit animal, Fantôme, Jiugwaai, Dieu/Divinité.</li>\n<li><strong>Capacité de nuisance</strong> : Figurant, Sbire, Adversaire, Allié, Boss, Divinité.</li>\n<li><strong>Niveau de menace</strong> : Profane → Apprenti → Initié → Accompli → Renommé.</li>\n</ul>\n<h2>Aptitudes</h2>\n<p>Les PNJ ont quatre aptitudes (Physique, Martiale, Mentale, Sociale) avec une spécialité optionnelle chacune.</p>\n<h2>Capacités surnaturelles</h2>\n<p>Les PNJ peuvent avoir des capacités importées depuis le compendium <em>Capacités Surnaturelles</em>.</p>\n",
"format": 1,
"markdown": ""
},
"sort": 800000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
},
{
"_id": "CDEHelpP09Initi",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP09Initi",
"name": "Initiative & Combat",
"type": "text",
"text": {
"content": "\n<h1>Initiative &amp; Combat</h1>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/initiative-wheel.png\" alt=\"Roue d'initiative\"/><figcaption>La Roue d'Initiative : 24 crans, couleurs Wu Xing</figcaption></figure>\n<h2>La Roue d'Initiative</h2>\n<p>La roue est un cercle de <strong>24 crans</strong> numérotés. L'initiative de chaque personnage est calculée comme suit :</p>\n<ul>\n<li><strong>Personnage joueur</strong> : Prouesse + valeur de compétence de la première action.</li>\n<li><strong>PNJ</strong> : Aptitude physique + aptitude de la première action.</li>\n</ul>\n<p>Les crans sont colorés selon le cycle Wu Xing (4 crans par couleur, 6 couleurs). <strong>Un effet qui dure 6 crans</strong> court jusqu'au prochain cran de la même couleur.</p>\n<h2>Ordre d'action</h2>\n<p>Les personnages agissent <strong>du numéro le plus élevé au plus bas</strong>. Après chaque action, le jeton avance dans le sens horaire du <strong>coût de l'action</strong> :</p>\n<table>\n<thead><tr><th>Action</th><th>Coût (crans)</th></tr></thead>\n<tbody>\n<tr><td>Défense</td><td>1</td></tr>\n<tr><td>Déplacement</td><td>2</td></tr>\n<tr><td>Attaque</td><td>3</td></tr>\n<tr><td>Retarder</td><td>6</td></tr>\n</tbody>\n</table>\n<h2>Accès à la roue</h2>\n<p>Ouvrez la roue depuis la barre latérale du chat (icône roue) ou via la console : <code>game.cde.CDEWheelApp.open()</code>.</p>\n",
"format": 1,
"markdown": ""
},
"sort": 900000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
},
{
"_id": "CDEHelpP10Extra",
"_key": "!journal.pages!CDEGuideMain0001.CDEHelpP10Extra",
"name": "Loksyu, Tin Ji & Migration",
"type": "text",
"text": {
"content": "\n<h1>Loksyu, Tin Ji &amp; Outils de MJ</h1>\n<h2>Loksyu (落穗) et Tin Ji (天機)</h2>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/loksyu-app.png\" alt=\"Application Loksyu\"/><figcaption>Application Loksyu : compteurs Yin/Yang et Tin Ji</figcaption></figure>\n<p>Ces deux compteurs sont <strong>partagés entre tous les joueurs</strong> et le MJ :</p>\n<ul>\n<li><strong>Loksyu</strong> — Se divise en Yin et Yang. Les jets de dés alimentent ces compteurs selon les résultats. Les joueurs peuvent puiser dans le Loksyu pour améliorer leurs jets.</li>\n<li><strong>Tin Ji</strong> — Le compteur de destin. Peut être dépensé pour des effets exceptionnels.</li>\n</ul>\n<p>Accédez via la barre du chat ou : <code>game.cde.CDELoksyuApp.open()</code></p>\n<h2>Migration de l'ancien système</h2>\n<figure><img src=\"systems/fvtt-chroniques-de-l-etrange/images/ui/migration-dialog.png\" alt=\"Outil de migration\"/><figcaption>Outil de migration : importation depuis l'ancien système</figcaption></figure>\n<p>Si vous possédez des fiches de personnage créées dans l'ancien système CDE (non maintenu), l'outil de migration les convertit automatiquement :</p>\n<ol>\n<li>Ouvrez <strong>Paramètres de la partie → Paramètres du système → Importer des personnages</strong>.</li>\n<li>Glissez les fichiers JSON des anciens personnages dans la zone de dépôt.</li>\n<li>Vérifiez l'aperçu et cliquez <strong>Importer</strong>.</li>\n</ol>\n<p>Les personnages migrés apparaissent dans la liste des Acteurs.</p>\n",
"format": 1,
"markdown": ""
},
"sort": 1000000,
"title": {
"show": true,
"level": 1
},
"image": {
"caption": ""
},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"flags": {},
"ownership": {
"default": -1
}
}
],
"sort": 0,
"ownership": {
"default": 0
},
"flags": {},
"folder": null
}
Binary file not shown.
+15 -15
View File
@@ -1,15 +1,15 @@
2026/04/27-20:01:11.390845 7fed927fc6c0 Recovering log #4 2026/05/12-00:36:56.314091 7ff671fef6c0 Recovering log #4
2026/04/27-20:01:11.400505 7fed927fc6c0 Delete type=3 #2 2026/05/12-00:36:56.323263 7ff671fef6c0 Delete type=3 #2
2026/04/27-20:01:11.400599 7fed927fc6c0 Delete type=0 #4 2026/05/12-00:36:56.323317 7ff671fef6c0 Delete type=0 #4
2026/04/27-22:04:19.362803 7feb10fff6c0 Level-0 table #9: started 2026/05/12-00:37:07.971141 7ff6637fe6c0 Level-0 table #9: started
2026/04/27-22:04:19.365860 7feb10fff6c0 Level-0 table #9: 1386 bytes OK 2026/05/12-00:37:07.974291 7ff6637fe6c0 Level-0 table #9: 1387 bytes OK
2026/04/27-22:04:19.371899 7feb10fff6c0 Delete type=0 #7 2026/05/12-00:37:07.980872 7ff6637fe6c0 Delete type=0 #7
2026/04/27-22:04:19.393316 7feb10fff6c0 Manual compaction at level-0 from '!items!3aig6MWvZCRoWXPW' @ 72057594037927935 : 1 .. '!items!cXaQG1TBE0jzrbNt' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.009643 7ff6637fe6c0 Manual compaction at level-0 from '!items!3aig6MWvZCRoWXPW' @ 72057594037927935 : 1 .. '!items!cXaQG1TBE0jzrbNt' @ 0 : 0; will stop at (end)
2026/04/27-22:04:19.414834 7feb10fff6c0 Manual compaction at level-1 from '!items!3aig6MWvZCRoWXPW' @ 72057594037927935 : 1 .. '!items!cXaQG1TBE0jzrbNt' @ 0 : 0; will stop at '!items!cXaQG1TBE0jzrbNt' @ 8 : 1 2026/05/12-00:37:08.009699 7ff6637fe6c0 Manual compaction at level-1 from '!items!3aig6MWvZCRoWXPW' @ 72057594037927935 : 1 .. '!items!cXaQG1TBE0jzrbNt' @ 0 : 0; will stop at '!items!cXaQG1TBE0jzrbNt' @ 8 : 1
2026/04/27-22:04:19.414848 7feb10fff6c0 Compacting 1@1 + 1@2 files 2026/05/12-00:37:08.009706 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/04/27-22:04:19.418022 7feb10fff6c0 Generated table #10@1: 4 keys, 1386 bytes 2026/05/12-00:37:08.012872 7ff6637fe6c0 Generated table #10@1: 4 keys, 1387 bytes
2026/04/27-22:04:19.418047 7feb10fff6c0 Compacted 1@1 + 1@2 files => 1386 bytes 2026/05/12-00:37:08.012903 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 1387 bytes
2026/04/27-22:04:19.424351 7feb10fff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/12-00:37:08.019075 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/27-22:04:19.424476 7feb10fff6c0 Delete type=2 #5 2026/05/12-00:37:08.019187 7ff6637fe6c0 Delete type=2 #5
2026/04/27-22:04:19.424608 7feb10fff6c0 Delete type=2 #9 2026/05/12-00:37:08.019315 7ff6637fe6c0 Delete type=2 #9
2026/04/27-22:04:19.434505 7feb10fff6c0 Manual compaction at level-1 from '!items!cXaQG1TBE0jzrbNt' @ 8 : 1 .. '!items!cXaQG1TBE0jzrbNt' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.051276 7ff6637fe6c0 Manual compaction at level-1 from '!items!cXaQG1TBE0jzrbNt' @ 8 : 1 .. '!items!cXaQG1TBE0jzrbNt' @ 0 : 0; will stop at (end)
+5 -5
View File
@@ -1,5 +1,5 @@
2026/04/27-17:47:13.055628 7f2779bff6c0 Delete type=3 #1 2026/05/12-00:36:37.149746 7fdfd57ec6c0 Delete type=3 #1
2026/04/27-17:47:13.058468 7f272b7fe6c0 Level-0 table #5: started 2026/05/12-00:36:37.152016 7fdfd4feb6c0 Level-0 table #5: started
2026/04/27-17:47:13.061813 7f272b7fe6c0 Level-0 table #5: 1330 bytes OK 2026/05/12-00:36:37.155425 7fdfd4feb6c0 Level-0 table #5: 1330 bytes OK
2026/04/27-17:47:13.067956 7f272b7fe6c0 Delete type=0 #3 2026/05/12-00:36:37.161916 7fdfd4feb6c0 Delete type=0 #3
2026/04/27-17:47:13.068111 7f272b7fe6c0 Manual compaction at level-0 from '!items!3aig6MWvZCRoWXPW' @ 72057594037927935 : 1 .. '!items!cXaQG1TBE0jzrbNt' @ 0 : 0; will stop at (end) 2026/05/12-00:36:37.162057 7fdfd4feb6c0 Manual compaction at level-0 from '!items!3aig6MWvZCRoWXPW' @ 72057594037927935 : 1 .. '!items!cXaQG1TBE0jzrbNt' @ 0 : 0; will stop at (end)
Binary file not shown.
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
MANIFEST-000006
View File
+15
View File
@@ -0,0 +1,15 @@
2026/05/12-00:36:56.374154 7ff663fff6c0 Recovering log #4
2026/05/12-00:36:56.383721 7ff663fff6c0 Delete type=3 #2
2026/05/12-00:36:56.383779 7ff663fff6c0 Delete type=0 #4
2026/05/12-00:37:08.062905 7ff6637fe6c0 Level-0 table #9: started
2026/05/12-00:37:08.066411 7ff6637fe6c0 Level-0 table #9: 10124 bytes OK
2026/05/12-00:37:08.072622 7ff6637fe6c0 Delete type=0 #7
2026/05/12-00:37:08.086421 7ff6637fe6c0 Manual compaction at level-0 from '!journal!CDEGuideMain0001' @ 72057594037927935 : 1 .. '!journal.pages!CDEGuideMain0001.wgqIHHVlO9miegn1' @ 0 : 0; will stop at (end)
2026/05/12-00:37:08.096808 7ff6637fe6c0 Manual compaction at level-1 from '!journal!CDEGuideMain0001' @ 72057594037927935 : 1 .. '!journal.pages!CDEGuideMain0001.wgqIHHVlO9miegn1' @ 0 : 0; will stop at '!journal.pages!CDEGuideMain0001.wgqIHHVlO9miegn1' @ 17 : 1
2026/05/12-00:37:08.096817 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/05/12-00:37:08.100204 7ff6637fe6c0 Generated table #10@1: 21 keys, 18033 bytes
2026/05/12-00:37:08.100229 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 18033 bytes
2026/05/12-00:37:08.106245 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/05/12-00:37:08.106373 7ff6637fe6c0 Delete type=2 #5
2026/05/12-00:37:08.106502 7ff6637fe6c0 Delete type=2 #9
2026/05/12-00:37:08.113213 7ff6637fe6c0 Manual compaction at level-1 from '!journal.pages!CDEGuideMain0001.wgqIHHVlO9miegn1' @ 17 : 1 .. '!journal.pages!CDEGuideMain0001.wgqIHHVlO9miegn1' @ 0 : 0; will stop at (end)
+5
View File
@@ -0,0 +1,5 @@
2026/05/12-00:36:37.177388 7fdfd5fed6c0 Delete type=3 #1
2026/05/12-00:36:37.178463 7fdfd4feb6c0 Level-0 table #5: started
2026/05/12-00:36:37.181940 7fdfd4feb6c0 Level-0 table #5: 9307 bytes OK
2026/05/12-00:36:37.188105 7fdfd4feb6c0 Delete type=0 #3
2026/05/12-00:36:37.188350 7fdfd4feb6c0 Manual compaction at level-0 from '!journal!CDEGuideMain0001' @ 72057594037927935 : 1 .. '!journal.pages!CDEGuideMain0001.CDEHelpP10Extra' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+15 -15
View File
@@ -1,15 +1,15 @@
2026/04/27-20:01:11.418750 7fed93fff6c0 Recovering log #4 2026/05/12-00:36:56.336872 7ff671fef6c0 Recovering log #4
2026/04/27-20:01:11.428738 7fed93fff6c0 Delete type=3 #2 2026/05/12-00:36:56.346806 7ff671fef6c0 Delete type=3 #2
2026/04/27-20:01:11.428793 7fed93fff6c0 Delete type=0 #4 2026/05/12-00:36:56.346886 7ff671fef6c0 Delete type=0 #4
2026/04/27-22:04:19.624820 7feb10fff6c0 Level-0 table #9: started 2026/05/12-00:37:07.990253 7ff6637fe6c0 Level-0 table #9: started
2026/04/27-22:04:19.629114 7feb10fff6c0 Level-0 table #9: 8881 bytes OK 2026/05/12-00:37:07.993684 7ff6637fe6c0 Level-0 table #9: 8881 bytes OK
2026/04/27-22:04:19.636111 7feb10fff6c0 Delete type=0 #7 2026/05/12-00:37:07.999919 7ff6637fe6c0 Delete type=0 #7
2026/04/27-22:04:19.657606 7feb10fff6c0 Manual compaction at level-0 from '!items!0NDBw1YB54q3hLH0' @ 72057594037927935 : 1 .. '!items!ykekdZlirabRobEF' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.009675 7ff6637fe6c0 Manual compaction at level-0 from '!items!0NDBw1YB54q3hLH0' @ 72057594037927935 : 1 .. '!items!ykekdZlirabRobEF' @ 0 : 0; will stop at (end)
2026/04/27-22:04:19.668457 7feb10fff6c0 Manual compaction at level-1 from '!items!0NDBw1YB54q3hLH0' @ 72057594037927935 : 1 .. '!items!ykekdZlirabRobEF' @ 0 : 0; will stop at '!items!ykekdZlirabRobEF' @ 108 : 1 2026/05/12-00:37:08.019406 7ff6637fe6c0 Manual compaction at level-1 from '!items!0NDBw1YB54q3hLH0' @ 72057594037927935 : 1 .. '!items!ykekdZlirabRobEF' @ 0 : 0; will stop at '!items!ykekdZlirabRobEF' @ 108 : 1
2026/04/27-22:04:19.668467 7feb10fff6c0 Compacting 1@1 + 1@2 files 2026/05/12-00:37:08.019418 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/04/27-22:04:19.672553 7feb10fff6c0 Generated table #10@1: 54 keys, 8881 bytes 2026/05/12-00:37:08.023624 7ff6637fe6c0 Generated table #10@1: 54 keys, 8881 bytes
2026/04/27-22:04:19.672595 7feb10fff6c0 Compacted 1@1 + 1@2 files => 8881 bytes 2026/05/12-00:37:08.023709 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 8881 bytes
2026/04/27-22:04:19.678837 7feb10fff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/12-00:37:08.029864 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/27-22:04:19.678963 7feb10fff6c0 Delete type=2 #5 2026/05/12-00:37:08.030009 7ff6637fe6c0 Delete type=2 #5
2026/04/27-22:04:19.679140 7feb10fff6c0 Delete type=2 #9 2026/05/12-00:37:08.030163 7ff6637fe6c0 Delete type=2 #9
2026/04/27-22:04:19.691620 7feb10fff6c0 Manual compaction at level-1 from '!items!ykekdZlirabRobEF' @ 108 : 1 .. '!items!ykekdZlirabRobEF' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.051296 7ff6637fe6c0 Manual compaction at level-1 from '!items!ykekdZlirabRobEF' @ 108 : 1 .. '!items!ykekdZlirabRobEF' @ 0 : 0; will stop at (end)
+5 -5
View File
@@ -1,5 +1,5 @@
2026/04/27-17:47:13.085519 7f27793fe6c0 Delete type=3 #1 2026/05/12-00:36:37.205346 7fdfd6fef6c0 Delete type=3 #1
2026/04/27-17:47:13.087023 7f272b7fe6c0 Level-0 table #5: started 2026/05/12-00:36:37.206616 7fdfd4feb6c0 Level-0 table #5: started
2026/04/27-17:47:13.091030 7f272b7fe6c0 Level-0 table #5: 5923 bytes OK 2026/05/12-00:36:37.210149 7fdfd4feb6c0 Level-0 table #5: 5923 bytes OK
2026/04/27-17:47:13.097545 7f272b7fe6c0 Delete type=0 #3 2026/05/12-00:36:37.216280 7fdfd4feb6c0 Delete type=0 #3
2026/04/27-17:47:13.097759 7f272b7fe6c0 Manual compaction at level-0 from '!items!0NDBw1YB54q3hLH0' @ 72057594037927935 : 1 .. '!items!ykekdZlirabRobEF' @ 0 : 0; will stop at (end) 2026/05/12-00:36:37.216387 7fdfd4feb6c0 Manual compaction at level-0 from '!items!0NDBw1YB54q3hLH0' @ 72057594037927935 : 1 .. '!items!ykekdZlirabRobEF' @ 0 : 0; will stop at (end)
Binary file not shown.
+15 -15
View File
@@ -1,15 +1,15 @@
2026/04/27-20:01:11.433002 7fed92ffd6c0 Recovering log #4 2026/05/12-00:36:56.348871 7ff6717ee6c0 Recovering log #4
2026/04/27-20:01:11.442974 7fed92ffd6c0 Delete type=3 #2 2026/05/12-00:36:56.358876 7ff6717ee6c0 Delete type=3 #2
2026/04/27-20:01:11.443041 7fed92ffd6c0 Delete type=0 #4 2026/05/12-00:36:56.358970 7ff6717ee6c0 Delete type=0 #4
2026/04/27-22:04:19.348217 7feb10fff6c0 Level-0 table #9: started 2026/05/12-00:37:07.981024 7ff6637fe6c0 Level-0 table #9: started
2026/04/27-22:04:19.355163 7feb10fff6c0 Level-0 table #9: 595 bytes OK 2026/05/12-00:37:07.984143 7ff6637fe6c0 Level-0 table #9: 596 bytes OK
2026/04/27-22:04:19.362679 7feb10fff6c0 Delete type=0 #7 2026/05/12-00:37:07.990088 7ff6637fe6c0 Delete type=0 #7
2026/04/27-22:04:19.393301 7feb10fff6c0 Manual compaction at level-0 from '!items!HKq5ANSGiBIdcnki' @ 72057594037927935 : 1 .. '!items!HKq5ANSGiBIdcnki' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.009661 7ff6637fe6c0 Manual compaction at level-0 from '!items!HKq5ANSGiBIdcnki' @ 72057594037927935 : 1 .. '!items!HKq5ANSGiBIdcnki' @ 0 : 0; will stop at (end)
2026/04/27-22:04:19.393355 7feb10fff6c0 Manual compaction at level-1 from '!items!HKq5ANSGiBIdcnki' @ 72057594037927935 : 1 .. '!items!HKq5ANSGiBIdcnki' @ 0 : 0; will stop at '!items!HKq5ANSGiBIdcnki' @ 2 : 1 2026/05/12-00:37:08.030253 7ff6637fe6c0 Manual compaction at level-1 from '!items!HKq5ANSGiBIdcnki' @ 72057594037927935 : 1 .. '!items!HKq5ANSGiBIdcnki' @ 0 : 0; will stop at '!items!HKq5ANSGiBIdcnki' @ 2 : 1
2026/04/27-22:04:19.393364 7feb10fff6c0 Compacting 1@1 + 1@2 files 2026/05/12-00:37:08.030264 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/04/27-22:04:19.396559 7feb10fff6c0 Generated table #10@1: 1 keys, 595 bytes 2026/05/12-00:37:08.033436 7ff6637fe6c0 Generated table #10@1: 1 keys, 596 bytes
2026/04/27-22:04:19.396573 7feb10fff6c0 Compacted 1@1 + 1@2 files => 595 bytes 2026/05/12-00:37:08.033469 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 596 bytes
2026/04/27-22:04:19.402962 7feb10fff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/12-00:37:08.040221 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/27-22:04:19.403060 7feb10fff6c0 Delete type=2 #5 2026/05/12-00:37:08.040443 7ff6637fe6c0 Delete type=2 #5
2026/04/27-22:04:19.403166 7feb10fff6c0 Delete type=2 #9 2026/05/12-00:37:08.040599 7ff6637fe6c0 Delete type=2 #9
2026/04/27-22:04:19.434477 7feb10fff6c0 Manual compaction at level-1 from '!items!HKq5ANSGiBIdcnki' @ 2 : 1 .. '!items!HKq5ANSGiBIdcnki' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.051309 7ff6637fe6c0 Manual compaction at level-1 from '!items!HKq5ANSGiBIdcnki' @ 2 : 1 .. '!items!HKq5ANSGiBIdcnki' @ 0 : 0; will stop at (end)
+5 -5
View File
@@ -1,5 +1,5 @@
2026/04/27-17:47:13.116591 7f272bfff6c0 Delete type=3 #1 2026/05/12-00:36:37.230762 7fdfd67ee6c0 Delete type=3 #1
2026/04/27-17:47:13.117666 7f272b7fe6c0 Level-0 table #5: started 2026/05/12-00:36:37.231622 7fdfd4feb6c0 Level-0 table #5: started
2026/04/27-17:47:13.121072 7f272b7fe6c0 Level-0 table #5: 559 bytes OK 2026/05/12-00:36:37.234740 7fdfd4feb6c0 Level-0 table #5: 559 bytes OK
2026/04/27-17:47:13.127453 7f272b7fe6c0 Delete type=0 #3 2026/05/12-00:36:37.240940 7fdfd4feb6c0 Delete type=0 #3
2026/04/27-17:47:13.127641 7f272b7fe6c0 Manual compaction at level-0 from '!items!HKq5ANSGiBIdcnki' @ 72057594037927935 : 1 .. '!items!HKq5ANSGiBIdcnki' @ 0 : 0; will stop at (end) 2026/05/12-00:36:37.241065 7fdfd4feb6c0 Manual compaction at level-0 from '!items!HKq5ANSGiBIdcnki' @ 72057594037927935 : 1 .. '!items!HKq5ANSGiBIdcnki' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+15 -15
View File
@@ -1,15 +1,15 @@
2026/04/27-20:01:11.327150 7fed93fff6c0 Recovering log #4 2026/05/12-00:36:56.263994 7ff663fff6c0 Recovering log #4
2026/04/27-20:01:11.338223 7fed93fff6c0 Delete type=3 #2 2026/05/12-00:36:56.274398 7ff663fff6c0 Delete type=3 #2
2026/04/27-20:01:11.338311 7fed93fff6c0 Delete type=0 #4 2026/05/12-00:36:56.274458 7ff663fff6c0 Delete type=0 #4
2026/04/27-22:04:19.636280 7feb10fff6c0 Level-0 table #9: started 2026/05/12-00:37:07.886611 7ff6637fe6c0 Level-0 table #9: started
2026/04/27-22:04:19.639967 7feb10fff6c0 Level-0 table #9: 34454 bytes OK 2026/05/12-00:37:07.890996 7ff6637fe6c0 Level-0 table #9: 34454 bytes OK
2026/04/27-22:04:19.646926 7feb10fff6c0 Delete type=0 #7 2026/05/12-00:37:07.897482 7ff6637fe6c0 Delete type=0 #7
2026/04/27-22:04:19.657623 7feb10fff6c0 Manual compaction at level-0 from '!items!2nKXEHLG0fXtSOdy' @ 72057594037927935 : 1 .. '!items!tlIc1bmIAbQeUwj7' @ 0 : 0; will stop at (end) 2026/05/12-00:37:07.928654 7ff6637fe6c0 Manual compaction at level-0 from '!items!2nKXEHLG0fXtSOdy' @ 72057594037927935 : 1 .. '!items!tlIc1bmIAbQeUwj7' @ 0 : 0; will stop at (end)
2026/04/27-22:04:19.679254 7feb10fff6c0 Manual compaction at level-1 from '!items!2nKXEHLG0fXtSOdy' @ 72057594037927935 : 1 .. '!items!tlIc1bmIAbQeUwj7' @ 0 : 0; will stop at '!items!tlIc1bmIAbQeUwj7' @ 40 : 1 2026/05/12-00:37:07.928707 7ff6637fe6c0 Manual compaction at level-1 from '!items!2nKXEHLG0fXtSOdy' @ 72057594037927935 : 1 .. '!items!tlIc1bmIAbQeUwj7' @ 0 : 0; will stop at '!items!tlIc1bmIAbQeUwj7' @ 40 : 1
2026/04/27-22:04:19.679269 7feb10fff6c0 Compacting 1@1 + 1@2 files 2026/05/12-00:37:07.928714 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/04/27-22:04:19.684098 7feb10fff6c0 Generated table #10@1: 20 keys, 34454 bytes 2026/05/12-00:37:07.932971 7ff6637fe6c0 Generated table #10@1: 20 keys, 34454 bytes
2026/04/27-22:04:19.684137 7feb10fff6c0 Compacted 1@1 + 1@2 files => 34454 bytes 2026/05/12-00:37:07.932984 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 34454 bytes
2026/04/27-22:04:19.691170 7feb10fff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/12-00:37:07.939008 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/27-22:04:19.691333 7feb10fff6c0 Delete type=2 #5 2026/05/12-00:37:07.939170 7ff6637fe6c0 Delete type=2 #5
2026/04/27-22:04:19.691497 7feb10fff6c0 Delete type=2 #9 2026/05/12-00:37:07.939306 7ff6637fe6c0 Delete type=2 #9
2026/04/27-22:04:19.691633 7feb10fff6c0 Manual compaction at level-1 from '!items!tlIc1bmIAbQeUwj7' @ 40 : 1 .. '!items!tlIc1bmIAbQeUwj7' @ 0 : 0; will stop at (end) 2026/05/12-00:37:07.970948 7ff6637fe6c0 Manual compaction at level-1 from '!items!tlIc1bmIAbQeUwj7' @ 40 : 1 .. '!items!tlIc1bmIAbQeUwj7' @ 0 : 0; will stop at (end)
+5 -5
View File
@@ -1,5 +1,5 @@
2026/04/27-17:47:13.145290 7f2778bfd6c0 Delete type=3 #1 2026/05/12-00:36:37.257477 7fdfd57ec6c0 Delete type=3 #1
2026/04/27-17:47:13.146592 7f272b7fe6c0 Level-0 table #5: started 2026/05/12-00:36:37.258479 7fdfd4feb6c0 Level-0 table #5: started
2026/04/27-17:47:13.150681 7f272b7fe6c0 Level-0 table #5: 32988 bytes OK 2026/05/12-00:36:37.261916 7fdfd4feb6c0 Level-0 table #5: 32988 bytes OK
2026/04/27-17:47:13.157088 7f272b7fe6c0 Delete type=0 #3 2026/05/12-00:36:37.269084 7fdfd4feb6c0 Delete type=0 #3
2026/04/27-17:47:13.157210 7f272b7fe6c0 Manual compaction at level-0 from '!items!2nKXEHLG0fXtSOdy' @ 72057594037927935 : 1 .. '!items!tlIc1bmIAbQeUwj7' @ 0 : 0; will stop at (end) 2026/05/12-00:36:37.269269 7fdfd4feb6c0 Manual compaction at level-0 from '!items!2nKXEHLG0fXtSOdy' @ 72057594037927935 : 1 .. '!items!tlIc1bmIAbQeUwj7' @ 0 : 0; will stop at (end)
Binary file not shown.
+15 -15
View File
@@ -1,15 +1,15 @@
2026/04/27-20:01:11.445769 7fed937fe6c0 Recovering log #4 2026/05/12-00:36:56.360962 7ff663fff6c0 Recovering log #4
2026/04/27-20:01:11.456194 7fed937fe6c0 Delete type=3 #2 2026/05/12-00:36:56.371878 7ff663fff6c0 Delete type=3 #2
2026/04/27-20:01:11.456260 7fed937fe6c0 Delete type=0 #4 2026/05/12-00:36:56.371942 7ff663fff6c0 Delete type=0 #4
2026/04/27-22:04:19.567804 7feb10fff6c0 Level-0 table #9: started 2026/05/12-00:37:08.051413 7ff6637fe6c0 Level-0 table #9: started
2026/04/27-22:04:19.572524 7feb10fff6c0 Level-0 table #9: 50410 bytes OK 2026/05/12-00:37:08.055337 7ff6637fe6c0 Level-0 table #9: 50053 bytes OK
2026/04/27-22:04:19.579249 7feb10fff6c0 Delete type=0 #7 2026/05/12-00:37:08.062742 7ff6637fe6c0 Delete type=0 #7
2026/04/27-22:04:19.579507 7feb10fff6c0 Manual compaction at level-0 from '!actors!4ZjFZ1HoJV9mJStt' @ 72057594037927935 : 1 .. '!actors!zVpmacwoWEG8YTCQ' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.086408 7ff6637fe6c0 Manual compaction at level-0 from '!actors!4ZjFZ1HoJV9mJStt' @ 72057594037927935 : 1 .. '!actors!zVpmacwoWEG8YTCQ' @ 0 : 0; will stop at (end)
2026/04/27-22:04:19.589759 7feb10fff6c0 Manual compaction at level-1 from '!actors!4ZjFZ1HoJV9mJStt' @ 72057594037927935 : 1 .. '!actors!zVpmacwoWEG8YTCQ' @ 0 : 0; will stop at '!actors!zVpmacwoWEG8YTCQ' @ 98 : 1 2026/05/12-00:37:08.086444 7ff6637fe6c0 Manual compaction at level-1 from '!actors!4ZjFZ1HoJV9mJStt' @ 72057594037927935 : 1 .. '!actors!zVpmacwoWEG8YTCQ' @ 0 : 0; will stop at '!actors!zVpmacwoWEG8YTCQ' @ 98 : 1
2026/04/27-22:04:19.589777 7feb10fff6c0 Compacting 1@1 + 1@2 files 2026/05/12-00:37:08.086448 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/04/27-22:04:19.594306 7feb10fff6c0 Generated table #10@1: 49 keys, 50410 bytes 2026/05/12-00:37:08.090154 7ff6637fe6c0 Generated table #10@1: 49 keys, 50053 bytes
2026/04/27-22:04:19.594339 7feb10fff6c0 Compacted 1@1 + 1@2 files => 50410 bytes 2026/05/12-00:37:08.090193 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 50053 bytes
2026/04/27-22:04:19.600590 7feb10fff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/12-00:37:08.096495 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/27-22:04:19.600711 7feb10fff6c0 Delete type=2 #5 2026/05/12-00:37:08.096568 7ff6637fe6c0 Delete type=2 #5
2026/04/27-22:04:19.600847 7feb10fff6c0 Delete type=2 #9 2026/05/12-00:37:08.096695 7ff6637fe6c0 Delete type=2 #9
2026/04/27-22:04:19.614717 7feb10fff6c0 Manual compaction at level-1 from '!actors!zVpmacwoWEG8YTCQ' @ 98 : 1 .. '!actors!zVpmacwoWEG8YTCQ' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.113181 7ff6637fe6c0 Manual compaction at level-1 from '!actors!zVpmacwoWEG8YTCQ' @ 98 : 1 .. '!actors!zVpmacwoWEG8YTCQ' @ 0 : 0; will stop at (end)
+5 -5
View File
@@ -1,5 +1,5 @@
2026/04/27-17:47:13.175381 7f2778bfd6c0 Delete type=3 #1 2026/05/12-00:36:37.286062 7fdfd5fed6c0 Delete type=3 #1
2026/04/27-17:47:13.176524 7f272b7fe6c0 Level-0 table #5: started 2026/05/12-00:36:37.287117 7fdfd4feb6c0 Level-0 table #5: started
2026/04/27-17:47:13.180270 7f272b7fe6c0 Level-0 table #5: 21686 bytes OK 2026/05/12-00:36:37.291305 7fdfd4feb6c0 Level-0 table #5: 21686 bytes OK
2026/04/27-17:47:13.186334 7f272b7fe6c0 Delete type=0 #3 2026/05/12-00:36:37.297295 7fdfd4feb6c0 Delete type=0 #3
2026/04/27-17:47:13.186548 7f272b7fe6c0 Manual compaction at level-0 from '!actors!4ZjFZ1HoJV9mJStt' @ 72057594037927935 : 1 .. '!actors!zVpmacwoWEG8YTCQ' @ 0 : 0; will stop at (end) 2026/05/12-00:36:37.297424 7fdfd4feb6c0 Manual compaction at level-0 from '!actors!4ZjFZ1HoJV9mJStt' @ 72057594037927935 : 1 .. '!actors!zVpmacwoWEG8YTCQ' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+15 -15
View File
@@ -1,15 +1,15 @@
2026/04/27-20:01:11.404989 7fed937fe6c0 Recovering log #4 2026/05/12-00:36:56.324955 7ff6717ee6c0 Recovering log #4
2026/04/27-20:01:11.415714 7fed937fe6c0 Delete type=3 #2 2026/05/12-00:36:56.335151 7ff6717ee6c0 Delete type=3 #2
2026/04/27-20:01:11.415769 7fed937fe6c0 Delete type=0 #4 2026/05/12-00:36:56.335204 7ff6717ee6c0 Delete type=0 #4
2026/04/27-22:04:19.600959 7feb10fff6c0 Level-0 table #9: started 2026/05/12-00:37:08.000036 7ff6637fe6c0 Level-0 table #9: started
2026/04/27-22:04:19.606292 7feb10fff6c0 Level-0 table #9: 4932 bytes OK 2026/05/12-00:37:08.003325 7ff6637fe6c0 Level-0 table #9: 4934 bytes OK
2026/04/27-22:04:19.614475 7feb10fff6c0 Delete type=0 #7 2026/05/12-00:37:08.009477 7ff6637fe6c0 Delete type=0 #7
2026/04/27-22:04:19.624799 7feb10fff6c0 Manual compaction at level-0 from '!items!DC2kimCi9sWxqhXG' @ 72057594037927935 : 1 .. '!items!qzfAEhmvVxEMzm0k' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.009687 7ff6637fe6c0 Manual compaction at level-0 from '!items!DC2kimCi9sWxqhXG' @ 72057594037927935 : 1 .. '!items!qzfAEhmvVxEMzm0k' @ 0 : 0; will stop at (end)
2026/04/27-22:04:19.647175 7feb10fff6c0 Manual compaction at level-1 from '!items!DC2kimCi9sWxqhXG' @ 72057594037927935 : 1 .. '!items!qzfAEhmvVxEMzm0k' @ 0 : 0; will stop at '!items!qzfAEhmvVxEMzm0k' @ 10 : 1 2026/05/12-00:37:08.040730 7ff6637fe6c0 Manual compaction at level-1 from '!items!DC2kimCi9sWxqhXG' @ 72057594037927935 : 1 .. '!items!qzfAEhmvVxEMzm0k' @ 0 : 0; will stop at '!items!qzfAEhmvVxEMzm0k' @ 10 : 1
2026/04/27-22:04:19.647190 7feb10fff6c0 Compacting 1@1 + 1@2 files 2026/05/12-00:37:08.040743 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/04/27-22:04:19.651146 7feb10fff6c0 Generated table #10@1: 5 keys, 4932 bytes 2026/05/12-00:37:08.044737 7ff6637fe6c0 Generated table #10@1: 5 keys, 4934 bytes
2026/04/27-22:04:19.651182 7feb10fff6c0 Compacted 1@1 + 1@2 files => 4932 bytes 2026/05/12-00:37:08.044781 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 4934 bytes
2026/04/27-22:04:19.657147 7feb10fff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/12-00:37:08.050905 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/27-22:04:19.657307 7feb10fff6c0 Delete type=2 #5 2026/05/12-00:37:08.051035 7ff6637fe6c0 Delete type=2 #5
2026/04/27-22:04:19.657500 7feb10fff6c0 Delete type=2 #9 2026/05/12-00:37:08.051186 7ff6637fe6c0 Delete type=2 #9
2026/04/27-22:04:19.668439 7feb10fff6c0 Manual compaction at level-1 from '!items!qzfAEhmvVxEMzm0k' @ 10 : 1 .. '!items!qzfAEhmvVxEMzm0k' @ 0 : 0; will stop at (end) 2026/05/12-00:37:08.051322 7ff6637fe6c0 Manual compaction at level-1 from '!items!qzfAEhmvVxEMzm0k' @ 10 : 1 .. '!items!qzfAEhmvVxEMzm0k' @ 0 : 0; will stop at (end)
+5 -5
View File
@@ -1,5 +1,5 @@
2026/04/27-17:47:13.202702 7f27793fe6c0 Delete type=3 #1 2026/05/12-00:36:37.311794 7fdfd6fef6c0 Delete type=3 #1
2026/04/27-17:47:13.203697 7f272b7fe6c0 Level-0 table #5: started 2026/05/12-00:36:37.312497 7fdfd4feb6c0 Level-0 table #5: started
2026/04/27-17:47:13.207185 7f272b7fe6c0 Level-0 table #5: 4830 bytes OK 2026/05/12-00:36:37.315659 7fdfd4feb6c0 Level-0 table #5: 4830 bytes OK
2026/04/27-17:47:13.213948 7f272b7fe6c0 Delete type=0 #3 2026/05/12-00:36:37.322060 7fdfd4feb6c0 Delete type=0 #3
2026/04/27-17:47:13.214169 7f272b7fe6c0 Manual compaction at level-0 from '!items!DC2kimCi9sWxqhXG' @ 72057594037927935 : 1 .. '!items!qzfAEhmvVxEMzm0k' @ 0 : 0; will stop at (end) 2026/05/12-00:36:37.322200 7fdfd4feb6c0 Manual compaction at level-0 from '!items!DC2kimCi9sWxqhXG' @ 72057594037927935 : 1 .. '!items!qzfAEhmvVxEMzm0k' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
+1
View File
@@ -0,0 +1 @@
MANIFEST-000022
View File
+8
View File
@@ -0,0 +1,8 @@
2026/05/12-00:36:56.386114 7ff670fed6c0 Recovering log #20
2026/05/12-00:36:56.396758 7ff670fed6c0 Delete type=3 #18
2026/05/12-00:36:56.396824 7ff670fed6c0 Delete type=0 #20
2026/05/12-00:37:08.080126 7ff6637fe6c0 Level-0 table #25: started
2026/05/12-00:37:08.080172 7ff6637fe6c0 Level-0 table #25: 0 bytes OK
2026/05/12-00:37:08.086288 7ff6637fe6c0 Delete type=0 #23
2026/05/12-00:37:08.096790 7ff6637fe6c0 Manual compaction at level-0 from '!scenes!2C6gyZpvPxWlsVZi' @ 72057594037927935 : 1 .. '!scenes.levels!olYe9bhuXwRWQ8j7.defaultLevel0000' @ 0 : 0; will stop at (end)
2026/05/12-00:37:08.113200 7ff6637fe6c0 Manual compaction at level-1 from '!scenes!2C6gyZpvPxWlsVZi' @ 72057594037927935 : 1 .. '!scenes.levels!olYe9bhuXwRWQ8j7.defaultLevel0000' @ 0 : 0; will stop at (end)
+8
View File
@@ -0,0 +1,8 @@
2026/05/12-00:34:51.728359 7ff671fef6c0 Recovering log #16
2026/05/12-00:34:51.737993 7ff671fef6c0 Delete type=3 #14
2026/05/12-00:34:51.738056 7ff671fef6c0 Delete type=0 #16
2026/05/12-00:35:02.012940 7ff6637fe6c0 Level-0 table #21: started
2026/05/12-00:35:02.012962 7ff6637fe6c0 Level-0 table #21: 0 bytes OK
2026/05/12-00:35:02.019210 7ff6637fe6c0 Delete type=0 #19
2026/05/12-00:35:02.033237 7ff6637fe6c0 Manual compaction at level-0 from '!scenes!2C6gyZpvPxWlsVZi' @ 72057594037927935 : 1 .. '!scenes.levels!olYe9bhuXwRWQ8j7.defaultLevel0000' @ 0 : 0; will stop at (end)
2026/05/12-00:35:02.033284 7ff6637fe6c0 Manual compaction at level-1 from '!scenes!2C6gyZpvPxWlsVZi' @ 72057594037927935 : 1 .. '!scenes.levels!olYe9bhuXwRWQ8j7.defaultLevel0000' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+15 -15
View File
@@ -1,15 +1,15 @@
2026/04/27-20:01:11.343717 7fed927fc6c0 Recovering log #4 2026/05/12-00:36:56.276952 7ff6717ee6c0 Recovering log #4
2026/04/27-20:01:11.353301 7fed927fc6c0 Delete type=3 #2 2026/05/12-00:36:56.286901 7ff6717ee6c0 Delete type=3 #2
2026/04/27-20:01:11.353373 7fed927fc6c0 Delete type=0 #4 2026/05/12-00:36:56.286957 7ff6717ee6c0 Delete type=0 #4
2026/04/27-22:04:19.372038 7feb10fff6c0 Level-0 table #9: started 2026/05/12-00:37:07.897667 7ff6637fe6c0 Level-0 table #9: started
2026/04/27-22:04:19.376931 7feb10fff6c0 Level-0 table #9: 124022 bytes OK 2026/05/12-00:37:07.902498 7ff6637fe6c0 Level-0 table #9: 124022 bytes OK
2026/04/27-22:04:19.383704 7feb10fff6c0 Delete type=0 #7 2026/05/12-00:37:07.909088 7ff6637fe6c0 Delete type=0 #7
2026/04/27-22:04:19.393329 7feb10fff6c0 Manual compaction at level-0 from '!items!2f51pcvFkcZjaxDk' @ 72057594037927935 : 1 .. '!items!yVN7PZw35iIaBl0H' @ 0 : 0; will stop at (end) 2026/05/12-00:37:07.928672 7ff6637fe6c0 Manual compaction at level-0 from '!items!2f51pcvFkcZjaxDk' @ 72057594037927935 : 1 .. '!items!yVN7PZw35iIaBl0H' @ 0 : 0; will stop at (end)
2026/04/27-22:04:19.403238 7feb10fff6c0 Manual compaction at level-1 from '!items!2f51pcvFkcZjaxDk' @ 72057594037927935 : 1 .. '!items!yVN7PZw35iIaBl0H' @ 0 : 0; will stop at '!items!yVN7PZw35iIaBl0H' @ 50 : 1 2026/05/12-00:37:07.949109 7ff6637fe6c0 Manual compaction at level-1 from '!items!2f51pcvFkcZjaxDk' @ 72057594037927935 : 1 .. '!items!yVN7PZw35iIaBl0H' @ 0 : 0; will stop at '!items!yVN7PZw35iIaBl0H' @ 50 : 1
2026/04/27-22:04:19.403249 7feb10fff6c0 Compacting 1@1 + 1@2 files 2026/05/12-00:37:07.949120 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/04/27-22:04:19.408494 7feb10fff6c0 Generated table #10@1: 25 keys, 124022 bytes 2026/05/12-00:37:07.954259 7ff6637fe6c0 Generated table #10@1: 25 keys, 124022 bytes
2026/04/27-22:04:19.408531 7feb10fff6c0 Compacted 1@1 + 1@2 files => 124022 bytes 2026/05/12-00:37:07.954276 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 124022 bytes
2026/04/27-22:04:19.414410 7feb10fff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/12-00:37:07.961206 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/27-22:04:19.414523 7feb10fff6c0 Delete type=2 #5 2026/05/12-00:37:07.961331 7ff6637fe6c0 Delete type=2 #5
2026/04/27-22:04:19.414716 7feb10fff6c0 Delete type=2 #9 2026/05/12-00:37:07.961477 7ff6637fe6c0 Delete type=2 #9
2026/04/27-22:04:19.434492 7feb10fff6c0 Manual compaction at level-1 from '!items!yVN7PZw35iIaBl0H' @ 50 : 1 .. '!items!yVN7PZw35iIaBl0H' @ 0 : 0; will stop at (end) 2026/05/12-00:37:07.970977 7ff6637fe6c0 Manual compaction at level-1 from '!items!yVN7PZw35iIaBl0H' @ 50 : 1 .. '!items!yVN7PZw35iIaBl0H' @ 0 : 0; will stop at (end)
+5 -5
View File
@@ -1,5 +1,5 @@
2026/04/27-17:47:13.236474 7f2778bfd6c0 Delete type=3 #1 2026/05/12-00:36:37.338931 7fdfd67ee6c0 Delete type=3 #1
2026/04/27-17:47:13.238827 7f272b7fe6c0 Level-0 table #5: started 2026/05/12-00:36:37.341086 7fdfd4feb6c0 Level-0 table #5: started
2026/04/27-17:47:13.244095 7f272b7fe6c0 Level-0 table #5: 120353 bytes OK 2026/05/12-00:36:37.345847 7fdfd4feb6c0 Level-0 table #5: 120353 bytes OK
2026/04/27-17:47:13.250468 7f272b7fe6c0 Delete type=0 #3 2026/05/12-00:36:37.352605 7fdfd4feb6c0 Delete type=0 #3
2026/04/27-17:47:13.250702 7f272b7fe6c0 Manual compaction at level-0 from '!items!2f51pcvFkcZjaxDk' @ 72057594037927935 : 1 .. '!items!yVN7PZw35iIaBl0H' @ 0 : 0; will stop at (end) 2026/05/12-00:36:37.352812 7fdfd4feb6c0 Manual compaction at level-0 from '!items!2f51pcvFkcZjaxDk' @ 72057594037927935 : 1 .. '!items!yVN7PZw35iIaBl0H' @ 0 : 0; will stop at (end)
Binary file not shown.
+15 -15
View File
@@ -1,15 +1,15 @@
2026/04/27-20:01:11.363742 7fed937fe6c0 Recovering log #4 2026/05/12-00:36:56.290045 7ff671fef6c0 Recovering log #4
2026/04/27-20:01:11.374069 7fed937fe6c0 Delete type=3 #2 2026/05/12-00:36:56.299803 7ff671fef6c0 Delete type=3 #2
2026/04/27-20:01:11.374139 7fed937fe6c0 Delete type=0 #4 2026/05/12-00:36:56.299870 7ff671fef6c0 Delete type=0 #4
2026/04/27-22:04:19.383837 7feb10fff6c0 Level-0 table #9: started 2026/05/12-00:37:07.909338 7ff6637fe6c0 Level-0 table #9: started
2026/04/27-22:04:19.387155 7feb10fff6c0 Level-0 table #9: 8786 bytes OK 2026/05/12-00:37:07.913145 7ff6637fe6c0 Level-0 table #9: 8790 bytes OK
2026/04/27-22:04:19.393117 7feb10fff6c0 Delete type=0 #7 2026/05/12-00:37:07.919132 7ff6637fe6c0 Delete type=0 #7
2026/04/27-22:04:19.393340 7feb10fff6c0 Manual compaction at level-0 from '!items!APN91pQL0NBfZsG7' @ 72057594037927935 : 1 .. '!items!xxZKGqDVxAfr140W' @ 0 : 0; will stop at (end) 2026/05/12-00:37:07.928684 7ff6637fe6c0 Manual compaction at level-0 from '!items!APN91pQL0NBfZsG7' @ 72057594037927935 : 1 .. '!items!xxZKGqDVxAfr140W' @ 0 : 0; will stop at (end)
2026/04/27-22:04:19.424676 7feb10fff6c0 Manual compaction at level-1 from '!items!APN91pQL0NBfZsG7' @ 72057594037927935 : 1 .. '!items!xxZKGqDVxAfr140W' @ 0 : 0; will stop at '!items!xxZKGqDVxAfr140W' @ 32 : 1 2026/05/12-00:37:07.961574 7ff6637fe6c0 Manual compaction at level-1 from '!items!APN91pQL0NBfZsG7' @ 72057594037927935 : 1 .. '!items!xxZKGqDVxAfr140W' @ 0 : 0; will stop at '!items!xxZKGqDVxAfr140W' @ 32 : 1
2026/04/27-22:04:19.424688 7feb10fff6c0 Compacting 1@1 + 1@2 files 2026/05/12-00:37:07.961583 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/04/27-22:04:19.428328 7feb10fff6c0 Generated table #10@1: 16 keys, 8786 bytes 2026/05/12-00:37:07.964786 7ff6637fe6c0 Generated table #10@1: 16 keys, 8790 bytes
2026/04/27-22:04:19.428349 7feb10fff6c0 Compacted 1@1 + 1@2 files => 8786 bytes 2026/05/12-00:37:07.964814 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 8790 bytes
2026/04/27-22:04:19.434197 7feb10fff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/12-00:37:07.970646 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/27-22:04:19.434290 7feb10fff6c0 Delete type=2 #5 2026/05/12-00:37:07.970759 7ff6637fe6c0 Delete type=2 #5
2026/04/27-22:04:19.434410 7feb10fff6c0 Delete type=2 #9 2026/05/12-00:37:07.970874 7ff6637fe6c0 Delete type=2 #9
2026/04/27-22:04:19.434517 7feb10fff6c0 Manual compaction at level-1 from '!items!xxZKGqDVxAfr140W' @ 32 : 1 .. '!items!xxZKGqDVxAfr140W' @ 0 : 0; will stop at (end) 2026/05/12-00:37:07.970988 7ff6637fe6c0 Manual compaction at level-1 from '!items!xxZKGqDVxAfr140W' @ 32 : 1 .. '!items!xxZKGqDVxAfr140W' @ 0 : 0; will stop at (end)
+5 -5
View File
@@ -1,5 +1,5 @@
2026/04/27-17:47:13.267650 7f272bfff6c0 Delete type=3 #1 2026/05/12-00:36:37.372690 7fdfd57ec6c0 Delete type=3 #1
2026/04/27-17:47:13.268718 7f272b7fe6c0 Level-0 table #5: started 2026/05/12-00:36:37.373512 7fdfd4feb6c0 Level-0 table #5: started
2026/04/27-17:47:13.272210 7f272b7fe6c0 Level-0 table #5: 8622 bytes OK 2026/05/12-00:36:37.376679 7fdfd4feb6c0 Level-0 table #5: 8622 bytes OK
2026/04/27-17:47:13.278603 7f272b7fe6c0 Delete type=0 #3 2026/05/12-00:36:37.382725 7fdfd4feb6c0 Delete type=0 #3
2026/04/27-17:47:13.278822 7f272b7fe6c0 Manual compaction at level-0 from '!items!APN91pQL0NBfZsG7' @ 72057594037927935 : 1 .. '!items!xxZKGqDVxAfr140W' @ 0 : 0; will stop at (end) 2026/05/12-00:36:37.382856 7fdfd4feb6c0 Manual compaction at level-0 from '!items!APN91pQL0NBfZsG7' @ 72057594037927935 : 1 .. '!items!xxZKGqDVxAfr140W' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+15 -15
View File
@@ -1,15 +1,15 @@
2026/04/27-20:01:11.377813 7fed93fff6c0 Recovering log #4 2026/05/12-00:36:56.301895 7ff670fed6c0 Recovering log #4
2026/04/27-20:01:11.387818 7fed93fff6c0 Delete type=3 #2 2026/05/12-00:36:56.312457 7ff670fed6c0 Delete type=3 #2
2026/04/27-20:01:11.387923 7fed93fff6c0 Delete type=0 #4 2026/05/12-00:36:56.312525 7ff670fed6c0 Delete type=0 #4
2026/04/27-22:04:19.614736 7feb10fff6c0 Level-0 table #9: started 2026/05/12-00:37:07.919212 7ff6637fe6c0 Level-0 table #9: started
2026/04/27-22:04:19.618218 7feb10fff6c0 Level-0 table #9: 4526 bytes OK 2026/05/12-00:37:07.922365 7ff6637fe6c0 Level-0 table #9: 4529 bytes OK
2026/04/27-22:04:19.624621 7feb10fff6c0 Delete type=0 #7 2026/05/12-00:37:07.928522 7ff6637fe6c0 Delete type=0 #7
2026/04/27-22:04:19.647145 7feb10fff6c0 Manual compaction at level-0 from '!items!2IYbyCPF9LJojzsj' @ 72057594037927935 : 1 .. '!items!uOpWyMGK3oiUJ1Sl' @ 0 : 0; will stop at (end) 2026/05/12-00:37:07.928694 7ff6637fe6c0 Manual compaction at level-0 from '!items!2IYbyCPF9LJojzsj' @ 72057594037927935 : 1 .. '!items!uOpWyMGK3oiUJ1Sl' @ 0 : 0; will stop at (end)
2026/04/27-22:04:19.657646 7feb10fff6c0 Manual compaction at level-1 from '!items!2IYbyCPF9LJojzsj' @ 72057594037927935 : 1 .. '!items!uOpWyMGK3oiUJ1Sl' @ 0 : 0; will stop at '!items!uOpWyMGK3oiUJ1Sl' @ 30 : 1 2026/05/12-00:37:07.939386 7ff6637fe6c0 Manual compaction at level-1 from '!items!2IYbyCPF9LJojzsj' @ 72057594037927935 : 1 .. '!items!uOpWyMGK3oiUJ1Sl' @ 0 : 0; will stop at '!items!uOpWyMGK3oiUJ1Sl' @ 30 : 1
2026/04/27-22:04:19.657657 7feb10fff6c0 Compacting 1@1 + 1@2 files 2026/05/12-00:37:07.939396 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/04/27-22:04:19.660952 7feb10fff6c0 Generated table #10@1: 15 keys, 4526 bytes 2026/05/12-00:37:07.942722 7ff6637fe6c0 Generated table #10@1: 15 keys, 4529 bytes
2026/04/27-22:04:19.660988 7feb10fff6c0 Compacted 1@1 + 1@2 files => 4526 bytes 2026/05/12-00:37:07.942780 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 4529 bytes
2026/04/27-22:04:19.668099 7feb10fff6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/12-00:37:07.948850 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/04/27-22:04:19.668233 7feb10fff6c0 Delete type=2 #5 2026/05/12-00:37:07.948918 7ff6637fe6c0 Delete type=2 #5
2026/04/27-22:04:19.668365 7feb10fff6c0 Delete type=2 #9 2026/05/12-00:37:07.949040 7ff6637fe6c0 Delete type=2 #9
2026/04/27-22:04:19.691602 7feb10fff6c0 Manual compaction at level-1 from '!items!uOpWyMGK3oiUJ1Sl' @ 30 : 1 .. '!items!uOpWyMGK3oiUJ1Sl' @ 0 : 0; will stop at (end) 2026/05/12-00:37:07.970963 7ff6637fe6c0 Manual compaction at level-1 from '!items!uOpWyMGK3oiUJ1Sl' @ 30 : 1 .. '!items!uOpWyMGK3oiUJ1Sl' @ 0 : 0; will stop at (end)
+5 -5
View File
@@ -1,5 +1,5 @@
2026/04/27-17:47:13.302798 7f2779bff6c0 Delete type=3 #1 2026/05/12-00:36:37.398172 7fdfd5fed6c0 Delete type=3 #1
2026/04/27-17:47:13.303703 7f272b7fe6c0 Level-0 table #5: started 2026/05/12-00:36:37.399093 7fdfd4feb6c0 Level-0 table #5: started
2026/04/27-17:47:13.307266 7f272b7fe6c0 Level-0 table #5: 4359 bytes OK 2026/05/12-00:36:37.402237 7fdfd4feb6c0 Level-0 table #5: 4359 bytes OK
2026/04/27-17:47:13.314111 7f272b7fe6c0 Delete type=0 #3 2026/05/12-00:36:37.408926 7fdfd4feb6c0 Delete type=0 #3
2026/04/27-17:47:13.314243 7f272b7fe6c0 Manual compaction at level-0 from '!items!2IYbyCPF9LJojzsj' @ 72057594037927935 : 1 .. '!items!uOpWyMGK3oiUJ1Sl' @ 0 : 0; will stop at (end) 2026/05/12-00:36:37.409067 7fdfd4feb6c0 Manual compaction at level-0 from '!items!2IYbyCPF9LJojzsj' @ 72057594037927935 : 1 .. '!items!uOpWyMGK3oiUJ1Sl' @ 0 : 0; will stop at (end)
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,165 @@
{
"folder": "sRyI5qmuFAzCdgGq",
"name": "Lok Zi-Jyun",
"type": "npc",
"img": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/CdE/lok_zi_jyun.Avatar.webp?1710026739974",
"system": {
"type": "",
"levelofthreat": 0,
"powerofnuisance": 0,
"initiative": 1,
"anti_initiative": 24,
"aptitudes": {
"physical": {
"value": 2,
"speciality": ""
},
"martial": {
"value": 3,
"speciality": ""
},
"mental": {
"value": 3,
"speciality": "Enquête"
},
"social": {
"value": 2,
"speciality": "Interrogatoire"
},
"spiritual": {
"value": 1,
"speciality": ""
}
},
"vitality": {
"value": 8,
"calcul": 0,
"note": ""
},
"hei": {
"value": 4,
"calcul": 0,
"note": ""
},
"description": "",
"threat": "0",
"nuisance": "0"
},
"prototypeToken": {
"name": "Lok Zi-Jyun",
"displayName": 0,
"actorLink": false,
"appendNumber": false,
"prependAdjective": false,
"texture": {
"src": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/CdE/lok_zi_jyun.Token.webp?1710026739974",
"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": 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": "chroniquesdeletrange",
"systemVersion": "2.0.7",
"coreVersion": "13.351",
"createdTime": 1701299385970,
"modifiedTime": 1715368926185,
"lastModifiedBy": "oWO8UFosc6mCdtAr",
"compendiumSource": null,
"duplicateSource": null,
"exportSource": {
"worldId": "chroniques-de-letrange",
"uuid": "Actor.iqoRWF0cPvlsL9NY",
"coreVersion": "13.351",
"systemId": "chroniquesdeletrange",
"systemVersion": "2.4.0"
}
},
"ownership": {
"default": 0
}
}
@@ -0,0 +1,200 @@
{
"folder": "7Ezze35hGqYvFR27",
"name": "Sbire du démon",
"type": "npc",
"img": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/CdE/sbire_du_d_mon.Avatar.webp?1701298726849",
"system": {
"type": "",
"levelofthreat": 0,
"powerofnuisance": 0,
"initiative": 1,
"anti_initiative": 24,
"aptitudes": {
"physical": {
"value": 3,
"speciality": ""
},
"martial": {
"value": 3,
"speciality": ""
},
"mental": {
"value": 2,
"speciality": ""
},
"social": {
"value": 2,
"speciality": ""
},
"spiritual": {
"value": 2,
"speciality": ""
}
},
"vitality": {
"value": 12,
"calcul": 0,
"note": ""
},
"hei": {
"value": 8,
"calcul": 0,
"note": ""
},
"description": "",
"threat": "0",
"nuisance": "0"
},
"prototypeToken": {
"name": "Sbire du démon",
"displayName": 0,
"actorLink": false,
"appendNumber": false,
"prependAdjective": false,
"texture": {
"src": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/CdE/sbire_du_d_mon.Token.webp?1701298726849",
"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": [
{
"name": "Arme Crocs (3)",
"type": "supernatural",
"img": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/systems/chroniquesdeletrange/images/capacites-surnaturelles/ki-rin.png",
"system": {
"reference": "LdB p. 350",
"description": "<h1>Arme</h1><p><span class=\"fontstyle0\">Bien des créatures disposent de cette Capacité surnaturelle, sous une forme ou une autre. Cornes, griffes, crocs, excroissances osseuses, flammes naturelles, projection de venin, etc., ces armes peuvent occasionner des dommages importants durant un affrontement.</span></p><p><span class=\"fontstyle0\">La valeur de larme sajoute aux dégâts que le </span><span class=\"fontstyle2\"><em>jiugwaai</em> </span><span class=\"fontstyle0\">inflige par son attaque. Selon la nature de larme, lattaque peut se faire au corps à corps (des griffes ou une aura glacée) ou à distance (un crachat acide ou un éclair) auquel cas la portée est indiquée.</span></p>",
"notes": "<h1>Vos notes…</h1>",
"supernatural": {
"reference": ""
}
},
"effects": [],
"folder": "Pd8AxjsdV0FjAUub",
"flags": {
"core": {}
},
"_stats": {
"systemId": "chroniquesdeletrange",
"systemVersion": "0.1.3",
"coreVersion": "13.351",
"createdTime": 1700491358365,
"modifiedTime": 1701298759309,
"lastModifiedBy": "oWO8UFosc6mCdtAr",
"compendiumSource": "Compendium.chroniquesdeletrange.capacites-surnaturelles.Item.As0zz39FZdgFTCUS",
"duplicateSource": null,
"exportSource": null
},
"_id": "7B1TO4mhlvcpFGvs",
"sort": 0,
"ownership": {
"default": 0
}
}
],
"effects": [],
"flags": {},
"_stats": {
"systemId": "chroniquesdeletrange",
"systemVersion": "2.0.2",
"coreVersion": "13.351",
"createdTime": 1701298702582,
"modifiedTime": 1704633831462,
"lastModifiedBy": "oWO8UFosc6mCdtAr",
"compendiumSource": null,
"duplicateSource": null,
"exportSource": {
"worldId": "chroniques-de-letrange",
"uuid": "Actor.zzYpLkjrIVDAmqPZ",
"coreVersion": "13.351",
"systemId": "chroniquesdeletrange",
"systemVersion": "2.4.0"
}
},
"ownership": {
"default": 0
}
}
@@ -0,0 +1,165 @@
{
"folder": "MkYhJmOo4OfONJvM",
"name": "Sbire Ganger",
"type": "npc",
"img": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/CdE/sbire_ganger.Avatar.webp?1710234619398",
"system": {
"type": "",
"levelofthreat": 0,
"powerofnuisance": 0,
"initiative": 1,
"anti_initiative": 24,
"aptitudes": {
"physical": {
"value": 3,
"speciality": ""
},
"martial": {
"value": 3,
"speciality": ""
},
"mental": {
"value": 2,
"speciality": ""
},
"social": {
"value": 2,
"speciality": ""
},
"spiritual": {
"value": 2,
"speciality": ""
}
},
"vitality": {
"value": 12,
"calcul": 0,
"note": ""
},
"hei": {
"value": 8,
"calcul": 0,
"note": ""
},
"description": "",
"threat": "0",
"nuisance": "0"
},
"prototypeToken": {
"name": "Sbire Ganger",
"displayName": 0,
"actorLink": false,
"appendNumber": false,
"prependAdjective": false,
"texture": {
"src": "https://assets.forge-vtt.com/630dd9fa56bd61d804eb1dec/tokenizer/CdE/sbire_ganger.Token.webp?1710234619398",
"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": 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": "chroniquesdeletrange",
"systemVersion": "2.0.4",
"coreVersion": "13.351",
"createdTime": 1701298966616,
"modifiedTime": 1710234617883,
"lastModifiedBy": "oWO8UFosc6mCdtAr",
"compendiumSource": null,
"duplicateSource": null,
"exportSource": {
"worldId": "chroniques-de-letrange",
"uuid": "Actor.8WAURJbqWS0tgSbj",
"coreVersion": "13.351",
"systemId": "chroniquesdeletrange",
"systemVersion": "2.4.0"
}
},
"ownership": {
"default": 0
}
}
-1
View File
@@ -154,7 +154,6 @@ export const TEMPLATE_PARTIALS = [
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-treasures.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-treasures.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-items.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-items.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-kungfus.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-kungfus.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-spells.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-supernaturals.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-supernaturals.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-spells.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-spells.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-kungfus.html", "systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-kungfus.html",
+50
View File
@@ -12,12 +12,22 @@
*/ */
import { SYSTEM_ID } from "./constants.js" import { SYSTEM_ID } from "./constants.js"
import { CDEMigrationApp } from "../ui/apps/migration-app.js"
/** /**
* Register all world/client settings for the system. * Register all world/client settings for the system.
* Called during the "init" hook before sheets and data-models are set up. * Called during the "init" hook before sheets and data-models are set up.
*/ */
export function registerSettings() { export function registerSettings() {
game.settings.registerMenu(SYSTEM_ID, "migrationTool", {
name: "CDE.MigrationTitle",
label: "CDE.MigrationMenuLabel",
hint: "CDE.MigrationMenuHint",
icon: "fas fa-file-import",
type: CDEMigrationApp,
restricted: true,
})
game.settings.register(SYSTEM_ID, "loksyuData", { game.settings.register(SYSTEM_ID, "loksyuData", {
scope: "world", scope: "world",
config: false, config: false,
@@ -37,6 +47,13 @@ export function registerSettings() {
type: Number, type: Number,
default: 0, default: 0,
}) })
game.settings.register(SYSTEM_ID, "welcomeSceneLoaded", {
scope: "world",
config: false,
type: Boolean,
default: false,
})
} }
/** /**
@@ -46,3 +63,36 @@ export function registerSettings() {
export async function migrateIfNeeded() { export async function migrateIfNeeded() {
// No migrations required yet. // No migrations required yet.
} }
/**
* On first startup, import the "Accueil" scene from the cde-scenes compendium
* into the world and activate it. Only runs once (tracked via the
* `welcomeSceneLoaded` setting). GM-only.
*/
export async function loadWelcomeSceneIfNeeded() {
if (!game.user.isGM) return
if (game.settings.get(SYSTEM_ID, "welcomeSceneLoaded")) return
try {
const pack = game.packs.get(`${SYSTEM_ID}.cde-scenes`)
if (!pack) return
const index = await pack.getIndex()
const entry = index.find(e => e.name === "Accueil")
if (!entry) return
// Check if the scene already exists in the world (e.g. manually imported)
const existing = game.scenes.find(s => s.name === "Accueil")
let scene = existing
if (!scene) {
const doc = await pack.getDocument(entry._id)
;[scene] = await Scene.createDocuments([doc.toObject()])
}
await game.settings.set(SYSTEM_ID, "welcomeSceneLoaded", true)
await scene.activate()
} catch (err) {
console.error("CHRONIQUESDELETRANGE | loadWelcomeSceneIfNeeded failed:", err)
}
}
+4
View File
@@ -188,4 +188,8 @@ export default class CharacterDataModel extends foundry.abstract.TypeDataModel {
return schema return schema
} }
prepareDerivedData() {
this.anti_initiative = 25 - (this.initiative ?? 0)
}
} }
+6
View File
@@ -56,4 +56,10 @@ export default class NpcDataModel extends foundry.abstract.TypeDataModel {
}), }),
} }
} }
prepareDerivedData() {
this.anti_initiative = 25 - (this.initiative ?? 0)
this.vitality.calcul = (this.aptitudes.physical.value ?? 0) * 4
this.hei.calcul = (this.aptitudes.spiritual.value ?? 0) * 4
}
} }
+1 -18
View File
@@ -11,25 +11,8 @@
* @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ * @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
*/ */
import { ACTOR_TYPES } from "../config/constants.js"
export class CDEActor extends Actor { export class CDEActor extends Actor {
getRollData() { getRollData() {
const data = this.toObject(false).system return this.toObject(false).system
return data
}
prepareBaseData() {
super.prepareBaseData()
if (this.type === ACTOR_TYPES.character) {
this.system.anti_initiative = 25 - (this.system.initiative ?? 0)
}
if (this.type === ACTOR_TYPES.npc) {
this.system.vitality.calcul = (this.system.aptitudes.physical.value ?? 0) * 4
this.system.hei.calcul = (this.system.aptitudes.spiritual.value ?? 0) * 4
this.system.anti_initiative = 25 - (this.system.initiative ?? 0)
}
} }
} }
+12 -30
View File
@@ -18,43 +18,25 @@ export class CDEMessage extends ChatMessage {
return html return html
} }
getAssociatedActor() {
if (this.speaker.scene && this.speaker.token) {
const scene = game.scenes.get(this.speaker.scene)
const token = scene?.tokens.get(this.speaker.token)
if (token) return token.actor
}
return game.actors.get(this.speaker.actor)
}
#enrichChatCard(html) { #enrichChatCard(html) {
const actor = this.getAssociatedActor() const tokenDoc = (this.speaker.scene && this.speaker.token)
? game.scenes.get(this.speaker.scene)?.tokens.get(this.speaker.token)
: null
const actor = tokenDoc?.actor ?? game.actors.get(this.speaker.actor) ?? null
let img const [img, nameText] = this.isContentVisible
let nameText ? [actor?.img ?? this.author.avatar, this.alias]
if (this.isContentVisible) { : [this.author.avatar, this.author.name]
img = actor?.img ?? this.author.avatar
nameText = this.alias
} else {
img = this.author.avatar
nameText = this.author.name
}
const avatar = document.createElement("a") const avatarImg = Object.assign(document.createElement("img"), { src: img, alt: nameText })
avatar.classList.add("avatar") const avatar = Object.assign(document.createElement("a"), { className: "avatar" })
if (actor) avatar.dataset.uuid = actor.uuid if (actor) avatar.dataset.uuid = actor.uuid
const avatarImg = document.createElement("img")
Object.assign(avatarImg, { src: img, alt: nameText })
avatar.append(avatarImg) avatar.append(avatarImg)
const name = document.createElement("span") const title = Object.assign(document.createElement("span"), { className: "title", textContent: nameText })
name.classList.add("name-stacked") const name = Object.assign(document.createElement("span"), { className: "name-stacked" })
const title = document.createElement("span")
title.classList.add("title")
title.append(nameText)
name.append(title) name.append(title)
const sender = html.querySelector(".message-sender") html.querySelector(".message-sender")?.replaceChildren(avatar, name)
sender?.replaceChildren(avatar, name)
} }
} }
+412
View File
@@ -0,0 +1,412 @@
/**
* Chroniques de l'Étrange — Système FoundryVTT
*
* Chroniques de l'Étrange est un jeu de rôle édité par Antre-Monde Éditions.
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
* affilié à Antre-Monde Éditions,
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
*
* @author LeRatierBretonnien
* @copyright 20242026 LeRatierBretonnien
* @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
*/
/**
* Migrates actor JSON from the legacy CDE system to the current system format.
*
* This module is pure logic — it does not interact with Foundry APIs directly.
* All transformation is done in-memory; the caller is responsible for creating
* Foundry documents from the returned data.
*/
// ── Element label → key ──────────────────────────────────────────────────────
const ELEMENT_LABEL_TO_KEY = {
"métal": "metal",
"metal": "metal",
"eau": "eau",
"terre": "terre",
"feu": "feu",
"bois": "bois",
}
/** Normalise a French element label to its system key (e.g. "Métal" → "metal"). */
function elementKey(label = "") {
return ELEMENT_LABEL_TO_KEY[label.toLowerCase().trim()] ?? "metal"
}
// ── Hei polarity label → key ──────────────────────────────────────────────────
function heiKey(label = "") {
const l = label.toLowerCase().trim()
if (l === "yin/yang" || l === "yinyang") return "yinyang"
if (l === "yang") return "yang"
return "yin"
}
// ── Spell discipline inference from speciality name ───────────────────────────
/** Maps French speciality labels (lowercase, accents stripped) → school key. */
const SPECIALITY_TO_DISCIPLINE = {
// internalcinnabar
"essence": "internalcinnabar",
"esprit": "internalcinnabar",
"mind": "internalcinnabar",
"purification": "internalcinnabar",
"manipulation": "internalcinnabar",
"aura": "internalcinnabar",
// alchemy
"acupuncture": "alchemy",
"élixirs": "alchemy",
"elixirs": "alchemy",
"poisons": "alchemy",
"arsenal": "alchemy",
"potions": "alchemy",
// masteryoftheway
"malédiction": "masteryoftheway",
"malediction": "masteryoftheway",
"transfiguration": "masteryoftheway",
"nécromancie": "masteryoftheway",
"necromancie": "masteryoftheway",
"contrôle climatique": "masteryoftheway",
"controle climatique": "masteryoftheway",
"magie d'or": "masteryoftheway",
"magie dor": "masteryoftheway",
// exorcism
"invocation": "exorcism",
"pistage": "exorcism",
"traçage": "exorcism",
"tracage": "exorcism",
"protection": "exorcism",
"châtiment": "exorcism",
"chatiment": "exorcism",
"domination": "exorcism",
// geomancy
"neutralisation": "geomancy",
"divination": "geomancy",
"prière terrestre": "geomancy",
"priere terrestre": "geomancy",
"prière céleste": "geomancy",
"priere celeste": "geomancy",
"géomancie": "geomancy",
"geomancie": "geomancy",
"feng shui": "geomancy",
"fungseoi": "geomancy",
}
/**
* Attempt to infer the magic school (discipline) from a spell's speciality name.
* Falls back to scanning the item name for school keywords if needed.
*/
function inferDiscipline(specialityName = "", itemName = "") {
const key = specialityName.toLowerCase().trim()
if (SPECIALITY_TO_DISCIPLINE[key]) return SPECIALITY_TO_DISCIPLINE[key]
// Fuzzy fallback: check item name for school markers
const name = itemName.toLowerCase()
if (name.includes("exorcis")) return "exorcism"
if (name.includes("géomanci") || name.includes("geomanci")) return "geomancy"
if (name.includes("alchimi")) return "alchemy"
if (name.includes("cinnabre") || name.includes("interne")) return "internalcinnabar"
if (name.includes("maîtrise") || name.includes("maitrise") || name.includes("tao")) return "masteryoftheway"
return "internalcinnabar"
}
// ── KungFu activation mapping ─────────────────────────────────────────────────
function mapActivation(oldActivation = "") {
const s = oldActivation.toLowerCase()
if (s.includes("infligés") || s.includes("infliges")) return "damage-inflicted"
if (s.includes("reçus") || s.includes("recus")) return "damage-received"
if (s.includes("réaction") || s.includes("reaction")) return "reaction"
if (s.includes("dés-fastes") || s.includes("des-fastes") || s.includes("fastes")) return "dice"
if (s.includes("aide")) return "action-aid"
if (s.includes("attaque") && s.includes("défense")) return "action-attack-defense"
if (s.includes("attaque") && s.includes("defense")) return "action-attack-defense"
if (s.includes("attaque")) return "action-attack"
if (s.includes("défense") || s.includes("defense")) return "action-defense"
return "action-attack"
}
// ── Helpers ───────────────────────────────────────────────────────────────────
const DEFAULT_ACTOR_IMG = "icons/svg/mystery-man.svg"
const DEFAULT_ITEM_IMG = "icons/svg/item-bag.svg"
// ── Item migration ────────────────────────────────────────────────────────────
function migrateEquipmentItem(oldItem) {
const s = oldItem.system ?? {}
return {
name: oldItem.name,
type: "item",
img: oldItem.img || DEFAULT_ITEM_IMG,
system: {
reference: s.reference ?? "",
description: s.description ?? "",
quantity: Number(s.quantity ?? 1),
weight: Number(s.weight ?? 0),
notes: s.notes ?? "",
},
}
}
function migrateKungfuItem(oldItem) {
const s = oldItem.system ?? {}
const techs = s.techniques ?? {}
const migratedTechs = {}
for (const key of ["technique1", "technique2", "technique3"]) {
const t = techs[key] ?? {}
migratedTechs[key] = {
check: Boolean(t.check),
name: t.name ?? "",
activation: mapActivation(t.activation ?? ""),
technique: t.technique ?? "",
}
}
return {
name: oldItem.name,
type: "kungfu",
img: oldItem.img || DEFAULT_ITEM_IMG,
system: {
reference: s.reference ?? "",
description: s.description ?? "",
orientation: s.orientation || "yin",
aspect: s.aspect || "metal",
skill: s.skill || "kungfu",
speciality: s.speciality ?? "",
style: s.style ?? "",
techniques: migratedTechs,
notes: s.notes ?? "",
},
}
}
function migrateSpellItem(oldItem) {
const s = oldItem.system ?? {}
return {
name: oldItem.name,
type: "spell",
img: oldItem.img || DEFAULT_ITEM_IMG,
system: {
reference: s.reference ?? "",
description: s.description ?? "",
specialityname: s.specialityname ?? "",
associatedelement: elementKey(s.associatedelement ?? ""),
heiType: heiKey(s.hei ?? ""),
heiCost: Number(s.heiCost ?? 0),
difficulty: Number(s.difficulty ?? 0),
realizationtimeritual: s.realizationtimeritual ?? "",
realizationtimeaccelerated: s.realizationtimeaccelerated ?? "",
flashback: s.flashback ?? "",
components: s.components ?? "",
effects: s.effects ?? "",
examples: s.examples ?? "",
notes: s.notes ?? "",
discipline: inferDiscipline(s.specialityname ?? "", oldItem.name ?? ""),
},
}
}
function migrateSupernaturalItem(oldItem) {
const s = oldItem.system ?? {}
// Old system stored a nested `supernatural: { reference }` — prefer that reference if top-level is empty
const nestedRef = s.supernatural?.reference ?? ""
return {
name: oldItem.name,
type: "supernatural",
img: oldItem.img || DEFAULT_ITEM_IMG,
system: {
reference: s.reference || nestedRef,
description: s.description ?? "",
notes: s.notes ?? "",
heiType: "yin",
heiCost: 0,
trigger: "",
effects: "",
},
}
}
function migrateItem(oldItem) {
switch (oldItem.type) {
case "item": return migrateEquipmentItem(oldItem)
case "kungfu": return migrateKungfuItem(oldItem)
case "spell": return migrateSpellItem(oldItem)
case "supernatural": return migrateSupernaturalItem(oldItem)
default:
// Unknown item type: keep as generic equipment
return migrateEquipmentItem({ ...oldItem, type: "item" })
}
}
// ── Actor migration ───────────────────────────────────────────────────────────
function migrateCharacter(old) {
const s = old.system ?? {}
// aspects: keep only { chinese, label, value }
const aspect = {}
for (const [k, v] of Object.entries(s.aspect ?? {})) {
aspect[k] = { chinese: v.chinese ?? "", label: v.label ?? "", value: Number(v.value ?? 0) }
}
// skills: keep { label, specialities, value }
const skills = {}
for (const [k, v] of Object.entries(s.skills ?? {})) {
skills[k] = { label: v.label ?? "", specialities: v.specialities ?? "", value: Number(v.value ?? 0) }
}
// resources: keep { label, specialities, value, debt }
const resources = {}
for (const [k, v] of Object.entries(s.resources ?? {})) {
resources[k] = { label: v.label ?? "", specialities: v.specialities ?? "", value: Number(v.value ?? 0), debt: Boolean(v.debt) }
}
// components: keep { value }
const component = {}
for (const [k, v] of Object.entries(s.component ?? {})) {
component[k] = { value: v.value ?? "" }
}
// magics: keep { visible, value, speciality: { [key]: { check } } }
const MAGIC_SPECIALITIES = {
internalcinnabar: ["essence", "mind", "purification", "manipulation", "aura"],
alchemy: ["acupuncture", "elixirs", "poisons", "arsenal", "potions"],
masteryoftheway: ["curse", "transfiguration", "necromancy", "climatecontrol", "goldenmagic"],
exorcism: ["invocation", "tracking", "protection", "punishment", "domination"],
geomancy: ["neutralization", "divination", "earthlyprayer", "heavenlyprayer", "fungseoi"],
}
const magics = {}
for (const [school, specs] of Object.entries(MAGIC_SPECIALITIES)) {
const om = s.magics?.[school] ?? {}
const speciality = {}
for (const spec of specs) {
speciality[spec] = { check: Boolean(om.speciality?.[spec]?.check) }
}
magics[school] = { visible: Boolean(om.visible), value: Number(om.value ?? 0), speciality }
}
// threetreasures: strip `min` from heiyang/heiyin; keep dicelevel as-is
const tt = s.threetreasures ?? {}
const threetreasures = {
heiyang: { value: Number(tt.heiyang?.value ?? 0), max: Number(tt.heiyang?.max ?? 0) },
heiyin: { value: Number(tt.heiyin?.value ?? 0), max: Number(tt.heiyin?.max ?? 0) },
dicelevel: {
level0d: {
san: { value: Number(tt.dicelevel?.level0d?.san?.value ?? 0), max: Number(tt.dicelevel?.level0d?.san?.max ?? 0) },
zing: { value: Number(tt.dicelevel?.level0d?.zing?.value ?? 0), max: Number(tt.dicelevel?.level0d?.zing?.max ?? 0) },
},
level1d: {
san: { value: Number(tt.dicelevel?.level1d?.san?.value ?? 0), max: Number(tt.dicelevel?.level1d?.san?.max ?? 0) },
zing: { value: Number(tt.dicelevel?.level1d?.zing?.value ?? 0), max: Number(tt.dicelevel?.level1d?.zing?.max ?? 0) },
},
level2d: {
san: { value: Number(tt.dicelevel?.level2d?.san?.value ?? 0), max: Number(tt.dicelevel?.level2d?.san?.max ?? 0) },
zing: { value: Number(tt.dicelevel?.level2d?.zing?.value ?? 0), max: Number(tt.dicelevel?.level2d?.zing?.max ?? 0) },
},
},
}
// biography (old separate field) merged into description
const description = s.description || s.biography || ""
return {
name: old.name,
type: "character",
img: old.img || DEFAULT_ACTOR_IMG,
system: {
concept: s.concept ?? "",
guardian: parseInt(s.guardian ?? "0") || 0,
initiative: Number(s.initiative ?? 1),
anti_initiative: Number(s.anti_initiative ?? 24),
description,
aspect,
skills,
resources,
component,
magics,
threetreasures,
experience: {
value: Number(s.experience?.value ?? 0),
max: Number(s.experience?.max ?? 0),
min: Number(s.experience?.min ?? 0),
},
},
items: (old.items ?? []).map(migrateItem),
}
}
function migrateNpc(old) {
const s = old.system ?? {}
const aptitudes = {}
for (const [k, v] of Object.entries(s.aptitudes ?? {})) {
aptitudes[k] = { value: Number(v.value ?? 0), speciality: v.speciality ?? "" }
}
return {
name: old.name,
type: "npc",
img: old.img || DEFAULT_ACTOR_IMG,
system: {
type: s.type ?? "",
// Old system had separate `levelofthreat`/`powerofnuisance` as numbers
// and string copies `threat`/`nuisance` — use the numeric fields
threat: Number(s.levelofthreat ?? s.threat ?? 0),
nuisance: Number(s.powerofnuisance ?? s.nuisance ?? 0),
initiative: Number(s.initiative ?? 1),
anti_initiative: Number(s.anti_initiative ?? 24),
aptitudes,
vitality: {
value: Number(s.vitality?.value ?? 0),
calcul: Number(s.vitality?.calcul ?? 0),
note: s.vitality?.note ?? "",
},
hei: {
value: Number(s.hei?.value ?? 0),
calcul: Number(s.hei?.calcul ?? 0),
note: s.hei?.note ?? "",
},
description: s.description ?? "",
},
items: (old.items ?? []).map(migrateItem),
}
}
// ── Public API ────────────────────────────────────────────────────────────────
/**
* Migrate a single legacy actor JSON to the current system format.
*
* @param {object} oldJson Parsed JSON from the old system export
* @returns {{ name: string, type: string, img: string, system: object, items: object[] }}
* @throws {Error} if the actor type is unrecognised
*/
export function migrateActor(oldJson) {
switch (oldJson.type) {
case "character": return migrateCharacter(oldJson)
case "npc": return migrateNpc(oldJson)
default:
throw new Error(`Unknown actor type "${oldJson.type}" in "${oldJson.name}"`)
}
}
/**
* Parse one or more legacy JSON strings and return migrated actor data.
* Accepts a single actor object or an array of actor objects in one file.
*
* @param {string} jsonText Raw JSON text from a file
* @returns {Array<{ name, type, img, system, items }>}
*/
export function parseLegacyJson(jsonText) {
const parsed = JSON.parse(jsonText)
if (typeof parsed !== "object" || parsed === null) {
throw new Error("Le fichier JSON doit contenir un objet acteur ou un tableau d'acteurs")
}
const actors = Array.isArray(parsed) ? parsed : [parsed]
return actors.map(migrateActor)
}
+5 -1
View File
@@ -12,7 +12,7 @@
*/ */
import { ACTOR_TYPES, ITEM_TYPES, MAGICS, SUBTYPES, SYSTEM_ID } from "./config/constants.js" import { ACTOR_TYPES, ITEM_TYPES, MAGICS, SUBTYPES, SYSTEM_ID } from "./config/constants.js"
import { registerSettings, migrateIfNeeded } from "./config/settings.js" import { registerSettings, migrateIfNeeded, loadWelcomeSceneIfNeeded } from "./config/settings.js"
import { preLocalizeConfig } from "./config/localize.js" import { preLocalizeConfig } from "./config/localize.js"
import { configureRuntime } from "./config/runtime.js" import { configureRuntime } from "./config/runtime.js"
import { CharacterDataModel, NpcDataModel } from "./data/actors/index.js" import { CharacterDataModel, NpcDataModel } from "./data/actors/index.js"
@@ -30,6 +30,7 @@ import { CDETinjiApp } from "./ui/apps/tinji-app.js"
import { CDEWheelApp } from "./ui/apps/wheel-app.js" import { CDEWheelApp } from "./ui/apps/wheel-app.js"
import { injectRollActions, refreshAllRollActions } from "./ui/roll-actions.js" import { injectRollActions, refreshAllRollActions } from "./ui/roll-actions.js"
import { CDECombat } from "./documents/combat.js" import { CDECombat } from "./documents/combat.js"
import { showWelcomeMessage, injectWelcomeActions } from "./ui/apps/welcome.js"
Hooks.once("i18nInit", preLocalizeConfig) Hooks.once("i18nInit", preLocalizeConfig)
@@ -130,7 +131,9 @@ Hooks.once("init", async () => {
Hooks.once("ready", async () => { Hooks.once("ready", async () => {
await migrateIfNeeded() await migrateIfNeeded()
await loadWelcomeSceneIfNeeded()
CDEWheelApp.registerHooks() CDEWheelApp.registerHooks()
if (game.user.isGM) showWelcomeMessage()
}) })
/** Add Loksyu + Tin Ji quick-access buttons to the chat panel (FoundryVTT v13) */ /** Add Loksyu + Tin Ji quick-access buttons to the chat panel (FoundryVTT v13) */
@@ -173,6 +176,7 @@ Hooks.on("renderChatLog", (_app, html) => {
/** Inject Loksyu / TinJi action buttons into roll-result chat messages */ /** Inject Loksyu / TinJi action buttons into roll-result chat messages */
Hooks.on("renderChatMessageHTML", (message, html) => { Hooks.on("renderChatMessageHTML", (message, html) => {
injectRollActions(message, html) injectRollActions(message, html)
if (message.flags?.[SYSTEM_ID]?.welcome) injectWelcomeActions(message, html)
}) })
/** Refresh all visible roll-result buttons whenever Loksyu or TinJi settings change */ /** Refresh all visible roll-result buttons whenever Loksyu or TinJi settings change */
+2
View File
@@ -15,3 +15,5 @@ export { CDELoksyuApp } from "./loksyu-app.js"
export { CDETinjiApp } from "./tinji-app.js" export { CDETinjiApp } from "./tinji-app.js"
export { updateLoksyuFromRoll, updateTinjiFromRoll } from "./singletons.js" export { updateLoksyuFromRoll, updateTinjiFromRoll } from "./singletons.js"
export { CDEWheelApp } from "./wheel-app.js" export { CDEWheelApp } from "./wheel-app.js"
export { CDEMigrationApp } from "./migration-app.js"
export { showWelcomeMessage, injectWelcomeActions } from "./welcome.js"
+148
View File
@@ -0,0 +1,148 @@
/**
* Chroniques de l'Étrange — Système FoundryVTT
*
* Chroniques de l'Étrange est un jeu de rôle édité par Antre-Monde Éditions.
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
* affilié à Antre-Monde Éditions,
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
*
* @author LeRatierBretonnien
* @copyright 20242026 LeRatierBretonnien
* @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
*/
import { parseLegacyJson } from "../../migration/migrator.js"
const MIGRATION_TEMPLATE = "systems/fvtt-chroniques-de-l-etrange/templates/apps/cde-migration-app.html"
/**
* Dialog for importing legacy CDE actor JSON files into the current system.
*
* Accessible via the System Settings menu (registerMenu).
* Supports multi-file selection and shows a preview table before importing.
*/
export class CDEMigrationApp extends foundry.applications.api.HandlebarsApplicationMixin(
foundry.applications.api.ApplicationV2
) {
static DEFAULT_OPTIONS = {
id: "cde-migration-app",
classes: ["cde-migration-app"],
tag: "div",
window: {
title: "CDE.MigrationTitle",
icon: "fas fa-file-import",
resizable: false,
},
position: { width: 560, height: "auto" },
actions: {
clearFiles: CDEMigrationApp.#clearFiles,
doImport: CDEMigrationApp.#doImport,
},
}
static PARTS = {
form: { template: MIGRATION_TEMPLATE },
}
/** @type {Array<{name: string, type: string, img: string, system: object, items: object[], _srcFile: string}>} */
#pending = []
/** @type {string[]} - error messages per file */
#errors = []
async _prepareContext(options) {
return {
pending: this.#pending,
errors: this.#errors,
hasPending: this.#pending.length > 0,
hasErrors: this.#errors.length > 0,
count: this.#pending.length,
}
}
/** After render, wire up the file input. */
_onRender(context, options) {
super._onRender(context, options)
const input = this.element.querySelector(".cde-migration-file-input")
input?.addEventListener("change", this.#onFileChange.bind(this))
const dropZone = this.element.querySelector(".cde-migration-drop-zone")
if (dropZone) {
dropZone.addEventListener("dragover", (e) => { e.preventDefault(); dropZone.classList.add("is-dragover") })
dropZone.addEventListener("dragleave", () => dropZone.classList.remove("is-dragover"))
dropZone.addEventListener("drop", (e) => {
e.preventDefault()
dropZone.classList.remove("is-dragover")
this.#processFiles(Array.from(e.dataTransfer.files))
})
}
}
async #onFileChange(event) {
const files = Array.from(event.target.files ?? [])
event.target.value = ""
await this.#processFiles(files)
}
async #processFiles(files) {
for (const file of files) {
if (!file.name.endsWith(".json")) {
this.#errors.push(game.i18n.format("CDE.MigrationErrorNotJson", { file: file.name }))
continue
}
try {
const text = await file.text()
const actors = parseLegacyJson(text)
for (const actor of actors) {
actor._srcFile = file.name
// Avoid duplicates by name
if (!this.#pending.some(p => p.name === actor.name)) {
this.#pending.push(actor)
}
}
} catch (err) {
this.#errors.push(game.i18n.format("CDE.MigrationErrorParse", { file: file.name, error: err.message }))
}
}
this.render()
}
static async #clearFiles() {
this.#pending = []
this.#errors = []
this.render()
}
static async #doImport() {
if (!this.#pending.length) return
const created = []
const failed = []
for (const data of this.#pending) {
try {
const { _srcFile, ...actorData } = data
const actor = await Actor.create(actorData)
created.push(actor.name)
} catch (err) {
failed.push(`${data.name}: ${err.message}`)
console.error(`CHRONIQUESDELETRANGE | Migration failed for "${data.name}":`, err)
}
}
this.#pending = []
this.#errors = failed
this.render()
if (created.length) {
ui.notifications.info(
game.i18n.format("CDE.MigrationSuccess", { count: created.length, names: created.join(", ") })
)
}
if (failed.length) {
ui.notifications.warn(
game.i18n.format("CDE.MigrationPartialError", { count: failed.length })
)
}
}
}

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