Compare commits

...

10 Commits

Author SHA1 Message Date
5206ba1e41 Rework des fiches creature/PJ et Tinji/Loksyu 2026-03-30 17:49:27 +02:00
637ea883dd Rework des fiches creature/PJ et Tinji/Loksyu 2026-03-30 17:37:38 +02:00
40c8c523b7 Rework des fiches creature/PJ et Tinji/Loksyu 2026-03-30 17:07:28 +02:00
b0e1867ef5 Rework des fiches creature/PJ et Tinji/Loksyu 2026-03-30 17:06:08 +02:00
8753784dc9 Rework des fiches creature/PJ et Tinji/Loksyu 2026-03-30 17:05:34 +02:00
b19dcdef3c Rework des fiches creature/PJ et Tinji/Loksyu 2026-03-30 16:39:27 +02:00
0b7de1c98b Rework des fiches creature/PJ et Tinji/Loksyu 2026-03-30 16:37:04 +02:00
cab77645b7 Rework des fiches creature/PJ et Tinji/Loksyu 2026-03-30 16:36:38 +02:00
0689fae792 feat: Loksyu & TinJi standalone AppV2 apps with chat buttons and dice automation
- CDELoksyuApp: standalone HandlebarsApplicationMixin(ApplicationV2) app
  - 5-element Wu Xing grid with yin/yang inputs per element
  - Per-element reset buttons + global reset-all
  - Auto-refresh via updateActor hook

- CDETinjiApp: standalone AppV2 for the collective Tin Ji dice pool
  - Large neon counter with +/- buttons and direct input
  - Spend button sends a chat message with remaining count

- singletons.js: shared utilities
  - getSingletonActor: find or auto-create singleton actor
  - updateLoksyuFromRoll: compute lokAspect from Wu Xing cycle, update yin/yang
  - updateTinjiFromRoll: add tinji face count to value

- rolling.js: auto-update both singletons after every dice roll
  (weapon path + main roll path)

- system.js: renderChatLog hook injects Loksyu/TinJi footer buttons
  in the chat sidebar

- loksyu.js / tinji.js: actor sheets redirect to standalone apps
  when opened via the sidebar

- CSS: .cde-loksyu-standalone, .cde-tinji-standalone, .cde-chat-app-buttons,
  .cde-tinji-spend-msg styles added

- i18n: new keys in fr-cde.json and en-cde.json for all new UI strings
  (LoksyuNotFound, TinjiNotFound, Reset, ResetAll, SpendTinji, etc.)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-30 09:51:39 +02:00
6fda4b9246 Refactor des fiches de creatures 2026-03-30 09:27:11 +02:00
138 changed files with 3692 additions and 1991 deletions

View File

@@ -1,5 +1,5 @@
3.0.0
- Refonte complète vers DataModels (FoundryVTT V13) et Application V2
13.0.0
- Refonte complète en DataModels (FoundryVTT V13) et Application V2
- Suppression du code legacy (module/) — réécriture totale en src/
- Pipeline LESS (cde-theme.less → cde-theme.css) via lessc/esbuild
- DataModels pour acteurs : character, npc, tinji, loksyu
@@ -15,18 +15,3 @@
* .cde-neon-tabs avec underline néon animé
- Corrections AppV2 : DEFAULT_OPTIONS sans mergeObject, fix tabs display
- ID système : fvtt-chroniques-de-l-etrange
2.3.3
- Image monastère titre sans accentuations
2.3.2
- Correction CSS sur fiche de créature
2.3.1
- Thème de dés par défaut dans Dice-so-Nice!
2.3.0
- Ajout avatar dans Tchat
---
Bugs connus et non encore traités

View File

