7 Commits

Author SHA1 Message Date
uberwald e4a87e3d91 Gestion du dé d'usure dans les capacités
Release Creation / build (release) Successful in 57s
2026-06-06 23:12:12 +02:00
uberwald a152ad11ba App employés/clients
Release Creation / build (release) Successful in 57s
2026-06-03 20:14:26 +02:00
uberwald befb8e97c2 App employés/clients 2026-06-03 20:13:56 +02:00
uberwald 6f3996d216 Ajout dialog employé 2026-06-03 19:20:09 +02:00
uberwald 48660c9430 Fix target selection
Release Creation / build (release) Successful in 1m45s
2026-06-01 13:54:55 +02:00
uberwald 0b88e53d77 Gestion degats à deux mains, suppression bonus degats inutiles
Release Creation / build (release) Successful in 1m58s
2026-06-01 08:26:01 +02:00
uberwald 2c73108f63 SUpression bonus degats, gestion armes à 2mains 2026-05-31 23:23:55 +02:00
42 changed files with 1835 additions and 80 deletions
+52 -2
View File
@@ -104,7 +104,6 @@
"DNC.Dialog.InitiativeIntro": "<strong>{actorName}</strong> lance l'initiative.", "DNC.Dialog.InitiativeIntro": "<strong>{actorName}</strong> lance l'initiative.",
"DNC.Dialog.InitiativeCurrent": "DEX actuelle : <strong>{dex}</strong>, bonus de fiche : <strong>{initiativeBonus}</strong>", "DNC.Dialog.InitiativeCurrent": "DEX actuelle : <strong>{dex}</strong>, bonus de fiche : <strong>{initiativeBonus}</strong>",
"DNC.Dialog.CharacteristicUsed": "Caracteristique utilisee", "DNC.Dialog.CharacteristicUsed": "Caracteristique utilisee",
"DNC.Dialog.ActorDamageBonus": "Bonus de degats de l'acteur",
"DNC.Dialog.DamageCappedByDv": "Le DV martial actuel ({dv}) plafonne cette arme : {base} devient {damage}.", "DNC.Dialog.DamageCappedByDv": "Le DV martial actuel ({dv}) plafonne cette arme : {base} devient {damage}.",
"DNC.Dialog.SpellAutoDisadvantage": "Le cout depasse le rang du lanceur : le jet se fera automatiquement avec desavantage.", "DNC.Dialog.SpellAutoDisadvantage": "Le cout depasse le rang du lanceur : le jet se fera automatiquement avec desavantage.",
"DNC.Dialog.UseUsageDie": "Utiliser <strong>{itemName}</strong> et lancer son de d'usage actuel.", "DNC.Dialog.UseUsageDie": "Utiliser <strong>{itemName}</strong> et lancer son de d'usage actuel.",
@@ -205,5 +204,56 @@
"DNC.Chat.DamageUsageStable": "L'arme tient bon, ses degats restent inchanges.", "DNC.Chat.DamageUsageStable": "L'arme tient bon, ses degats restent inchanges.",
"DNC.Chat.DamageUsageExhausted": "L'arme est epuisee, elle ne peut plus causer de degats.", "DNC.Chat.DamageUsageExhausted": "L'arme est epuisee, elle ne peut plus causer de degats.",
"DNC.UI.DamageExhausted": "Epuise", "DNC.UI.DamageExhausted": "Epuise",
"DNC.Warn.DamageExhausted": "Cette arme est epuisee et ne peut plus causer de degats." "DNC.Warn.DamageExhausted": "Cette arme est epuisee et ne peut plus causer de degats.",
"DNC.Dialog.EmployeesTitle": "Employés",
"DNC.Dialog.EmployeesTabEmployes": "Employés",
"DNC.Dialog.EmployeesTabCombat": "Combat",
"DNC.Dialog.EmployeesTabCharacteristics": "Caractéristiques",
"DNC.Dialog.EmployeesTabMagie": "Magie",
"DNC.Dialog.EmployeesTabClients": "Clients",
"DNC.Empty.NoEmployees": "Aucun employé trouvé.",
"DNC.UI.Weapons": "Armes",
"DNC.UI.Armors": "Armures",
"DNC.UI.Spells": "Sortilèges",
"DNC.UI.Capacities": "Capacités",
"DNC.UI.Employee": "Employé",
"DNC.UI.Dv": "DV",
"DNC.UI.MeleeAttacks": "Attaques CàC",
"DNC.UI.RangedAttacks": "Attaques Dist.",
"DNC.UI.Damage": "Dégâts",
"DNC.UI.Range": "Portée",
"DNC.UI.Ammunition": "Munitions",
"DNC.UI.ProtectionDie": "Protection Δ",
"DNC.UI.Encumbrance": "Encombrement",
"DNC.UI.RemainingProtection": "Protection restante",
"DNC.UI.Notes": "Notes",
"DNC.UI.Description": "Description",
"DNC.UI.Rank": "Rang",
"DNC.UI.Focus": "Focus",
"DNC.UI.Chaos": "Chaos",
"DNC.WeaponCategory.Melee": "Corps à corps",
"DNC.WeaponCategory.Ranged": "Distance",
"DNC.WeaponHands.Label": "Mains",
"DNC.WeaponHands.One": "1 main",
"DNC.WeaponHands.Two": "2 mains",
"DNC.WeaponRange.Contact": "Contact",
"DNC.Ammunition.Exhausted": "Épuisées",
"DNC.Empty.NoWeapons": "Aucune arme répertoriée.",
"DNC.Empty.NoArmors": "Aucune armure répertoriée.",
"DNC.Empty.NoSpells": "Aucun sortilège répertorié.",
"DNC.Empty.NoCapacities": "Aucune capacité répertoriée.",
"DNC.Empty.NoClients": "Aucun client répertorié.",
"DNC.Client.Species": "Espèce",
"DNC.Client.Category": "Catégorie",
"DNC.Client.Role": "Rôle"
} }
+10
View File
@@ -21,6 +21,16 @@
gap: @spacing-md; gap: @spacing-md;
} }
// Réduction de la taille des champs PV
.dnc-actor-sheet .hp-field .counter-field input[type="number"] {
max-width: 60px;
text-align: center;
}
.dnc-actor-sheet .hp-field .counter-field {
gap: @spacing-xs;
}
.dnc-actor-sheet .profile-card-wide { .dnc-actor-sheet .profile-card-wide {
width: 100%; width: 100%;
} }
+32 -5
View File
@@ -44,7 +44,7 @@
} }
.chat-card-kicker { .chat-card-kicker {
margin: 0 0 0.25rem; margin: 0 0 @spacing-xs;
font-size: 0.7rem; font-size: 0.7rem;
font-weight: 700; font-weight: 700;
letter-spacing: 0.16em; letter-spacing: 0.16em;
@@ -189,7 +189,7 @@
} }
.chat-details li + li { .chat-details li + li {
margin-top: 0.25rem; margin-top: @spacing-xs;
} }
.chat-actions { .chat-actions {
@@ -225,9 +225,36 @@
.chat-targeting { .chat-targeting {
position: relative; position: relative;
z-index: 1; z-index: 1;
display: grid; display: flex;
gap: @spacing-sm; gap: @spacing-sm;
margin-top: @spacing-sm; margin-top: @spacing-sm;
flex-wrap: wrap;
align-items: end;
.chat-action-button {
padding: 0.35rem 0.65rem;
min-height: 1.75rem;
font-size: 0.82rem;
flex: 0 0 auto;
}
}
// Boutons Appliquer et Avec Armure sur une seule ligne
.dnc-chat-card-damage .chat-actions-inline {
flex-wrap: nowrap;
gap: @spacing-xs;
}
.dnc-chat-card-damage .chat-actions-inline .chat-action-button {
padding: 0.3rem 0.55rem;
min-height: 1.65rem;
font-size: 0.78rem;
flex: 0 0 auto;
}
.dnc-chat-card-damage .chat-actions-inline .chat-action-button i {
font-size: 0.85rem;
margin-right: 0.2rem;
} }
.chat-control { .chat-control {
@@ -255,7 +282,7 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
min-height: 2rem; min-height: 2rem;
padding: 0.25rem 0.7rem; padding: @spacing-xs 0.7rem;
border-radius: 999px; border-radius: 999px;
border: 1px solid fade(@color-border, 38%); border: 1px solid fade(@color-border, 38%);
background: rgba(255, 255, 255, 0.5); background: rgba(255, 255, 255, 0.5);
@@ -417,7 +444,7 @@
} }
.chat-chaos-result-title { .chat-chaos-result-title {
margin: 0 0 0.25rem; margin: 0 0 @spacing-xs;
font-weight: 800; font-weight: 800;
} }
+1
View File
@@ -5,3 +5,4 @@
@import "item-sheet"; @import "item-sheet";
@import "dialogs"; @import "dialogs";
@import "chat"; @import "chat";
@import "employes-dialog";
+394
View File
@@ -0,0 +1,394 @@
// ============================================
// Dialog Employés - Styles
// Structure 2 niveaux : Employés/Clients → sous-onglets par PC/client
// ============================================
// Conteneur principal
.dnc-employes-dialog {
display: flex;
flex-direction: column;
gap: @spacing-md;
padding: @spacing-lg;
min-width: 640px;
max-width: 90vw;
max-height: 90vh;
overflow-y: auto;
.sheet-header {
display: block;
grid-template-columns: none;
gap: normal;
h1 {
font-family: @font-display;
font-size: 1.4rem;
color: @color-accent;
text-transform: uppercase;
letter-spacing: 0.04em;
margin: 0;
text-align: center;
}
}
}
// Wrapper pour DialogV2
.dnc-employes-dialog-wrapper {
.dialog-content,
.window-content {
padding: 0;
}
// Cacher le footer (bouton requis par l'API mais inutile visuellement)
.dialog-footer {
display: none;
}
}
// ============================================
// Onglets racine (niveau 1)
// ============================================
.dnc-root-tabs {
display: flex;
justify-content: center;
gap: @spacing-md;
border-bottom: 2px solid @color-border;
padding-bottom: @spacing-sm;
}
.dnc-root-tab {
border: 1px solid fade(@color-border, 55%);
border-radius: @radius-md @radius-md 0 0;
background: @color-panel-strong;
color: @color-ink;
font-size: 0.9rem;
font-weight: 700;
padding: @spacing-sm @spacing-lg;
display: flex;
align-items: center;
gap: @spacing-xs;
cursor: pointer;
transition: all 0.2s ease;
i { font-size: 0.9rem; }
&.active {
background: @color-accent;
border-color: darken(@color-accent, 12%);
color: #fff;
box-shadow: 0 -3px 8px fade(@color-shadow, 20%);
position: relative;
bottom: -2px;
}
&:hover:not(.active) {
background: fade(@color-panel-strong, 120%);
border-color: fade(@color-border, 75%);
}
}
// Panneaux racine
.dnc-root-panels {
position: relative;
}
.dnc-root-panel {
display: none;
&.active {
display: block;
}
}
// ============================================
// Sous-onglets (niveau 2 — par PC ou client)
// ============================================
.dnc-pc-tabs {
display: flex;
gap: @spacing-xs;
flex-wrap: wrap;
padding: @spacing-sm 0;
border-bottom: 1px solid fade(@color-border, 40%);
margin-bottom: @spacing-md;
}
.dnc-pc-tab {
border: 1px solid fade(@color-border, 45%);
border-radius: @radius-sm;
background: rgba(255, 255, 255, 0.5);
color: @color-muted;
font-size: 0.82rem;
font-weight: 600;
padding: @spacing-xs @spacing-md;
cursor: pointer;
transition: all 0.15s ease;
white-space: nowrap;
&.active {
background: @color-accent;
border-color: darken(@color-accent, 10%);
color: #fff;
font-weight: 700;
}
&:hover:not(.active) {
background: fade(@color-panel-strong, 110%);
color: @color-ink;
}
}
// Panneaux par PC / client
.dnc-pc-panels {
position: relative;
}
.dnc-pc-panel,
.dnc-client-panel {
display: none;
&.active {
display: block;
}
}
// ============================================
// Fiche compacte par employé (3 sections)
// ============================================
.dnc-compact-section {
padding: @spacing-md;
margin-bottom: @spacing-sm;
border: 1px solid fade(@color-border, 40%);
border-radius: @radius-md;
background: @color-panel;
& + .dnc-compact-section {
margin-top: @spacing-sm;
}
}
.dnc-compact-section-title {
font-family: @font-display;
font-size: 0.9rem;
font-weight: 700;
color: @color-accent;
text-transform: uppercase;
letter-spacing: 0.05em;
margin: 0 0 @spacing-sm;
display: flex;
align-items: center;
gap: @spacing-xs;
border-bottom: 1px solid fade(@color-border, 35%);
padding-bottom: @spacing-xs;
i { font-size: 0.85rem; }
}
.dnc-compact-concept {
font-size: 0.8rem;
color: @color-muted;
font-style: italic;
margin: @spacing-xs 0 0;
}
// Grille de caractéristiques compacte
.dnc-compact-carac-grid {
display: flex;
flex-wrap: wrap;
gap: @spacing-sm;
}
.dnc-carac-stat {
display: flex;
flex-direction: column;
align-items: center;
min-width: 48px;
background: rgba(255, 255, 255, 0.6);
border: 1px solid fade(@color-border, 40%);
border-radius: @radius-sm;
padding: @spacing-xs @spacing-sm;
}
.dnc-carac-label {
font-size: 0.68rem;
text-transform: uppercase;
letter-spacing: 0.04em;
color: @color-muted;
font-weight: 700;
}
.dnc-carac-value {
font-size: 1rem;
font-weight: 700;
color: @color-ink;
&.warning {
color: @color-failure;
}
}
// Combat stats badges
.dnc-compact-combat-stats {
display: flex;
gap: @spacing-sm;
flex-wrap: wrap;
margin-bottom: @spacing-sm;
}
.dnc-combat-badge {
font-size: 0.82rem;
padding: @spacing-xs @spacing-sm;
background: @color-panel-strong;
border: 1px solid fade(@color-border, 40%);
border-radius: @radius-sm;
color: @color-ink;
}
// Listes d'items compactes
.dnc-compact-item-list {
margin-top: @spacing-sm;
}
.dnc-compact-subhead {
font-size: 0.78rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
color: @color-muted;
margin: 0 0 @spacing-xs;
display: flex;
align-items: center;
gap: @spacing-xs;
i { color: @color-accent; }
}
.dnc-compact-item {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: @spacing-xs;
padding: @spacing-xs 0;
border-top: 1px dashed fade(@color-border, 35%);
font-size: 0.85rem;
&:first-of-type {
border-top: 0;
}
}
.dnc-compact-item-name {
font-weight: 600;
color: @color-ink;
}
.dnc-compact-item-detail {
font-size: 0.78rem;
color: @color-muted;
background: rgba(255, 255, 255, 0.5);
border-radius: @radius-sm;
padding: 0.05rem 0.3rem;
}
// Stats magie compactes
.dnc-compact-magic-stats {
display: flex;
flex-wrap: wrap;
gap: @spacing-sm;
margin-bottom: @spacing-sm;
}
// Réutilisation de .item-meta pour les badges de sortilège/capacité
.item-meta {
border-radius: 999px;
background: @color-panel-strong;
color: @color-ink;
font-size: 0.75rem;
padding: 0.1rem 0.5rem;
}
// ============================================
// Fiche client dans sous-panneau
// ============================================
.client-summary {
margin: @spacing-sm 0 0;
color: @color-ink;
font-size: 0.9rem;
line-height: 1.45;
}
.client-description {
margin-top: @spacing-md;
padding-top: @spacing-md;
border-top: 1px solid fade(@color-border, 45%);
p {
margin: 0;
color: @color-muted;
font-size: 0.85rem;
line-height: 1.5;
}
}
// ============================================
// Roll buttons dans vue clients
// ============================================
.dnc-roll-btn {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.1rem 0.5rem;
font-size: 0.78rem;
font-family: @font-body;
color: @color-ink;
background: @color-panel-strong;
border: 1px solid fade(@color-border, 60%);
border-radius: @radius-sm;
cursor: pointer;
transition: background 0.15s;
i { font-size: 0.7rem; color: @color-accent; }
&:hover {
background: @color-accent;
color: #fff;
border-color: @color-accent;
i { color: #fff; }
}
}
.dnc-carac-rollable {
cursor: pointer;
transition: background 0.15s;
border-radius: @radius-sm;
position: relative;
&:hover {
background: fade(@color-accent, 18%);
.dnc-carac-label { color: @color-accent; }
}
}
// Bouton ouvrir fiche dans les titres de section
.dnc-compact-section-title {
display: flex;
align-items: center;
gap: 0.4rem;
.dnc-open-sheet-btn {
margin-left: auto;
background: none;
border: 1px solid fade(@color-border, 50%);
border-radius: @radius-sm;
color: @color-muted;
cursor: pointer;
font-size: 0.7rem;
padding: 0.1rem 0.35rem;
line-height: 1;
transition: color 0.15s, border-color 0.15s;
&:hover {
color: @color-accent;
border-color: @color-accent;
}
}
}
+1
View File
@@ -20,6 +20,7 @@
@color-failure: #842c2c; @color-failure: #842c2c;
@color-shadow: rgba(0, 0, 0, 0.22); @color-shadow: rgba(0, 0, 0, 0.22);
@spacing-xs: 0.25rem;
@spacing-sm: 0.4rem; @spacing-sm: 0.4rem;
@spacing-md: 0.75rem; @spacing-md: 0.75rem;
@spacing-lg: 1rem; @spacing-lg: 1rem;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,363 @@
/**
* Donjon & Cie - Systeme FoundryVTT
*
* Fenêtre de dialogue pour afficher les employés (PJ)
* Structure : 2 onglets racine (Employés / Clients),
* avec sous-onglets par PC ou par client.
*
* @author LeRatierBretonnien
* @copyright 20252026 LeRatierBretonnien
* @license CC BY-NC-SA 4.0
*/
import { DonjonEtCieUtility } from "../donjon-et-cie-utility.mjs";
import { DONJON_ET_CIE } from "../donjon-et-cie-config.mjs";
export class DonjonEtCieEmployesDialog {
/**
* Ouvre la fenêtre des employés
*/
static async open() {
const pcs = this.#getPlayerCharacters();
const clientTokens = this.#getClients();
const characteristicKeys = this.#getCharacteristicKeys();
const pcsData = await Promise.all(
pcs.map(async (pc) => this.#preparePcData(pc, characteristicKeys))
);
const templateContext = {
pcs: pcsData,
clients: clientTokens.map(t => this.#prepareClientData(t)),
characteristicKeys
};
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-donjon-et-cie/templates/dialogs/employes-dialog.hbs",
templateContext
);
return foundry.applications.api.DialogV2.wait({
window: {
title: "Employés",
icon: "fa-solid fa-hard-hat"
},
classes: ["fvtt-donjon-et-cie", "dnc-employes-dialog-wrapper"],
content,
modal: false,
buttons: [
{
action: "close",
label: "Fermer",
icon: "fa-solid fa-xmark",
callback: () => true
}
],
rejectClose: false,
render: (event, dialog) => this.#setupTabs(dialog)
});
}
/**
* Récupère les personnages joueurs (employés)
*/
static #getPlayerCharacters() {
return game.actors.filter(a => a.type === "character" || a.hasPlayerOwner);
}
/**
* Récupère les PNJ clients de la scène courante (retourne les TokenDocuments)
*/
static #getClients() {
const scene = canvas?.scene ?? game.scenes?.current;
if (!scene) return [];
return scene.tokens.filter(token => {
const actor = token.actor;
return actor &&
!actor.hasPlayerOwner &&
actor.type === "pnj" &&
actor.system.categorie === "Client";
});
}
/**
* Récupère les clés des caractéristiques
*/
static #getCharacteristicKeys() {
return Object.entries(DONJON_ET_CIE.characteristics).map(([key, metadata]) => ({
key,
label: metadata.label,
short: metadata.short
}));
}
/**
* Prépare les données d'un PJ pour l'affichage
*/
static async #preparePcData(pc, characteristicKeys) {
const sys = pc.system || {};
const items = pc.items?.contents || [];
const pvValue = sys.sante?.pv?.value ?? 0;
const pvMax = sys.sante?.pv?.max ?? 0;
const dv = sys.sante?.dv ?? "1d6";
const meleeAttacks = sys.combat?.attaquesCorpsACorps ?? 1;
const rangedAttacks = sys.combat?.attaquesDistance ?? 1;
const magicResources = DonjonEtCieUtility.getMagicResourceContext(pc);
const weapons = [];
const armors = [];
const spells = [];
const capacities = [];
for (const item of items) {
const itemSys = item.system || {};
if (item.type === "arme") {
const categoryLabel = itemSys.categorie === "distance" ? "Distance" : "Corps à corps";
const handsLabel = (itemSys.mains ?? 1) > 1 ? "2 mains" : "1 main";
weapons.push({
name: item.name,
categoryLabel,
handsLabel,
damage: itemSys.degatsEstUsageDe
? `${itemSys.degats}(Δ)`
: (itemSys.degats || "—"),
range: itemSys.portee || "Contact",
ammunition: itemSys.munitionsDelta != null
? (itemSys.munitionsDelta === 0 ? "Épuisées" : `Δ${itemSys.munitionsDelta}`)
: ""
});
}
if (item.type === "armure") {
armors.push({
name: item.name,
protectionDie: `Δ${itemSys.delta || 0}`,
encumbrance: itemSys.encombrement || "—",
remainingProtection: itemSys.resultatProtection || "—"
});
}
if (item.type === "sortilege") {
spells.push({
name: item.name,
usageLabel: itemSys.delta > 0 ? DonjonEtCieUtility.formatUsageDie(itemSys.delta) : null
});
}
if (item.type === "capacite") {
capacities.push({
name: item.name,
usageLabel: itemSys.delta > 0 ? DonjonEtCieUtility.formatUsageDie(itemSys.delta) : null
});
}
}
// Spread des valeurs de caractéristiques directement sur l'objet pc
const characteristics = {};
characteristicKeys.forEach(({ key }) => {
characteristics[key] = sys.caracteristiques?.[key]?.value ?? 0;
});
return {
actorId: pc.id,
name: pc.name,
concept: pc.system.concept || "",
pvValue,
pvMax,
dv,
meleeAttacks,
rangedAttacks,
weapons,
armors,
spells,
capacities,
magicRank: magicResources.rank,
focusDisplay: magicResources.focusDisplay,
chaosDisplay: magicResources.chaosLabel,
...characteristics
};
}
/**
* Prépare les données d'un client (PNJ) depuis son TokenDocument
*/
static #prepareClientData(token) {
const client = token.actor;
const sys = client.system || {};
const items = client.items?.contents || [];
// Attaques système du modèle PNJ
const attaques = (sys.attaques || []).filter(a => a.nom || a.degats);
// Items du PNJ
const weapons = [];
const spells = [];
const capacities = [];
for (const item of items) {
const itemSys = item.system || {};
if (item.type === "arme") {
weapons.push({
name: item.name,
damage: itemSys.degatsEstUsageDe
? `${itemSys.degats}(Δ)`
: (itemSys.degats || "—"),
categoryLabel: itemSys.categorie === "distance" ? "Distance" : "Corps à corps"
});
}
if (item.type === "sortilege") {
spells.push({
name: item.name,
usageLabel: itemSys.delta > 0 ? DonjonEtCieUtility.formatUsageDie(itemSys.delta) : null
});
}
if (item.type === "capacite") {
capacities.push({
name: item.name,
usageLabel: itemSys.delta > 0 ? DonjonEtCieUtility.formatUsageDie(itemSys.delta) : null
});
}
}
return {
id: client.id,
tokenUuid: token.uuid,
name: client.name,
species: sys.espece || "",
category: sys.categorie || "",
role: sys.role || "",
summary: sys.resume || "",
pvValue: sys.sante?.pv?.value ?? 0,
pvMax: sys.sante?.pv?.max ?? 0,
dv: sys.sante?.dv || "1d8",
armureDelta: sys.defense?.armure?.delta ?? 0,
armureProtection: sys.defense?.armure?.resultatProtection ?? 0,
courageDelta: sys.defense?.courage?.delta ?? 0,
attaques,
weapons,
spells,
capacities,
pouvoirsSpeciaux: sys.pouvoirsSpeciaux || "",
hasMagie: spells.length > 0 || capacities.length > 0 || !!(sys.pouvoirsSpeciaux || "").trim()
};
}
/**
* Configuration des onglets à 2 niveaux
* Niveau 1 : Employés / Clients (root tabs)
* Niveau 2 : un onglet par PC ou par client (sub-tabs)
*/
static #setupTabs(dialog) {
const root = dialog.element;
// ---- Onglets racine ----
const rootTabs = root.querySelectorAll('.dnc-root-tab');
const rootPanels = root.querySelectorAll('.dnc-root-panel');
const activateRoot = (tabName) => {
rootTabs.forEach(btn => btn.classList.toggle('active', btn.dataset.rootTab === tabName));
rootPanels.forEach(panel => panel.classList.toggle('active', panel.dataset.rootPanel === tabName));
};
rootTabs.forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
activateRoot(btn.dataset.rootTab);
});
});
// Activer premier onglet racine par défaut
if (rootTabs.length > 0) activateRoot(rootTabs[0].dataset.rootTab);
// ---- Sous-onglets PC ----
const pcTabs = root.querySelectorAll('.dnc-pc-tab[data-pc-tab]');
const pcPanels = root.querySelectorAll('.dnc-pc-panel[data-pc-panel]');
const activatePc = (actorId) => {
pcTabs.forEach(btn => btn.classList.toggle('active', btn.dataset.pcTab === actorId));
pcPanels.forEach(panel => panel.classList.toggle('active', panel.dataset.pcPanel === actorId));
};
pcTabs.forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
activatePc(btn.dataset.pcTab);
});
});
if (pcTabs.length > 0) activatePc(pcTabs[0].dataset.pcTab);
// ---- Sous-onglets Clients ----
const clientTabs = root.querySelectorAll('.dnc-pc-tab[data-client-tab]');
const clientPanels = root.querySelectorAll('.dnc-client-panel[data-client-panel]');
const activateClient = (clientId) => {
clientTabs.forEach(btn => btn.classList.toggle('active', btn.dataset.clientTab === clientId));
clientPanels.forEach(panel => panel.classList.toggle('active', panel.dataset.clientPanel === clientId));
};
clientTabs.forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
activateClient(btn.dataset.clientTab);
});
});
if (clientTabs.length > 0) activateClient(clientTabs[0].dataset.clientTab);
// ---- Ouverture des fiches ----
root.addEventListener('click', (e) => {
const btn = e.target.closest('.dnc-open-sheet-btn');
if (!btn) return;
e.preventDefault();
e.stopPropagation();
const type = btn.dataset.openSheet;
if (type === "pc") {
const actor = game.actors.get(btn.dataset.actorId);
actor?.sheet.render(true);
} else if (type === "client") {
const tokenDoc = fromUuidSync(btn.dataset.tokenUuid);
const actor = tokenDoc?.actor ?? game.actors.get(btn.dataset.actorId);
actor?.sheet.render(true);
}
});
// ---- Jets de dés depuis la vue clients (même dialogs que la fiche PNJ) ----
root.addEventListener('click', async (e) => {
const btn = e.target.closest('.dnc-roll-btn, .dnc-carac-rollable');
if (!btn) return;
e.preventDefault();
e.stopPropagation();
// Résolution de l'actor : token UUID pour gérer les tokens non-liés
const tokenUuid = btn.dataset.tokenUuid;
const actorId = btn.dataset.actorId;
let actor = null;
if (tokenUuid) {
const tokenDoc = fromUuidSync(tokenUuid);
actor = tokenDoc?.actor ?? null;
}
if (!actor && actorId) {
actor = game.actors.get(actorId);
}
if (!actor) return;
const action = btn.dataset.pnjAction;
switch (action) {
case "rollArmure": return actor.rollPnjArmor();
case "rollCourage": return actor.rollPnjCourage();
case "rollAttaque": return actor.rollPnjAttackDamage(btn.dataset.attackIndex ?? 0);
}
});
}
}
@@ -202,16 +202,18 @@ export class DonjonEtCieRollDialog {
static async createDamage(actor, item) { static async createDamage(actor, item) {
const damageContext = DonjonEtCieUtility.getMartialDamageContext(actor, item); const damageContext = DonjonEtCieUtility.getMartialDamageContext(actor, item);
const isMeleeTwoHanded = item.type === "arme" && item.system?.categorie === "melee" && Number(item.system?.mains ?? 1) > 1;
const defaultMode = isMeleeTwoHanded ? "avantage" : "normal";
const content = await foundry.applications.handlebars.renderTemplate( const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-donjon-et-cie/templates/dialogs/damage-roll.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/damage-roll.hbs",
{ {
actorName: actor?.name ?? item.actor?.name ?? "", actorName: actor?.name ?? item.actor?.name ?? "",
item, item,
actorBonus: actor?.system?.combat?.degatsBonus ?? 0,
damageFormula: damageContext.effectiveFormula || item.system.degats, damageFormula: damageContext.effectiveFormula || item.system.degats,
damageBase: damageContext.baseFormula || item.system.degats, damageBase: damageContext.baseFormula || item.system.degats,
damageCapped: damageContext.capped, damageCapped: damageContext.capped,
martialDvLabel: damageContext.martialDvSides ? `d${damageContext.martialDvSides}` : damageContext.martialDvFormula martialDvLabel: damageContext.martialDvSides ? `d${damageContext.martialDvSides}` : damageContext.martialDvFormula,
defaultMode
} }
); );
+65
View File
@@ -18,6 +18,7 @@ import { DonjonEtCieItem } from "./donjon-et-cie-item.mjs";
import * as models from "./models/index.mjs"; import * as models from "./models/index.mjs";
import * as sheets from "./applications/sheets/_module.mjs"; import * as sheets from "./applications/sheets/_module.mjs";
import { DonjonEtCieRollDialog } from "./applications/donjon-et-cie-roll-dialog.mjs"; import { DonjonEtCieRollDialog } from "./applications/donjon-et-cie-roll-dialog.mjs";
import { DonjonEtCieEmployesDialog } from "./applications/donjon-et-cie-employes-dialog.mjs";
import { DonjonEtCieRolls } from "./donjon-et-cie-rolls.mjs"; import { DonjonEtCieRolls } from "./donjon-et-cie-rolls.mjs";
import { DonjonEtCieMacros } from "./donjon-et-cie-macros.mjs"; import { DonjonEtCieMacros } from "./donjon-et-cie-macros.mjs";
@@ -43,6 +44,18 @@ function injectActorDirectoryMissionPackButton(app, element) {
void game.system.donjonEtCie.macros.openMissionPackDialog(); void game.system.donjonEtCie.macros.openMissionPackDialog();
}); });
headerActions.append(button); headerActions.append(button);
// Bouton "Employés / Clients"
const empButton = document.createElement("button");
empButton.type = "button";
empButton.className = "dnc-employes-button";
empButton.title = game.i18n.localize("DNC.Dialog.EmployeesTitle");
empButton.setAttribute("aria-label", game.i18n.localize("DNC.Dialog.EmployeesTitle"));
empButton.innerHTML = `<i class="fa-solid fa-hard-hat" inert></i><span>${game.i18n.localize("DNC.Dialog.EmployeesTitle")}</span>`;
empButton.addEventListener("click", () => {
void DonjonEtCieEmployesDialog.open();
});
headerActions.append(empButton);
} }
function onChatActionClick(event) { function onChatActionClick(event) {
@@ -233,10 +246,62 @@ Hooks.once("init", async () => {
} }
}); });
// Gestion de la commande /employes
// Enregistrement officiel via ChatLogV2.CHAT_COMMANDS (comme mgt2-compendium-amiral-denisov)
function registerEmployesCommand() {
const ChatLogV2 = foundry.applications.sidebar.tabs.ChatLog;
if (ChatLogV2?.CHAT_COMMANDS) {
ChatLogV2.CHAT_COMMANDS.employes = {
rgx: /^\/employes(?:\s+(.*))?$/i,
fn: () => {
DonjonEtCieEmployesDialog.open();
return false;
},
};
console.log("DNC | Commande /employes enregistrée via ChatLog.CHAT_COMMANDS");
} else {
console.warn("DNC | ChatLog.CHAT_COMMANDS indisponible, utilisation des hooks de fallback");
}
}
Hooks.once("init", () => {
registerEmployesCommand();
});
Hooks.once("ready", () => { Hooks.once("ready", () => {
DonjonEtCieMacros.registerSocketListeners(); DonjonEtCieMacros.registerSocketListeners();
document.addEventListener("click", onChatActionClick); document.addEventListener("click", onChatActionClick);
void maybeCreateWelcomeMessage(); void maybeCreateWelcomeMessage();
// Hooks de fallback pour compatibilité
Hooks.on("preCreateChatMessage", (message, data, options, userId) => {
const content = data.content?.trim()?.toLowerCase();
if (content === "/employes" || content?.startsWith("/employes ")) {
DonjonEtCieEmployesDialog.open();
return false;
}
return true;
});
Hooks.on("chatMessage", (...args) => {
// Gestion compatibilité v13/v14
let message;
if (args[0]?.content !== undefined) {
message = args[0].content; // v14
} else if (typeof args[1] === "string") {
message = args[1]; // v13
} else {
return true;
}
const trimmed = message?.trim()?.toLowerCase();
if (trimmed === "/employes" || trimmed?.startsWith("/employes ")) {
DonjonEtCieEmployesDialog.open();
return false;
}
return true;
});
}); });
Hooks.on("renderActorDirectory", (app, element) => { Hooks.on("renderActorDirectory", (app, element) => {
+13 -5
View File
@@ -372,11 +372,15 @@ export class DonjonEtCieRolls {
} }
if (!isUsageDie && !item.system.degats) return null; if (!isUsageDie && !item.system.degats) return null;
// Arme à 2 mains de corps à corps : avantage automatique
const isMeleeTwoHanded = item.type === "arme" && item.system?.categorie === "melee" && Number(item.system?.mains ?? 1) > 1;
if (isMeleeTwoHanded && mode === "normal") {
mode = "avantage";
}
const damageContext = DonjonEtCieUtility.getMartialDamageContext(actor, item); const damageContext = DonjonEtCieUtility.getMartialDamageContext(actor, item);
const actorBonus = Number(actor?.system?.combat?.degatsBonus ?? 0);
const totalBonus = actorBonus;
const effectiveDamage = damageContext.effectiveFormula || (isUsageDie ? `1d${degatsDelta}` : item.system.degats); const effectiveDamage = damageContext.effectiveFormula || (isUsageDie ? `1d${degatsDelta}` : item.system.degats);
const formula = totalBonus ? `${effectiveDamage} + ${totalBonus}` : effectiveDamage; const formula = effectiveDamage;
const result = await this.#resolveFormulaRoll(formula, {}, { mode, favorable: "high" }); const result = await this.#resolveFormulaRoll(formula, {}, { mode, favorable: "high" });
const targets = DonjonEtCieUtility.getSceneDamageTargets(); const targets = DonjonEtCieUtility.getSceneDamageTargets();
const rollDieLabels = result.rolls.map((roll) => { const rollDieLabels = result.rolls.map((roll) => {
@@ -396,7 +400,6 @@ export class DonjonEtCieRolls {
keptDieLabel, keptDieLabel,
values: result.values, values: result.values,
total: result.kept, total: result.kept,
bonus: totalBonus,
baseDamage: baseDamageDisplay, baseDamage: baseDamageDisplay,
effectiveDamage, effectiveDamage,
damageCapped: damageContext.capped, damageCapped: damageContext.capped,
@@ -414,7 +417,6 @@ export class DonjonEtCieRolls {
baseDamage: baseDamageDisplay, baseDamage: baseDamageDisplay,
effectiveDamage, effectiveDamage,
damageCapped: damageContext.capped, damageCapped: damageContext.capped,
bonus: totalBonus,
values: result.values, values: result.values,
mode: result.mode mode: result.mode
}; };
@@ -427,6 +429,12 @@ export class DonjonEtCieRolls {
return null; return null;
} }
// Arme à 2 mains de corps à corps : avantage automatique
const isMeleeTwoHanded = item.type === "arme" && item.system?.categorie === "melee" && Number(item.system?.mains ?? 1) > 1;
if (isMeleeTwoHanded && mode === "normal") {
mode = "avantage";
}
const resolved = await this.#resolveFormulaRoll(`1d${before}`, {}, { mode, favorable: "high" }); const resolved = await this.#resolveFormulaRoll(`1d${before}`, {}, { mode, favorable: "high" });
const result = resolved.kept; const result = resolved.kept;
const degraded = result <= 3; const degraded = result <= 3;
+4 -3
View File
@@ -36,6 +36,7 @@ export class DonjonEtCieUtility {
"systems/fvtt-donjon-et-cie/templates/dialogs/usage-roll.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/usage-roll.hbs",
"systems/fvtt-donjon-et-cie/templates/dialogs/mission-pack-dialog.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/mission-pack-dialog.hbs",
"systems/fvtt-donjon-et-cie/templates/dialogs/mission-pack-campaign-dialog.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/mission-pack-campaign-dialog.hbs",
"systems/fvtt-donjon-et-cie/templates/dialogs/employes-dialog.hbs",
"systems/fvtt-donjon-et-cie/templates/chat/roll-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/roll-card.hbs",
"systems/fvtt-donjon-et-cie/templates/chat/spell-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/spell-card.hbs",
"systems/fvtt-donjon-et-cie/templates/chat/chaos-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/chaos-card.hbs",
@@ -76,8 +77,8 @@ export class DonjonEtCieUtility {
const scene = canvas?.scene ?? game.scenes?.current; const scene = canvas?.scene ?? game.scenes?.current;
const tokens = scene?.tokens?.contents ?? []; const tokens = scene?.tokens?.contents ?? [];
const selectedTokens = canvas?.tokens?.controlled ?? []; const targetedTokens = game.user?.targets ?? new Set();
const selectedTokenUuid = selectedTokens.length === 1 ? selectedTokens[0]?.uuid : null; const targetedTokenUuid = targetedTokens?.first()?.document.uuid ?? null;
return tokens return tokens
.map((token) => { .map((token) => {
@@ -93,7 +94,7 @@ export class DonjonEtCieUtility {
tokenUuid: token.uuid, tokenUuid: token.uuid,
actorUuid: actor.uuid, actorUuid: actor.uuid,
label, label,
isSelected: token.uuid === selectedTokenUuid isSelected: token.uuid === targetedTokenUuid
}; };
}) })
.filter(Boolean) .filter(Boolean)
+1 -1
View File
@@ -17,7 +17,7 @@ export default class CapaciteDataModel extends BaseItemDataModel {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
...super.defineSchema(), ...super.defineSchema(),
cout: new fields.StringField({ initial: "" }), delta: new fields.NumberField({ initial: 4, integer: true }),
effet: new fields.StringField({ initial: "" }) effet: new fields.StringField({ initial: "" })
}; };
} }
-1
View File
@@ -50,7 +50,6 @@ export default class EmployeDataModel extends foundry.abstract.TypeDataModel {
}), }),
combat: new fields.SchemaField({ combat: new fields.SchemaField({
initiativeBonus: new fields.NumberField({ initial: 0, integer: true }), initiativeBonus: new fields.NumberField({ initial: 0, integer: true }),
degatsBonus: new fields.NumberField({ initial: 0, integer: true }),
attaquesCorpsACorps: new fields.NumberField({ initial: 1, integer: true }), attaquesCorpsACorps: new fields.NumberField({ initial: 1, integer: true }),
attaquesDistance: new fields.NumberField({ initial: 1, integer: true }) attaquesDistance: new fields.NumberField({ initial: 1, integer: true })
}), }),
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000165 MANIFEST-000197
+7 -7
View File
@@ -1,7 +1,7 @@
2026/05/28-20:45:21.096499 7f3977fff6c0 Recovering log #163 2026/06/06-23:10:57.541038 7f15cfbff6c0 Recovering log #195
2026/05/28-20:45:21.107112 7f3977fff6c0 Delete type=3 #161 2026/06/06-23:10:57.550756 7f15cfbff6c0 Delete type=3 #193
2026/05/28-20:45:21.107149 7f3977fff6c0 Delete type=0 #163 2026/06/06-23:10:57.550776 7f15cfbff6c0 Delete type=0 #195
2026/05/28-20:46:40.064748 7f39767fc6c0 Level-0 table #168: started 2026/06/06-23:11:47.018151 7f15cdbfb6c0 Level-0 table #200: started
2026/05/28-20:46:40.064805 7f39767fc6c0 Level-0 table #168: 0 bytes OK 2026/06/06-23:11:47.018166 7f15cdbfb6c0 Level-0 table #200: 0 bytes OK
2026/05/28-20:46:40.070966 7f39767fc6c0 Delete type=0 #166 2026/06/06-23:11:47.024292 7f15cdbfb6c0 Delete type=0 #198
2026/05/28-20:46:40.093671 7f39767fc6c0 Manual compaction at level-0 from '!folders!K9aiFu0dE6UYiXBd' @ 72057594037927935 : 1 .. '!items!zyqLzmpbHxK3jt5q' @ 0 : 0; will stop at (end) 2026/06/06-23:11:47.037199 7f15cdbfb6c0 Manual compaction at level-0 from '!folders!K9aiFu0dE6UYiXBd' @ 72057594037927935 : 1 .. '!items!zyqLzmpbHxK3jt5q' @ 0 : 0; will stop at (end)
+7 -7
View File
@@ -1,7 +1,7 @@
2026/05/27-07:43:05.019282 7f3ebcffe6c0 Recovering log #160 2026/06/04-14:50:00.699171 7f48093fc6c0 Recovering log #191
2026/05/27-07:43:05.053650 7f3ebcffe6c0 Delete type=0 #160 2026/06/04-14:50:00.708677 7f48093fc6c0 Delete type=3 #189
2026/05/27-07:43:05.053700 7f3ebcffe6c0 Delete type=3 #159 2026/06/04-14:50:00.708723 7f48093fc6c0 Delete type=0 #191
2026/05/27-12:02:21.293983 7f3e6effd6c0 Level-0 table #164: started 2026/06/04-15:50:07.956118 7f4808bfb6c0 Level-0 table #196: started
2026/05/27-12:02:21.294005 7f3e6effd6c0 Level-0 table #164: 0 bytes OK 2026/06/04-15:50:07.956134 7f4808bfb6c0 Level-0 table #196: 0 bytes OK
2026/05/27-12:02:21.299943 7f3e6effd6c0 Delete type=0 #162 2026/06/04-15:50:07.962406 7f4808bfb6c0 Delete type=0 #194
2026/05/27-12:02:21.306619 7f3e6effd6c0 Manual compaction at level-0 from '!folders!K9aiFu0dE6UYiXBd' @ 72057594037927935 : 1 .. '!items!zyqLzmpbHxK3jt5q' @ 0 : 0; will stop at (end) 2026/06/04-15:50:07.974303 7f4808bfb6c0 Manual compaction at level-0 from '!folders!K9aiFu0dE6UYiXBd' @ 72057594037927935 : 1 .. '!items!zyqLzmpbHxK3jt5q' @ 0 : 0; will stop at (end)
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000152 MANIFEST-000184
+7 -7
View File
@@ -1,7 +1,7 @@
2026/05/28-20:45:21.112773 7f3976ffd6c0 Recovering log #150 2026/06/06-23:10:57.552568 7f15cf3fe6c0 Recovering log #182
2026/05/28-20:45:21.122136 7f3976ffd6c0 Delete type=3 #148 2026/06/06-23:10:57.561722 7f15cf3fe6c0 Delete type=3 #180
2026/05/28-20:45:21.122181 7f3976ffd6c0 Delete type=0 #150 2026/06/06-23:10:57.561745 7f15cf3fe6c0 Delete type=0 #182
2026/05/28-20:46:40.081166 7f39767fc6c0 Level-0 table #155: started 2026/06/06-23:11:47.024342 7f15cdbfb6c0 Level-0 table #187: started
2026/05/28-20:46:40.081191 7f39767fc6c0 Level-0 table #155: 0 bytes OK 2026/06/06-23:11:47.024356 7f15cdbfb6c0 Level-0 table #187: 0 bytes OK
2026/05/28-20:46:40.087200 7f39767fc6c0 Delete type=0 #153 2026/06/06-23:11:47.031271 7f15cdbfb6c0 Delete type=0 #185
2026/05/28-20:46:40.093692 7f39767fc6c0 Manual compaction at level-0 from '!tables!PPsxQgHwLCQ2gjSW' @ 72057594037927935 : 1 .. '!tables.results!wJZXUo4q5b5vE3Dy.zFTPLMc9zOl5hISV' @ 0 : 0; will stop at (end) 2026/06/06-23:11:47.037203 7f15cdbfb6c0 Manual compaction at level-0 from '!tables!PPsxQgHwLCQ2gjSW' @ 72057594037927935 : 1 .. '!tables.results!wJZXUo4q5b5vE3Dy.zFTPLMc9zOl5hISV' @ 0 : 0; will stop at (end)
+7 -7
View File
@@ -1,7 +1,7 @@
2026/05/27-07:43:05.067367 7f3e6ffff6c0 Recovering log #147 2026/06/04-14:50:00.712544 7f480abff6c0 Recovering log #178
2026/05/27-07:43:05.105287 7f3e6ffff6c0 Delete type=0 #147 2026/06/04-14:50:00.722759 7f480abff6c0 Delete type=3 #176
2026/05/27-07:43:05.105329 7f3e6ffff6c0 Delete type=3 #146 2026/06/04-14:50:00.722803 7f480abff6c0 Delete type=0 #178
2026/05/27-12:02:21.300011 7f3e6effd6c0 Level-0 table #151: started 2026/06/04-15:50:07.962494 7f4808bfb6c0 Level-0 table #183: started
2026/05/27-12:02:21.300030 7f3e6effd6c0 Level-0 table #151: 0 bytes OK 2026/06/04-15:50:07.962517 7f4808bfb6c0 Level-0 table #183: 0 bytes OK
2026/05/27-12:02:21.306452 7f3e6effd6c0 Delete type=0 #149 2026/06/04-15:50:07.968348 7f4808bfb6c0 Delete type=0 #181
2026/05/27-12:02:21.306630 7f3e6effd6c0 Manual compaction at level-0 from '!tables!PPsxQgHwLCQ2gjSW' @ 72057594037927935 : 1 .. '!tables.results!wJZXUo4q5b5vE3Dy.zFTPLMc9zOl5hISV' @ 0 : 0; will stop at (end) 2026/06/04-15:50:07.974311 7f4808bfb6c0 Manual compaction at level-0 from '!tables!PPsxQgHwLCQ2gjSW' @ 72057594037927935 : 1 .. '!tables.results!wJZXUo4q5b5vE3Dy.zFTPLMc9zOl5hISV' @ 0 : 0; will stop at (end)
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000087 MANIFEST-000017
+7 -8
View File
@@ -1,8 +1,7 @@
2026/05/28-20:45:21.125760 7f39777fe6c0 Recovering log #85 2026/06/06-23:10:57.562841 7f15ce3fc6c0 Recovering log #14
2026/05/28-20:45:21.135844 7f39777fe6c0 Delete type=3 #83 2026/06/06-23:10:57.573701 7f15ce3fc6c0 Delete type=3 #12
2026/05/28-20:45:21.135894 7f39777fe6c0 Delete type=0 #85 2026/06/06-23:10:57.573721 7f15ce3fc6c0 Delete type=0 #14
2026/05/28-20:46:40.071060 7f39767fc6c0 Level-0 table #90: started 2026/06/06-23:11:47.031359 7f15cdbfb6c0 Level-0 table #20: started
2026/05/28-20:46:40.071086 7f39767fc6c0 Level-0 table #90: 0 bytes OK 2026/06/06-23:11:47.031371 7f15cdbfb6c0 Level-0 table #20: 0 bytes OK
2026/05/28-20:46:40.081072 7f39767fc6c0 Delete type=0 #88 2026/06/06-23:11:47.037096 7f15cdbfb6c0 Delete type=0 #18
2026/05/28-20:46:40.093682 7f39767fc6c0 Manual compaction at level-0 from '!journal!69Da9YvF9BfOV7oK' @ 72057594037927935 : 1 .. '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 0 : 0; will stop at (end) 2026/06/06-23:11:47.037206 7f15cdbfb6c0 Manual compaction at level-0 from '!journal!69Da9YvF9BfOV7oK' @ 72057594037927935 : 1 .. '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 0 : 0; will stop at (end)
2026/05/28-20:46:40.093703 7f39767fc6c0 Manual compaction at level-1 from '!journal!69Da9YvF9BfOV7oK' @ 72057594037927935 : 1 .. '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 0 : 0; will stop at (end)
+11 -8
View File
@@ -1,8 +1,11 @@
2026/05/27-07:43:05.111469 7f3ebd7ff6c0 Recovering log #82 2026/06/04-14:50:00.786072 7f480abff6c0 Delete type=3 #1
2026/05/27-07:43:05.152415 7f3ebd7ff6c0 Delete type=0 #82 2026/06/04-15:50:07.968402 7f4808bfb6c0 Level-0 table #15: started
2026/05/27-07:43:05.152455 7f3ebd7ff6c0 Delete type=3 #81 2026/06/04-15:50:07.968417 7f4808bfb6c0 Level-0 table #15: 0 bytes OK
2026/05/27-12:02:21.287508 7f3e6effd6c0 Level-0 table #86: started 2026/06/04-15:50:07.974190 7f4808bfb6c0 Delete type=0 #13
2026/05/27-12:02:21.287536 7f3e6effd6c0 Level-0 table #86: 0 bytes OK 2026/06/04-15:50:07.981412 7f4808bfb6c0 Manual compaction at level-0 from '!journal!69Da9YvF9BfOV7oK' @ 72057594037927935 : 1 .. '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 0 : 0; will stop at '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 3 : 1
2026/05/27-12:02:21.293897 7f3e6effd6c0 Delete type=0 #84 2026/06/04-15:50:07.981419 7f4808bfb6c0 Compacting 1@0 + 0@1 files
2026/05/27-12:02:21.306608 7f3e6effd6c0 Manual compaction at level-0 from '!journal!69Da9YvF9BfOV7oK' @ 72057594037927935 : 1 .. '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 0 : 0; will stop at (end) 2026/06/04-15:50:07.984886 7f4808bfb6c0 Generated table #16@0: 2 keys, 3183 bytes
2026/05/27-12:02:21.319818 7f3e6effd6c0 Manual compaction at level-1 from '!journal!69Da9YvF9BfOV7oK' @ 72057594037927935 : 1 .. '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 0 : 0; will stop at (end) 2026/06/04-15:50:07.984899 7f4808bfb6c0 Compacted 1@0 + 0@1 files => 3183 bytes
2026/06/04-15:50:07.990637 7f4808bfb6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/06/04-15:50:07.990821 7f4808bfb6c0 Delete type=2 #10
2026/06/04-15:50:08.000067 7f4808bfb6c0 Manual compaction at level-0 from '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 3 : 1 .. '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
+353 -1
View File
@@ -271,6 +271,13 @@
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: 0.75rem; gap: 0.75rem;
} }
.dnc-actor-sheet .hp-field .counter-field input[type="number"] {
max-width: 60px;
text-align: center;
}
.dnc-actor-sheet .hp-field .counter-field {
gap: 0.25rem;
}
.dnc-actor-sheet .profile-card-wide { .dnc-actor-sheet .profile-card-wide {
width: 100%; width: 100%;
} }
@@ -879,9 +886,31 @@
.chat-targeting { .chat-targeting {
position: relative; position: relative;
z-index: 1; z-index: 1;
display: grid; display: flex;
gap: 0.4rem; gap: 0.4rem;
margin-top: 0.4rem; margin-top: 0.4rem;
flex-wrap: wrap;
align-items: end;
}
.chat-targeting .chat-action-button {
padding: 0.35rem 0.65rem;
min-height: 1.75rem;
font-size: 0.82rem;
flex: 0 0 auto;
}
.dnc-chat-card-damage .chat-actions-inline {
flex-wrap: nowrap;
gap: 0.25rem;
}
.dnc-chat-card-damage .chat-actions-inline .chat-action-button {
padding: 0.3rem 0.55rem;
min-height: 1.65rem;
font-size: 0.78rem;
flex: 0 0 auto;
}
.dnc-chat-card-damage .chat-actions-inline .chat-action-button i {
font-size: 0.85rem;
margin-right: 0.2rem;
} }
.chat-control { .chat-control {
display: grid; display: grid;
@@ -1083,5 +1112,328 @@
.initiative-sync p { .initiative-sync p {
margin: 0.4rem 0 0; margin: 0.4rem 0 0;
} }
.dnc-employes-dialog {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 1rem;
min-width: 640px;
max-width: 90vw;
max-height: 90vh;
overflow-y: auto;
}
.dnc-employes-dialog .sheet-header {
display: block;
grid-template-columns: none;
gap: normal;
}
.dnc-employes-dialog .sheet-header h1 {
font-family: "IM Fell English SC", "Palatino Linotype", "Book Antiqua", Palatino, serif;
font-size: 1.4rem;
color: #8b2e17;
text-transform: uppercase;
letter-spacing: 0.04em;
margin: 0;
text-align: center;
}
.dnc-employes-dialog-wrapper .dialog-content,
.dnc-employes-dialog-wrapper .window-content {
padding: 0;
}
.dnc-employes-dialog-wrapper .dialog-footer {
display: none;
}
.dnc-root-tabs {
display: flex;
justify-content: center;
gap: 0.75rem;
border-bottom: 2px solid #5b4634;
padding-bottom: 0.4rem;
}
.dnc-root-tab {
border: 1px solid rgba(91, 70, 52, 0.55);
border-radius: 10px 10px 0 0;
background: #e2d0b1;
color: #221b18;
font-size: 0.9rem;
font-weight: 700;
padding: 0.4rem 1rem;
display: flex;
align-items: center;
gap: 0.25rem;
cursor: pointer;
transition: all 0.2s ease;
}
.dnc-root-tab i {
font-size: 0.9rem;
}
.dnc-root-tab.active {
background: #8b2e17;
border-color: #561d0e;
color: #fff;
box-shadow: 0 -3px 8px rgba(0, 0, 0, 0.2);
position: relative;
bottom: -2px;
}
.dnc-root-tab:hover:not(.active) {
background: #e2d0b1;
border-color: rgba(91, 70, 52, 0.75);
}
.dnc-root-panels {
position: relative;
}
.dnc-root-panel {
display: none;
}
.dnc-root-panel.active {
display: block;
}
.dnc-pc-tabs {
display: flex;
gap: 0.25rem;
flex-wrap: wrap;
padding: 0.4rem 0;
border-bottom: 1px solid rgba(91, 70, 52, 0.4);
margin-bottom: 0.75rem;
}
.dnc-pc-tab {
border: 1px solid rgba(91, 70, 52, 0.45);
border-radius: 6px;
background: rgba(255, 255, 255, 0.5);
color: #6d5a4f;
font-size: 0.82rem;
font-weight: 600;
padding: 0.25rem 0.75rem;
cursor: pointer;
transition: all 0.15s ease;
white-space: nowrap;
}
.dnc-pc-tab.active {
background: #8b2e17;
border-color: #5f2010;
color: #fff;
font-weight: 700;
}
.dnc-pc-tab:hover:not(.active) {
background: #e2d0b1;
color: #221b18;
}
.dnc-pc-panels {
position: relative;
}
.dnc-pc-panel,
.dnc-client-panel {
display: none;
}
.dnc-pc-panel.active,
.dnc-client-panel.active {
display: block;
}
.dnc-compact-section {
padding: 0.75rem;
margin-bottom: 0.4rem;
border: 1px solid rgba(91, 70, 52, 0.4);
border-radius: 10px;
background: #f1e5d0;
}
.dnc-compact-section + .dnc-compact-section {
margin-top: 0.4rem;
}
.dnc-compact-section-title {
font-family: "IM Fell English SC", "Palatino Linotype", "Book Antiqua", Palatino, serif;
font-size: 0.9rem;
font-weight: 700;
color: #8b2e17;
text-transform: uppercase;
letter-spacing: 0.05em;
margin: 0 0 0.4rem;
display: flex;
align-items: center;
gap: 0.25rem;
border-bottom: 1px solid rgba(91, 70, 52, 0.35);
padding-bottom: 0.25rem;
}
.dnc-compact-section-title i {
font-size: 0.85rem;
}
.dnc-compact-concept {
font-size: 0.8rem;
color: #6d5a4f;
font-style: italic;
margin: 0.25rem 0 0;
}
.dnc-compact-carac-grid {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.dnc-carac-stat {
display: flex;
flex-direction: column;
align-items: center;
min-width: 48px;
background: rgba(255, 255, 255, 0.6);
border: 1px solid rgba(91, 70, 52, 0.4);
border-radius: 6px;
padding: 0.25rem 0.4rem;
}
.dnc-carac-label {
font-size: 0.68rem;
text-transform: uppercase;
letter-spacing: 0.04em;
color: #6d5a4f;
font-weight: 700;
}
.dnc-carac-value {
font-size: 1rem;
font-weight: 700;
color: #221b18;
}
.dnc-carac-value.warning {
color: #842c2c;
}
.dnc-compact-combat-stats {
display: flex;
gap: 0.4rem;
flex-wrap: wrap;
margin-bottom: 0.4rem;
}
.dnc-combat-badge {
font-size: 0.82rem;
padding: 0.25rem 0.4rem;
background: #e2d0b1;
border: 1px solid rgba(91, 70, 52, 0.4);
border-radius: 6px;
color: #221b18;
}
.dnc-compact-item-list {
margin-top: 0.4rem;
}
.dnc-compact-subhead {
font-size: 0.78rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
color: #6d5a4f;
margin: 0 0 0.25rem;
display: flex;
align-items: center;
gap: 0.25rem;
}
.dnc-compact-subhead i {
color: #8b2e17;
}
.dnc-compact-item {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0;
border-top: 1px dashed rgba(91, 70, 52, 0.35);
font-size: 0.85rem;
}
.dnc-compact-item:first-of-type {
border-top: 0;
}
.dnc-compact-item-name {
font-weight: 600;
color: #221b18;
}
.dnc-compact-item-detail {
font-size: 0.78rem;
color: #6d5a4f;
background: rgba(255, 255, 255, 0.5);
border-radius: 6px;
padding: 0.05rem 0.3rem;
}
.dnc-compact-magic-stats {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-bottom: 0.4rem;
}
.item-meta {
border-radius: 999px;
background: #e2d0b1;
color: #221b18;
font-size: 0.75rem;
padding: 0.1rem 0.5rem;
}
.client-summary {
margin: 0.4rem 0 0;
color: #221b18;
font-size: 0.9rem;
line-height: 1.45;
}
.client-description {
margin-top: 0.75rem;
padding-top: 0.75rem;
border-top: 1px solid rgba(91, 70, 52, 0.45);
}
.client-description p {
margin: 0;
color: #6d5a4f;
font-size: 0.85rem;
line-height: 1.5;
}
.dnc-roll-btn {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.1rem 0.5rem;
font-size: 0.78rem;
font-family: "Signika", sans-serif;
color: #221b18;
background: #e2d0b1;
border: 1px solid rgba(91, 70, 52, 0.6);
border-radius: 6px;
cursor: pointer;
transition: background 0.15s;
}
.dnc-roll-btn i {
font-size: 0.7rem;
color: #8b2e17;
}
.dnc-roll-btn:hover {
background: #8b2e17;
color: #fff;
border-color: #8b2e17;
}
.dnc-roll-btn:hover i {
color: #fff;
}
.dnc-carac-rollable {
cursor: pointer;
transition: background 0.15s;
border-radius: 6px;
position: relative;
}
.dnc-carac-rollable:hover {
background: rgba(139, 46, 23, 0.18);
}
.dnc-carac-rollable:hover .dnc-carac-label {
color: #8b2e17;
}
.dnc-compact-section-title {
display: flex;
align-items: center;
gap: 0.4rem;
}
.dnc-compact-section-title .dnc-open-sheet-btn {
margin-left: auto;
background: none;
border: 1px solid rgba(91, 70, 52, 0.5);
border-radius: 6px;
color: #6d5a4f;
cursor: pointer;
font-size: 0.7rem;
padding: 0.1rem 0.35rem;
line-height: 1;
transition: color 0.15s, border-color 0.15s;
}
.dnc-compact-section-title .dnc-open-sheet-btn:hover {
color: #8b2e17;
border-color: #8b2e17;
}
/*# sourceMappingURL=donjon-et-cie.css.map */ /*# sourceMappingURL=donjon-et-cie.css.map */
/*# sourceMappingURL=donjon-et-cie.css.map */ /*# sourceMappingURL=donjon-et-cie.css.map */
File diff suppressed because one or more lines are too long
-4
View File
@@ -46,10 +46,6 @@
<span>Attaques distance</span> <span>Attaques distance</span>
<input type="number" name="system.combat.attaquesDistance" value="{{system.combat.attaquesDistance}}"> <input type="number" name="system.combat.attaquesDistance" value="{{system.combat.attaquesDistance}}">
</label> </label>
<label>
<span>Bonus degats</span>
<input type="number" name="system.combat.degatsBonus" value="{{system.combat.degatsBonus}}">
</label>
</div> </div>
</header> </header>
+1 -2
View File
@@ -14,7 +14,6 @@
{{#if modeLabel}}<span class="chat-pill">{{modeLabel}}</span>{{/if}} {{#if modeLabel}}<span class="chat-pill">{{modeLabel}}</span>{{/if}}
<span class="chat-pill success">Dé {{keptDieLabel}}</span> <span class="chat-pill success">Dé {{keptDieLabel}}</span>
{{#if damageCapped}}<span class="chat-pill">{{localize "DNC.Chat.DamageCapped" damage=effectiveDamage dv=martialDvLabel}}</span>{{/if}} {{#if damageCapped}}<span class="chat-pill">{{localize "DNC.Chat.DamageCapped" damage=effectiveDamage dv=martialDvLabel}}</span>{{/if}}
{{#if bonus}}<span class="chat-pill">Bonus +{{bonus}}</span>{{/if}}
</div> </div>
<p class="chat-formula">{{formula}}</p> <p class="chat-formula">{{formula}}</p>
{{#if rollDieLabels.[1]}} {{#if rollDieLabels.[1]}}
@@ -23,7 +22,7 @@
<p class="roll-values">{{#each rollDieLabels}}<span>{{this}}</span>{{/each}}</p> <p class="roll-values">{{#each rollDieLabels}}<span>{{this}}</span>{{/each}}</p>
</div> </div>
{{/if}} {{/if}}
<p class="chat-note"><strong>Base</strong> : {{baseDamage}}{{#if damageCapped}} · <strong>{{localize "DNC.Chat.MartialDv"}}</strong> : {{martialDvLabel}} · <strong>{{localize "DNC.Chat.EffectiveDamage"}}</strong> : {{effectiveDamage}}{{/if}}{{#if bonus}} · <strong>Bonus</strong> : +{{bonus}}{{/if}}</p> <p class="chat-note"><strong>Base</strong> : {{baseDamage}}{{#if damageCapped}} · <strong>{{localize "DNC.Chat.MartialDv"}}</strong> : {{martialDvLabel}} · <strong>{{localize "DNC.Chat.EffectiveDamage"}}</strong> : {{effectiveDamage}}{{/if}}</p>
<div class="chat-targeting"> <div class="chat-targeting">
<label class="chat-control"> <label class="chat-control">
<span class="chat-keyline-label">Cible</span> <span class="chat-keyline-label">Cible</span>
+3 -4
View File
@@ -3,13 +3,12 @@
{{#if damageCapped}} {{#if damageCapped}}
<p>{{localize "DNC.Dialog.DamageCappedByDv" dv=martialDvLabel damage=damageFormula base=damageBase}}</p> <p>{{localize "DNC.Dialog.DamageCappedByDv" dv=martialDvLabel damage=damageFormula base=damageBase}}</p>
{{/if}} {{/if}}
<p>{{localize "DNC.Dialog.ActorDamageBonus"}} : <strong>{{actorBonus}}</strong></p>
<label> <label>
<span>{{localize "DNC.UI.Mode"}}</span> <span>{{localize "DNC.UI.Mode"}}</span>
<select name="mode"> <select name="mode">
<option value="normal">{{localize "DNC.UI.ModeNormal"}}</option> <option value="normal" {{#if (eq defaultMode "normal")}}selected{{/if}}>{{localize "DNC.UI.ModeNormal"}}</option>
<option value="avantage">{{localize "DNC.UI.ModeAdvantage"}}</option> <option value="avantage" {{#if (eq defaultMode "avantage")}}selected{{/if}}>{{localize "DNC.UI.ModeAdvantage"}}</option>
<option value="desavantage">{{localize "DNC.UI.ModeDisadvantage"}}</option> <option value="desavantage" {{#if (eq defaultMode "desavantage")}}selected{{/if}}>{{localize "DNC.UI.ModeDisadvantage"}}</option>
</select> </select>
</label> </label>
</div> </div>
+366
View File
@@ -0,0 +1,366 @@
<div class="dnc-employes-dialog" role="region" aria-labelledby="employes-title">
<header class="sheet-header">
<h1 id="employes-title">{{localize "DNC.Dialog.EmployeesTitle"}}</h1>
</header>
<!-- ===== ONGLETS RACINE ===== -->
<nav class="dnc-root-tabs" role="tablist" aria-label="{{localize 'DNC.Dialog.EmployeesTitle'}}">
<button type="button"
class="dnc-root-tab active"
data-root-tab="employes"
role="tab"
aria-selected="true"
aria-controls="root-panel-employes"
id="root-tab-employes"
>
<i class="fa-solid fa-hard-hat" aria-hidden="true"></i> {{localize "DNC.Dialog.EmployeesTabEmployes"}}
</button>
<button type="button"
class="dnc-root-tab"
data-root-tab="clients"
role="tab"
aria-selected="false"
aria-controls="root-panel-clients"
id="root-tab-clients"
>
<i class="fa-solid fa-users" aria-hidden="true"></i> {{localize "DNC.Dialog.EmployeesTabClients"}}
</button>
</nav>
<div class="dnc-root-panels">
<!-- ===== ROOT PANEL : EMPLOYÉS ===== -->
<div class="dnc-root-panel active"
data-root-panel="employes"
role="tabpanel"
aria-labelledby="root-tab-employes"
id="root-panel-employes"
>
{{#if pcs.length}}
<!-- Sous-onglets par employé -->
<nav class="dnc-pc-tabs" role="tablist" aria-label="{{localize 'DNC.Dialog.EmployeesTabEmployes'}}">
{{#each pcs}}
<button type="button"
class="dnc-pc-tab{{#if @first}} active{{/if}}"
data-pc-tab="{{this.actorId}}"
role="tab"
aria-selected="{{#if @first}}true{{else}}false{{/if}}"
aria-controls="pc-panel-{{this.actorId}}"
id="pc-tab-{{this.actorId}}"
>
{{this.name}}
</button>
{{/each}}
</nav>
<!-- Sous-panneaux par employé -->
<div class="dnc-pc-panels">
{{#each pcs}}
<div class="dnc-pc-panel{{#if @first}} active{{/if}}"
data-pc-panel="{{this.actorId}}"
role="tabpanel"
aria-labelledby="pc-tab-{{this.actorId}}"
id="pc-panel-{{this.actorId}}"
>
<!-- SECTION : CARACTÉRISTIQUES -->
<section class="dnc-compact-section dnc-compact-carac">
<h3 class="dnc-compact-section-title">
<i class="fa-solid fa-chart-line" aria-hidden="true"></i>
{{localize "DNC.Dialog.EmployeesTabCharacteristics"}}
<button type="button" class="dnc-open-sheet-btn" data-open-sheet="pc" data-actor-id="{{this.actorId}}" title="Ouvrir la fiche">
<i class="fa-solid fa-external-link-alt"></i>
</button>
</h3>
<div class="dnc-compact-carac-grid">
<div class="dnc-carac-stat">
<span class="dnc-carac-label">{{localize "DNC.UI.Pv"}}</span>
<span class="dnc-carac-value{{#if (lt this.pvValue 3)}} warning{{/if}}">{{this.pvValue}}/{{this.pvMax}}</span>
</div>
<div class="dnc-carac-stat">
<span class="dnc-carac-label">{{localize "DNC.UI.Dv"}}</span>
<span class="dnc-carac-value">{{this.dv}}</span>
</div>
{{#each ../characteristicKeys}}
<div class="dnc-carac-stat">
<span class="dnc-carac-label">{{this.short}}</span>
<span class="dnc-carac-value">{{lookup .. this.key}}</span>
</div>
{{/each}}
</div>
{{#if this.concept}}
<p class="dnc-compact-concept">{{this.concept}}</p>
{{/if}}
</section>
<!-- SECTION : COMBAT -->
<section class="dnc-compact-section dnc-compact-combat">
<h3 class="dnc-compact-section-title">
<i class="fa-solid fa-sword" aria-hidden="true"></i>
{{localize "DNC.Dialog.EmployeesTabCombat"}}
</h3>
<div class="dnc-compact-combat-stats">
<span class="dnc-combat-badge">⚔️ ×{{this.meleeAttacks}} {{localize "DNC.UI.MeleeAttacks"}}</span>
<span class="dnc-combat-badge">🏹 ×{{this.rangedAttacks}} {{localize "DNC.UI.RangedAttacks"}}</span>
</div>
{{#if this.weapons.length}}
<div class="dnc-compact-item-list">
<h4 class="dnc-compact-subhead"><i class="fa-solid fa-sword"></i> {{localize "DNC.UI.Weapons"}}</h4>
{{#each this.weapons}}
<div class="dnc-compact-item">
<span class="dnc-compact-item-name">{{this.name}}</span>
<span class="item-meta">{{this.categoryLabel}}</span>
<span class="item-meta">{{this.handsLabel}}</span>
<span class="dnc-compact-item-detail">{{localize "DNC.UI.Damage"}} {{this.damage}}</span>
{{#if this.ammunition}}<span class="dnc-compact-item-detail">{{localize "DNC.UI.Ammunition"}} {{this.ammunition}}</span>{{/if}}
</div>
{{/each}}
</div>
{{/if}}
{{#if this.armors.length}}
<div class="dnc-compact-item-list">
<h4 class="dnc-compact-subhead"><i class="fa-solid fa-shield-halved"></i> {{localize "DNC.UI.Armors"}}</h4>
{{#each this.armors}}
<div class="dnc-compact-item">
<span class="dnc-compact-item-name">{{this.name}}</span>
<span class="dnc-compact-item-detail">{{this.protectionDie}}</span>
{{#if this.remainingProtection}}<span class="dnc-compact-item-detail">ARM {{this.remainingProtection}}</span>{{/if}}
</div>
{{/each}}
</div>
{{/if}}
</section>
<!-- SECTION : MAGIE -->
<section class="dnc-compact-section dnc-compact-magie">
<h3 class="dnc-compact-section-title">
<i class="fa-solid fa-book-sparkles" aria-hidden="true"></i>
{{localize "DNC.Dialog.EmployeesTabMagie"}}
</h3>
<div class="dnc-compact-magic-stats">
<div class="dnc-carac-stat">
<span class="dnc-carac-label">{{localize "DNC.UI.Rank"}}</span>
<span class="dnc-carac-value">{{this.magicRank}}</span>
</div>
<div class="dnc-carac-stat">
<span class="dnc-carac-label">{{localize "DNC.UI.Focus"}}</span>
<span class="dnc-carac-value">{{this.focusDisplay}}</span>
</div>
<div class="dnc-carac-stat">
<span class="dnc-carac-label">{{localize "DNC.UI.Chaos"}}</span>
<span class="dnc-carac-value">{{this.chaosDisplay}}</span>
</div>
</div>
{{#if this.spells.length}}
<div class="dnc-compact-item-list">
<h4 class="dnc-compact-subhead"><i class="fa-solid fa-book-sparkles"></i> Sortilèges</h4>
{{#each this.spells}}
<div class="dnc-compact-item">
<span class="dnc-compact-item-name">{{this.name}}</span>
{{#if this.usageLabel}}<span class="item-meta">{{this.usageLabel}}</span>{{/if}}
</div>
{{/each}}
</div>
{{/if}}
{{#if this.capacities.length}}
<div class="dnc-compact-item-list">
<h4 class="dnc-compact-subhead"><i class="fa-solid fa-brain"></i> Capacités</h4>
{{#each this.capacities}}
<div class="dnc-compact-item">
<span class="dnc-compact-item-name">{{this.name}}</span>
{{#if this.usageLabel}}<span class="item-meta">{{this.usageLabel}}</span>{{/if}}
</div>
{{/each}}
</div>
{{/if}}
</section>
</div>
{{/each}}
</div>
{{else}}
<p class="empty-state">{{localize "DNC.Empty.NoEmployees"}}</p>
{{/if}}
</div>
<!-- ===== ROOT PANEL : CLIENTS ===== -->
<div class="dnc-root-panel"
data-root-panel="clients"
role="tabpanel"
aria-labelledby="root-tab-clients"
id="root-panel-clients"
>
{{#if clients.length}}
<!-- Sous-onglets par client -->
<nav class="dnc-pc-tabs dnc-client-tabs" role="tablist" aria-label="{{localize 'DNC.Dialog.EmployeesTabClients'}}">
{{#each clients}}
<button type="button"
class="dnc-pc-tab{{#if @first}} active{{/if}}"
data-client-tab="{{this.id}}"
role="tab"
aria-selected="{{#if @first}}true{{else}}false{{/if}}"
aria-controls="client-panel-{{this.id}}"
id="client-tab-{{this.id}}"
>
{{this.name}}
</button>
{{/each}}
</nav>
<!-- Sous-panneaux par client -->
<div class="dnc-pc-panels">
{{#each clients}}
<div class="dnc-client-panel{{#if @first}} active{{/if}}"
data-client-panel="{{this.id}}"
role="tabpanel"
aria-labelledby="client-tab-{{this.id}}"
id="client-panel-{{this.id}}"
>
<!-- SECTION : IDENTITÉ + SANTÉ -->
<section class="dnc-compact-section">
<h3 class="dnc-compact-section-title">
<i class="fa-solid fa-id-card" aria-hidden="true"></i>
{{this.name}}
<button type="button" class="dnc-open-sheet-btn" data-open-sheet="client" data-token-uuid="{{this.tokenUuid}}" data-actor-id="{{this.id}}" title="Ouvrir la fiche">
<i class="fa-solid fa-external-link-alt"></i>
</button>
</h3>
<div class="dnc-compact-carac-grid">
{{#if this.species}}<div class="dnc-carac-stat"><span class="dnc-carac-label">Espèce</span><span class="dnc-carac-value">{{this.species}}</span></div>{{/if}}
{{#if this.category}}<div class="dnc-carac-stat"><span class="dnc-carac-label">Catégorie</span><span class="dnc-carac-value">{{this.category}}</span></div>{{/if}}
{{#if this.role}}<div class="dnc-carac-stat"><span class="dnc-carac-label">Rôle</span><span class="dnc-carac-value">{{this.role}}</span></div>{{/if}}
<div class="dnc-carac-stat">
<span class="dnc-carac-label">{{localize "DNC.UI.Pv"}}</span>
<span class="dnc-carac-value{{#if (lt this.pvValue 3)}} warning{{/if}}">{{this.pvValue}}/{{this.pvMax}}</span>
</div>
<div class="dnc-carac-stat">
<span class="dnc-carac-label">{{localize "DNC.UI.Dv"}}</span>
<span class="dnc-carac-value">{{this.dv}}</span>
</div>
{{#if this.armureDelta}}
<div class="dnc-carac-stat dnc-carac-rollable"
data-pnj-action="rollArmure"
data-token-uuid="{{this.tokenUuid}}"
data-actor-id="{{this.id}}"
title="Lancer armure Δ{{this.armureDelta}}"
>
<span class="dnc-carac-label">Armure <i class="fa-solid fa-dice" style="font-size:0.6rem"></i></span>
<span class="dnc-carac-value">Δ{{this.armureDelta}}</span>
</div>
{{/if}}
{{#if this.courageDelta}}
<div class="dnc-carac-stat dnc-carac-rollable"
data-pnj-action="rollCourage"
data-token-uuid="{{this.tokenUuid}}"
data-actor-id="{{this.id}}"
title="Lancer courage Δ{{this.courageDelta}}"
>
<span class="dnc-carac-label">Courage <i class="fa-solid fa-dice" style="font-size:0.6rem"></i></span>
<span class="dnc-carac-value">Δ{{this.courageDelta}}</span>
</div>
{{/if}}
</div>
{{#if this.summary}}<p class="client-summary">{{this.summary}}</p>{{/if}}
</section>
<!-- SECTION : ATTAQUES -->
{{#if this.attaques.length}}
<section class="dnc-compact-section dnc-compact-combat">
<h3 class="dnc-compact-section-title">
<i class="fa-solid fa-sword" aria-hidden="true"></i>
{{localize "DNC.Dialog.EmployeesTabCombat"}}
</h3>
<div class="dnc-compact-item-list">
{{#each this.attaques}}
<div class="dnc-compact-item">
<span class="dnc-compact-item-name">{{this.nom}}</span>
<button type="button"
class="dnc-roll-btn"
data-pnj-action="rollAttaque"
data-attack-index="{{@index}}"
data-token-uuid="{{../tokenUuid}}"
data-actor-id="{{../id}}"
><i class="fa-solid fa-dice"></i> {{this.degats}}</button>
{{#if this.notes}}<span class="dnc-compact-item-detail">{{this.notes}}</span>{{/if}}
</div>
{{/each}}
</div>
{{#if this.weapons.length}}
<div class="dnc-compact-item-list" style="margin-top:0.5rem">
<h4 class="dnc-compact-subhead"><i class="fa-solid fa-sword"></i> {{localize "DNC.UI.Weapons"}}</h4>
{{#each this.weapons}}
<div class="dnc-compact-item">
<span class="dnc-compact-item-name">{{this.name}}</span>
<span class="item-meta">{{this.categoryLabel}}</span>
<span class="dnc-compact-item-detail">{{this.damage}}</span>
</div>
{{/each}}
</div>
{{/if}}
</section>
{{else if this.weapons.length}}
<section class="dnc-compact-section dnc-compact-combat">
<h3 class="dnc-compact-section-title">
<i class="fa-solid fa-sword" aria-hidden="true"></i>
{{localize "DNC.Dialog.EmployeesTabCombat"}}
</h3>
<div class="dnc-compact-item-list">
{{#each this.weapons}}
<div class="dnc-compact-item">
<span class="dnc-compact-item-name">{{this.name}}</span>
<span class="item-meta">{{this.categoryLabel}}</span>
<span class="dnc-compact-item-detail">{{this.damage}}</span>
</div>
{{/each}}
</div>
</section>
{{/if}}
<!-- SECTION : MAGIE / CAPACITÉS -->
{{#if this.hasMagie}}
<section class="dnc-compact-section dnc-compact-magie">
<h3 class="dnc-compact-section-title">
<i class="fa-solid fa-book-sparkles" aria-hidden="true"></i>
{{localize "DNC.Dialog.EmployeesTabMagie"}}
</h3>
{{#if this.spells.length}}
<div class="dnc-compact-item-list">
<h4 class="dnc-compact-subhead"><i class="fa-solid fa-book-sparkles"></i> Sortilèges</h4>
{{#each this.spells}}
<div class="dnc-compact-item">
<span class="dnc-compact-item-name">{{this.name}}</span>
{{#if this.usageLabel}}<span class="item-meta">{{this.usageLabel}}</span>{{/if}}
</div>
{{/each}}
</div>
{{/if}}
{{#if this.capacities.length}}
<div class="dnc-compact-item-list">
<h4 class="dnc-compact-subhead"><i class="fa-solid fa-brain"></i> Capacités</h4>
{{#each this.capacities}}
<div class="dnc-compact-item">
<span class="dnc-compact-item-name">{{this.name}}</span>
{{#if this.usageLabel}}<span class="item-meta">{{this.usageLabel}}</span>{{/if}}
</div>
{{/each}}
</div>
{{/if}}
{{#if this.pouvoirsSpeciaux}}
<div class="client-description">{{{this.pouvoirsSpeciaux}}}</div>
{{/if}}
</section>
{{/if}}
</div>
{{/each}}
</div>
{{else}}
<p class="empty-state">{{localize "DNC.Empty.NoClients"}}</p>
{{/if}}
</div>
</div>
</div>
+2 -2
View File
@@ -136,8 +136,8 @@
{{#if isCapacity}} {{#if isCapacity}}
<label> <label>
<span>Cout</span> <span>Dé d'usure</span>
<input type="text" name="system.cout" value="{{system.cout}}"> <select name="system.delta">{{selectOptions config.usageDieOptions selected=system.delta localize=false}}</select>
</label> </label>
<label class="span-two"> <label class="span-two">
<span>Effet</span> <span>Effet</span>