@@ -12,49 +12,12 @@ 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,
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'.
- L'organisation du reste des images provient du système produit par MysteryMan (merci à lui)
Code Author :
- Developed by LeRatierBretonnien
MUSIQUES / MUSICS: MODULE À PART À PARTIR DE LA v. 1.0.0
- Les musiques de Mathieu Verdier, composées spécialement pour les CdE,
à l'occasion du financement participatif de ce jeu de rôle,
sont librement téléchargeables sur le site d'Antre Monde Éditions :
https://antre-monde.com/les-chroniques-de-letrengae/
CODE / CODING:
- David R.D. 'Mystery Man From Outerspace' Bercovici
- Christophe 'Kristov / Qaw' Laudon (il a grandement collaboré !)
Avec l'aide précieuse de / with precious help from
(sur / on "La Fonderie" Discord - Many thanks to them!):
- LeRatierBretonnien
- Maxime
(et d'autres / and others)
COMPENDIUMS: MODULES À PART À PARTIR DE LA v. 1.0.0
Basés sur / based on:
Antre Monde Éditions' 'Les Chroniques de l'étrange' RPG books by:
- Romain 'Rom1' d'Huissier
- Cédric 'JTR the Hobbit' Lameire
Saisis par / Entered by:
David R.D. 'Mystery Man From Outerspace' Bercovici
MODULES: MODULES À PART À PARTIR DE LA v. 1.0.0
Les Compendiums sont téléchargeables depuis deux sources :
- Compendiums gratuits :
https://antre-monde.com/les-chroniques-de-letrengae/
- Compendiums payants :
https://preview.drivethrurpg.com/fr/publisher/15811/antre-monde-editions
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
Code LICENCE :
C BY-NC-SA 4.0
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
This license requires that reusers give credit to the creator. It allows reusers to distribute, remix, adapt, and build upon the material in any medium or format, for noncommercial purposes only. If others modify or adapt the material, they must license the modified material under identical terms.

View File

@@ -860,6 +860,162 @@ section.npc .cde-neon-tabs .item.active {
.cde-aptitudes-table .cde-spec-cell {
flex: 1 1 0;
}
.cde-aptitudes-table .cde-roll-cell {
width: 28px;
flex-shrink: 0;
text-align: center;
}
.cde-aptitudes-table .cde-roll-cell .cde-roll-trigger {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
color: #7d94b8;
border-radius: 4px;
transition: color 0.15s, background 0.15s;
}
.cde-aptitudes-table .cde-roll-cell .cde-roll-trigger:hover {
color: #cc44ff;
background: rgba(204, 68, 255, 0.12);
}
.cde-empty-list {
color: #7d94b8;
font-style: italic;
font-size: 11px;
text-align: center;
padding: 10px 0;
}
.cde-super-add-row {
display: flex;
justify-content: flex-end;
padding: 4px 0 8px;
}
.cde-super-add-btn {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 11px;
font-family: "Averia", sans-serif;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #7d94b8;
cursor: pointer;
padding: 4px 10px;
border-radius: 8px;
border: 1px solid #1a2436;
transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.cde-super-add-btn i {
font-size: 10px;
}
.cde-super-add-btn:hover {
color: #cc44ff;
border-color: #cc44ff;
background: rgba(204, 68, 255, 0.08);
}
.cde-super-card {
border: 1px solid #1a2436;
border-left: 3px solid #cc44ff;
border-radius: 8px;
background: rgba(16, 22, 34, 0.7);
margin-bottom: 10px;
overflow: hidden;
transition: box-shadow 0.15s;
}
.cde-super-card:hover {
box-shadow: 0 0 8px rgba(204, 68, 255, 0.2);
}
.cde-super-card:hover .cde-super-controls {
opacity: 1;
}
.cde-super-header {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 10px;
}
.cde-super-img {
width: 28px;
height: 28px;
object-fit: contain;
border-radius: 4px;
flex-shrink: 0;
}
.cde-super-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
}
.cde-super-name {
font-size: 14px;
font-weight: 700;
font-family: "Averia", sans-serif;
color: #e2e8f4;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cde-super-meta {
display: flex;
align-items: center;
gap: 4px;
}
.cde-super-controls {
display: flex;
align-items: center;
gap: 4px;
opacity: 0;
transition: opacity 0.12s;
flex-shrink: 0;
}
.cde-super-controls a {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 3px;
color: #7d94b8;
cursor: pointer;
transition: color 0.12s, background 0.12s;
}
.cde-super-controls a i {
font-size: 11px;
}
.cde-super-controls a:hover {
color: #e2e8f4;
background: rgba(38, 56, 83, 0.3);
}
.cde-super-desc {
padding: 6px 12px 8px 46px;
border-top: 1px solid rgba(26, 36, 54, 0.6);
background: rgba(16, 22, 34, 0.4);
font-size: 11px;
color: #7d94b8;
line-height: 1.5;
}
.cde-super-desc p {
margin: 0 0 4px;
}
.cde-super-desc p:last-child {
margin-bottom: 0;
}
.cde-super-desc em {
color: #e2e8f4;
}
.cde-super-desc strong {
color: #cc44ff;
}
.cde-super-empty {
padding: 16px;
text-align: center;
font-size: 12px;
color: #7d94b8;
font-style: italic;
}
.cde-npc-tracks {
margin-top: 12px;
}
@@ -915,6 +1071,546 @@ section.npc .cde-neon-tabs .item.active {
.cde-npc-tracks .cde-track-note input {
width: 100%;
}
.cde-chat-app-buttons {
display: flex;
gap: 6px;
padding: 6px 8px 4px;
border-top: 1px solid #1a2436;
pointer-events: auto;
}
.cde-chat-app-buttons .cde-chat-btn {
flex: 1 1 0;
pointer-events: auto;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 5px 8px;
font-size: 11px;
font-family: "Orbitron", "Averia", sans-serif;
text-transform: uppercase;
letter-spacing: 0.06em;
background: #101622;
border: 1px solid #1a2436;
border-radius: 4px;
color: #7d94b8;
cursor: pointer;
transition: all 0.2s;
}
.cde-chat-app-buttons .cde-chat-btn i {
font-size: 12px;
}
.cde-chat-app-buttons .cde-chat-btn:hover {
background: rgba(74, 158, 255, 0.1);
border-color: #4a9eff;
color: #4a9eff;
box-shadow: 0 0 8px rgba(74, 158, 255, 0.3);
}
.cde-chat-app-buttons .cde-chat-btn--tinji:hover {
background: rgba(255, 61, 90, 0.1);
border-color: #ff3d5a;
color: #ff3d5a;
box-shadow: 0 0 8px rgba(255, 61, 90, 0.3);
}
.cde-loksyu-standalone .cde-loksyu-app-body {
padding: 12px;
}
.cde-loksyu-standalone .cde-loksyu-elements {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
margin-bottom: 10px;
}
.cde-loksyu-standalone .cde-lok-card {
flex: 0 0 calc(33.333% - 6px);
min-width: 140px;
max-width: 160px;
background: rgba(16, 22, 34, 0.8);
border: 1px solid #1a2436;
border-radius: 6px;
padding: 10px 8px;
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
overflow: hidden;
transition: border-color 0.2s;
}
.cde-loksyu-standalone .cde-lok-card--wood:hover {
border-color: #4a9e3f;
}
.cde-loksyu-standalone .cde-lok-card--wood .cde-lok-input:focus {
border-bottom-color: #4a9e3f;
}
.cde-loksyu-standalone .cde-lok-card--fire:hover {
border-color: #ff3d5a;
}
.cde-loksyu-standalone .cde-lok-card--fire .cde-lok-input:focus {
border-bottom-color: #ff3d5a;
}
.cde-loksyu-standalone .cde-lok-card--earth:hover {
border-color: #c88a3a;
}
.cde-loksyu-standalone .cde-lok-card--earth .cde-lok-input:focus {
border-bottom-color: #c88a3a;
}
.cde-loksyu-standalone .cde-lok-card--metal:hover {
border-color: #7d94b8;
}
.cde-loksyu-standalone .cde-lok-card--metal .cde-lok-input:focus {
border-bottom-color: #7d94b8;
}
.cde-loksyu-standalone .cde-lok-card--water:hover {
border-color: #4a9eff;
}
.cde-loksyu-standalone .cde-lok-card--water .cde-lok-input:focus {
border-bottom-color: #4a9eff;
}
.cde-loksyu-standalone .cde-lok-header {
display: flex;
align-items: center;
gap: 6px;
}
.cde-loksyu-standalone .cde-lok-header img.cde-lok-icon {
border-radius: 4px;
flex-shrink: 0;
}
.cde-loksyu-standalone .cde-lok-titles {
flex: 1 1 0;
min-width: 0;
display: flex;
flex-direction: column;
}
.cde-loksyu-standalone .cde-lok-titles .cde-lok-name {
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #e2e8f4;
}
.cde-loksyu-standalone .cde-lok-titles .cde-lok-qual {
font-size: 9px;
color: #7d94b8;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cde-loksyu-standalone .cde-lok-reset {
color: #7d94b8;
font-size: 11px;
flex-shrink: 0;
cursor: pointer;
}
.cde-loksyu-standalone .cde-lok-reset:hover {
color: #e2e8f4;
}
.cde-loksyu-standalone .cde-lok-values {
display: flex;
flex-direction: column;
gap: 4px;
}
.cde-loksyu-standalone .cde-lok-polarity {
display: flex;
align-items: center;
gap: 4px;
}
.cde-loksyu-standalone .cde-lok-polarity .cde-lok-pol-label {
font-size: 9px;
color: #7d94b8;
width: 30px;
flex-shrink: 0;
}
.cde-loksyu-standalone .cde-lok-polarity--yang .cde-lok-pol-label {
color: #e2e8f4;
}
.cde-loksyu-standalone .cde-lok-input {
flex: 1 1 0;
background: transparent;
border: none;
border-bottom: 1px solid #1a2436;
color: #e2e8f4;
font-size: 13px;
font-weight: 700;
text-align: center;
padding: 2px 0;
transition: border-bottom-color 0.2s;
width: 100%;
}
.cde-loksyu-standalone .cde-lok-input:focus {
outline: none;
}
.cde-loksyu-standalone .cde-lok-input[disabled] {
opacity: 0.5;
cursor: default;
}
.cde-loksyu-standalone .cde-loksyu-visual-row {
text-align: center;
margin: 6px 0;
}
.cde-loksyu-standalone .cde-loksyu-visual-row .cde-lok-visual {
max-width: 120px;
max-height: 160px;
width: auto;
height: auto;
opacity: 0.6;
}
.cde-loksyu-standalone .cde-lok-footer {
display: flex;
justify-content: center;
padding-top: 6px;
border-top: 1px solid #1a2436;
margin-top: 6px;
}
.cde-loksyu-standalone .cde-lok-reset-all {
display: flex;
align-items: center;
gap: 6px;
padding: 5px 14px;
font-size: 11px;
background: rgba(255, 61, 90, 0.12);
border: 1px solid rgba(255, 61, 90, 0.3);
border-radius: 4px;
color: #7d94b8;
cursor: pointer;
transition: all 0.2s;
}
.cde-loksyu-standalone .cde-lok-reset-all:hover {
background: rgba(255, 61, 90, 0.2);
border-color: #ff3d5a;
color: #e2e8f4;
}
.cde-tinji-standalone .cde-tinji-app-body {
padding: 16px 12px;
display: flex;
align-items: center;
gap: 16px;
}
.cde-tinji-standalone .cde-tinji-display {
flex: 1 1 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.cde-tinji-standalone .cde-tinji-chinese-large {
font-size: 40px;
color: #ff3d5a;
text-shadow: 0 0 20px rgba(255, 61, 90, 0.6);
line-height: 1;
font-family: serif;
}
.cde-tinji-standalone .cde-tinji-label {
font-size: 11px;
font-family: "Orbitron", sans-serif;
text-transform: uppercase;
letter-spacing: 0.12em;
color: #e2e8f4;
opacity: 0.75;
}
.cde-tinji-standalone .cde-tinji-counter {
display: flex;
align-items: center;
gap: 8px;
margin: 4px 0;
}
.cde-tinji-standalone .cde-tinji-step {
width: 30px;
height: 30px;
background: #0d1520;
border: 1px solid #263853;
border-radius: 50%;
color: #e2e8f4;
font-size: 18px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
line-height: 1;
padding: 0;
}
.cde-tinji-standalone .cde-tinji-step:hover {
background: rgba(255, 61, 90, 0.25);
border-color: #ff3d5a;
color: #e2e8f4;
}
.cde-tinji-standalone .cde-tinji-direct {
width: 72px;
background: transparent;
border: none;
border-bottom: 2px solid #ff3d5a;
color: #ff3d5a;
font-size: 36px;
font-weight: 700;
text-align: center;
text-shadow: 0 0 12px rgba(255, 61, 90, 0.5);
padding: 0;
}
.cde-tinji-standalone .cde-tinji-direct:focus {
outline: none;
}
.cde-tinji-standalone .cde-tinji-direct[disabled] {
opacity: 0.7;
cursor: default;
}
.cde-tinji-standalone .cde-tinji-hint {
font-size: 10px;
color: #7d94b8;
text-align: center;
}
.cde-tinji-standalone .cde-tinji-actions {
display: flex;
gap: 6px;
margin-top: 6px;
}
.cde-tinji-standalone .cde-tinji-spend-btn,
.cde-tinji-standalone .cde-tinji-reset-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 6px 12px;
font-size: 11px;
font-weight: 600;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
letter-spacing: 0.03em;
}
.cde-tinji-standalone .cde-tinji-spend-btn {
background: rgba(255, 61, 90, 0.25);
border: 1px solid #ff3d5a;
color: #e2e8f4;
}
.cde-tinji-standalone .cde-tinji-spend-btn:hover {
background: rgba(255, 61, 90, 0.45);
border-color: #ff7085;
}
.cde-tinji-standalone .cde-tinji-spend-btn[disabled] {
opacity: 0.55;
cursor: not-allowed;
pointer-events: none;
}
.cde-tinji-standalone .cde-tinji-reset-btn {
background: #0d1520;
border: 1px solid #263853;
color: #e2e8f4;
opacity: 0.85;
}
.cde-tinji-standalone .cde-tinji-reset-btn:hover {
border-color: #e2e8f4;
opacity: 1;
}
.cde-tinji-standalone .cde-tinji-visual {
width: 90px;
height: auto;
opacity: 0.85;
flex-shrink: 0;
}
.cde-tinji-spend-msg {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
padding: 8px 10px;
background: rgba(255, 61, 90, 0.18);
border: 1px solid rgba(255, 61, 90, 0.35);
border-left: 4px solid #ff3d5a;
border-radius: 4px;
color: #e2e8f4;
}
.cde-tinji-spend-msg .cde-tinji-icon {
font-size: 18px;
color: #ff3d5a;
text-shadow: 0 0 6px rgba(255, 61, 90, 0.7);
flex-shrink: 0;
font-weight: 700;
line-height: 1;
}
.cde-tinji-spend-msg i {
color: #ff3d5a;
font-size: 15px;
filter: drop-shadow(0 0 4px rgba(255, 61, 90, 0.6));
flex-shrink: 0;
}
.cde-tinji-spend-msg strong {
color: #ff3d5a;
font-size: 13px;
letter-spacing: 0.02em;
}
.cde-tinji-spend-msg .cde-tinji-remain {
margin-left: auto;
font-size: 11px;
color: rgba(226, 232, 244, 0.65);
font-style: italic;
white-space: nowrap;
}
.cde-loksyu-draw-msg {
display: flex;
flex-direction: column;
gap: 5px;
font-size: 12px;
padding: 9px 11px;
background: linear-gradient(135deg, rgba(0, 212, 212, 0.1) 0%, rgba(16, 22, 34, 0.95) 100%);
border: 1px solid rgba(0, 212, 212, 0.4);
border-left: 4px solid #00d4d4;
border-radius: 5px;
color: #e2e8f4;
box-shadow: 0 2px 8px rgba(0, 212, 212, 0.12);
}
.cde-loksyu-draw-msg .cde-loksyu-draw-header {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 5px;
font-size: 13px;
line-height: 1.4;
}
.cde-loksyu-draw-msg .cde-loksyu-draw-aspect-icon {
width: 20px;
height: 20px;
object-fit: contain;
filter: drop-shadow(0 0 4px rgba(0, 212, 212, 0.6));
flex-shrink: 0;
}
.cde-loksyu-draw-msg .cde-loksyu-draw-user {
font-weight: 700;
color: #00d4d4;
letter-spacing: 0.03em;
}
.cde-loksyu-draw-msg .cde-loksyu-draw-action {
color: rgba(226, 232, 244, 0.65);
font-size: 12px;
font-style: italic;
}
.cde-loksyu-draw-msg .cde-loksyu-draw-type {
font-weight: 700;
font-size: 13px;
color: #c9a227;
text-transform: uppercase;
letter-spacing: 0.05em;
text-shadow: 0 0 6px rgba(201, 162, 39, 0.4);
}
.cde-loksyu-draw-msg .cde-loksyu-draw-from {
color: rgba(226, 232, 244, 0.6);
font-size: 11px;
}
.cde-loksyu-draw-msg .cde-loksyu-draw-from em {
color: #00d4d4;
font-style: normal;
font-weight: 600;
}
.cde-loksyu-draw-msg .cde-loksyu-draw-footer {
display: flex;
align-items: center;
gap: 5px;
font-size: 11px;
color: rgba(226, 232, 244, 0.55);
padding-top: 4px;
border-top: 1px solid rgba(0, 212, 212, 0.2);
}
.cde-loksyu-draw-msg .cde-loksyu-draw-footer i {
color: #00d4d4;
font-size: 10px;
}
.cde-loksyu-draw-msg .cde-loksyu-draw-footer .cde-loksyu-remain {
color: #00d4d4;
font-weight: 700;
}
.cde-roll-actions {
margin-top: 10px;
padding-top: 8px;
border-top: 1px solid rgba(26, 36, 54, 0.6);
}
.cde-roll-actions .cde-roll-actions-title {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: rgba(226, 232, 244, 0.5);
margin-bottom: 6px;
display: flex;
align-items: center;
gap: 4px;
}
.cde-roll-actions .cde-roll-actions-title i {
font-size: 9px;
}
.cde-roll-actions .cde-roll-actions-btns {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.cde-roll-actions .cde-roll-action-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 4px 8px;
font-size: 11px;
font-family: "Courier New", Courier, monospace;
border-radius: 3px;
border: 1px solid rgba(26, 36, 54, 0.8);
background: rgba(16, 22, 34, 0.9);
color: #e2e8f4;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
pointer-events: auto;
line-height: 1.4;
}
.cde-roll-actions .cde-roll-action-btn:hover:not(:disabled) {
border-color: rgba(0, 212, 212, 0.7);
background: rgba(0, 212, 212, 0.12);
box-shadow: 0 0 6px rgba(0, 212, 212, 0.25);
color: #00d4d4;
}
.cde-roll-actions .cde-roll-action-btn:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.cde-roll-actions .cde-roll-action-btn .cde-roll-action-icon {
width: 16px;
height: 16px;
object-fit: contain;
filter: drop-shadow(0 0 2px rgba(0, 212, 212, 0.4));
}
.cde-roll-actions .cde-roll-action-btn .cde-roll-action-label {
flex: 1;
}
.cde-roll-actions .cde-roll-action-btn .cde-roll-action-count {
background: rgba(26, 36, 54, 0.6);
border-radius: 2px;
padding: 1px 4px;
font-size: 10px;
font-weight: 700;
min-width: 18px;
text-align: center;
}
.cde-roll-actions .cde-roll-action-btn .cde-roll-action-tinji-char {
font-size: 14px;
line-height: 1;
color: #ff3d5a;
text-shadow: 0 0 4px rgba(255, 61, 90, 0.6);
}
.cde-roll-actions .cde-roll-action-btn.cde-roll-action--success:hover:not(:disabled) {
border-color: rgba(201, 162, 39, 0.7);
background: rgba(201, 162, 39, 0.1);
box-shadow: 0 0 6px rgba(201, 162, 39, 0.25);
color: #c9a227;
}
.cde-roll-actions .cde-roll-action-btn.cde-roll-action--faste:hover:not(:disabled) {
border-color: rgba(0, 212, 212, 0.7);
background: rgba(0, 212, 212, 0.1);
box-shadow: 0 0 6px rgba(0, 212, 212, 0.25);
color: #00d4d4;
}
.cde-roll-actions .cde-roll-action-btn.cde-roll-action--tinji:hover:not(:disabled) {
border-color: rgba(255, 61, 90, 0.7);
background: rgba(255, 61, 90, 0.12);
box-shadow: 0 0 6px rgba(255, 61, 90, 0.3);
color: #ff3d5a;
}
.cde-roll-actions .cde-roll-action-btn.cde-roll-action--tinji:hover:not(:disabled) .cde-roll-action-count {
background: rgba(255, 61, 90, 0.25);
}
.cde-loksyu-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
@@ -2783,6 +3479,38 @@ ol.item-list li.item .item-controls a.item-control:hover {
letter-spacing: 0.1em;
color: #7d94b8;
}
.cde-roll-result .cde-rr-hero .cde-rr-loksyu-bonus {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: 13px;
font-weight: 700;
padding: 2px 6px;
border-radius: 3px;
animation: cde-pulse-in 0.3s ease;
}
.cde-roll-result .cde-rr-hero .cde-rr-loksyu-bonus.cde-rr-loksyu-bonus--success {
background: rgba(201, 162, 39, 0.2);
border: 1px solid rgba(201, 162, 39, 0.5);
color: #c9a227;
text-shadow: 0 0 6px rgba(201, 162, 39, 0.6);
}
.cde-roll-result .cde-rr-hero .cde-rr-loksyu-bonus.cde-rr-loksyu-bonus--faste {
background: rgba(0, 212, 212, 0.15);
border: 1px solid rgba(0, 212, 212, 0.4);
color: #00d4d4;
text-shadow: 0 0 6px rgba(0, 212, 212, 0.5);
}
@keyframes cde-pulse-in {
from {
opacity: 0;
transform: scale(0.7);
}
to {
opacity: 1;
transform: scale(1);
}
}
.cde-roll-result .cde-rr-details {
display: flex;
flex-direction: column;
@@ -2806,6 +3534,17 @@ ol.item-list li.item .item-controls a.item-control:hover {
text-align: center;
text-shadow: 0 0 6px currentColor;
}
.cde-roll-result .cde-rr-row .cde-rr-loksyu-bonus {
font-size: 10px;
font-weight: 700;
padding: 1px 4px;
border-radius: 2px;
background: rgba(0, 212, 212, 0.15);
border: 1px solid rgba(0, 212, 212, 0.4);
color: #00d4d4;
text-shadow: 0 0 4px rgba(0, 212, 212, 0.5);
flex-shrink: 0;
}
.cde-roll-result .cde-rr-row .cde-rr-icon {
width: 22px;
height: 22px;

View File

@@ -891,6 +891,167 @@ section.npc .cde-neon-tabs .item.active { color: @cde-supernatural; borde
.cde-aptitudes-table {
// Inherits .cde-skills-table styles; just ensure consistent width
.cde-spec-cell { flex: 1 1 0; }
.cde-roll-cell {
width: 28px;
flex-shrink: 0;
text-align: center;
.cde-roll-trigger {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
color: @cde-muted;
border-radius: 4px;
transition: color 0.15s, background 0.15s;
&:hover {
color: @cde-supernatural;
background: fade(@cde-supernatural, 12%);
}
}
}
}
// Empty list placeholder
.cde-empty-list {
color: @cde-muted;
font-style: italic;
font-size: 11px;
text-align: center;
padding: 10px 0;
}
// NPC supernatural item cards
// ── Supernatural abilities card (NPC) ───────────────────────────────────────
.cde-super-add-row {
display: flex;
justify-content: flex-end;
padding: 4px 0 8px;
}
.cde-super-add-btn {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 11px;
font-family: "Averia", sans-serif;
text-transform: uppercase;
letter-spacing: 0.08em;
color: @cde-muted;
cursor: pointer;
padding: 4px 10px;
border-radius: @cde-radius;
border: 1px solid @cde-border;
transition: color 0.12s, border-color 0.12s, background 0.12s;
i { font-size: 10px; }
&:hover { color: @cde-supernatural; border-color: @cde-supernatural; background: fade(@cde-supernatural, 8%); }
}
.cde-super-card {
border: 1px solid @cde-border;
border-left: 3px solid @cde-supernatural;
border-radius: @cde-radius;
background: fade(@cde-surface, 70%);
margin-bottom: 10px;
overflow: hidden;
transition: box-shadow 0.15s;
&:hover {
box-shadow: 0 0 8px fade(@cde-supernatural, 20%);
.cde-super-controls { opacity: 1; }
}
}
.cde-super-header {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 10px;
}
.cde-super-img {
width: 28px;
height: 28px;
object-fit: contain;
border-radius: 4px;
flex-shrink: 0;
}
.cde-super-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
}
.cde-super-name {
font-size: 14px;
font-weight: 700;
font-family: "Averia", sans-serif;
color: @cde-text;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cde-super-meta {
display: flex;
align-items: center;
gap: 4px;
}
.cde-super-controls {
display: flex;
align-items: center;
gap: 4px;
opacity: 0;
transition: opacity 0.12s;
flex-shrink: 0;
a {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 3px;
color: @cde-muted;
cursor: pointer;
transition: color 0.12s, background 0.12s;
i { font-size: 11px; }
&:hover { color: @cde-text; background: fade(@cde-border-hi, 30%); }
}
}
.cde-super-desc {
padding: 6px 12px 8px 46px;
border-top: 1px solid fade(@cde-border, 60%);
background: fade(@cde-surface, 40%);
font-size: 11px;
color: @cde-muted;
line-height: 1.5;
p { margin: 0 0 4px; &:last-child { margin-bottom: 0; } }
em { color: @cde-text; }
strong { color: @cde-supernatural; }
}
.cde-super-empty {
padding: 16px;
text-align: center;
font-size: 12px;
color: @cde-muted;
font-style: italic;
}
// NPC vitality / hei tracker
@@ -956,6 +1117,586 @@ section.npc .cde-neon-tabs .item.active { color: @cde-supernatural; borde
// ============================================================
// Loksyu — 5 elemental cards grid
// ============================================================
// ============================================================
// Chat buttons — Loksyu / TinJi quick-access
// ============================================================
.cde-chat-app-buttons {
display: flex;
gap: 6px;
padding: 6px 8px 4px;
border-top: 1px solid @cde-border;
pointer-events: auto; // sidebar has pointer-events:none — must override
.cde-chat-btn {
flex: 1 1 0;
pointer-events: auto;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 5px 8px;
font-size: 11px;
font-family: "Orbitron", "Averia", sans-serif;
text-transform: uppercase;
letter-spacing: 0.06em;
background: @cde-surface;
border: 1px solid @cde-border;
border-radius: 4px;
color: @cde-muted;
cursor: pointer;
transition: all 0.2s;
i { font-size: 12px; }
&:hover {
background: fade(@cde-spell, 10%);
border-color: @cde-spell;
color: @cde-spell;
box-shadow: 0 0 8px fade(@cde-spell, 30%);
}
&--tinji:hover {
background: fade(@cde-kungfu, 10%);
border-color: @cde-kungfu;
color: @cde-kungfu;
box-shadow: 0 0 8px fade(@cde-kungfu, 30%);
}
}
}
// ============================================================
// Loksyu standalone app
// ============================================================
.cde-loksyu-standalone {
.cde-loksyu-app-body {
padding: 12px;
}
.cde-loksyu-elements {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
margin-bottom: 10px;
}
.cde-lok-card {
flex: 0 0 calc(33.333% - 6px);
min-width: 140px;
max-width: 160px;
background: fade(@cde-surface, 80%);
border: 1px solid @cde-border;
border-radius: 6px;
padding: 10px 8px;
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
overflow: hidden;
transition: border-color 0.2s;
&--wood { &:hover { border-color: #4a9e3f; } .cde-lok-input:focus { border-bottom-color: #4a9e3f; } }
&--fire { &:hover { border-color: @cde-kungfu; } .cde-lok-input:focus { border-bottom-color: @cde-kungfu; } }
&--earth { &:hover { border-color: #c88a3a; } .cde-lok-input:focus { border-bottom-color: #c88a3a; } }
&--metal { &:hover { border-color: @cde-muted; } .cde-lok-input:focus { border-bottom-color: @cde-muted; } }
&--water { &:hover { border-color: @cde-spell; } .cde-lok-input:focus { border-bottom-color: @cde-spell; } }
}
.cde-lok-header {
display: flex;
align-items: center;
gap: 6px;
img.cde-lok-icon { border-radius: 4px; flex-shrink: 0; }
}
.cde-lok-titles {
flex: 1 1 0;
min-width: 0;
display: flex;
flex-direction: column;
.cde-lok-name {
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: @cde-text;
}
.cde-lok-qual {
font-size: 9px;
color: @cde-muted;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.cde-lok-reset {
color: @cde-muted;
font-size: 11px;
flex-shrink: 0;
cursor: pointer;
&:hover { color: @cde-text; }
}
.cde-lok-values {
display: flex;
flex-direction: column;
gap: 4px;
}
.cde-lok-polarity {
display: flex;
align-items: center;
gap: 4px;
.cde-lok-pol-label {
font-size: 9px;
color: @cde-muted;
width: 30px;
flex-shrink: 0;
}
&--yang .cde-lok-pol-label { color: @cde-text; }
}
.cde-lok-input {
flex: 1 1 0;
background: transparent;
border: none;
border-bottom: 1px solid @cde-border;
color: @cde-text;
font-size: 13px;
font-weight: 700;
text-align: center;
padding: 2px 0;
transition: border-bottom-color 0.2s;
width: 100%;
&:focus { outline: none; }
&[disabled] { opacity: 0.5; cursor: default; }
}
.cde-loksyu-visual-row {
text-align: center;
margin: 6px 0;
.cde-lok-visual {
max-width: 120px;
max-height: 160px;
width: auto;
height: auto;
opacity: 0.6;
}
}
.cde-lok-footer {
display: flex;
justify-content: center;
padding-top: 6px;
border-top: 1px solid @cde-border;
margin-top: 6px;
}
.cde-lok-reset-all {
display: flex;
align-items: center;
gap: 6px;
padding: 5px 14px;
font-size: 11px;
background: fade(@cde-kungfu, 12%);
border: 1px solid fade(@cde-kungfu, 30%);
border-radius: 4px;
color: @cde-muted;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: fade(@cde-kungfu, 20%);
border-color: @cde-kungfu;
color: @cde-text;
}
}
}
// ============================================================
// TinJi standalone app
// ============================================================
.cde-tinji-standalone {
.cde-tinji-app-body {
padding: 16px 12px;
display: flex;
align-items: center;
gap: 16px;
}
.cde-tinji-display {
flex: 1 1 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.cde-tinji-chinese-large {
font-size: 40px;
color: @cde-kungfu;
text-shadow: 0 0 20px fade(@cde-kungfu, 60%);
line-height: 1;
font-family: serif;
}
.cde-tinji-label {
font-size: 11px;
font-family: "Orbitron", sans-serif;
text-transform: uppercase;
letter-spacing: 0.12em;
color: @cde-text;
opacity: 0.75;
}
.cde-tinji-counter {
display: flex;
align-items: center;
gap: 8px;
margin: 4px 0;
}
.cde-tinji-step {
width: 30px;
height: 30px;
background: @cde-surface2;
border: 1px solid @cde-border-hi;
border-radius: 50%;
color: @cde-text;
font-size: 18px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
line-height: 1;
padding: 0;
&:hover {
background: fade(@cde-kungfu, 25%);
border-color: @cde-kungfu;
color: @cde-text;
}
}
.cde-tinji-direct {
width: 72px;
background: transparent;
border: none;
border-bottom: 2px solid @cde-kungfu;
color: @cde-kungfu;
font-size: 36px;
font-weight: 700;
text-align: center;
text-shadow: 0 0 12px fade(@cde-kungfu, 50%);
padding: 0;
&:focus { outline: none; }
&[disabled] { opacity: 0.7; cursor: default; }
}
.cde-tinji-hint {
font-size: 10px;
color: @cde-muted;
text-align: center;
}
.cde-tinji-actions {
display: flex;
gap: 6px;
margin-top: 6px;
}
.cde-tinji-spend-btn,
.cde-tinji-reset-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 6px 12px;
font-size: 11px;
font-weight: 600;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
letter-spacing: 0.03em;
}
.cde-tinji-spend-btn {
background: fade(@cde-kungfu, 25%);
border: 1px solid @cde-kungfu;
color: @cde-text;
&:hover { background: fade(@cde-kungfu, 45%); border-color: lighten(@cde-kungfu, 10%); }
&[disabled] { opacity: 0.55; cursor: not-allowed; pointer-events: none; }
}
.cde-tinji-reset-btn {
background: @cde-surface2;
border: 1px solid @cde-border-hi;
color: @cde-text;
opacity: 0.85;
&:hover { border-color: @cde-text; opacity: 1; }
}
.cde-tinji-visual {
width: 90px;
height: auto;
opacity: 0.85;
flex-shrink: 0;
}
}
// Chat Tin Ji spend message
.cde-tinji-spend-msg {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
padding: 8px 10px;
background: fade(@cde-kungfu, 18%);
border: 1px solid fade(@cde-kungfu, 35%);
border-left: 4px solid @cde-kungfu;
border-radius: 4px;
color: @cde-text;
.cde-tinji-icon {
font-size: 18px;
color: @cde-kungfu;
text-shadow: 0 0 6px fade(@cde-kungfu, 70%);
flex-shrink: 0;
font-weight: 700;
line-height: 1;
}
i {
color: @cde-kungfu;
font-size: 15px;
filter: drop-shadow(0 0 4px fade(@cde-kungfu, 60%));
flex-shrink: 0;
}
strong {
color: @cde-kungfu;
font-size: 13px;
letter-spacing: 0.02em;
}
.cde-tinji-remain {
margin-left: auto;
font-size: 11px;
color: fade(@cde-text, 65%);
font-style: italic;
white-space: nowrap;
}
}
// Post-roll Loksyu draw message — rich notification card
.cde-loksyu-draw-msg {
display: flex;
flex-direction: column;
gap: 5px;
font-size: 12px;
padding: 9px 11px;
background: linear-gradient(135deg, fade(@cde-item, 10%) 0%, fade(@cde-surface, 95%) 100%);
border: 1px solid fade(@cde-item, 40%);
border-left: 4px solid @cde-item;
border-radius: 5px;
color: @cde-text;
box-shadow: 0 2px 8px fade(@cde-item, 12%);
.cde-loksyu-draw-header {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 5px;
font-size: 13px;
line-height: 1.4;
}
.cde-loksyu-draw-aspect-icon {
width: 20px;
height: 20px;
object-fit: contain;
filter: drop-shadow(0 0 4px fade(@cde-item, 60%));
flex-shrink: 0;
}
.cde-loksyu-draw-user {
font-weight: 700;
color: @cde-item;
letter-spacing: 0.03em;
}
.cde-loksyu-draw-action {
color: fade(@cde-text, 65%);
font-size: 12px;
font-style: italic;
}
.cde-loksyu-draw-type {
font-weight: 700;
font-size: 13px;
color: #c9a227;
text-transform: uppercase;
letter-spacing: 0.05em;
text-shadow: 0 0 6px fade(#c9a227, 40%);
}
.cde-loksyu-draw-from {
color: fade(@cde-text, 60%);
font-size: 11px;
em {
color: @cde-item;
font-style: normal;
font-weight: 600;
}
}
.cde-loksyu-draw-footer {
display: flex;
align-items: center;
gap: 5px;
font-size: 11px;
color: fade(@cde-text, 55%);
padding-top: 4px;
border-top: 1px solid fade(@cde-item, 20%);
i {
color: @cde-item;
font-size: 10px;
}
.cde-loksyu-remain {
color: @cde-item;
font-weight: 700;
}
}
}
// Post-roll action buttons section inside a roll-result card
.cde-roll-actions {
margin-top: 10px;
padding-top: 8px;
border-top: 1px solid fade(@cde-border, 60%);
.cde-roll-actions-title {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: fade(@cde-text, 50%);
margin-bottom: 6px;
display: flex;
align-items: center;
gap: 4px;
i { font-size: 9px; }
}
.cde-roll-actions-btns {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.cde-roll-action-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 4px 8px;
font-size: 11px;
font-family: "Courier New", Courier, monospace;
border-radius: 3px;
border: 1px solid fade(@cde-border, 80%);
background: fade(@cde-surface, 90%);
color: @cde-text;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
pointer-events: auto;
line-height: 1.4;
&:hover:not(:disabled) {
border-color: fade(@cde-item, 70%);
background: fade(@cde-item, 12%);
box-shadow: 0 0 6px fade(@cde-item, 25%);
color: @cde-item;
}
&:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.cde-roll-action-icon {
width: 16px;
height: 16px;
object-fit: contain;
filter: drop-shadow(0 0 2px fade(@cde-item, 40%));
}
.cde-roll-action-label {
flex: 1;
}
.cde-roll-action-count {
background: fade(@cde-border, 60%);
border-radius: 2px;
padding: 1px 4px;
font-size: 10px;
font-weight: 700;
min-width: 18px;
text-align: center;
}
.cde-roll-action-tinji-char {
font-size: 14px;
line-height: 1;
color: @cde-kungfu;
text-shadow: 0 0 4px fade(@cde-kungfu, 60%);
}
&.cde-roll-action--success {
&:hover:not(:disabled) {
border-color: fade(#c9a227, 70%);
background: fade(#c9a227, 10%);
box-shadow: 0 0 6px fade(#c9a227, 25%);
color: #c9a227;
}
}
&.cde-roll-action--faste {
&:hover:not(:disabled) {
border-color: fade(@cde-item, 70%);
background: fade(@cde-item, 10%);
box-shadow: 0 0 6px fade(@cde-item, 25%);
color: @cde-item;
}
}
&.cde-roll-action--tinji {
&:hover:not(:disabled) {
border-color: fade(@cde-kungfu, 70%);
background: fade(@cde-kungfu, 12%);
box-shadow: 0 0 6px fade(@cde-kungfu, 30%);
color: @cde-kungfu;
.cde-roll-action-count {
background: fade(@cde-kungfu, 25%);
}
}
}
}
}
.cde-loksyu-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
@@ -2793,6 +3534,37 @@ ol.item-list {
color: @cde-muted;
}
}
// Loksyu bonus badge (shown when dice drawn from Loksyu)
.cde-rr-loksyu-bonus {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: 13px;
font-weight: 700;
padding: 2px 6px;
border-radius: 3px;
animation: cde-pulse-in 0.3s ease;
&.cde-rr-loksyu-bonus--success {
background: fade(#c9a227, 20%);
border: 1px solid fade(#c9a227, 50%);
color: #c9a227;
text-shadow: 0 0 6px fade(#c9a227, 60%);
}
&.cde-rr-loksyu-bonus--faste {
background: fade(@cde-item, 15%);
border: 1px solid fade(@cde-item, 40%);
color: @cde-item;
text-shadow: 0 0 6px fade(@cde-item, 50%);
}
}
}
@keyframes cde-pulse-in {
from { opacity: 0; transform: scale(0.7); }
to { opacity: 1; transform: scale(1); }
}
// ---- Detail rows ----
@@ -2821,6 +3593,19 @@ ol.item-list {
text-shadow: 0 0 6px currentColor;
}
// Inline Loksyu bonus badge in detail rows
.cde-rr-loksyu-bonus {
font-size: 10px;
font-weight: 700;
padding: 1px 4px;
border-radius: 2px;
background: fade(@cde-item, 15%);
border: 1px solid fade(@cde-item, 40%);
color: @cde-item;
text-shadow: 0 0 4px fade(@cde-item, 50%);
flex-shrink: 0;
}
.cde-rr-icon {
width: 22px;
height: 22px;

863
dist/system.js vendored

File diff suppressed because it is too large Load Diff

8
dist/system.js.map vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,76 +0,0 @@
{
"fvtt-chroniques-de-l-etrange": {
"system": "fvtt-chroniques-de-l-etrange",
"topology": "standard",
"quantity" : "quantity",
"aliases": {},
"sources": {
"Lampe torche": {
"name": "Lampe torche",
"type": "item",
"consumable": false,
"states": 2,
"light": [
{
"bright": 30, "dim": 300, "angle": 20, "color": "#c2fffb", "alpha": 0.5, "attenuation": 0.8
}
]
},
"怪 Lampe torche": {
"name": "怪 Lampe torche",
"type": "item",
"consumable": false,
"states": 2,
"light": [
{
"bright": 30, "dim": 300, "angle": 20, "color": "#c2fffb", "alpha": 0.5, "attenuation": 0.8
}
]
},
"Flashlight": {
"name": "Flashlight",
"type": "item",
"consumable": false,
"states": 2,
"light": [
{
"bright": 30, "dim": 300, "angle": 20, "color": "#c2fffb", "alpha": 0.5, "attenuation": 0.8
}
]
},
"怪 Flashlight": {
"name": "怪 Flashlight",
"type": "item",
"consumable": false,
"states": 2,
"light": [
{
"bright": 30, "dim": 300, "angle": 20, "color": "#c2fffb", "alpha": 0.5, "attenuation": 0.8
}
]
},
"Linterna eléctrica": {
"name": "Linterna eléctrica",
"type": "item",
"consumable": false,
"states": 2,
"light": [
{
"bright": 30, "dim": 300, "angle": 20, "color": "#c2fffb", "alpha": 0.5, "attenuation": 0.8
}
]
},
"怪 Linterna eléctrica": {
"name": "怪 Linterna eléctrica",
"type": "item",
"consumable": false,
"states": 2,
"light": [
{
"bright": 30, "dim": 300, "angle": 20, "color": "#c2fffb", "alpha": 0.5, "attenuation": 0.8
}
]
}
}
}
}

View File

@@ -1,367 +0,0 @@
{
"CDE.ItemCreate": "Create Equipment",
"CDE.ItemEdit": "Edit Equipment",
"CDE.ItemDelete": "Delete Equipment",
"CDE.ItemNew": "New Equipment",
"CDE.TypeOfItem": "Type of Equipment",
"CDE.Weapon": "Weapon",
"CDE.Armor": "Armor",
"CDE.Sanhei": "Sanhei",
"CDE.Other": "Other",
"CDE.ResourceMin": "Min",
"CDE.ResourceValue": "Value",
"CDE.ResourceMax": "Max",
"CDE.DefineTemplate": "Define as Template",
"CDE.UnsetTemplate": "Unset Template",
"CDE.NoTemplate": "No Template",
"CDE.Quantity": "Quantity",
"CDE.Weight": "Weight",
"CDE.Damage": "Damage",
"CDE.Range": "Range",
"CDE.Masterized": "You have mastered this Technique",
"CDE.Create": "Create",
"CDE.New": "New",
"CDE.Name": "Name",
"CDE.PCName": "Character's Name",
"CDE.Concept": "Concept",
"CDE.CelestialGuardian": "Celestial Guardian",
"CDE.CelestialGuardian1": "White Tiger (Metal)",
"CDE.CelestialGuardian2": "Black Tortoise (Water)",
"CDE.CelestialGuardian3": "Yellow Kilin (Earth)",
"CDE.CelestialGuardian4": "Vermilion Bird (Fire)",
"CDE.CelestialGuardian5": "Azure Dragon (Wood)",
"CDE.Description": "Description",
"CDE.Description2": "Description",
"CDE.Attributes": "Attributes",
"CDE.Skills": "Skills",
"CDE.Magics": "Magics",
"CDE.Resources": "Resources",
"CDE.Treasures": "Three Treasures",
"CDE.NgHang": "Ng Hang",
"CDE.Items": "Equipment",
"CDE.KungFu": "Kung Fu",
"CDE.Spells": "Spells",
"CDE.ModernJapan": "Modern Japan",
"CDE.Die": "Click Here to Throw Dice For ",
"CDE.DieSP": "Click Here to Throw Dice For This Speciality: ",
"CDE.DieDM": "Click Here to Throw Dice For One Of These Fields",
"CDE.Debt": "Check If You Have Incurred a Debt For ",
"CDE.Specialities": "Specialities",
"CDE.Fields": "Fields",
"CDE.Art": "Art",
"CDE.Investigation": "Investigation",
"CDE.Erudition": "Erudition",
"CDE.Knavery": "Knavery",
"CDE.Wordliness": "Wordliness",
"CDE.Prowess": "Prowess",
"CDE.Sciences": "Sciences",
"CDE.Technologies": "Technologies",
"CDE.RangedCombat": "Ranged Combat",
"CDE.Supply": "Supply",
"CDE.Inquiry": "Inquiry",
"CDE.Influence": "Influence",
"CDE.Components2": "Components & Ingredients",
"CDE.Component": "Component #",
"CDE.ChanceThrow": "Selecting a Component at Random in a Hurry",
"CDE.TypesOfMagic": "Types of Magic",
"CDE.InternalCinnabar": "Internal Cinnabar",
"CDE.Alchemy": "Alchemy",
"CDE.MasteryOfTheWay": "Mastery of the Way",
"CDE.Exorcism": "Exorcism",
"CDE.Geomancy": "Geomancy",
"CDE.Metal": "㊎ Metal ",
"CDE.MetalQualities": " aggressive, passionate, combative, enthusiastic…",
"CDE.Water": "㊌ Water ",
"CDE.WaterQualities": " flexible, diligent, adaptable, disciplined…",
"CDE.Earth": "㊏ Earth ",
"CDE.EarthQualities": " stubborn, resilient, enduring, patient…",
"CDE.Fire": "㊋ Fire ",
"CDE.FireQualities": " warm, creative, empathetic, inspired…",
"CDE.Wood": "㊍ Wood ",
"CDE.WoodQualities": " intuitive, observant, open, instinctive…",
"CDE.PracticeSpecialty": "Check If You Practice This Speciality: ",
"CDE.Essence": "Essence",
"CDE.Mind": "Mind",
"CDE.Purification": "Purification",
"CDE.Manipulation": "Manipulation",
"CDE.Aura": "Aura",
"CDE.Acupuncture": "Acupuncture",
"CDE.Elixirs": "Elixirs",
"CDE.Poisons": "Poisons",
"CDE.Arsenal": "Arsenal",
"CDE.Potions": "Potions",
"CDE.Curse": "Curse",
"CDE.Transfiguration": "Transfiguration",
"CDE.Necromancy": "Necromancy",
"CDE.ClimateControl": "Climate Control",
"CDE.GoldenMagic": "Golden Magic",
"CDE.Invocation": "Invocation",
"CDE.Tracking": "Tracking",
"CDE.Protection": "Protection",
"CDE.Punishment": "Punishment",
"CDE.Domination": "Domination",
"CDE.Neutralization": "Neutralization",
"CDE.Divination": "Divination",
"CDE.EarthlyPrayer": "Earthly Prayer",
"CDE.HeavenlyPrayer": "Heavenly Prayer",
"CDE.Fungseoi": "Fungseoi",
"CDE.HEI": "HEI",
"CDE.Essence2": "(Essence)",
"CDE.YANG-YIN": "YANG ● YIN",
"CDE.Max-Present-Present-Max": "Max ● Present ● Present ● Max",
"CDE.SAN-ZING": "SAN ● ZING",
"CDE.MentalHealth-PhysicalHealth": "(Mental Health ● Physical Health)",
"CDE.Max-Present-Malus-Present-Max": "Max ● Present ● Malus ● Present ● Max",
"CDE.PTAO": "PTAO",
"CDE.Experience": "(Experience)",
"CDE.Total-Present": "Total ● Present",
"CDE.Present": "Present",
"CDE.Total": "Total",
"CDE.Max": "Max",
"CDE.Orientation": "Orientation",
"CDE.Aspect": "Aspect",
"CDE.Skill": "Skill",
"CDE.Speciality": "Speciality",
"CDE.Style": "Style",
"CDE.Field": "Field",
"CDE.Technique": "Technique",
"CDE.Activation": "Activation",
"CDE.KFCreate": "Create Martial Art",
"CDE.KFEdit": "Edit Martial Art",
"CDE.KFDelete": "Delete Martial Art",
"CDE.KFNew": "New Martial Art",
"CDE.SpecialityName": "Speciality Name",
"CDE.AssociatedElement": "Associated Element",
"CDE.HEI2": "Hei",
"CDE.RealizationTimeRitual": "Realization Time (Ritual)",
"CDE.RealizationTimeAccelerated": "Realization Time (Accelerated)",
"CDE.Data": "Data",
"CDE.Flashback": "Flashback",
"CDE.Components": "Components",
"CDE.Effects": "Effects",
"CDE.Examples": "Examples",
"CDE.SpellCreate": "Create Spell",
"CDE.SpellEdit": "Edit Spell",
"CDE.SpellDelete": "Delete Spell",
"CDE.SpellNew": "New Spell",
"CDE.SkillPromptName": "Skill Roll",
"CDE.Modifiers": "Modifiers",
"CDE.BonusMalus": "Bonus/Malus D",
"CDE.WoundMalus": "Wound Malus",
"CDE.HeiSpend": "HEI Spend",
"CDE.SpellBonus": "Spell Bonus",
"CDE.SpellPower": "Spell Power",
"CDE.BonusAuspiciousDice": "Bonus/Auspicious-Dice",
"CDE.MagicPromptName": "Magic Roll",
"CDE.OneMagicRoll": "1. Magic Roll",
"CDE.DoNotModify": "Do not Modify this Aspect",
"CDE.AspectSkill": "Aspect/Skill",
"CDE.TwoPowerOfSpell": "2. Power of Spell",
"CDE.AspectSpeciality": "Aspect/Speciality",
"CDE.RollDifficulty": "Roll Difficulty",
"CDE.TypeOfThrow": "Type of Throwing: Visible by…",
"CDE.Everybody": "Everybody",
"CDE.JustDMAndMe": "Just the DM and Me",
"CDE.DMOnly": "DM Only",
"CDE.MeOnly": "Me Only",
"CDE.AuspiciousDice": "Auspicious-Dice",
"CDE.AuspiciousDie": "Auspicious-Die",
"CDE.Validate": "Validate",
"CDE.Cancel": "Cancel",
"CDE.NPCName": "NPC's Name",
"CDE.TypeOfCreature": "Type of Creature",
"CDE.Threat": "Capacity of Threat",
"CDE.Profane": "Profane",
"CDE.Apprentice": "Apprentice",
"CDE.Initiate": "Initiate",
"CDE.Accomplished": "Accomplished",
"CDE.Renowned": "Renowned",
"CDE.Nuisance": "Level of Nuisance",
"CDE.Figurant": "Figurant",
"CDE.Minion": "Minion",
"CDE.Adversary": "Adversary",
"CDE.Ally": "Ally",
"CDE.Boss": "Boss",
"CDE.Divinity": "Divinity",
"CDE.Aptitudes": "Aptitudes",
"CDE.SuperNatural": "Supernatural Abilities",
"CDE.Physical": "● Physical",
"CDE.Martial": "● Martial",
"CDE.Mental": "● Mental",
"CDE.Social": "● Social",
"CDE.Spiritual": "● Spiritual",
"CDE.Vitality": "Vitality",
"CDE.Hei": "Hei",
"CDE.Notes": "Notes",
"CDE.SupernaturalCreate": "Create Supernatural Ability",
"CDE.SupernaturalEdit": "Edit Supernatural Ability",
"CDE.SupernaturalDelete": "Delete Supernatural Ability",
"CDE.SupernaturalNew": "New Supernatural Ability",
"CDE.LoksyuName": "Loksyu Name",
"CDE.Loksyu": "Loksyu",
"CDE.MetalYang": "㊎ Yang Metal (3)",
"CDE.MetalYin": "㊎ Yin Metal (8)",
"CDE.WaterYang": "㊌ Yang Water (1)",
"CDE.WaterYin": "㊌ Yin Water (6)",
"CDE.EarthYang": "㊏Yang Earth (5)",
"CDE.EarthYin": "㊏Yin Earth (0)",
"CDE.FireYang": "㊋ Yang Fire (7)",
"CDE.FireYin": "㊋ Yin Fire (2)",
"CDE.WoodYang": "㊍ Yang Wood (9)",
"CDE.WoodYin": "㊍ Yin Wood (4)",
"CDE.Yin": "● Yin",
"CDE.Yang": "○ Yang",
"CDE.Successes": "Successes",
"CDE.Beneficial": "Auspicious-Dice",
"CDE.Noxious": "Noxious-Dice",
"CDE.TinJi": "Tin Ji:",
"CDE.Loksyu2": "Loksyu:",
"CDE.Results": "Results:",
"CDE.SpecialAspect": "Speciality Aspect",
"CDE.Randomize": "Randomize",
"CDE.RandomizeSentence": "# of the Component Selected at Random in a Hurry",
"CDE.Reference": "Reference",
"CDE.Error0": "Unable to proceed: total dice number to be thrown is less than 1.",
"CDE.Error1": "Unable to proceed: you don't possess this Skill.",
"CDE.Error2": "Unable to proceed: you don't possess any Speciality in this Skill.",
"CDE.Error3": "Unable to proceed: you don't possess this Resource.",
"CDE.Error4": "Unable to proceed: you don't possess any Field in this Resource.",
"CDE.Error5": "Unable to proceed: you don't possess this Magical Skill.",
"CDE.Error6": "Unable to proceed: you don't possess this Speciality in this Magical Skill.",
"CDE.Error10": "Unable to proceed: you've got 0 or less in this Aspect.",
"CDE.Error12": "Unable to proceed: you can't possess any Speciality in this Skill which score is less than 1.",
"CDE.Error14": "Unable to proceed: you can't possess any Field in this Resource which score is less than 1.",
"CDE.Error16": "Unable to proceed: you can't possess any Speciality in this Magical Skill which score is less than 1.",
"CDE.Preferences": "Preferences for this actor",
"CDE.TypeOfThrowCheck": "Always ask before making a throw",
"CDE.TypeOfThrow4ThisTime": "Type of Throwing for this time: Visible by…",
"CDE.TypeOfThrowTitle": "Modify Type of Throw?",
"CDE.CancelChanges": "Cancel Changes",
"CDE.NextTimeGoToTheSettings": "All these are parametrable in the Settings",
"CDE.OneDie": "Die",
"CDE.ManyDice": "Dice",
"CDE.HasCastASpell": "has cast a spell ",
"CDE.MsgMagic1": "The number of Hei points spent on the ",
"CDE.MsgMagic2": "speciality spell is ",
"CDE.MsgMagic3": ". The power to be invoked is ",
"CDE.MsgMagic4": ", if the spell is cast successfully.",
"CDE.TinJiName": "Tin Ji Name",
"CDE.TinJi2": "Tin Ji",
"CDE.UpperCaseSuccesses": "SUCCESSES",
"CDE.UpperCaseAuspiciousDice": "AUSPICIOUS-DICE",
"CDE.UpperCaseNoxiousDice": "NOXIOUS-DICE",
"CDE.UpperCaseLoksyu": "LOKSYU",
"CDE.UpperCaseTinJi": "TIN JI",
"CDE.UpperCaseMetal": "METAL",
"CDE.UpperCaseWater": "WATER",
"CDE.UpperCaseEarth": "EARTH",
"CDE.UpperCaseFire": "FIRE",
"CDE.UpperCaseWood": "WOOD",
"CDE.Visible": "Visible Specialities",
"CDE.MartialArts": "Martial Arts",
"CDE.Initiative": "Initiative",
"CDE.TurnOrder": "Determining your Initiative",
"CDE.InitiativeSpeciality": "First action (Skill) you plan to perform",
"CDE.InitiativeNPCSpeciality": "First action (Aptitude) you plan to perform",
"CDE.DeterminateInitiative": "Determine Initiative: Prowess + first action",
"CDE.DeterminateNPCInitiative": "Determine Initiative: Physical Aptitude + first action",
"CDE.PlusInitiative": "More Initiative",
"CDE.MinusInitiative": "Less Initiative",
"TYPES": {
"Actor": {
"character": "Fat si",
"npc": "Creature",
"tinji": "Tin Ji",
"loksyu": "Loksyu"
},
"Item": {
"item": "Equipment",
"kungfu": "Martial Art",
"spell": "Spell",
"supernatural": "Supernatural Ability"
}
},
"CDE.WeaponCreate": "Create Weapon",
"CDE.ArmorCreate": "Create Armor",
"CDE.SanheiCreate": "Create San Hei",
"CDE.IngredientCreate": "Create Ingredient",
"CDE.WeaponNew": "New Weapon",
"CDE.ArmorNew": "New Armor",
"CDE.SanheiNew": "New San Hei",
"CDE.IngredientNew": "New Ingredient",
"CDE.ArmorType": "Armor",
"CDE.SanheiType": "San Hei",
"CDE.IngredientType": "Ingredient",
"CDE.ItemType": "Item",
"CDE.Weapons": "Weapons",
"CDE.Armors": "Armors",
"CDE.Sanheis": "San Hei",
"CDE.CreatureMortel": "Mortal",
"CDE.CreatureDemon": "Demon",
"CDE.CreatureEsprit": "Spirit",
"CDE.CreatureEspritAnimal": "Animal Spirit",
"CDE.CreatureFantome": "Ghost",
"CDE.CreatureJiugwaai": "Jiugwaai",
"CDE.CreatureDieu": "God / Divinity"
}

View File

@@ -1,340 +0,0 @@
{
"CDE.ItemCreate": "Crear un Equipo",
"CDE.ItemEdit": "Editar el Equipo",
"CDE.ItemDelete": "Borrar el Equipo",
"CDE.ItemNew": "Nuevo Equipo",
"CDE.TypeOfItem": "Tipo de Equipo",
"CDE.Weapon": "Arma",
"CDE.Armor": "Armor",
"CDE.Sanhei": "Sanhei",
"CDE.Other": "Otro",
"CDE.ResourceMin": "Min",
"CDE.ResourceValue": "Valor",
"CDE.ResourceMax": "Máx",
"CDE.DefineTemplate": " Establecer como Plantilla",
"CDE.UnsetTemplate": "Deactivar la Plantilla",
"CDE.NoTemplate": "Sin Plantilla",
"CDE.Quantity": "Cantidad",
"CDE.Weight": "Peso",
"CDE.Damage": "Daños",
"CDE.Range": "Alcance",
"CDE.Masterized": "Ha dominado esta técnica",
"CDE.Create": "Crear",
"CDE.New": "Nuevo",
"CDE.Name": "Nombre",
"CDE.PCName": "Nombre del Personaje",
"CDE.Concept": "Concepto",
"CDE.CelestialGuardian": "Guardián Celestial",
"CDE.CelestialGuardian1": "Tigre Blanco (Metal)",
"CDE.CelestialGuardian2": "Tortuga Negra (Agua)",
"CDE.CelestialGuardian3": "Kirin Amarillo (Tierra)",
"CDE.CelestialGuardian4": "Pájaro Escarlata (Fuego)",
"CDE.CelestialGuardian5": "Dragón Azul (Madera)",
"CDE.Description": "Descripción",
"CDE.Description2" : "Descripción",
"CDE.Attributes": "Atributos",
"CDE.Skills": "Habilidades",
"CDE.Magics": "Magias",
"CDE.Resources": "Recursos",
"CDE.Treasures": "Tres Tesoros",
"CDE.NgHang": "Ng Hang",
"CDE.Items": "Equipo",
"CDE.KungFu": "Kung Fu",
"CDE.Spells": "Hechizos",
"CDE.ModernJapan": "Japón contemporáneo",
"CDE.Die": "Haga clic aquí para tirar los dados para ",
"CDE.DieSP": "Haz clic aquí para tirar los dados para la especialidad ",
"CDE.DieDM": "Haga clic aquí para tirar los dados para una de estas áreas",
"CDE.Debt": "Marque esta casilla si ha contraído una deuda en ",
"CDE.Specialities": "Especialidades",
"CDE.Fields": "Áreas",
"CDE.Art": "Arte",
"CDE.Investigation": "Encuesta",
"CDE.Erudition": "Erudición",
"CDE.Knavery": "Engaño",
"CDE.Wordliness": "Mundanerías",
"CDE.Prowess": "Hazaña",
"CDE.Sciences": "Ciensas",
"CDE.Technologies": "Tecnologías",
"CDE.RangedCombat": "Combate a Distancia",
"CDE.Supply": "Suministro",
"CDE.Inquiry": "Información",
"CDE.Influence": "Influencia",
"CDE.Components2": "Componentes e Ingredientes",
"CDE.Component": "Componente #",
"CDE.ChanceThrow": "Eligir un ingrediente al azar y rápidamente",
"CDE.TypesOfMagic": "Tipos de Magia",
"CDE.InternalCinnabar": "Cinabrio Interno",
"CDE.Alchemy": "Alquimia",
"CDE.MasteryOfTheWay": "Dominio del Camino",
"CDE.Exorcism": "Exorcismo",
"CDE.Geomancy": "Geomancia",
"CDE.Metal": "㊎ Metal ",
"CDE.MetalQualities": " agresivo, apasionado, combativo, entusiasta…",
"CDE.Water": "㊌ Agua ",
"CDE.WaterQualities": " flexible, diligente, adaptable, disciplinado…",
"CDE.Earth": "㊏ Tierra ",
"CDE.EarthQualities": " terco, resistente, perdurable, paciente…",
"CDE.Fire": "㊋ Fuego ",
"CDE.FireQualities": " cálido, creativo, empático, inspirado…",
"CDE.Wood": "㊍ Madera ",
"CDE.WoodQualities": " intuitivo, observador, abierto, instintivo…",
"CDE.PracticeSpecialty": "Marque esta casilla si practica la especialidad ",
"CDE.Essence": "Esencia",
"CDE.Mind": "Mente",
"CDE.Purification": "Purificación",
"CDE.Manipulation": "Manejo",
"CDE.Aura": "Aura",
"CDE.Acupuncture": "Acupunctura",
"CDE.Elixirs": "Elixires",
"CDE.Poisons": "Venenos",
"CDE.Arsenal": "Arsenal",
"CDE.Potions": "Pociones",
"CDE.Curse": "Maldición",
"CDE.Transfiguration": "Transfiguración",
"CDE.Necromancy": "Nigromancia",
"CDE.ClimateControl": "Control Climatico",
"CDE.GoldenMagic": "Magia de Oro",
"CDE.Invocation": "Invocación",
"CDE.Tracking": "Seguimiento",
"CDE.Protection": "Protección",
"CDE.Punishment": "Castigo",
"CDE.Domination": "Dominación",
"CDE.Neutralization": "Neutralización",
"CDE.Divination": "Adivinación",
"CDE.EarthlyPrayer": "Oración Terrenal",
"CDE.HeavenlyPrayer": "Oración Celestial",
"CDE.Fungseoi": "Fungseoi",
"CDE.HEI": "HEI",
"CDE.Essence2": "(Esencia)",
"CDE.YANG-YIN": "YANG ● YIN",
"CDE.Max-Present-Present-Max": "Máx ● Actual ● Actual ● Máx",
"CDE.SAN-ZING": "SAN ● ZING",
"CDE.MentalHealth-PhysicalHealth": "(Salud Mental ● Salud Física)",
"CDE.Max-Present-Malus-Present-Max": "Máx ● Actual ● Malos ● Actual ● Máx",
"CDE.PTAO": "PTAO",
"CDE.Experience": "(Experiencia)",
"CDE.Total-Present": "Total ● Actual",
"CDE.Present": "Actual",
"CDE.Total": "Total",
"CDE.Max": "Máx",
"CDE.Orientation": "Orientación",
"CDE.Aspect": "Aspecto",
"CDE.Skill": "Habilidad",
"CDE.Speciality": "Especialidad",
"CDE.Field": "Área",
"CDE.Style": "Estilo",
"CDE.Technique": "Técnica",
"CDE.Activation": "Activación",
"CDE.KFCreate": "Crear un Arte Marcial",
"CDE.KFEdit": "Editar el Arte Marcial",
"CDE.KFDelete": "Borrar el Arte Marcial",
"CDE.KFNew": "Nuevo Arte Marcial",
"CDE.SpecialityName": "Nombre Especialidad",
"CDE.AssociatedElement": "Elemento Asociado",
"CDE.HEI2": "Hei",
"CDE.RealizationTimeRitual": "Tiempo de Realización (Ritual)",
"CDE.RealizationTimeAccelerated": "Tiempo de Realización (Acelerado)",
"CDE.Flashback": "Flashback",
"CDE.Data": "Data",
"CDE.Components": "Componentes",
"CDE.Effects": "Efectos",
"CDE.Examples": "Ejemplos",
"CDE.SpellCreate": "Crear un Hechizo",
"CDE.SpellEdit": "Editar el Hechizo",
"CDE.SpellDelete": "Borrar el Hechizo",
"CDE.SpellNew": "Nuevo Hechizo",
"CDE.SkillPromptName": "",
"CDE.BonusMalus": "D Bonos/Malos",
"CDE.BonusAuspiciousDice": "Bonos/Dados-buenos",
"CDE.MagicPromptName": "",
"CDE.OneMagicRoll": "1. Tirada Mágica",
"CDE.DoNotModify": "No cambie el Aspecto",
"CDE.AspectSkill": "Aspecto/Habilidad",
"CDE.TwoPowerOfSpell": "2. Poder de Hechizo",
"CDE.AspectSpeciality": "Aspecto/Especialidad",
"CDE.RollDifficulty": "Dificultad de la Tirada",
"CDE.TypeOfThrow": "Tipo de tirada: visible por…",
"CDE.Everybody": "Todo el mundo",
"CDE.JustDMAndMe": "Solo el DM y yo",
"CDE.DMOnly": "El DM solamente",
"CDE.MeOnly": "Yo solamente",
"CDE.AuspiciousDice": "Dados-buenos",
"CDE.AuspiciousDie": "Dada-bueno",
"CDE.Validate": "Validar",
"CDE.Cancel": "Anular",
"CDE.NPCName": "Nombre del NPC",
"CDE.TypeOfCreature": "Tipo de Criatura",
"CDE.Threat": "Nivel de Amenaza",
"CDE.Profane": "Profano",
"CDE.Apprentice": "Aprendiz",
"CDE.Initiate": "Iniciado",
"CDE.Accomplished": "Consumado",
"CDE.Renowned": "Renombrado",
"CDE.Nuisance": "Capacitad de Molesta",
"CDE.Figurant": "Extra",
"CDE.Minion": "Esbirro",
"CDE.Adversary": "Adversario",
"CDE.Ally": "Aliado",
"CDE.Boss": "Jefe",
"CDE.Divinity": "Deidad",
"CDE.Aptitudes": "Aptitudes",
"CDE.SuperNatural": "Habilidades Sobrenaturales",
"CDE.Physical": "● Física",
"CDE.Martial": "● Marcial",
"CDE.Mental": "● Mental",
"CDE.Social": "● Social",
"CDE.Spiritual": "● Espiritual",
"CDE.Vitality": "Vitalidad",
"CDE.Hei": "Hei",
"CDE.Notes": "Notas",
"CDE.SupernaturalCreate": "Crear una Habilidad Sobrenatural",
"CDE.SupernaturalEdit": "Editar la Habilidad Sobrenatural",
"CDE.SupernaturalDelete": "Borrar la Habilidad Sobrenatural",
"CDE.SupernaturalNew": "Nueva Habilidad Sobrenatural",
"CDE.LoksyuName": "Nombre del Loksyu",
"CDE.Loksyu": "Loksyu",
"CDE.MetalYang": "㊎ Metal Yang (3)",
"CDE.MetalYin": "㊎ Metal Yin (8)",
"CDE.WaterYang": "㊌ Agua Yang (1)",
"CDE.WaterYin": "㊌ Agua Yin (6)",
"CDE.EarthYang": "㊏ Tierra Yang (5)",
"CDE.EarthYin": "㊏ Tierra Yin (0)",
"CDE.FireYang": "㊋ Fuego Yang (7)",
"CDE.FireYin": "㊋ Fuego Yin (2)",
"CDE.WoodYang": "㊍ Madera Yang (9)",
"CDE.WoodYin": "㊍ Madera Yin (4)",
"CDE.Yin": "● Yin",
"CDE.Yang": "○ Yang",
"CDE.Successes": "Éxitos",
"CDE.Beneficial": "Dados-Buenos",
"CDE.Noxious": "Dados-Dañinos",
"CDE.TinJi": "Tin Ji:",
"CDE.Loksyu2": "Loksyu:",
"CDE.Results": "Resultados:",
"CDE.SpecialAspect": "Aspecto de Especialidad",
"CDE.Randomize": "Dibujo aleatorio",
"CDE.RandomizeSentence": "# del ingrediente eligido al azar y rápidamente",
"CDE.Reference": "Referencia",
"CDE.Error0": "No se puede continuar: el número total de dados que se van a lanzar es inferior a 1.",
"CDE.Error1": "No se puede continuar: no posee esta habilidad.",
"CDE.Error2": "No se puede continuar: no posee ninguna especialidad en esta habilidad.",
"CDE.Error3": "No se puede continuar: no posee este recurso.",
"CDE.Error4": "No se puede continuar: no posee ningún área en este recurso.",
"CDE.Error5": "No se puede continuar: no posee esta habilidad mágica.",
"CDE.Error6": "No se puede continuar: no posee esta especialidad en esta habilidad mágica.",
"CDE.Error10": "No se puede continuar: tienes 0 o menos en este aspecto.",
"CDE.Error12": "No se puede continuar: no puede poseer ninguna especialidad en esta habilidad cuya puntuación sea inferior a 1.",
"CDE.Error14": "No se puede continuar: no puede poseer ningún área en este recurso cuya puntuación sea inferior a 1.",
"CDE.Error16": "No se puede continuar: no puede poseer ninguna especialidad en esta habilidad mágica cuya puntuación sea inferior a 1.",
"CDE.Preferences": "Preferencias para este actor",
"CDE.TypeOfThrowCheck": "Preguntar siempre antes de realizar una tirada",
"CDE.TypeOfThrow4ThisTime": "Tipo de tirada por esta vez: visible por…",
"CDE.TypeOfThrowTitle": "¿Modificar el tipo de tirada?",
"CDE.CancelChanges": "Cancelar cambios",
"CDE.NextTimeGoToTheSettings": "Todos estos son configurables en la configuración.",
"CDE.OneDie": "dado",
"CDE.ManyDice": "dados",
"CDE.HasCastASpell": "ha lanzado un hechizo ",
"CDE.MsgMagic1": "El número de puntos Hei gastados en el hechizo especial ",
"CDE.MsgMagic2": "es ",
"CDE.MsgMagic3": ". El poder para invocar es ",
"CDE.MsgMagic4": ", si el hechizo se lanza con éxito.",
"CDE.TinJiName": "Nombre de la Tin Ji",
"CDE.TinJi2": "Tin Ji",
"CDE.UpperCaseSuccesses": "ÉXITOS",
"CDE.UpperCaseAuspiciousDice": "DADOS-BUENOS",
"CDE.UpperCaseNoxiousDice": "DADOS-DAÑINOS",
"CDE.UpperCaseLoksyu": "LOKSYU",
"CDE.UpperCaseTinJi": "TIN JI",
"CDE.UpperCaseMetal": "METAL",
"CDE.UpperCaseWater": "AGUA",
"CDE.UpperCaseEarth": "TIERRA",
"CDE.UpperCaseFire": "FUEGO",
"CDE.UpperCaseWood": "MADERA",
"CDE.Visible": "Especialidades visibles",
"CDE.MartialArts": "Artes Marciales",
"CDE.Initiative": "Iniciativa",
"CDE.TurnOrder": "Determinando su iniciativa",
"CDE.InitiativeSpeciality": "Primera acción (Habilidad) que planea realizar",
"CDE.InitiativeNPCSpeciality": "Primera acción (Aptitud) que planea realizar",
"CDE.DeterminateInitiative": "Determinar la iniciativa: Hazaña + primera acción",
"CDE.DeterminateNPCInitiative": "Determinar la iniciativa: Aptitud física + primera acción",
"CDE.PlusInitiative": "Más iniciativa",
"CDE.MinusInitiative": "Menos iniciativa",
"TYPES": {
"Actor": {
"character": "Fat si",
"npc": "Criatura",
"tinji": "Tin Ji",
"loksyu": "Loksyu"
},
"Item": {
"item": "Equipo",
"kungfu": "Arte marcial",
"spell": "Hechizo",
"supernatural": "Habilidad Sobrenatural"
}
}
}

View File

@@ -151,6 +151,7 @@
"CDE.Loksyu": "Loksyu",
"CDE.Loksyu2": "Loksyu :",
"CDE.LoksyuName": "Nom du Loksyu",
"CDE.LoksyuNotFound": "Aucun acteur Loksyu trouvé. Le Maître du Jeu doit en créer un.",
"CDE.MagicPromptName": "Jet de Magie",
"CDE.Magics": "Magies",
"CDE.Manipulation": "Manipulation",
@@ -187,6 +188,9 @@
"CDE.NextTimeGoToTheSettings": "Tout ceci est paramétrable dans les Préférences",
"CDE.NgHang": "Ng Hang",
"CDE.NoTemplate": "Aucun Modèle",
"CDE.NoSupernaturals": "Aucun pouvoir surnaturel",
"CDE.NoSpells": "Aucun sortilège",
"CDE.NoKungFu": "Aucun art martial",
"CDE.Notes": "Notes",
"CDE.Noxious": "Dés-Néfastes",
"CDE.Nuisance": "Capacité de Nuisance",
@@ -278,6 +282,20 @@
"CDE.TinJi": "Tin Ji :",
"CDE.TinJi2": "Tin Ji",
"CDE.TinJiName": "Nom de la Tin Ji",
"CDE.TinjiNotFound": "Aucun acteur Tin Ji trouvé. Le Maître du Jeu doit en créer un.",
"CDE.TinjiEmpty": "Il n'y a plus de dés de Tin Ji disponibles.",
"CDE.TinjiSpent": "{name} dépense 1 dé de Tin Ji.",
"CDE.PostRollActions": "Puiser dans le Loksyu / Dépenser Tin Ji",
"CDE.LoksyuDrawsA": "pioche",
"CDE.LoksyuFromAspect": "du",
"CDE.LoksyuRemaining": "dé(s) restant(s)",
"CDE.LoksyuEmpty": "Le Loksyu ne contient plus de dés pour cet aspect.",
"CDE.TinjiRemaining": "restant(s)",
"CDE.SpendTinji": "Dépenser un dé",
"CDE.Reset": "Réinitialiser",
"CDE.ResetAll": "Tout réinitialiser",
"CDE.Decrement": "Diminuer",
"CDE.Increment": "Augmenter",
"CDE.Total": "Total",
"CDE.Total-Present": "Total ● Actuel",
"CDE.Tracking": "Traque",

View File

@@ -1,8 +1,8 @@
{
"name": "fvtt-chroniques-de-l-etrange",
"version": "3.0.0",
"description": "Refonte du système Chroniques de l'Étrange avec DataModels et Application V2.",
"main": "dist/system.js",
"version": "13.0.0",
"description": "Système Chroniques de l'Étrange avec DataModels et Application V2.",
"main": "src/system.js",
"scripts": {
"build:css": "lessc css/cde-theme.less css/cde-theme.css",
"build:js": "esbuild src/system.js --bundle --format=esm --target=es2022 --sourcemap --outfile=dist/system.js",
@@ -21,6 +21,7 @@
"type": "module",
"devDependencies": {
"esbuild": "^0.27.4",
"jscpd": "^4.0.8",
"less": "^4.2.0"
},
"dependencies": {

Binary file not shown.

View File

@@ -1 +0,0 @@
MANIFEST-000062

View File

@@ -1,7 +0,0 @@
2026/03/27-23:40:07.003627 7f90f7fff6c0 Recovering log #60
2026/03/27-23:40:07.014585 7f90f7fff6c0 Delete type=3 #58
2026/03/27-23:40:07.014647 7f90f7fff6c0 Delete type=0 #60
2026/03/28-01:06:04.060694 7f90f5ffb6c0 Level-0 table #65: started
2026/03/28-01:06:04.060719 7f90f5ffb6c0 Level-0 table #65: 0 bytes OK
2026/03/28-01:06:04.067602 7f90f5ffb6c0 Delete type=0 #63
2026/03/28-01:06:04.080111 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!ZWBHiWW5QlUeseAX' @ 72057594037927935 : 1 .. '!journal.pages!ZWBHiWW5QlUeseAX.jtQXIqLfyet8Nlte' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +0,0 @@
2026/03/27-21:21:30.280454 7f90f67fc6c0 Recovering log #56
2026/03/27-21:21:30.290347 7f90f67fc6c0 Delete type=3 #54
2026/03/27-21:21:30.290399 7f90f67fc6c0 Delete type=0 #56
2026/03/27-21:21:32.095874 7f90f5ffb6c0 Level-0 table #61: started
2026/03/27-21:21:32.095896 7f90f5ffb6c0 Level-0 table #61: 0 bytes OK
2026/03/27-21:21:32.102427 7f90f5ffb6c0 Delete type=0 #59
2026/03/27-21:21:32.116179 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!ZWBHiWW5QlUeseAX' @ 72057594037927935 : 1 .. '!journal.pages!ZWBHiWW5QlUeseAX.jtQXIqLfyet8Nlte' @ 0 : 0; will stop at (end)

View File

@@ -1 +0,0 @@
MANIFEST-000178

View File

@@ -1,8 +0,0 @@
2026/03/27-23:40:06.991314 7f90f77fe6c0 Recovering log #176
2026/03/27-23:40:07.001875 7f90f77fe6c0 Delete type=3 #174
2026/03/27-23:40:07.001926 7f90f77fe6c0 Delete type=0 #176
2026/03/28-01:06:04.048472 7f90f5ffb6c0 Level-0 table #181: started
2026/03/28-01:06:04.048493 7f90f5ffb6c0 Level-0 table #181: 0 bytes OK
2026/03/28-01:06:04.054367 7f90f5ffb6c0 Delete type=0 #179
2026/03/28-01:06:04.054479 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!TniC3ok9W0hDYxJS' @ 72057594037927935 : 1 .. '!journal.pages!yZsG9QaBHT3cUfNd.AHcfBcO96nUCELxv' @ 0 : 0; will stop at (end)
2026/03/28-01:06:04.054500 7f90f5ffb6c0 Manual compaction at level-1 from '!journal!TniC3ok9W0hDYxJS' @ 72057594037927935 : 1 .. '!journal.pages!yZsG9QaBHT3cUfNd.AHcfBcO96nUCELxv' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +0,0 @@
2026/03/27-21:21:30.268953 7f90f6ffd6c0 Recovering log #172
2026/03/27-21:21:30.278662 7f90f6ffd6c0 Delete type=3 #170
2026/03/27-21:21:30.278730 7f90f6ffd6c0 Delete type=0 #172
2026/03/27-21:21:32.067762 7f90f5ffb6c0 Level-0 table #177: started
2026/03/27-21:21:32.067784 7f90f5ffb6c0 Level-0 table #177: 0 bytes OK
2026/03/27-21:21:32.074619 7f90f5ffb6c0 Delete type=0 #175
2026/03/27-21:21:32.089332 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!TniC3ok9W0hDYxJS' @ 72057594037927935 : 1 .. '!journal.pages!yZsG9QaBHT3cUfNd.AHcfBcO96nUCELxv' @ 0 : 0; will stop at (end)
2026/03/27-21:21:32.089368 7f90f5ffb6c0 Manual compaction at level-1 from '!journal!TniC3ok9W0hDYxJS' @ 72057594037927935 : 1 .. '!journal.pages!yZsG9QaBHT3cUfNd.AHcfBcO96nUCELxv' @ 0 : 0; will stop at (end)

View File

@@ -1 +0,0 @@
MANIFEST-000750

View File

@@ -1,8 +0,0 @@
2026/03/27-23:40:06.977536 7f90f67fc6c0 Recovering log #748
2026/03/27-23:40:06.987624 7f90f67fc6c0 Delete type=3 #746
2026/03/27-23:40:06.987699 7f90f67fc6c0 Delete type=0 #748
2026/03/28-01:06:04.041962 7f90f5ffb6c0 Level-0 table #753: started
2026/03/28-01:06:04.041998 7f90f5ffb6c0 Level-0 table #753: 0 bytes OK
2026/03/28-01:06:04.048371 7f90f5ffb6c0 Delete type=0 #751
2026/03/28-01:06:04.054466 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!f6UhPlIUh2O0F36q' @ 72057594037927935 : 1 .. '!journal.pages!f6UhPlIUh2O0F36q.keqszrb6FAI7CVZx' @ 0 : 0; will stop at (end)
2026/03/28-01:06:04.054494 7f90f5ffb6c0 Manual compaction at level-1 from '!journal!f6UhPlIUh2O0F36q' @ 72057594037927935 : 1 .. '!journal.pages!f6UhPlIUh2O0F36q.keqszrb6FAI7CVZx' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +0,0 @@
2026/03/27-21:21:30.255554 7f90f77fe6c0 Recovering log #744
2026/03/27-21:21:30.266879 7f90f77fe6c0 Delete type=3 #742
2026/03/27-21:21:30.266948 7f90f77fe6c0 Delete type=0 #744
2026/03/27-21:21:32.081193 7f90f5ffb6c0 Level-0 table #749: started
2026/03/27-21:21:32.081218 7f90f5ffb6c0 Level-0 table #749: 0 bytes OK
2026/03/27-21:21:32.089124 7f90f5ffb6c0 Delete type=0 #747
2026/03/27-21:21:32.089363 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!f6UhPlIUh2O0F36q' @ 72057594037927935 : 1 .. '!journal.pages!f6UhPlIUh2O0F36q.keqszrb6FAI7CVZx' @ 0 : 0; will stop at (end)
2026/03/27-21:21:32.089373 7f90f5ffb6c0 Manual compaction at level-1 from '!journal!f6UhPlIUh2O0F36q' @ 72057594037927935 : 1 .. '!journal.pages!f6UhPlIUh2O0F36q.keqszrb6FAI7CVZx' @ 0 : 0; will stop at (end)

View File

@@ -1 +0,0 @@
MANIFEST-004371

View File

@@ -1,7 +0,0 @@
2026/03/27-23:40:06.951174 7f90f77fe6c0 Recovering log #4369
2026/03/27-23:40:06.961084 7f90f77fe6c0 Delete type=3 #4367
2026/03/27-23:40:06.961153 7f90f77fe6c0 Delete type=0 #4369
2026/03/28-01:06:04.029376 7f90f5ffb6c0 Level-0 table #4374: started
2026/03/28-01:06:04.029452 7f90f5ffb6c0 Level-0 table #4374: 0 bytes OK
2026/03/28-01:06:04.035591 7f90f5ffb6c0 Delete type=0 #4372
2026/03/28-01:06:04.054447 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!0lxwWrzKsdTBQhH0' @ 72057594037927935 : 1 .. '!journal.pages!wgSyae4GTJDkmBOm.6Ql0lgquUCTrMyTZ' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +0,0 @@
2026/03/27-21:21:30.230925 7f90f67fc6c0 Recovering log #4365
2026/03/27-21:21:30.240633 7f90f67fc6c0 Delete type=3 #4363
2026/03/27-21:21:30.240686 7f90f67fc6c0 Delete type=0 #4365
2026/03/27-21:21:32.060793 7f90f5ffb6c0 Level-0 table #4370: started
2026/03/27-21:21:32.060834 7f90f5ffb6c0 Level-0 table #4370: 0 bytes OK
2026/03/27-21:21:32.067619 7f90f5ffb6c0 Delete type=0 #4368
2026/03/27-21:21:32.089309 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!0lxwWrzKsdTBQhH0' @ 72057594037927935 : 1 .. '!journal.pages!wgSyae4GTJDkmBOm.6Ql0lgquUCTrMyTZ' @ 0 : 0; will stop at (end)

View File

@@ -1 +0,0 @@
MANIFEST-000091

View File

@@ -1,8 +0,0 @@
2026/03/27-23:40:07.041219 7f90f6ffd6c0 Recovering log #89
2026/03/27-23:40:07.052133 7f90f6ffd6c0 Delete type=3 #87
2026/03/27-23:40:07.052198 7f90f6ffd6c0 Delete type=0 #89
2026/03/28-01:06:04.074099 7f90f5ffb6c0 Level-0 table #94: started
2026/03/28-01:06:04.074129 7f90f5ffb6c0 Level-0 table #94: 0 bytes OK
2026/03/28-01:06:04.080023 7f90f5ffb6c0 Delete type=0 #92
2026/03/28-01:06:04.080128 7f90f5ffb6c0 Manual compaction at level-0 from '!tables!J9VdvrwkbyKxMAT7' @ 72057594037927935 : 1 .. '!tables.results!jGKjfCyk4ROSy9fU.zRzADzATtijaBdNX' @ 0 : 0; will stop at (end)
2026/03/28-01:06:04.080153 7f90f5ffb6c0 Manual compaction at level-1 from '!tables!J9VdvrwkbyKxMAT7' @ 72057594037927935 : 1 .. '!tables.results!jGKjfCyk4ROSy9fU.zRzADzATtijaBdNX' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +0,0 @@
2026/03/27-21:21:30.316292 7f90f77fe6c0 Recovering log #85
2026/03/27-21:21:30.326791 7f90f77fe6c0 Delete type=3 #83
2026/03/27-21:21:30.326838 7f90f77fe6c0 Delete type=0 #85
2026/03/27-21:21:32.102543 7f90f5ffb6c0 Level-0 table #90: started
2026/03/27-21:21:32.102564 7f90f5ffb6c0 Level-0 table #90: 0 bytes OK
2026/03/27-21:21:32.109807 7f90f5ffb6c0 Delete type=0 #88
2026/03/27-21:21:32.116185 7f90f5ffb6c0 Manual compaction at level-0 from '!tables!J9VdvrwkbyKxMAT7' @ 72057594037927935 : 1 .. '!tables.results!jGKjfCyk4ROSy9fU.zRzADzATtijaBdNX' @ 0 : 0; will stop at (end)
2026/03/27-21:21:32.123641 7f90f5ffb6c0 Manual compaction at level-1 from '!tables!J9VdvrwkbyKxMAT7' @ 72057594037927935 : 1 .. '!tables.results!jGKjfCyk4ROSy9fU.zRzADzATtijaBdNX' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

@@ -1 +0,0 @@
MANIFEST-000072

View File

View File

@@ -1,8 +0,0 @@
2026/03/27-23:40:07.028975 7f90f7fff6c0 Recovering log #70
2026/03/27-23:40:07.039557 7f90f7fff6c0 Delete type=3 #68
2026/03/27-23:40:07.039615 7f90f7fff6c0 Delete type=0 #70
2026/03/28-01:06:04.054602 7f90f5ffb6c0 Level-0 table #75: started
2026/03/28-01:06:04.054622 7f90f5ffb6c0 Level-0 table #75: 0 bytes OK
2026/03/28-01:06:04.060604 7f90f5ffb6c0 Delete type=0 #73
2026/03/28-01:06:04.080099 7f90f5ffb6c0 Manual compaction at level-0 from '!macros!apyHJT40enTKFUfX' @ 72057594037927935 : 1 .. '!macros!suexsLbORUfE9ptz' @ 0 : 0; will stop at (end)
2026/03/28-01:06:04.080138 7f90f5ffb6c0 Manual compaction at level-1 from '!macros!apyHJT40enTKFUfX' @ 72057594037927935 : 1 .. '!macros!suexsLbORUfE9ptz' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +0,0 @@
2026/03/27-21:21:30.304400 7f90f67fc6c0 Recovering log #66
2026/03/27-21:21:30.314598 7f90f67fc6c0 Delete type=3 #64
2026/03/27-21:21:30.314668 7f90f67fc6c0 Delete type=0 #66
2026/03/27-21:21:32.089478 7f90f5ffb6c0 Level-0 table #71: started
2026/03/27-21:21:32.089505 7f90f5ffb6c0 Level-0 table #71: 0 bytes OK
2026/03/27-21:21:32.095733 7f90f5ffb6c0 Delete type=0 #69
2026/03/27-21:21:32.116170 7f90f5ffb6c0 Manual compaction at level-0 from '!macros!apyHJT40enTKFUfX' @ 72057594037927935 : 1 .. '!macros!suexsLbORUfE9ptz' @ 0 : 0; will stop at (end)
2026/03/27-21:21:32.123631 7f90f5ffb6c0 Manual compaction at level-1 from '!macros!apyHJT40enTKFUfX' @ 72057594037927935 : 1 .. '!macros!suexsLbORUfE9ptz' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

@@ -1 +0,0 @@
MANIFEST-000250

View File

View File

@@ -1,8 +0,0 @@
2026/03/27-23:40:07.017004 7f90f6ffd6c0 Recovering log #248
2026/03/27-23:40:07.027187 7f90f6ffd6c0 Delete type=3 #246
2026/03/27-23:40:07.027254 7f90f6ffd6c0 Delete type=0 #248
2026/03/28-01:06:04.067738 7f90f5ffb6c0 Level-0 table #253: started
2026/03/28-01:06:04.067767 7f90f5ffb6c0 Level-0 table #253: 0 bytes OK
2026/03/28-01:06:04.073991 7f90f5ffb6c0 Delete type=0 #251
2026/03/28-01:06:04.080120 7f90f5ffb6c0 Manual compaction at level-0 from '!macros!Admg6zBHid4mfbJY' @ 72057594037927935 : 1 .. '!macros!wY3tga12higX7soz' @ 0 : 0; will stop at (end)
2026/03/28-01:06:04.080146 7f90f5ffb6c0 Manual compaction at level-1 from '!macros!Admg6zBHid4mfbJY' @ 72057594037927935 : 1 .. '!macros!wY3tga12higX7soz' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +0,0 @@
2026/03/27-21:21:30.291918 7f90f7fff6c0 Recovering log #244
2026/03/27-21:21:30.302711 7f90f7fff6c0 Delete type=3 #242
2026/03/27-21:21:30.302762 7f90f7fff6c0 Delete type=0 #244
2026/03/27-21:21:32.109958 7f90f5ffb6c0 Level-0 table #249: started
2026/03/27-21:21:32.109983 7f90f5ffb6c0 Level-0 table #249: 0 bytes OK
2026/03/27-21:21:32.116069 7f90f5ffb6c0 Delete type=0 #247
2026/03/27-21:21:32.116191 7f90f5ffb6c0 Manual compaction at level-0 from '!macros!Admg6zBHid4mfbJY' @ 72057594037927935 : 1 .. '!macros!wY3tga12higX7soz' @ 0 : 0; will stop at (end)
2026/03/27-21:21:32.116205 7f90f5ffb6c0 Manual compaction at level-1 from '!macros!Admg6zBHid4mfbJY' @ 72057594037927935 : 1 .. '!macros!wY3tga12higX7soz' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

@@ -1 +0,0 @@
MANIFEST-000992

View File

@@ -1,8 +0,0 @@
2026/03/27-23:40:06.964100 7f90f7fff6c0 Recovering log #990
2026/03/27-23:40:06.975062 7f90f7fff6c0 Delete type=3 #988
2026/03/27-23:40:06.975129 7f90f7fff6c0 Delete type=0 #990
2026/03/28-01:06:04.035671 7f90f5ffb6c0 Level-0 table #995: started
2026/03/28-01:06:04.035691 7f90f5ffb6c0 Level-0 table #995: 0 bytes OK
2026/03/28-01:06:04.041795 7f90f5ffb6c0 Delete type=0 #993
2026/03/28-01:06:04.054457 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!OgzOugwIXfHtijaY' @ 72057594037927935 : 1 .. '!journal.pages!OgzOugwIXfHtijaY.OOev7kj2KoMOGoMD' @ 0 : 0; will stop at (end)
2026/03/28-01:06:04.054505 7f90f5ffb6c0 Manual compaction at level-1 from '!journal!OgzOugwIXfHtijaY' @ 72057594037927935 : 1 .. '!journal.pages!OgzOugwIXfHtijaY.OOev7kj2KoMOGoMD' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +0,0 @@
2026/03/27-21:21:30.242574 7f90f7fff6c0 Recovering log #986
2026/03/27-21:21:30.253063 7f90f7fff6c0 Delete type=3 #984
2026/03/27-21:21:30.253116 7f90f7fff6c0 Delete type=0 #986
2026/03/27-21:21:32.074771 7f90f5ffb6c0 Level-0 table #991: started
2026/03/27-21:21:32.074793 7f90f5ffb6c0 Level-0 table #991: 0 bytes OK
2026/03/27-21:21:32.081032 7f90f5ffb6c0 Delete type=0 #989
2026/03/27-21:21:32.089348 7f90f5ffb6c0 Manual compaction at level-0 from '!journal!OgzOugwIXfHtijaY' @ 72057594037927935 : 1 .. '!journal.pages!OgzOugwIXfHtijaY.OOev7kj2KoMOGoMD' @ 0 : 0; will stop at (end)
2026/03/27-21:21:32.089398 7f90f5ffb6c0 Manual compaction at level-1 from '!journal!OgzOugwIXfHtijaY' @ 72057594037927935 : 1 .. '!journal.pages!OgzOugwIXfHtijaY.OOev7kj2KoMOGoMD' @ 0 : 0; will stop at (end)

View File

@@ -1,10 +1,21 @@
/**
* 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/
*/
export const SYSTEM_ID = "fvtt-chroniques-de-l-etrange"
export const ACTOR_TYPES = {
character: "character",
npc: "npc",
tinji: "tinji",
loksyu: "loksyu",
}
export const ITEM_TYPES = {
@@ -93,6 +104,49 @@ export const MAGICS = {
},
}
/** Map aspect name → i18n label key */
export const ASPECT_LABELS = {
metal: "CDE.Metal",
water: "CDE.Water",
earth: "CDE.Earth",
fire: "CDE.Fire",
wood: "CDE.Wood",
}
/** Map aspect name → image path */
export const ASPECT_ICONS = {
metal: "systems/fvtt-chroniques-de-l-etrange/images/cde_metal.webp",
water: "systems/fvtt-chroniques-de-l-etrange/images/cde_eau.webp",
earth: "systems/fvtt-chroniques-de-l-etrange/images/cde_terre.webp",
fire: "systems/fvtt-chroniques-de-l-etrange/images/cde_feu.webp",
wood: "systems/fvtt-chroniques-de-l-etrange/images/cde_bois.webp",
}
/** Map aspect name → die face pair [yin, yang] (face=10 stored as 0) */
export const ASPECT_FACES = {
metal: [3, 8],
water: [1, 6],
earth: [0, 5], // 0 = face "10"
fire: [2, 7],
wood: [4, 9],
}
/** Ordered aspect names by index (metal=0, water=1, earth=2, fire=3, wood=4) */
export const ASPECT_NAMES = ["metal", "water", "earth", "fire", "wood"]
/**
* Wu Xing generating/overcoming cycle.
* For each active aspect, the five result categories in order:
* [successes, auspicious, noxious, loksyu, tinji]
*/
export const WU_XING_CYCLE = {
wood: ["wood", "fire", "water", "earth", "metal"],
fire: ["fire", "earth", "wood", "metal", "water"],
earth: ["earth", "metal", "fire", "water", "wood"],
metal: ["metal", "water", "earth", "wood", "fire"],
water: ["water", "wood", "metal", "fire", "earth"],
}
export const TEMPLATE_PARTIALS = [
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-skills.html",
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-magics.html",
@@ -105,4 +159,6 @@ export const TEMPLATE_PARTIALS = [
"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-items.html",
"systems/fvtt-chroniques-de-l-etrange/templates/apps/cde-loksyu-app.html",
"systems/fvtt-chroniques-de-l-etrange/templates/apps/cde-tinji-app.html",
]

View File

@@ -1,3 +1,16 @@
/**
* 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 { MAGICS, SUBTYPES } from "./constants.js"
export function preLocalizeConfig() {

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export function configureRuntime() {
CONFIG.Actor.compendiumBanner = "/systems/fvtt-chroniques-de-l-etrange/images/banners/actor-banner.webp"
CONFIG.Adventure.compendiumBanner = "/systems/fvtt-chroniques-de-l-etrange/images/banners/adventure-banner.webp"

48
src/config/settings.js Normal file
View File

@@ -0,0 +1,48 @@
/**
* 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 { SYSTEM_ID } from "./constants.js"
/**
* Register all world/client settings for the system.
* Called during the "init" hook before sheets and data-models are set up.
*/
export function registerSettings() {
game.settings.register(SYSTEM_ID, "loksyuData", {
scope: "world",
config: false,
type: Object,
default: {
wood: { yin: 0, yang: 0 },
fire: { yin: 0, yang: 0 },
earth: { yin: 0, yang: 0 },
metal: { yin: 0, yang: 0 },
water: { yin: 0, yang: 0 },
},
})
game.settings.register(SYSTEM_ID, "tinjiData", {
scope: "world",
config: false,
type: Number,
default: 0,
})
}
/**
* Run any pending data migrations on the "ready" hook.
* Reserved for future schema migrations.
*/
export async function migrateIfNeeded() {
// No migrations required yet.
}

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class CharacterDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,4 +1,15 @@
/**
* 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/
*/
export { default as CharacterDataModel } from "./character.js"
export { default as NpcDataModel } from "./npc.js"
export { default as TinjiDataModel } from "./tinji.js"
export { default as LoksyuDataModel } from "./loksyu.js"

View File

@@ -1,22 +0,0 @@
export default class LoksyuDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data
const numberField = (initial = 0, extra = {}) => new fields.NumberField({ required: true, nullable: false, integer: true, initial, ...extra })
const htmlField = (initial = "") => new fields.HTMLField({ required: true, nullable: false, initial, textSearch: true })
const polarity = () =>
new fields.SchemaField({
yin: new fields.SchemaField({ value: numberField(0, { min: 0 }) }),
yang: new fields.SchemaField({ value: numberField(0, { min: 0 }) }),
})
return {
fire: polarity(),
earth: polarity(),
metal: polarity(),
water: polarity(),
wood: polarity(),
description: htmlField(""),
}
}
}

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class NpcDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,12 +0,0 @@
export default class TinjiDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data
const numberField = (initial = 0, extra = {}) => new fields.NumberField({ required: true, nullable: false, integer: true, initial, ...extra })
const htmlField = (initial = "") => new fields.HTMLField({ required: true, nullable: false, initial, textSearch: true })
return {
value: numberField(0, { min: 0 }),
description: htmlField(""),
}
}
}

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class ArmorDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export { default as EquipmentDataModel } from "./item.js"
export { default as KungfuDataModel } from "./kungfu.js"
export { default as SpellDataModel } from "./spell.js"

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class IngredientDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class EquipmentDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class KungfuDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class SanheiDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class SpellDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class SupernaturalDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export default class WeaponDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data

View File

@@ -1,3 +1,16 @@
/**
* 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 { ACTOR_TYPES } from "../config/constants.js"
export class CDEActor extends Actor {

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
export class CDEMessage extends ChatMessage {
async renderHTML({ canDelete, canClose = false, ...rest } = {}) {
const html = await super.renderHTML({ canDelete, canClose, ...rest })

View File

@@ -1,17 +1,15 @@
/**
* 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/
*/
export class CDEItem extends Item {
get isWeapon() {
return this.system.subtype === "weapon"
}
get isArmor() {
return this.system.subtype === "armor"
}
get isSanhei() {
return this.system.subtype === "sanhei"
}
get isOther() {
return this.system.subtype === "other"
}
}

View File

@@ -1,125 +0,0 @@
import { SYSTEM_ID } from "./config/constants.js"
const MIGRATION_VERSION = "3.0.0"
export function registerSettings() {
game.settings.register(SYSTEM_ID, "migrationVersion", {
name: "Migration version",
scope: "world",
config: false,
type: String,
default: "0.0.0",
})
}
export async function migrateIfNeeded() {
const current = game.system.version ?? MIGRATION_VERSION
const stored = game.settings.get(SYSTEM_ID, "migrationVersion") ?? "0.0.0"
if (!foundry.utils.isNewerVersion(current, stored)) return
ui.notifications.info(`CHRONIQUESDELETRANGE | Migration vers ${current} en cours...`, { permanent: true })
await migrateActors()
await migrateItems()
await migrateCompendiumActors()
await migrateCompendiumItems()
await game.settings.set(SYSTEM_ID, "migrationVersion", current)
ui.notifications.info(`CHRONIQUESDELETRANGE | Migration vers ${current} terminée.`)
}
async function migrateActors() {
const updates = []
for (const actor of game.actors.contents) {
const updateData = migrateActorData(actor)
if (Object.keys(updateData).length > 0) {
updates.push(actor.update(updateData, { enforceTypes: false }))
}
}
await Promise.all(updates)
}
async function migrateCompendiumActors() {
const packs = game.packs.filter((p) => p.documentName === "Actor" && p.metadata.system === SYSTEM_ID)
for (const pack of packs) {
const content = await pack.getDocuments()
for (const actor of content) {
const updateData = migrateActorData(actor)
if (Object.keys(updateData).length > 0) {
await actor.update(updateData, { pack: pack.collection, enforceTypes: false })
}
}
}
}
async function migrateItems() {
const updates = []
for (const item of game.items.contents) {
const updateData = migrateItemData(item)
if (Object.keys(updateData).length > 0) {
updates.push(item.update(updateData, { enforceTypes: false }))
}
}
await Promise.all(updates)
}
async function migrateCompendiumItems() {
const packs = game.packs.filter((p) => p.documentName === "Item" && p.metadata.system === SYSTEM_ID)
for (const pack of packs) {
const content = await pack.getDocuments()
for (const item of content) {
const updateData = migrateItemData(item)
if (Object.keys(updateData).length > 0) {
await item.update(updateData, { pack: pack.collection, enforceTypes: false })
}
}
}
}
function migrateActorData(actor) {
const updateData = {}
const system = actor.system ?? {}
const actorType = actor.type
// Fix legacy typo: masteryofthway -> masteryoftheway
const legacyMagic = system.magics?.masteryofthway
if (legacyMagic && !system.magics?.masteryoftheway) {
updateData["system.magics.masteryoftheway"] = legacyMagic
updateData["system.magics.-=masteryofthway"] = null
}
// Ensure prefs.typeofthrow exists on relevant actor types
if ((actorType === "character" || actorType === "npc") && !system.prefs?.typeofthrow) {
const defaultCheck = actorType === "character"
updateData["system.prefs.typeofthrow"] = { check: defaultCheck, choice: "0" }
}
// Migrate NPC field renames: levelofthreat → threat, powerofnuisance → nuisance
if (actorType === "npc") {
if (system.levelofthreat !== undefined && system.threat === undefined) {
updateData["system.threat"] = system.levelofthreat
updateData["system.-=levelofthreat"] = null
}
if (system.powerofnuisance !== undefined && system.nuisance === undefined) {
updateData["system.nuisance"] = system.powerofnuisance
updateData["system.-=powerofnuisance"] = null
}
}
// Migrate character guardian from string to number
if (actorType === "character" && typeof system.guardian === "string") {
const guardianNum = parseInt(system.guardian, 10)
if (!isNaN(guardianNum)) {
updateData["system.guardian"] = guardianNum
}
}
return updateData
}
function migrateItemData(item) {
const updateData = {}
const system = item.system ?? {}
// Add item-specific migrations here as needed
return updateData
}

View File

@@ -1,7 +1,21 @@
/**
* 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 { ACTOR_TYPES, ITEM_TYPES, MAGICS, SUBTYPES, SYSTEM_ID } from "./config/constants.js"
import { registerSettings, migrateIfNeeded } from "./config/settings.js"
import { preLocalizeConfig } from "./config/localize.js"
import { configureRuntime } from "./config/runtime.js"
import { CharacterDataModel, LoksyuDataModel, NpcDataModel, TinjiDataModel } from "./data/actors/index.js"
import { CharacterDataModel, NpcDataModel } from "./data/actors/index.js"
import { EquipmentDataModel, KungfuDataModel, SpellDataModel, SupernaturalDataModel, WeaponDataModel, ArmorDataModel, SanheiDataModel, IngredientDataModel } from "./data/items/index.js"
import { CDEMessage } from "./documents/chat-message.js"
import { CDEActor } from "./documents/actor.js"
@@ -9,9 +23,11 @@ import { CDEItem } from "./documents/item.js"
import { registerDice } from "./ui/dice.js"
import { registerHandlebarsHelpers } from "./ui/helpers.js"
import { preloadPartials } from "./ui/templates.js"
import { CDELoksyuSheet, CDECharacterSheet, CDENpcSheet, CDETinjiSheet } from "./ui/sheets/actors/index.js"
import { CDECharacterSheet, CDENpcSheet } from "./ui/sheets/actors/index.js"
import { CDEItemSheet, CDEKungfuSheet, CDESpellSheet, CDESupernaturalSheet, CDEWeaponSheet, CDEArmorSheet, CDESanheiSheet, CDEIngredientSheet } from "./ui/sheets/items/index.js"
import { migrateIfNeeded, registerSettings } from "./migration.js"
import { CDELoksyuApp } from "./ui/apps/loksyu-app.js"
import { CDETinjiApp } from "./ui/apps/tinji-app.js"
import { injectRollActions, refreshAllRollActions } from "./ui/roll-actions.js"
Hooks.once("i18nInit", preLocalizeConfig)
@@ -22,11 +38,12 @@ Hooks.once("init", async () => {
game.system.CONST = { MAGICS, SUBTYPES }
// Expose standalone apps globally for macros
game.cde = { CDELoksyuApp, CDETinjiApp }
CONFIG.Actor.dataModels = {
[ACTOR_TYPES.character]: CharacterDataModel,
[ACTOR_TYPES.npc]: NpcDataModel,
[ACTOR_TYPES.tinji]: TinjiDataModel,
[ACTOR_TYPES.loksyu]: LoksyuDataModel,
}
CONFIG.Item.dataModels = {
[ITEM_TYPES.item]: EquipmentDataModel,
@@ -45,8 +62,8 @@ Hooks.once("init", async () => {
configureRuntime()
foundry.applications.apps.DocumentSheetConfig.unregisterSheet(Actor, "core", ActorSheet)
foundry.applications.apps.DocumentSheetConfig.unregisterSheet(Item, "core", ItemSheet)
foundry.applications.apps.DocumentSheetConfig.unregisterSheet(Actor, "core", foundry.appv1.sheets.ActorSheet)
foundry.applications.apps.DocumentSheetConfig.unregisterSheet(Item, "core", foundry.appv1.sheets.ItemSheet)
foundry.applications.apps.DocumentSheetConfig.registerSheet(Actor, SYSTEM_ID, CDECharacterSheet, {
types: [ACTOR_TYPES.character],
@@ -58,16 +75,6 @@ Hooks.once("init", async () => {
makeDefault: true,
label: "CDE NPC Sheet (V2)",
})
foundry.applications.apps.DocumentSheetConfig.registerSheet(Actor, SYSTEM_ID, CDETinjiSheet, {
types: [ACTOR_TYPES.tinji],
makeDefault: true,
label: "CDE Tinji Sheet (V2)",
})
foundry.applications.apps.DocumentSheetConfig.registerSheet(Actor, SYSTEM_ID, CDELoksyuSheet, {
types: [ACTOR_TYPES.loksyu],
makeDefault: true,
label: "CDE Loksyu Sheet (V2)",
})
foundry.applications.apps.DocumentSheetConfig.registerSheet(Item, SYSTEM_ID, CDEItemSheet, {
types: [ITEM_TYPES.item],
@@ -113,44 +120,56 @@ Hooks.once("init", async () => {
await preloadPartials()
registerHandlebarsHelpers()
registerDice()
Hooks.on("renderSettings", (_app, html) => injectCompendiumLink(html))
console.info(`CHRONIQUESDELETRANGE | Initialized`)
})
Hooks.once("ready", async () => {
if (!game.modules.get("lib-wrapper")?.active && game.user.isGM) {
ui.notifications.error("System fvtt-chroniques-de-l-etrange requires the 'libWrapper' module. Please install and activate it.")
}
await migrateIfNeeded()
})
function injectCompendiumLink(html) {
const header = html[0]?.querySelector?.("h4.divider")
if (!header) return
/** Add Loksyu + Tin Ji quick-access buttons to the chat panel (FoundryVTT v13) */
Hooks.on("renderChatLog", (_app, html) => {
const el = html instanceof HTMLElement ? html : (html[0] ?? html)
if (!el?.querySelector) return
const section = document.createElement("section")
section.classList.add("settings", "flexcol")
section.innerHTML = `
<section class="links flexcol">
<img class="logo-info" src="systems/fvtt-chroniques-de-l-etrange/images/logo_jeu.webp" />
<h4 class="divider">&nbsp;Lien utile&nbsp;<i class="fa-light fa-up-right-from-square"></i>&nbsp;</h4>
</section>
<section class="settings flexcol">
<button type="button" data-action="open-cde-link">
<i class="fa fa-download"></i>&nbsp;Compendium pour Les CdE&nbsp;<i class="fa-light fa-up-right-from-square"></i>
// Avoid double-injection on re-renders
if (el.querySelector(".cde-chat-app-buttons")) return
const wrapper = document.createElement("div")
wrapper.classList.add("cde-chat-app-buttons")
wrapper.innerHTML = `
<button type="button" class="cde-chat-btn cde-chat-btn--loksyu">
<i class="fas fa-yin-yang"></i> ${game.i18n.localize("CDE.Loksyu")}
</button>
<button type="button" class="cde-chat-btn cde-chat-btn--tinji">
<i class="fas fa-star"></i> ${game.i18n.localize("CDE.TinJi2")}
</button>
<details>
<summary><small>Guide d'installation</small></summary>
<small style="text-align: center;">
<p>Rendez-vous sur le site de l'éditeur, téléchargez les PDF contenant les liens vers les compendia, puis ajoutez leurs manifestes dans Foundry.</p>
</small>
</details>
</section>
`
section.querySelector("button[data-action='open-cde-link']")?.addEventListener("click", () => {
window.open("https://antre-monde.com/les-chroniques-de-letrengae/", "_blank")
// Use event delegation to avoid being swallowed by Foundry's own handlers
wrapper.addEventListener("click", (ev) => {
if (ev.target.closest(".cde-chat-btn--loksyu")) CDELoksyuApp.open()
if (ev.target.closest(".cde-chat-btn--tinji")) CDETinjiApp.open()
})
header.parentNode.insertBefore(section, header)
}
// Insert before the chat form — works on v12 and v13
const anchor = el.querySelector(".chat-form")
?? el.querySelector(".chat-message-form")
?? el.querySelector("form")
if (anchor) anchor.parentElement.insertBefore(wrapper, anchor)
else el.appendChild(wrapper)
})
/** Inject Loksyu / TinJi action buttons into roll-result chat messages */
Hooks.on("renderChatMessageHTML", (message, html) => {
injectRollActions(message, html)
})
/** Refresh all visible roll-result buttons whenever Loksyu or TinJi settings change */
Hooks.on("updateSetting", setting => {
if (!setting.key) return
if (setting.key.includes("loksyuData") || setting.key.includes("tinjiData")) {
refreshAllRollActions()
}
})

16
src/ui/apps/index.js Normal file
View File

@@ -0,0 +1,16 @@
/**
* 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/
*/
export { CDELoksyuApp } from "./loksyu-app.js"
export { CDETinjiApp } from "./tinji-app.js"
export { updateLoksyuFromRoll, updateTinjiFromRoll } from "./singletons.js"

124
src/ui/apps/loksyu-app.js Normal file
View File

@@ -0,0 +1,124 @@
/**
* 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 { getLoksyuData, setLoksyuData } from "./singletons.js"
import { SYSTEM_ID } from "../../config/constants.js"
export class CDELoksyuApp extends foundry.applications.api.HandlebarsApplicationMixin(
foundry.applications.api.ApplicationV2
) {
static DEFAULT_OPTIONS = {
id: "cde-loksyu-app",
tag: "div",
window: {
title: "CDE.Loksyu",
icon: "fas fa-yin-yang",
resizable: false,
},
classes: ["cde-app", "cde-loksyu-standalone"],
position: { width: 520, height: "auto" },
actions: {
resetElement: CDELoksyuApp.#onResetElement,
resetAll: CDELoksyuApp.#onResetAll,
},
}
static PARTS = {
main: {
template: `systems/${SYSTEM_ID}/templates/apps/cde-loksyu-app.html`,
},
}
/** @type {Function|null} bound hook handler */
_updateHook = null
/** Singleton accessor — open or bring to front */
static open() {
const existing = Array.from(foundry.applications.instances.values()).find(
(app) => app instanceof CDELoksyuApp
)
if (existing) { existing.bringToFront(); return existing }
const app = new CDELoksyuApp()
app.render(true)
return app
}
async _prepareContext() {
const sys = getLoksyuData()
const ELEMENTS = [
{ key: "wood", nameKey: "CDE.Wood", qualKey: "CDE.WoodQualities", img: `systems/${SYSTEM_ID}/images/cde_bois.webp` },
{ key: "fire", nameKey: "CDE.Fire", qualKey: "CDE.FireQualities", img: `systems/${SYSTEM_ID}/images/cde_feu.webp` },
{ key: "earth", nameKey: "CDE.Earth", qualKey: "CDE.EarthQualities", img: `systems/${SYSTEM_ID}/images/cde_terre.webp` },
{ key: "metal", nameKey: "CDE.Metal", qualKey: "CDE.MetalQualities", img: `systems/${SYSTEM_ID}/images/cde_metal.webp` },
{ key: "water", nameKey: "CDE.Water", qualKey: "CDE.WaterQualities", img: `systems/${SYSTEM_ID}/images/cde_eau.webp` },
]
return {
canEdit: game.user.isGM,
elements: ELEMENTS.map((el) => ({
...el,
yang: sys[el.key]?.yang ?? 0,
yin: sys[el.key]?.yin ?? 0,
})),
}
}
_onRender(context, options) {
super._onRender(context, options)
this.#bindInputs()
this._updateHook = Hooks.on("cde:loksyuUpdated", () => this.render())
}
_onClose(options) {
if (this._updateHook !== null) {
Hooks.off("cde:loksyuUpdated", this._updateHook)
this._updateHook = null
}
super._onClose(options)
}
#bindInputs() {
const inputs = this.element?.querySelectorAll("input[data-field]")
if (!inputs?.length) return
inputs.forEach((input) => {
input.addEventListener("change", async (ev) => {
const field = ev.currentTarget.dataset.field
const val = parseInt(ev.currentTarget.value, 10)
if (!field || isNaN(val)) return
// field is like "wood.yin" or "fire.yang"
const [aspect, dim] = field.split(".")
if (!aspect || !dim) return
const data = getLoksyuData()
if (!data[aspect]) data[aspect] = { yin: 0, yang: 0 }
data[aspect][dim] = Math.max(0, val)
await setLoksyuData(data)
})
})
}
static async #onResetElement(event, target) {
const key = target.dataset.element
if (!key) return
const data = getLoksyuData()
data[key] = { yin: 0, yang: 0 }
await setLoksyuData(data)
}
static async #onResetAll(_event, _target) {
const KEYS = ["wood", "fire", "earth", "metal", "water"]
const data = getLoksyuData()
for (const k of KEYS) data[k] = { yin: 0, yang: 0 }
await setLoksyuData(data)
}
}

83
src/ui/apps/singletons.js Normal file
View File

@@ -0,0 +1,83 @@
/**
* 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/
*/
/**
* Loksyu / TinJi settings-based helpers.
*
* Data is stored as world settings instead of singleton Actor documents.
*/
import { SYSTEM_ID, WU_XING_CYCLE, ASPECT_FACES } from "../../config/constants.js"
/** Read the current loksyu data object from world settings */
export function getLoksyuData() {
return game.settings.get(SYSTEM_ID, "loksyuData") ?? {
wood: {yin:0,yang:0}, fire: {yin:0,yang:0}, earth: {yin:0,yang:0}, metal: {yin:0,yang:0}, water: {yin:0,yang:0},
}
}
/** Write the loksyu data object to world settings */
export async function setLoksyuData(data) {
await game.settings.set(SYSTEM_ID, "loksyuData", data)
Hooks.callAll("cde:loksyuUpdated", data)
}
/** Read current TinJi value from world settings */
export function getTinjiValue() {
return game.settings.get(SYSTEM_ID, "tinjiData") ?? 0
}
/** Write TinJi value to world settings */
export async function setTinjiValue(value) {
await game.settings.set(SYSTEM_ID, "tinjiData", Math.max(0, value))
Hooks.callAll("cde:tinjiUpdated", Math.max(0, value))
}
/**
* After a WuXing roll, add the loksyu faces (yin + yang) of the relevant
* aspect to the loksyu settings data.
*
* @param {string} activeAspect - e.g. "fire"
* @param {Object} faces - Die face counts { 0: n, 1: n, …, 9: n }
*/
export async function updateLoksyuFromRoll(activeAspect, faces) {
const cycle = WU_XING_CYCLE[activeAspect]
if (!cycle) return
const lokAspect = cycle[3]
const [yinFace, yangFace] = ASPECT_FACES[lokAspect] ?? []
if (yinFace === undefined) return
const yinCount = faces[yinFace] ?? 0
const yangCount = faces[yangFace] ?? 0
if (yinCount === 0 && yangCount === 0) return
const data = getLoksyuData()
const current = data[lokAspect] ?? { yin: 0, yang: 0 }
data[lokAspect] = {
yin: (current.yin ?? 0) + yinCount,
yang: (current.yang ?? 0) + yangCount,
}
await setLoksyuData(data)
}
/**
* After a WuXing roll, add tinji faces to the TinJi settings.
*
* @param {number} count - Number of tinji faces rolled
*/
export async function updateTinjiFromRoll(count) {
if (!count || count <= 0) return
const current = getTinjiValue()
await setTinjiValue(current + count)
}

118
src/ui/apps/tinji-app.js Normal file
View File

@@ -0,0 +1,118 @@
/**
* 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 { getTinjiValue, setTinjiValue } from "./singletons.js"
import { SYSTEM_ID } from "../../config/constants.js"
export class CDETinjiApp extends foundry.applications.api.HandlebarsApplicationMixin(
foundry.applications.api.ApplicationV2
) {
static DEFAULT_OPTIONS = {
id: "cde-tinji-app",
tag: "div",
window: {
title: "CDE.TinJi2",
icon: "fas fa-star",
resizable: false,
},
classes: ["cde-app", "cde-tinji-standalone"],
position: { width: 320, height: "auto" },
actions: {
increment: CDETinjiApp.#onIncrement,
decrement: CDETinjiApp.#onDecrement,
reset: CDETinjiApp.#onReset,
spend: CDETinjiApp.#onSpend,
},
}
static PARTS = {
main: {
template: `systems/${SYSTEM_ID}/templates/apps/cde-tinji-app.html`,
},
}
/** @type {Function|null} */
_updateHook = null
static open() {
const existing = Array.from(foundry.applications.instances.values()).find(
(app) => app instanceof CDETinjiApp
)
if (existing) { existing.bringToFront(); return existing }
const app = new CDETinjiApp()
app.render(true)
return app
}
async _prepareContext() {
return {
canEdit: game.user.isGM,
value: getTinjiValue(),
}
}
_onRender(context, options) {
super._onRender(context, options)
this.#bindDirectInput()
this._updateHook = Hooks.on("cde:tinjiUpdated", () => this.render())
}
_onClose(options) {
if (this._updateHook !== null) {
Hooks.off("cde:tinjiUpdated", this._updateHook)
this._updateHook = null
}
super._onClose(options)
}
#bindDirectInput() {
const input = this.element?.querySelector("input.cde-tinji-direct")
if (!input) return
input.addEventListener("change", async (ev) => {
const val = parseInt(ev.currentTarget.value, 10)
if (!isNaN(val)) await setTinjiValue(val)
})
}
static async #onIncrement() {
await setTinjiValue(getTinjiValue() + 1)
}
static async #onDecrement() {
const current = getTinjiValue()
if (current <= 0) return
await setTinjiValue(current - 1)
}
static async #onReset() {
await setTinjiValue(0)
}
static async #onSpend() {
const current = getTinjiValue()
if (current <= 0) {
ui.notifications.warn(game.i18n.localize("CDE.TinjiEmpty"))
return
}
await setTinjiValue(current - 1)
ChatMessage.create({
user: game.user.id,
content: `<div class="cde-tinji-spend-msg">
<i class="fas fa-star"></i>
<strong>${game.i18n.localize("CDE.TinJi2")}</strong>
${game.i18n.format("CDE.TinjiSpent", { name: game.user.name })}
<span class="cde-tinji-remain">(${current - 1} ${game.i18n.localize("CDE.TinjiRemaining")})</span>
</div>`,
})
}
}

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
const DIGIT_LABELS = [
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-1.webp",
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-2.webp",

View File

@@ -1,3 +1,16 @@
/**
* 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 { MAGICS } from "../config/constants.js"
export function registerHandlebarsHelpers() {
@@ -61,6 +74,11 @@ export function registerHandlebarsHelpers() {
Handlebars.registerHelper("getElementIcon", function (aspect) {
const icons = {
metal: "/systems/fvtt-chroniques-de-l-etrange/images/cde_metal.webp",
water: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.webp",
earth: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.webp",
fire: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.webp",
wood: "/systems/fvtt-chroniques-de-l-etrange/images/cde_bois.webp",
// legacy French keys
eau: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.webp",
terre: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.webp",
feu: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.webp",

View File

@@ -1,3 +1,16 @@
/**
* 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/
*/
/**
* Initiative determination system for Chroniques de l'Étrange.
*
@@ -55,7 +68,7 @@ function buildNPCOptions(sys) {
function readInitFields(dialog) {
const root = dialog.element ?? dialog
const selectedKey = root.querySelector("select[name='firstaction']")?.value ?? ""
const modifier = parseInt(root.querySelector("input[name='modifier']")?.value ?? 0) || 0
const modifier = parseInt(root.querySelector("input[name='modifier']")?.value ?? "0", 10) || 0
return { selectedKey, modifier }
}

225
src/ui/roll-actions.js Normal file
View File

@@ -0,0 +1,225 @@
/**
* 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/
*/
/**
* Post-roll interactive action buttons injected into dice result chat messages.
* Allows players to pull dice from the Loksyu (as Successes or dés-fastes)
* and allows the GM to spend Tin Ji to intervene.
*
* After a draw, the originating roll result message is updated in-place
* with the new counts, without creating noise.
*/
import { getLoksyuData, setLoksyuData, getTinjiValue, setTinjiValue } from "./apps/singletons.js"
import { SYSTEM_ID, WU_XING_CYCLE, ASPECT_LABELS, ASPECT_ICONS } from "../config/constants.js"
const RESULT_TEMPLATE = "systems/fvtt-chroniques-de-l-etrange/templates/form/cde-dice-result.html"
/**
* Inject or refresh post-roll action buttons in the given chat message HTML element.
* Called from renderChatMessageHTML hook.
* @param {ChatMessage} message
* @param {HTMLElement} html - the chat message HTML element (li.chat-message)
*/
export function injectRollActions(message, html) {
const rollCard = html.querySelector(".cde-roll-result")
if (!rollCard) return
const aspect = rollCard.dataset.aspect
if (!aspect || !WU_XING_CYCLE[aspect]) return
refreshRollActions(rollCard, aspect, message)
}
/**
* Re-render the action buttons section based on current Loksyu / TinJi state.
*/
function refreshRollActions(rollCard, aspect, message) {
rollCard.querySelector(".cde-roll-actions")?.remove()
const cycle = WU_XING_CYCLE[aspect]
const fasteAspect = cycle[1]
const loksyu = getLoksyuData()
const tinji = getTinjiValue()
const successAvail = (loksyu[aspect]?.yin ?? 0) + (loksyu[aspect]?.yang ?? 0)
const fasteAvail = (loksyu[fasteAspect]?.yin ?? 0) + (loksyu[fasteAspect]?.yang ?? 0)
const isGM = game.user.isGM
const hasSomething = successAvail > 0 || fasteAvail > 0 || (isGM && tinji > 0)
if (!hasSomething) return
const aspLabel = game.i18n.localize(ASPECT_LABELS[aspect])
const fasteLabel = game.i18n.localize(ASPECT_LABELS[fasteAspect])
let btns = ""
if (successAvail > 0) {
btns += `<button class="cde-roll-action-btn cde-roll-action--success" data-action="loksyu-success">
<img src="${ASPECT_ICONS[aspect]}" class="cde-roll-action-icon" alt="${aspLabel}"/>
<span class="cde-roll-action-label">+1 ${game.i18n.localize("CDE.Successes")}</span>
<span class="cde-roll-action-count">${successAvail}</span>
</button>`
}
if (fasteAvail > 0) {
btns += `<button class="cde-roll-action-btn cde-roll-action--faste" data-action="loksyu-faste">
<img src="${ASPECT_ICONS[fasteAspect]}" class="cde-roll-action-icon" alt="${fasteLabel}"/>
<span class="cde-roll-action-label">+1 ${game.i18n.localize("CDE.AuspiciousDie")}</span>
<span class="cde-roll-action-count">${fasteAvail}</span>
</button>`
}
if (isGM && tinji > 0) {
btns += `<button class="cde-roll-action-btn cde-roll-action--tinji" data-action="tinji">
<span class="cde-roll-action-tinji-char">天</span>
<span class="cde-roll-action-label">${game.i18n.localize("CDE.TinJi2")}</span>
<span class="cde-roll-action-count">${tinji}</span>
</button>`
}
const wrapper = document.createElement("div")
wrapper.className = "cde-roll-actions"
wrapper.innerHTML = `
<div class="cde-roll-actions-title">
<i class="fas fa-yin-yang"></i>
${game.i18n.localize("CDE.PostRollActions")}
</div>
<div class="cde-roll-actions-btns">${btns}</div>
`
rollCard.appendChild(wrapper)
wrapper.addEventListener("click", async ev => {
const btn = ev.target.closest("[data-action]")
if (!btn || btn.disabled) return
const action = btn.dataset.action
if (action === "loksyu-success") {
await _drawFromLoksyu(message, aspect, "success", aspLabel)
} else if (action === "loksyu-faste") {
await _drawFromLoksyu(message, fasteAspect, "faste", fasteLabel)
} else if (action === "tinji") {
await _spendTinjiPostRoll()
}
// Buttons will be re-injected automatically via renderChatMessageHTML
// after message.update(). For tinji (no message update), refresh manually.
if (action === "tinji") refreshRollActions(rollCard, aspect, message)
})
}
/**
* Pull one die from a given Loksyu aspect slot, update Loksyu settings,
* and update the originating roll-result message in-place.
*
* @param {ChatMessage} message - the roll result chat message to update
* @param {string} aspect - which Loksyu aspect slot to draw from
* @param {"success"|"faste"} type
* @param {string} aspectLabel - localised aspect name for the notification
*/
async function _drawFromLoksyu(message, aspect, type, aspectLabel) {
const data = getLoksyuData()
const entry = data[aspect] ?? { yin: 0, yang: 0 }
const total = entry.yin + entry.yang
if (total <= 0) {
ui.notifications.warn(game.i18n.localize("CDE.LoksyuEmpty"))
return
}
// Remove 1 die (prefer yang first)
if (entry.yang > 0) entry.yang--
else entry.yin--
data[aspect] = entry
await setLoksyuData(data)
// Update the roll-result message in-place if it has stored flags
const flags = message?.flags?.[SYSTEM_ID]
if (flags?.rollResult && message.isOwner) {
const updated = foundry.utils.deepClone(flags.rollResult)
if (type === "success") {
updated.successesdice = (updated.successesdice ?? 0) + 1
updated.loksyuBonusSuc = (updated.loksyuBonusSuc ?? 0) + 1
// Recalculate weapon damage if applicable
if (updated.damageBase) updated.totalDamage = updated.successesdice * updated.damageBase
} else {
updated.auspiciousdice = (updated.auspiciousdice ?? 0) + 1
updated.loksyuBonusFaste = (updated.loksyuBonusFaste ?? 0) + 1
}
const newHtml = await foundry.applications.handlebars.renderTemplate(RESULT_TEMPLATE, updated)
await message.update({
content: newHtml,
[`flags.${SYSTEM_ID}.rollResult`]: updated,
})
// renderChatMessageHTML hook fires automatically → buttons re-injected
}
const remain = entry.yin + entry.yang
const typeLabel = type === "success"
? game.i18n.localize("CDE.Successes")
: game.i18n.localize("CDE.AuspiciousDie")
ChatMessage.create({
user: game.user.id,
content: `<div class="cde-loksyu-draw-msg">
<div class="cde-loksyu-draw-header">
<img src="${ASPECT_ICONS[aspect]}" class="cde-loksyu-draw-aspect-icon" alt="${aspectLabel}"/>
<span class="cde-loksyu-draw-user">${game.user.name}</span>
<span class="cde-loksyu-draw-action">${game.i18n.localize("CDE.LoksyuDrawsA")}</span>
<span class="cde-loksyu-draw-type">${typeLabel}</span>
<span class="cde-loksyu-draw-from">${game.i18n.localize("CDE.LoksyuFromAspect")} <em>${aspectLabel}</em></span>
</div>
<div class="cde-loksyu-draw-footer">
<i class="fas fa-yin-yang"></i>
<span>${game.i18n.localize("CDE.Loksyu")} ${aspectLabel} : </span>
<strong class="cde-loksyu-remain">${remain} ${game.i18n.localize("CDE.LoksyuRemaining")}</strong>
</div>
</div>`,
})
}
/**
* Spend 1 Tin Ji point (GM only) and post a notification.
*/
async function _spendTinjiPostRoll() {
if (!game.user.isGM) return
const current = getTinjiValue()
if (current <= 0) {
ui.notifications.warn(game.i18n.localize("CDE.TinjiEmpty"))
return
}
await setTinjiValue(current - 1)
ChatMessage.create({
user: game.user.id,
content: `<div class="cde-tinji-spend-msg">
<span class="cde-tinji-icon">天</span>
<span class="cde-tinji-text">
<strong>${game.user.name}</strong> ${game.i18n.localize("CDE.TinjiSpent").replace("{name}", game.user.name)}
</span>
<span class="cde-tinji-remain">(${current - 1} ${game.i18n.localize("CDE.TinjiRemaining")})</span>
</div>`,
})
}
/**
* Refresh all visible roll-result buttons when Loksyu or TinJi settings change.
* Wired up via Hooks.on("updateSetting", ...) in system.js.
*/
export function refreshAllRollActions() {
document.querySelectorAll(".chat-message .cde-roll-result[data-aspect]").forEach(card => {
const aspect = card.dataset.aspect
if (!aspect || !WU_XING_CYCLE[aspect]) return
// Find the ChatMessage document from the ancestor element's data-message-id
const msgEl = card.closest("[data-message-id]")
const msgId = msgEl?.dataset?.messageId
const message = msgId ? game.messages.get(msgId) : null
refreshRollActions(card, aspect, message)
})
}

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