19 Commits

Author SHA1 Message Date
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
uberwald 8718cfff05 AUto-select current target if any
Release Creation / build (release) Successful in 55s
2026-05-29 23:27:33 +02:00
uberwald 6742830f40 Gestion du dé d'usure des degats des armes, gestion des munitions, gestion de la limute dégats vs DV
Release Creation / build (release) Successful in 1m11s
2026-05-22 09:50:48 +02:00
uberwald 09f2349bab Fix PV boxes
Release Creation / build (release) Successful in 56s
2026-05-09 23:32:43 +02:00
uberwald d8eb23289e Fix PV boxes 2026-05-09 23:32:29 +02:00
uberwald 820a7d61cf Corrections sur focus
Release Creation / build (release) Successful in 1m4s
2026-05-07 00:14:20 +02:00
uberwald 755c349078 DsN OK, objets uniques, règles de campagne
Release Creation / build (release) Successful in 1m26s
2026-05-03 21:51:35 +02:00
uberwald 8fb27c2e82 UPdate sys helper
Release Creation / build (release) Successful in 2m29s
2026-05-01 09:33:41 +02:00
uberwald 98cf0bf8fd Add missing pack
Release Creation / build (release) Successful in 3m1s
2026-05-01 09:13:21 +02:00
uberwald 7d218f4a0a Gestion de paquetage, aide intégrée et message de bienvenue
Release Creation / build (release) Successful in 59s
2026-05-01 00:37:01 +02:00
uberwald 06b0ff7f78 Fiche PNJ : Autorise multi-attaques
Release Creation / build (release) Successful in 57s
2026-04-26 15:59:01 +02:00
uberwald c6ddc96148 Fiche PNJ : Autorise multi-attaques
Release Creation / build (release) Failing after 49s
2026-04-26 15:45:53 +02:00
uberwald 6883cc1020 Ajout entrainements
Release Creation / build (release) Failing after 51s
2026-04-19 19:06:47 +02:00
uberwald 1d6e6dd44e Corrections sur les clés de label manquantes
Release Creation / build (release) Failing after 54s
2026-04-16 21:53:26 +02:00
91 changed files with 5355 additions and 457 deletions
+13 -2
View File
@@ -24,9 +24,20 @@ jobs:
env: env:
version: ${{ steps.get_version.outputs.version-without-v }} version: ${{ steps.get_version.outputs.version-without-v }}
url: https://www.uberwald.me/gitea/${{ gitea.repository }} url: https://www.uberwald.me/gitea/${{ gitea.repository }}
manifest: https://www.uberwald.me/gitea/public/fvtt-donjon-et-cie/releases/download/latest/system.json manifest: https://www.uberwald.me/gitea/uberwald/fvtt-donjon-et-cie/releases/download/latest/system.json
download: https://www.uberwald.me/gitea/${{ gitea.repository }}/releases/download/${{ github.event.release.tag_name }}/fvtt-donjon-et-cie.zip download: https://www.uberwald.me/gitea/${{ gitea.repository }}/releases/download/${{ github.event.release.tag_name }}/fvtt-donjon-et-cie.zip
- name: Setup Node
uses: https://github.com/actions/setup-node@v4
with:
node-version: 24
- name: Install dependencies
run: npm ci
- name: Build system and pack compendiums
run: npm run build
- run: | - run: |
apt update -y apt update -y
apt install -y zip apt install -y zip
@@ -53,7 +64,7 @@ jobs:
token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }} token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }}
id: "fvtt-donjon-et-cie" id: "fvtt-donjon-et-cie"
version: ${{ github.event.release.tag_name }} version: ${{ github.event.release.tag_name }}
manifest: "https://www.uberwald.me/gitea/public/fvtt-donjon-et-cie/releases/download/latest/system.json" manifest: "https://www.uberwald.me/gitea/uberwald/fvtt-donjon-et-cie/releases/download/latest/system.json"
notes: "https://www.uberwald.me/gitea/${{ gitea.repository }}/releases/download/${{ github.event.release.tag_name }}/fvtt-donjon-et-cie.zip" notes: "https://www.uberwald.me/gitea/${{ gitea.repository }}/releases/download/${{ github.event.release.tag_name }}/fvtt-donjon-et-cie.zip"
compatibility-minimum: "13" compatibility-minimum: "13"
compatibility-verified: "14" compatibility-verified: "14"
Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

+231 -2
View File
@@ -9,8 +9,10 @@
"TYPES.Item.armure": "Armure", "TYPES.Item.armure": "Armure",
"TYPES.Item.equipement": "Equipement", "TYPES.Item.equipement": "Equipement",
"TYPES.Item.consommable": "Consommable", "TYPES.Item.consommable": "Consommable",
"TYPES.Item.entrainement": "Entrainement",
"DNC.Roll.Characteristic": "Jet de caracteristique", "DNC.Roll.Characteristic": "Jet de caracteristique",
"DNC.Roll.Attack": "Jet d'attaque", "DNC.Roll.Attack": "Jet d'attaque",
"DNC.Roll.Ammunition": "Munitions",
"DNC.Roll.Damage": "Jet de degats", "DNC.Roll.Damage": "Jet de degats",
"DNC.Roll.Favor": "Faveur", "DNC.Roll.Favor": "Faveur",
"DNC.Roll.HitDice": "Jet de de de vie", "DNC.Roll.HitDice": "Jet de de de vie",
@@ -23,8 +25,235 @@
"DNC.Chat.SelectTarget": "Choisissez une cible presente sur la scene.", "DNC.Chat.SelectTarget": "Choisissez une cible presente sur la scene.",
"DNC.Chat.TargetUnavailable": "La cible selectionnee n'est plus disponible.", "DNC.Chat.TargetUnavailable": "La cible selectionnee n'est plus disponible.",
"DNC.Chat.InvalidDamageTarget": "Impossible d'appliquer les degats a cette cible.", "DNC.Chat.InvalidDamageTarget": "Impossible d'appliquer les degats a cette cible.",
"DNC.Chat.Kicker.Resolution": "Resolution",
"DNC.Chat.Kicker.Magic": "Magie",
"DNC.Chat.Kicker.Wear": "Usure",
"DNC.Chat.Kicker.Network": "Reseau",
"DNC.Chat.Kicker.Logistics": "Logistique",
"DNC.Chat.Kicker.Initiative": "Ordre de marche",
"DNC.Chat.Success": "Reussite",
"DNC.Chat.Failure": "Echec",
"DNC.Chat.Favor": "Faveur",
"DNC.Chat.Target": "Cible",
"DNC.Chat.Kept": "Garde",
"DNC.Chat.Rolls": "Lancers",
"DNC.Chat.RollDamage": "Lancer les degats",
"DNC.Chat.RollAmmunition": "Munitions",
"DNC.Chat.DamageCapped": "Degats plafonnes a {damage} par {dv}",
"DNC.Chat.RollValue": "Jet",
"DNC.Chat.AutoDisadvantage": "Desavantage automatique",
"DNC.Chat.RiskChaos": "Risquer le Chaos",
"DNC.Chat.Result": "Resultat",
"DNC.Chat.Before": "Avant",
"DNC.Chat.After": "Apres",
"DNC.Chat.Total": "Total",
"DNC.Chat.Bonus": "Bonus",
"DNC.Chat.CombatSynced": "Combat synchronise",
"DNC.Chat.TargetValue": "Valeur cible",
"DNC.Chat.FavorDie": "De de faveur",
"DNC.Chat.CharacteristicValue": "Valeur de la caracteristique",
"DNC.Chat.HpCost": "Cout en PV",
"DNC.Chat.FocusSpent": "Focus depense",
"DNC.Chat.FocusRemaining": "Focus restant",
"DNC.Chat.HpSpent": "PV depenses",
"DNC.Chat.HpRemaining": "PV restants",
"DNC.Chat.CasterRank": "Rang du lanceur",
"DNC.Chat.Difficulty": "Difficulte",
"DNC.Chat.MartialDv": "DV martial",
"DNC.Chat.EffectiveDamage": "Degats retenus",
"DNC.Chat.Effect": "Effet",
"DNC.Chat.StoredProtection": "Protection stockee pour ce combat",
"DNC.Chat.ResourceWearsDown": "La ressource s'amenuise.",
"DNC.Chat.ResourceStable": "La ressource tient bon.",
"DNC.Chat.ResourceExhausted": "La ressource est epuisee.",
"DNC.Chat.AmmunitionWearsDown": "Les munitions diminuent d'un cran.",
"DNC.Chat.AmmunitionStable": "Le stock de munitions tient bon.",
"DNC.Chat.AmmunitionExhausted": "Il n'y a plus de munitions disponibles pour cette arme.",
"DNC.Chat.AutoDisadvantageCanceled": "le cout du sort depasse le rang du lanceur, mais une faveur l'a annule.",
"DNC.Chat.AutoDisadvantageApplies": "le cout du sort depasse le rang du lanceur.",
"DNC.Chat.FocusFrom": "depuis",
"DNC.Chat.FocusUsed": "utilise",
"DNC.Chat.FocusLeft": "restant",
"DNC.Chat.FocusDropsTo": "le focus descend a",
"DNC.Chat.FirstSpellScene": "premier sort de la scene",
"DNC.Chat.FocusNoBenefit": "aucun apport pour cette scene.",
"DNC.Chat.ChaosSubtitle": "La magie opere malgre l'echec ; le MJ determine les consequences.",
"DNC.Chat.SpellMaintained": "Sort maintenu",
"DNC.Chat.ChaosDieDrops": "Le de du Chaos descend d'un cran.",
"DNC.Chat.ChaosDieExhausted": "Le de du Chaos est trop faible pour etre reutilise dans le Donjon.",
"DNC.UI.Mode": "Mode",
"DNC.UI.ModeNormal": "Normal",
"DNC.UI.ModeAdvantage": "Avantage",
"DNC.UI.ModeDisadvantage": "Desavantage",
"DNC.UI.FavorBoost": "Coup de pouce",
"DNC.UI.NoFavor": "Aucune faveur",
"DNC.UI.RollAction": "Lancer",
"DNC.UI.AttackAction": "Attaquer",
"DNC.UI.UseAction": "Utiliser",
"DNC.UI.Characteristic": "Caracteristique",
"DNC.UI.Cost": "Cout",
"DNC.UI.CurrentHp": "PV actuels",
"DNC.UI.Rank": "Rang",
"DNC.UI.Focus": "Focus",
"DNC.UI.Chaos": "Chaos",
"DNC.UI.Weapon": "Arme",
"DNC.UI.Ammunition": "Munitions",
"DNC.UI.Damage": "Degats",
"DNC.UI.Range": "Portee",
"DNC.UI.Spell": "Sortilege",
"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.CharacteristicUsed": "Caracteristique utilisee",
"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.UseUsageDie": "Utiliser <strong>{itemName}</strong> et lancer son de d'usage actuel.",
"DNC.Dialog.CurrentValue": "Valeur actuelle",
"DNC.Warn.NoFavorAvailable": "Aucune faveur disponible pour {label}.",
"DNC.Warn.NoAmmunitionAvailable": "Il n'y a plus de munitions disponibles pour cette arme.",
"DNC.Warn.SpellInsufficientResources": "Le lanceur ne dispose pas d'assez de PV et de focus pour payer ce sort.",
"DNC.Warn.ChaosUnavailable": "Le Chaos n'est pas disponible pour ce sort.",
"DNC.Warn.TrainingExhausted": "Cet entrainement est epuise pour aujourd'hui. Reinitialisez son delta quotidien pour le lendemain.",
"DNC.Settings.MissionPackMode.Name": "Mode de paquetage de mission",
"DNC.Settings.MissionPackMode.Hint": "Choisit entre le tirage classique rapide et la regle optionnelle de campagne avec allocation des des par les joueurs.",
"DNC.Settings.MissionPackMode.Classic": "Classique",
"DNC.Settings.MissionPackMode.Campaign": "Campagne",
"DNC.Macro.MissionPack.Title": "Paquetage de debut de mission",
"DNC.Macro.MissionPack.ItemsAdded": "Objets ajoutes",
"DNC.Macro.MissionPack.ItemsMissing": "Objets manquants",
"DNC.Macro.MissionPack.Missing": "Introuvables",
"DNC.Macro.MissionPack.CompleteChatSummary": "L'integralite du paquetage a ete ajoutee a la fiche.",
"DNC.Macro.MissionPack.PartialChatSummary": "Les elements trouves du paquetage ont ete ajoutes a la fiche.",
"DNC.Macro.MissionPack.NoResult": "Aucun resultat exploitable",
"DNC.Macro.MissionPack.TableMissing": "Table introuvable : {table}",
"DNC.Macro.MissionPack.WarnNoActor": "Impossible de determiner l'employe cible. Selectionnez un token ou attribuez un personnage a l'utilisateur.",
"DNC.Macro.MissionPack.WarnMultipleTokens": "Selectionnez un seul token pour attribuer le paquetage.",
"DNC.Macro.MissionPack.WarnInvalidActor": "Le paquetage de mission ne peut etre attribue qu'a un employe.",
"DNC.Macro.MissionPack.WarnNoEmployees": "Aucun employe n'est disponible pour recevoir un paquetage.",
"DNC.Macro.MissionPack.WarnGMOnly": "Cette interface de paquetage est reservee au MJ.",
"DNC.Macro.MissionPack.WarnNothingAdded": "Aucun objet du paquetage n'a pu etre ajoute a la fiche.",
"DNC.Macro.MissionPack.Success": "{actor} recoit {count} objet(s) de paquetage.",
"DNC.Macro.MissionPack.Partial": "{actor} recoit {count} objet(s), mais {missing} entree(s) du paquetage sont introuvables.",
"DNC.Macro.MissionPack.DialogTitle": "Paquetage de mission",
"DNC.Macro.MissionPack.DialogIntro": "Selectionnez l'employe qui recevra le paquetage de debut de mission, puis lancez le tirage.",
"DNC.Macro.MissionPack.ActiveMode": "Mode :",
"DNC.Macro.MissionPack.ModeClassic": "Classique",
"DNC.Macro.MissionPack.ModeClassicHint": "Le systeme tire directement les 4 tables d equipement, comme dans les one-shots.",
"DNC.Macro.MissionPack.ModeCampaign": "Campagne",
"DNC.Macro.MissionPack.ModeCampaignHint": "Le joueur alloue 1d20, 1d12, 1d10 et 1d8 aux 4 categories, puis l anciennete est ajoutee a chaque resultat.",
"DNC.Macro.MissionPack.DialogActor": "Employe",
"DNC.Macro.MissionPack.DialogAction": "Generer le paquetage",
"DNC.Macro.MissionPack.SidebarButton": "Paquetage",
"DNC.Macro.MissionPack.CampaignDialogLead": "En mode campagne, le joueur proprietaire choisira l affectation de ses des dans un dialogue dedie.",
"DNC.Macro.MissionPack.CampaignDialogTitle": "Paquetage de campagne",
"DNC.Macro.MissionPack.CampaignDialogSubtitle": "Repartition logistique",
"DNC.Macro.MissionPack.CampaignDialogIntro": "{actor} prepare son paquetage de campagne. Repartissez les des entre les categories, puis indiquez la relation a l econome.",
"DNC.Macro.MissionPack.CampaignDialogAction": "Valider l allocation",
"DNC.Macro.MissionPack.CampaignDialogPlayer": "Joueur",
"DNC.Macro.MissionPack.CampaignDialogRequester": "Demande de",
"DNC.Macro.MissionPack.CampaignDialogRank": "Anciennete",
"DNC.Macro.MissionPack.CampaignDialogRelation": "Relation a l econome",
"DNC.Macro.MissionPack.CampaignDialogAssignHelp": "Attribuez un de different a chaque categorie de paquetage.",
"DNC.Macro.MissionPack.CampaignDialogHelp": "Le d20 alloue servira aussi pour verifier l equipement unique. L anciennete est ajoutee a chaque jet apres avantage ou desavantage.",
"DNC.Macro.MissionPack.CampaignController": "Choix joueur :",
"DNC.Macro.MissionPack.CampaignRelation": "Relation :",
"DNC.Macro.MissionPack.CampaignRequestCanceled": "Le paquetage de campagne pour {actor} n a pas ete valide par {player}.",
"DNC.Macro.MissionPack.WarnDiceRequired": "Attribuez un de a chacune des 4 categories du paquetage.",
"DNC.Macro.MissionPack.WarnDiceUnique": "Chaque de ne peut etre utilise qu une seule fois.",
"DNC.Macro.MissionPack.Relation.positive": "Positive",
"DNC.Macro.MissionPack.Relation.neutral": "Neutre",
"DNC.Macro.MissionPack.Relation.negative": "Negative",
"DNC.Macro.MissionPack.RollDetail": "{die} · {mode} · lancers {values} · garde {kept} · + anciennete {rank} = {total}",
"DNC.Macro.MissionPack.TotalClamped": "Total retenu sur la table : {clamped} (au lieu de {total}).",
"DNC.Macro.MissionPack.melee": "Arme de corps a corps",
"DNC.Macro.MissionPack.ranged": "Arme a distance",
"DNC.Macro.MissionPack.armor": "Armure",
"DNC.Macro.MissionPack.misc": "Encas et equipement divers",
"DNC.Macro.MissionPack.UniqueReference": "d20 de reference",
"DNC.Macro.MissionPack.UniqueActorRoll": "d20 du joueur",
"DNC.Macro.MissionPack.UniqueMatch": "Objet unique",
"DNC.Macro.MissionPack.UniqueMiss": "Pas d objet unique",
"DNC.Macro.MissionPack.UniqueGranted": "Objet unique ajoute :",
"DNC.Macro.MissionPack.UniqueTableRoll": "tirage d objet unique",
"DNC.Macro.MissionPack.UniqueRuleReminder": "Aucun objet unique supplementaire cette fois ci.",
"DNC.Welcome.Kicker": "Accueil",
"DNC.Welcome.Title": "Bienvenue dans Donjon & Cie",
"DNC.Welcome.Subtitle": "Systeme FoundryVTT · version {version}",
"DNC.Welcome.Seal": "GUIDE",
"DNC.Welcome.PillSheets": "Fiches Application V2",
"DNC.Welcome.PillChat": "Jets et cartes de chat",
"DNC.Welcome.PillHelp": "Aide integree",
"DNC.Welcome.Intro": "Le systeme est pret : vous pouvez creer des employes, gerer les PNJ, lancer les jets et utiliser les outils MJ directement depuis l'interface.",
"DNC.Welcome.BulletActors": "Les fiches Employe et PNJ couvrent les caracteristiques, ressources, objets et actions de jeu courantes.",
"DNC.Welcome.BulletItems": "Les objets peuvent etre lances, postes dans le chat et utilises avec leur de d'usure quand c'est pertinent.",
"DNC.Welcome.BulletMissionPack": "Le bouton Paquetage du repertoire des acteurs permet au MJ de generer rapidement l'equipement de debut de mission.",
"DNC.Welcome.HelpLabel": "Aide du systeme :",
"DNC.Welcome.HelpLinkLabel": "ouvrir le journal d'aide",
"DNC.Welcome.HelpFallback": "le compendium d'aide systeme est disponible dans les Compendiums.",
"DNC.Welcome.CreditsLabel": "Credits :",
"DNC.Welcome.CreditsText": "systeme FoundryVTT par LeRatierBretonnien. Donjon & Cie est un jeu de role edite par John Doe.",
"DNC.Welcome.OfficialLabel": "Page officielle :",
"DNC.Welcome.OfficialLinkText": "johndoe-rpg.com/catalogue/donjon-cie/",
"DNC.Welcome.Footer": "Commencez par ouvrir le journal d'aide si vous voulez un tour rapide des ecrans et des workflows du systeme.",
"DNC.Sheet.Create": "Creer", "DNC.Sheet.Create": "Creer",
"DNC.Sheet.Delete": "Supprimer", "DNC.Sheet.Delete": "Supprimer",
"DNC.Sheet.Edit": "Editer", "DNC.Sheet.Edit": "Editer",
"DNC.Sheet.Post": "Poster dans le chat" "DNC.Sheet.Post": "Poster dans le chat",
} "DNC.Roll.DamageUsage": "Usage des degats",
"DNC.Chat.RollDamageUsage": "Usage degats",
"DNC.Chat.DamageUsageWearsDown": "Les degats de l'arme diminuent d'un cran.",
"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.UI.DamageExhausted": "Epuise",
"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"
}
+87 -43
View File
@@ -1,44 +1,54 @@
.characteristic-list { .dnc-actor-sheet .characteristic-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: @spacing-sm; gap: @spacing-sm;
} }
.characteristic-row { .dnc-actor-sheet .characteristic-row {
display: grid; display: grid;
grid-template-columns: 2rem 1fr 4rem; grid-template-columns: 2rem 1fr 4rem;
gap: @spacing-sm; gap: @spacing-sm;
align-items: center; align-items: center;
} }
.characteristic-label { .dnc-actor-sheet .characteristic-label {
font-weight: 700; font-weight: 700;
} }
.profile-counters { .dnc-actor-sheet .profile-counters {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: @spacing-md; gap: @spacing-md;
} }
.profile-card-wide { // 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 {
width: 100%; width: 100%;
} }
.profile-layout { .dnc-actor-sheet .profile-layout {
display: grid; display: grid;
grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.85fr); grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.85fr);
gap: @spacing-lg; gap: @spacing-lg;
align-items: start; align-items: start;
} }
.profile-column { .dnc-actor-sheet .profile-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: @spacing-md; gap: @spacing-md;
} }
.favor-card { .dnc-actor-sheet .favor-card {
margin: @spacing-md 0; margin: @spacing-md 0;
padding: @spacing-md; padding: @spacing-md;
border: 1px dashed fade(@color-border, 55%); border: 1px dashed fade(@color-border, 55%);
@@ -46,29 +56,29 @@
background: fade(@color-panel-strong, 35%); background: fade(@color-panel-strong, 35%);
} }
.favor-header h2 { .dnc-actor-sheet .favor-header h2 {
margin-bottom: @spacing-sm; margin-bottom: @spacing-sm;
} }
.favor-help { .dnc-actor-sheet .favor-help {
margin: 0 0 @spacing-md; margin: 0 0 @spacing-md;
color: @color-muted; color: @color-muted;
font-size: 0.82rem; font-size: 0.82rem;
} }
.favor-list { .dnc-actor-sheet .favor-list {
display: grid; display: grid;
gap: @spacing-sm; gap: @spacing-sm;
} }
.favor-row { .dnc-actor-sheet .favor-row {
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) 4.5rem auto; grid-template-columns: minmax(0, 1fr) 4.5rem auto;
gap: @spacing-sm; gap: @spacing-sm;
align-items: end; align-items: end;
} }
.favor-meta { .dnc-actor-sheet .favor-meta {
min-height: 2.3rem; min-height: 2.3rem;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -79,59 +89,59 @@
font-weight: 700; font-weight: 700;
} }
.magic-layout { .dnc-actor-sheet .magic-layout {
align-items: start; align-items: start;
margin-bottom: @spacing-md; margin-bottom: @spacing-md;
} }
.magic-layout .sheet-column { .dnc-actor-sheet .magic-layout .sheet-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: @spacing-md; gap: @spacing-md;
} }
.magic-resource-grid { .dnc-actor-sheet .magic-resource-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
gap: @spacing-md; gap: @spacing-md;
align-items: end; align-items: end;
} }
.chaos-table-wrapper { .dnc-actor-sheet .chaos-table-wrapper {
margin-top: @spacing-md; margin-top: @spacing-md;
} }
.chaos-table-wrapper h3 { .dnc-actor-sheet .chaos-table-wrapper h3 {
margin: 0 0 @spacing-sm; margin: 0 0 @spacing-sm;
font-size: 0.95rem; font-size: 0.95rem;
} }
.chaos-table-caption { .dnc-actor-sheet .chaos-table-caption {
margin: 0 0 @spacing-sm; margin: 0 0 @spacing-sm;
color: @color-muted; color: @color-muted;
font-size: 0.78rem; font-size: 0.78rem;
font-style: italic; font-style: italic;
} }
.chaos-table { .dnc-actor-sheet .chaos-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
font-size: 0.8rem; font-size: 0.8rem;
} }
.chaos-table th, .dnc-actor-sheet .chaos-table th,
.chaos-table td { .dnc-actor-sheet .chaos-table td {
padding: 0.4rem 0.45rem; padding: 0.4rem 0.45rem;
border: 1px solid fade(@color-border, 35%); border: 1px solid fade(@color-border, 35%);
vertical-align: top; vertical-align: top;
} }
.chaos-table th { .dnc-actor-sheet .chaos-table th {
text-align: left; text-align: left;
background: fade(@color-panel-strong, 55%); background: fade(@color-panel-strong, 55%);
} }
.sheet-tabs { .dnc-actor-sheet .sheet-tabs {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: @spacing-sm; gap: @spacing-sm;
@@ -139,7 +149,7 @@
justify-content: center; justify-content: center;
} }
.sheet-tab { .dnc-actor-sheet .sheet-tab {
border: 1px solid fade(@color-border, 80%); border: 1px solid fade(@color-border, 80%);
border-radius: @radius-md; border-radius: @radius-md;
background: fade(@color-panel-strong, 55%); background: fade(@color-panel-strong, 55%);
@@ -148,32 +158,32 @@
padding: @spacing-sm @spacing-md; padding: @spacing-sm @spacing-md;
} }
.sheet-tab.active { .dnc-actor-sheet .sheet-tab.active {
background: @color-accent; background: @color-accent;
border-color: darken(@color-accent, 12%); border-color: darken(@color-accent, 12%);
color: #fff; color: #fff;
} }
.sheet-tab-panel { .dnc-actor-sheet .sheet-tab-panel {
display: none; display: none;
} }
.sheet-tab-panel.active { .dnc-actor-sheet .sheet-tab-panel.active {
display: block; display: block;
} }
.item-list { .dnc-actor-sheet .item-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: @spacing-md; gap: @spacing-md;
} }
.item-section.is-dragover { .dnc-actor-sheet .item-section.is-dragover {
border-color: @color-accent; border-color: @color-accent;
box-shadow: 0 0 0 2px fade(@color-accent, 20%); box-shadow: 0 0 0 2px fade(@color-accent, 20%);
} }
.item-dropzone { .dnc-actor-sheet .item-dropzone {
border: 1px dashed fade(@color-border, 75%); border: 1px dashed fade(@color-border, 75%);
border-radius: @radius-md; border-radius: @radius-md;
padding: @spacing-md; padding: @spacing-md;
@@ -188,18 +198,18 @@
font-weight: 600; font-weight: 600;
} }
.item-dropzone.has-items { .dnc-actor-sheet .item-dropzone.has-items {
padding: @spacing-sm @spacing-md; padding: @spacing-sm @spacing-md;
font-size: 0.8rem; font-size: 0.8rem;
} }
.item-section.is-dragover .item-dropzone { .dnc-actor-sheet .item-section.is-dragover .item-dropzone {
border-color: @color-accent; border-color: @color-accent;
background: fade(@color-accent, 12%); background: fade(@color-accent, 12%);
color: @color-ink; color: @color-ink;
} }
.item { .dnc-actor-sheet .item {
display: grid; display: grid;
grid-template-columns: 48px 1fr auto; grid-template-columns: 48px 1fr auto;
gap: @spacing-md; gap: @spacing-md;
@@ -208,12 +218,12 @@
padding-top: @spacing-md; padding-top: @spacing-md;
} }
.item:first-child { .dnc-actor-sheet .item:first-child {
border-top: 0; border-top: 0;
padding-top: 0; padding-top: 0;
} }
.item img { .dnc-actor-sheet .item img {
width: 48px; width: 48px;
height: 48px; height: 48px;
object-fit: cover; object-fit: cover;
@@ -222,19 +232,19 @@
background: #fff; background: #fff;
} }
.item-main p { .dnc-actor-sheet .item-main p {
margin: @spacing-sm 0 0; margin: @spacing-sm 0 0;
color: @color-muted; color: @color-muted;
} }
.item-title-row { .dnc-actor-sheet .item-title-row {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: @spacing-sm; gap: @spacing-sm;
align-items: center; align-items: center;
} }
.item-meta { .dnc-actor-sheet .item-meta {
border-radius: 999px; border-radius: 999px;
background: @color-panel-strong; background: @color-panel-strong;
color: @color-ink; color: @color-ink;
@@ -242,26 +252,60 @@
padding: 0.1rem 0.5rem; padding: 0.1rem 0.5rem;
} }
.item-actions { .dnc-actor-sheet .item-actions {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: @spacing-sm; gap: @spacing-sm;
justify-content: flex-end; justify-content: flex-end;
} }
.pnj-layout .sheet-column { .dnc-pnj-sheet .pnj-layout .sheet-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: @spacing-md; gap: @spacing-md;
} }
.pnj-combat-grid { .dnc-pnj-sheet .pnj-combat-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
gap: @spacing-md; gap: @spacing-md;
align-items: end; align-items: end;
} }
.pnj-combat-grid .span-two { .dnc-pnj-sheet .pnj-combat-grid .span-two {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
.dnc-pnj-sheet .pnj-attack-list {
display: flex;
flex-direction: column;
gap: @spacing-md;
}
.dnc-pnj-sheet .pnj-attack-list .section-header {
margin-bottom: 0;
}
.dnc-pnj-sheet .pnj-attack-list h3 {
margin: 0;
font-size: 0.95rem;
color: @color-ink;
}
.dnc-pnj-sheet .pnj-attack-rows {
display: flex;
flex-direction: column;
gap: @spacing-md;
}
.dnc-pnj-sheet .pnj-attack-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 4.5rem max-content;
gap: @spacing-md;
align-items: end;
}
.dnc-pnj-sheet .pnj-attack-row .item-actions {
align-self: end;
gap: @spacing-sm;
}
+31 -20
View File
@@ -60,7 +60,7 @@
padding: @spacing-lg; padding: @spacing-lg;
} }
.sheet-card { .dnc-sheet .sheet-card {
border: 2px solid @color-border; border: 2px solid @color-border;
border-radius: @radius-md; border-radius: @radius-md;
background: rgba(255, 248, 236, 0.92); background: rgba(255, 248, 236, 0.92);
@@ -78,23 +78,23 @@
letter-spacing: 0.04em; letter-spacing: 0.04em;
} }
.sheet-header { .dnc-sheet .sheet-header {
display: grid; display: grid;
grid-template-columns: 140px 1fr; grid-template-columns: 140px 1fr;
gap: @spacing-lg; gap: @spacing-lg;
align-items: start; align-items: start;
} }
.sheet-header.compact { .dnc-sheet .sheet-header.compact {
grid-template-columns: 110px 1fr; grid-template-columns: 110px 1fr;
gap: @spacing-md; gap: @spacing-md;
} }
.portrait { .dnc-sheet .portrait {
position: relative; position: relative;
} }
.portrait img { .dnc-sheet .portrait img {
width: 100%; width: 100%;
aspect-ratio: 1; aspect-ratio: 1;
object-fit: cover; object-fit: cover;
@@ -103,7 +103,7 @@
background: #fff; background: #fff;
} }
.icon-button { .dnc-sheet .icon-button {
position: absolute; position: absolute;
right: @spacing-sm; right: @spacing-sm;
bottom: @spacing-sm; bottom: @spacing-sm;
@@ -115,14 +115,14 @@
height: 2rem; height: 2rem;
} }
.identity-grid, .dnc-sheet .identity-grid,
.identity-grid.two-columns { .dnc-sheet .identity-grid.two-columns {
display: grid; display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-columns: repeat(4, minmax(0, 1fr));
gap: @spacing-md; gap: @spacing-md;
} }
.identity-grid.two-columns { .dnc-sheet .identity-grid.two-columns {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
@@ -135,12 +135,18 @@
gap: 0.25rem; gap: 0.25rem;
} }
.dnc-employe-sheet .sheet-header.compact .hp-field {
grid-column: span 2;
}
.dnc-employe-sheet .sheet-header.compact input[type="number"] { .dnc-employe-sheet .sheet-header.compact input[type="number"] {
max-width: 4.75rem; max-width: 4.75rem;
} }
.dnc-employe-sheet .sheet-header.compact .counter-field input[type="number"] { .dnc-employe-sheet .sheet-header.compact .counter-field input[name="system.sante.pv.value"],
max-width: 4rem; .dnc-employe-sheet .sheet-header.compact .counter-field input[name="system.sante.pv.max"] {
width: 5.25rem;
max-width: 5.25rem;
} }
.dnc-pnj-sheet .sheet-header.compact .identity-grid { .dnc-pnj-sheet .sheet-header.compact .identity-grid {
@@ -152,14 +158,19 @@
gap: 0.25rem; gap: 0.25rem;
} }
.dnc-pnj-sheet .sheet-header.compact .hp-field {
grid-column: span 2;
}
.dnc-pnj-sheet .sheet-header.compact input[type="number"] { .dnc-pnj-sheet .sheet-header.compact input[type="number"] {
width: 4.5rem; width: 4.5rem;
max-width: 4.5rem; max-width: 4.5rem;
} }
.dnc-pnj-sheet .sheet-header.compact .counter-field input[type="number"] { .dnc-pnj-sheet .sheet-header.compact .counter-field input[name="system.sante.pv.value"],
width: 3.75rem; .dnc-pnj-sheet .sheet-header.compact .counter-field input[name="system.sante.pv.max"] {
max-width: 3.75rem; width: 5.25rem;
max-width: 5.25rem;
} }
.dnc-sheet label { .dnc-sheet label {
@@ -175,20 +186,20 @@
color: @color-muted; color: @color-muted;
} }
.readonly-field { .dnc-sheet .readonly-field {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: @spacing-sm; gap: @spacing-sm;
} }
.readonly-field > span { .dnc-sheet .readonly-field > span {
font-size: 0.8rem; font-size: 0.8rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.04em; letter-spacing: 0.04em;
color: @color-muted; color: @color-muted;
} }
.readonly-field p { .dnc-sheet .readonly-field p {
margin: 0; margin: 0;
border: 1px solid rgba(91, 70, 52, 0.55); border: 1px solid rgba(91, 70, 52, 0.55);
border-radius: 6px; border-radius: 6px;
@@ -215,18 +226,18 @@
min-height: 4.5rem; min-height: 4.5rem;
} }
.sheet-columns { .dnc-sheet .sheet-columns {
display: grid; display: grid;
grid-template-columns: minmax(250px, 320px) 1fr; grid-template-columns: minmax(250px, 320px) 1fr;
gap: @spacing-lg; gap: @spacing-lg;
} }
.sheet-sections { .dnc-sheet .sheet-sections {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
gap: @spacing-lg; gap: @spacing-lg;
} }
.compact-sections { .dnc-sheet .compact-sections {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
+56 -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);
@@ -378,6 +405,30 @@
linear-gradient(135deg, #f7fbff 0%, #9fb8d7 100%); linear-gradient(135deg, #f7fbff 0%, #9fb8d7 100%);
} }
.dnc-chat-card-welcome {
border-color: fade(@color-accent, 34%);
background:
linear-gradient(180deg, rgba(255, 250, 240, 0.84) 0%, rgba(239, 219, 179, 0.94) 100%),
linear-gradient(135deg, #fffaf0 0%, #dcb772 100%);
}
.chat-welcome-intro {
font-size: 0.82rem;
line-height: 1.45;
}
.chat-welcome-link {
margin-top: @spacing-md;
padding: 0.6rem 0.8rem;
border: 1px solid fade(@color-accent, 24%);
border-radius: 12px;
background: fade(@color-accent, 8%);
}
.chat-welcome-link a.content-link {
font-weight: 700;
}
.dnc-chat-card-spell .chat-actions + .chat-actions { .dnc-chat-card-spell .chat-actions + .chat-actions {
margin-top: @spacing-sm; margin-top: @spacing-sm;
} }
@@ -393,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;
} }
+12 -12
View File
@@ -1,14 +1,14 @@
.counter-field { .dnc-sheet .counter-field {
display: flex; display: flex;
align-items: center; align-items: center;
gap: @spacing-sm; gap: @spacing-sm;
} }
.counter-field button, .dnc-sheet .counter-field button,
.item-actions button, .dnc-sheet .item-actions button,
.sheet-footer button, .dnc-sheet .sheet-footer button,
.text-button, .dnc-sheet .text-button,
.roll-button { .dnc-sheet .roll-button {
border: 1px solid @color-border; border: 1px solid @color-border;
border-radius: @radius-sm; border-radius: @radius-sm;
background: linear-gradient(180deg, #fdf9f2 0%, #e8d5ba 100%); background: linear-gradient(180deg, #fdf9f2 0%, #e8d5ba 100%);
@@ -17,20 +17,20 @@
padding: 0.35rem 0.65rem; padding: 0.35rem 0.65rem;
} }
.text-button { .dnc-sheet .text-button {
white-space: nowrap; white-space: nowrap;
} }
.roll-button { .dnc-sheet .roll-button {
width: 2rem; width: 2rem;
padding: 0; padding: 0;
} }
.with-controls { .dnc-sheet .with-controls {
gap: @spacing-sm; gap: @spacing-sm;
} }
.section-header { .dnc-sheet .section-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@@ -38,12 +38,12 @@
margin-bottom: @spacing-md; margin-bottom: @spacing-md;
} }
.empty-state { .dnc-sheet .empty-state {
margin: 0; margin: 0;
color: @color-muted; color: @color-muted;
font-style: italic; font-style: italic;
} }
.span-two { .dnc-sheet .span-two {
grid-column: span 2; grid-column: span 2;
} }
+146
View File
@@ -34,3 +34,149 @@
.dnc-roll-dialog .window-content { .dnc-roll-dialog .window-content {
background: linear-gradient(180deg, #f7efe0 0%, #e3d0b1 100%); background: linear-gradient(180deg, #f7efe0 0%, #e3d0b1 100%);
} }
.dnc-mission-pack-mode,
.dnc-mission-pack-note {
font-size: 0.9rem;
}
.dnc-mission-pack-campaign {
gap: @spacing-lg;
}
.dnc-mission-pack-hero {
padding: @spacing-lg;
border: 1px solid fade(@color-border, 35%);
border-radius: @radius-md;
background:
linear-gradient(180deg, fade(#ffffff, 65%) 0%, fade(@color-panel, 68%) 100%),
linear-gradient(135deg, fade(@color-accent, 8%) 0%, fade(@color-accent, 0%) 100%);
box-shadow: 0 8px 18px fade(@color-shadow, 10%);
}
.dnc-mission-pack-kicker {
margin: 0 0 0.25rem;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: fade(@color-accent, 82%);
}
.dnc-mission-pack-hero h2 {
margin: 0;
font-family: @font-display;
font-size: 1.4rem;
line-height: 1.1;
color: @color-accent;
}
.dnc-mission-pack-subtitle {
margin-top: 0.2rem;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: fade(@color-border, 78%);
}
.dnc-mission-pack-intro {
margin-top: @spacing-sm;
color: @color-muted;
}
.dnc-mission-pack-meta-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: @spacing-md;
}
.dnc-mission-pack-meta-card {
display: grid;
gap: 0.2rem;
padding: @spacing-md;
border: 1px solid fade(@color-border, 30%);
border-radius: @radius-md;
background: fade(#ffffff, 42%);
}
.dnc-mission-pack-meta-card span {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: @color-muted;
}
.dnc-mission-pack-meta-card strong {
font-size: 1rem;
}
.dnc-mission-pack-section {
display: grid;
gap: @spacing-md;
}
.dnc-mission-pack-select {
padding: @spacing-md;
border: 1px solid fade(@color-border, 32%);
border-radius: @radius-md;
background: fade(#ffffff, 36%);
}
.dnc-mission-pack-assignments {
display: grid;
gap: @spacing-md;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.dnc-mission-pack-assignment {
display: grid;
gap: @spacing-sm;
padding: @spacing-md;
border: 1px solid fade(@color-border, 35%);
border-radius: @radius-md;
background: fade(#ffffff, 38%);
box-shadow: inset 0 1px 0 fade(#ffffff, 55%);
}
.dnc-mission-pack-assignment span {
font-size: 0.82rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.dnc-mission-pack-note {
color: @color-muted;
}
.dnc-mission-pack-note-foot {
padding: @spacing-md;
border-top: 1px solid fade(@color-border, 24%);
}
.dnc-mission-pack-dialog .window-header {
background:
linear-gradient(180deg, fade(#fdf6e7, 96%) 0%, fade(#ead4aa, 96%) 100%),
linear-gradient(90deg, fade(@color-accent, 12%) 0%, fade(@color-accent, 0%) 100%);
border-bottom: 1px solid fade(@color-border, 35%);
}
.dnc-mission-pack-dialog .window-title {
color: @color-accent;
font-family: @font-display;
letter-spacing: 0.03em;
text-shadow: none;
}
.dnc-mission-pack-dialog .window-header button {
color: @color-ink;
}
@media (max-width: 640px) {
.dnc-mission-pack-meta-grid,
.dnc-mission-pack-assignments {
grid-template-columns: 1fr;
}
}
+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 -1
View File
@@ -26,7 +26,7 @@
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
.sheet-footer { .dnc-item-sheet .sheet-footer {
display: flex; display: flex;
gap: @spacing-md; gap: @spacing-md;
justify-content: flex-end; justify-content: flex-end;
+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);
}
});
}
}
@@ -32,7 +32,7 @@ export class DonjonEtCieRollDialog {
buttons: [ buttons: [
{ {
action: "roll", action: "roll",
label: "Lancer", label: game.i18n.localize("DNC.UI.RollAction"),
icon: "fa-solid fa-bolt", icon: "fa-solid fa-bolt",
default: true, default: true,
callback: async (event, button) => { callback: async (event, button) => {
@@ -70,7 +70,7 @@ export class DonjonEtCieRollDialog {
buttons: [ buttons: [
{ {
action: "roll", action: "roll",
label: "Lancer", label: game.i18n.localize("DNC.UI.RollAction"),
icon: "fa-solid fa-dice-d20", icon: "fa-solid fa-dice-d20",
default: true, default: true,
callback: async (event, button) => { callback: async (event, button) => {
@@ -107,7 +107,7 @@ export class DonjonEtCieRollDialog {
buttons: [ buttons: [
{ {
action: "roll", action: "roll",
label: "Attaquer", label: game.i18n.localize("DNC.UI.AttackAction"),
icon: "fa-solid fa-sword", icon: "fa-solid fa-sword",
default: true, default: true,
callback: async (event, button) => { callback: async (event, button) => {
@@ -153,7 +153,7 @@ export class DonjonEtCieRollDialog {
buttons: [ buttons: [
{ {
action: "roll", action: "roll",
label: "Lancer", label: game.i18n.localize("DNC.UI.RollAction"),
icon: "fa-solid fa-wand-magic-sparkles", icon: "fa-solid fa-wand-magic-sparkles",
default: true, default: true,
callback: async (event, button) => { callback: async (event, button) => {
@@ -185,7 +185,7 @@ export class DonjonEtCieRollDialog {
buttons: [ buttons: [
{ {
action: "roll", action: "roll",
label: "Utiliser", label: game.i18n.localize("DNC.UI.UseAction"),
icon: "fa-solid fa-hourglass-half", icon: "fa-solid fa-hourglass-half",
default: true, default: true,
callback: async (event, button) => { callback: async (event, button) => {
@@ -201,12 +201,19 @@ export class DonjonEtCieRollDialog {
} }
static async createDamage(actor, item) { static async createDamage(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,
damageBase: damageContext.baseFormula || item.system.degats,
damageCapped: damageContext.capped,
martialDvLabel: damageContext.martialDvSides ? `d${damageContext.martialDvSides}` : damageContext.martialDvFormula,
defaultMode
} }
); );
@@ -218,7 +225,7 @@ export class DonjonEtCieRollDialog {
buttons: [ buttons: [
{ {
action: "roll", action: "roll",
label: "Lancer", label: game.i18n.localize("DNC.UI.RollAction"),
icon: "fa-solid fa-burst", icon: "fa-solid fa-burst",
default: true, default: true,
callback: async (event, button) => { callback: async (event, button) => {
@@ -37,6 +37,7 @@ export default class DonjonEtCieActorSheet extends HandlebarsApplicationMixin(fo
rollDamage: DonjonEtCieActorSheet.#onRollDamage, rollDamage: DonjonEtCieActorSheet.#onRollDamage,
rollSpell: DonjonEtCieActorSheet.#onRollSpell, rollSpell: DonjonEtCieActorSheet.#onRollSpell,
rollUsage: DonjonEtCieActorSheet.#onRollUsage, rollUsage: DonjonEtCieActorSheet.#onRollUsage,
resetUsage: DonjonEtCieActorSheet.#onResetUsage,
useFavorService: DonjonEtCieActorSheet.#onUseFavorService, useFavorService: DonjonEtCieActorSheet.#onUseFavorService,
postItem: DonjonEtCieActorSheet.#onPostItem, postItem: DonjonEtCieActorSheet.#onPostItem,
adjustCounter: DonjonEtCieActorSheet.#onAdjustCounter adjustCounter: DonjonEtCieActorSheet.#onAdjustCounter
@@ -216,6 +217,11 @@ export default class DonjonEtCieActorSheet extends HandlebarsApplicationMixin(fo
return this.document.rollUsage(target.closest("[data-item-id]")?.dataset.itemId); return this.document.rollUsage(target.closest("[data-item-id]")?.dataset.itemId);
} }
static async #onResetUsage(event, target) {
event.preventDefault();
return this.document.resetUsage(target.closest("[data-item-id]")?.dataset.itemId);
}
static async #onUseFavorService(event, target) { static async #onUseFavorService(event, target) {
event.preventDefault(); event.preventDefault();
return this.document.useFavorService(target.dataset.department); return this.document.useFavorService(target.dataset.department);
@@ -29,7 +29,8 @@ export default class DonjonEtCieItemSheet extends HandlebarsApplicationMixin(fou
editImage: DonjonEtCieItemSheet.#onEditImage, editImage: DonjonEtCieItemSheet.#onEditImage,
postItem: DonjonEtCieItemSheet.#onPostItem, postItem: DonjonEtCieItemSheet.#onPostItem,
rollItem: DonjonEtCieItemSheet.#onRollItem, rollItem: DonjonEtCieItemSheet.#onRollItem,
rollDamageItem: DonjonEtCieItemSheet.#onRollDamageItem rollDamageItem: DonjonEtCieItemSheet.#onRollDamageItem,
resetUsageItem: DonjonEtCieItemSheet.#onResetUsageItem
} }
}; };
@@ -49,12 +50,17 @@ export default class DonjonEtCieItemSheet extends HandlebarsApplicationMixin(fou
isWeapon: item.type === "arme", isWeapon: item.type === "arme",
isArmor: item.type === "armure", isArmor: item.type === "armure",
isConsumable: item.type === "consommable", isConsumable: item.type === "consommable",
isTraining: item.type === "entrainement",
isSpell: item.type === "sortilege", isSpell: item.type === "sortilege",
canRollDamage: Boolean(item.system.degats), canRollDamage: Boolean(item.system.degats),
isEquipment: item.type === "equipement", isEquipment: item.type === "equipement",
isCapacity: item.type === "capacite", isCapacity: item.type === "capacite",
isLanguage: item.type === "langue", isLanguage: item.type === "langue",
isTrait: item.type === "trait", isTrait: item.type === "trait",
ammunitionUsageLabel: item.type === "arme" && Number(item.system.munitionsDelta ?? 0) > 0
? DonjonEtCieUtility.formatUsageDie(item.system.munitionsDelta)
: "—",
canResetUsage: item.type === "entrainement" && Number(item.system.deltaMax ?? 0) > 0 && Number(item.system.delta ?? 0) !== Number(item.system.deltaMax ?? 0),
armorProtectionDisplay: Number(item.system.resultatProtection ?? 0) > 0 ? item.system.resultatProtection : "—", armorProtectionDisplay: Number(item.system.resultatProtection ?? 0) > 0 ? item.system.resultatProtection : "—",
weaponCharacteristicLabel: item.type === "arme" ? DonjonEtCieUtility.getWeaponCharacteristicLabel(item.system.categorie) : null, weaponCharacteristicLabel: item.type === "arme" ? DonjonEtCieUtility.getWeaponCharacteristicLabel(item.system.categorie) : null,
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.system.description ?? "", { async: true }), enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.system.description ?? "", { async: true }),
@@ -119,4 +125,9 @@ export default class DonjonEtCieItemSheet extends HandlebarsApplicationMixin(fou
event.preventDefault(); event.preventDefault();
return this.document.rollDamage(); return this.document.rollDamage();
} }
static async #onResetUsageItem(event) {
event.preventDefault();
return this.document.resetUsageDie();
}
} }
@@ -43,7 +43,7 @@ export default class DonjonEtCieEmployeSheet extends DonjonEtCieActorSheet {
combatSections: ["armes", "armures", "consommables", "equipements"].map(getSection), combatSections: ["armes", "armures", "consommables", "equipements"].map(getSection),
spellSection: getSection("sortileges"), spellSection: getSection("sortileges"),
capacitySection: getSection("capacites"), capacitySection: getSection("capacites"),
profileSections: ["langues"].map(getSection) profileSections: ["langues", "entrainements"].map(getSection)
}; };
} }
} }
@@ -22,7 +22,9 @@ export default class DonjonEtCiePNJSheet extends DonjonEtCieActorSheet {
...super.DEFAULT_OPTIONS.actions, ...super.DEFAULT_OPTIONS.actions,
rollPnjArmor: DonjonEtCiePNJSheet.#onRollPnjArmor, rollPnjArmor: DonjonEtCiePNJSheet.#onRollPnjArmor,
rollPnjCourage: DonjonEtCiePNJSheet.#onRollPnjCourage, rollPnjCourage: DonjonEtCiePNJSheet.#onRollPnjCourage,
rollPnjAttackDamage: DonjonEtCiePNJSheet.#onRollPnjAttackDamage rollPnjAttackDamage: DonjonEtCiePNJSheet.#onRollPnjAttackDamage,
createPnjAttack: DonjonEtCiePNJSheet.#onCreatePnjAttack,
deletePnjAttack: DonjonEtCiePNJSheet.#onDeletePnjAttack
} }
}; };
@@ -48,7 +50,7 @@ export default class DonjonEtCiePNJSheet extends DonjonEtCieActorSheet {
armorDisplay: Number(system.defense?.armure?.delta ?? 0) ? `Δ${system.defense.armure.delta}` : "—", armorDisplay: Number(system.defense?.armure?.delta ?? 0) ? `Δ${system.defense.armure.delta}` : "—",
storedArmor: Number(system.defense?.armure?.resultatProtection ?? 0) > 0 ? system.defense.armure.resultatProtection : "—", storedArmor: Number(system.defense?.armure?.resultatProtection ?? 0) > 0 ? system.defense.armure.resultatProtection : "—",
courageDisplay: Number(system.defense?.courage?.delta ?? 0) ? `Δ${system.defense.courage.delta}` : "—", courageDisplay: Number(system.defense?.courage?.delta ?? 0) ? `Δ${system.defense.courage.delta}` : "—",
hasAttackDamage: Boolean(system.attaque?.degats) pnjAttacks: this.document.getPnjAttacks()
}; };
} }
@@ -64,6 +66,16 @@ export default class DonjonEtCiePNJSheet extends DonjonEtCieActorSheet {
static async #onRollPnjAttackDamage(event) { static async #onRollPnjAttackDamage(event) {
event.preventDefault(); event.preventDefault();
return this.document.rollPnjAttackDamage(); return this.document.rollPnjAttackDamage(event.target.closest("[data-attack-index]")?.dataset.attackIndex ?? 0);
}
static async #onCreatePnjAttack(event) {
event.preventDefault();
return this.document.createPnjAttack();
}
static async #onDeletePnjAttack(event) {
event.preventDefault();
return this.document.deletePnjAttack(event.target.closest("[data-attack-index]")?.dataset.attackIndex ?? 0);
} }
} }
+68 -4
View File
@@ -14,6 +14,12 @@ import { DonjonEtCieUtility } from "./donjon-et-cie-utility.mjs";
import { DonjonEtCieRollDialog } from "./applications/donjon-et-cie-roll-dialog.mjs"; import { DonjonEtCieRollDialog } from "./applications/donjon-et-cie-roll-dialog.mjs";
export class DonjonEtCieActor extends Actor { export class DonjonEtCieActor extends Actor {
static defaultPnjAttack = {
nom: "Attaque",
degats: "1d6",
notes: ""
};
prepareDerivedData() { prepareDerivedData() {
super.prepareDerivedData(); super.prepareDerivedData();
@@ -23,6 +29,19 @@ export class DonjonEtCieActor extends Actor {
} }
} }
async _preUpdate(changed, options, user) {
if (
foundry.utils.hasProperty(changed, "system.magie.focus.delta")
&& !foundry.utils.hasProperty(changed, "system.magie.focus.resultat")
&& !foundry.utils.hasProperty(changed, "system.magie.focus.sceneId")
) {
foundry.utils.setProperty(changed, "system.magie.focus.resultat", 0);
foundry.utils.setProperty(changed, "system.magie.focus.sceneId", "");
}
return super._preUpdate(changed, options, user);
}
getCharacteristicEntries() { getCharacteristicEntries() {
return DonjonEtCieUtility.getCharacteristicEntries(this.system); return DonjonEtCieUtility.getCharacteristicEntries(this.system);
} }
@@ -158,6 +177,50 @@ export class DonjonEtCieActor extends Actor {
if (item) return DonjonEtCieRollDialog.createUsage(item); if (item) return DonjonEtCieRollDialog.createUsage(item);
} }
async resetUsage(itemId) {
const item = this.items.get(itemId);
if (item?.type === "entrainement") return item.resetUsageDie();
}
getPnjAttacks() {
if (this.type !== "pnj") return [];
const attacks = Array.isArray(this.system.attaques) ? this.system.attaques : [];
if (attacks.length) return attacks.map((attack, index) => ({
index,
nom: attack.nom || `Attaque ${index + 1}`,
degats: attack.degats || "",
notes: attack.notes || ""
}));
const legacy = this.system.attaque;
if (legacy) {
return [{
index: 0,
nom: legacy.nom || "Attaque",
degats: legacy.degats || "",
notes: legacy.notes || ""
}];
}
return [{ index: 0, ...foundry.utils.deepClone(this.constructor.defaultPnjAttack) }];
}
async createPnjAttack() {
if (this.type !== "pnj") return null;
const attaques = this.getPnjAttacks().map(({ nom, degats, notes }) => ({ nom, degats, notes }));
attaques.push(foundry.utils.deepClone(this.constructor.defaultPnjAttack));
return this.update({ "system.attaques": attaques });
}
async deletePnjAttack(index) {
if (this.type !== "pnj") return null;
const attaques = this.getPnjAttacks().map(({ nom, degats, notes }) => ({ nom, degats, notes }));
attaques.splice(Number(index), 1);
if (!attaques.length) attaques.push(foundry.utils.deepClone(this.constructor.defaultPnjAttack));
return this.update({ "system.attaques": attaques });
}
#createPnjResourceProxy({ label, deltaPath, protectionPath = null }) { #createPnjResourceProxy({ label, deltaPath, protectionPath = null }) {
const delta = Number(foundry.utils.getProperty(this, deltaPath) ?? 0); const delta = Number(foundry.utils.getProperty(this, deltaPath) ?? 0);
const protection = protectionPath ? Number(foundry.utils.getProperty(this, protectionPath) ?? 0) : 0; const protection = protectionPath ? Number(foundry.utils.getProperty(this, protectionPath) ?? 0) : 0;
@@ -198,9 +261,10 @@ export class DonjonEtCieActor extends Actor {
})); }));
} }
async rollPnjAttackDamage() { async rollPnjAttackDamage(index = 0) {
const attackName = this.system.attaque?.nom || "Attaque"; const attack = this.getPnjAttacks()[Number(index)] ?? null;
const attackDamage = this.system.attaque?.degats || ""; const attackName = attack?.nom || "Attaque";
const attackDamage = attack?.degats || "";
if (!attackDamage) return null; if (!attackDamage) return null;
return DonjonEtCieRollDialog.createDamage(this, { return DonjonEtCieRollDialog.createDamage(this, {
@@ -208,7 +272,7 @@ export class DonjonEtCieActor extends Actor {
type: "attaque", type: "attaque",
system: { system: {
degats: attackDamage, degats: attackDamage,
portee: this.system.attaque?.notes || "" portee: attack?.notes || ""
} }
}); });
} }
+4 -2
View File
@@ -114,7 +114,8 @@ export const DONJON_ET_CIE = {
armes: { label: "Armes", createType: "arme" }, armes: { label: "Armes", createType: "arme" },
armures: { label: "Armures", createType: "armure" }, armures: { label: "Armures", createType: "armure" },
equipements: { label: "Equipements", createType: "equipement" }, equipements: { label: "Equipements", createType: "equipement" },
consommables: { label: "Consommables", createType: "consommable" } consommables: { label: "Consommables", createType: "consommable" },
entrainements: { label: "Entrainements", createType: "entrainement" }
}, },
sectionTypes: { sectionTypes: {
traits: ["trait"], traits: ["trait"],
@@ -124,6 +125,7 @@ export const DONJON_ET_CIE = {
armes: ["arme"], armes: ["arme"],
armures: ["armure"], armures: ["armure"],
equipements: ["equipement"], equipements: ["equipement"],
consommables: ["consommable"] consommables: ["consommable"],
entrainements: ["entrainement"]
} }
}; };
+41 -1
View File
@@ -27,18 +27,51 @@ export class DonjonEtCieItem extends Item {
return Number(this.system.delta ?? 0); return Number(this.system.delta ?? 0);
} }
get usageDieMax() {
return Number(this.system.deltaMax ?? this.system.delta ?? 0);
}
get ammunitionUsageDie() {
return Number(this.system.munitionsDelta ?? 0);
}
get damageUsageDie() {
return Number(this.system.degatsDelta ?? 0);
}
async roll() { async roll() {
if (this.type === "arme") return DonjonEtCieRollDialog.createWeapon(this.actor, this); if (this.type === "arme") return DonjonEtCieRollDialog.createWeapon(this.actor, this);
if (this.type === "sortilege") return DonjonEtCieRollDialog.createSpell(this.actor, this); if (this.type === "sortilege") return DonjonEtCieRollDialog.createSpell(this.actor, this);
if (this.usageDie) return DonjonEtCieRollDialog.createUsage(this); if (this.usageDie) return DonjonEtCieRollDialog.createUsage(this);
if (this.type === "entrainement" && this.usageDieMax > 0) {
ui.notifications.warn(game.i18n.localize("DNC.Warn.TrainingExhausted"));
return null;
}
return this.postToChat(); return this.postToChat();
} }
async rollDamage() { async rollDamage() {
if (!this.system.degats) return null; if (this.system.degatsEstUsageDe) {
if (!Number(this.system.degatsDelta ?? 0)) {
ui.notifications.warn(game.i18n.localize("DNC.Warn.DamageExhausted"));
return null;
}
} else if (!this.system.degats) {
return null;
}
return DonjonEtCieRollDialog.createDamage(this.actor, this); return DonjonEtCieRollDialog.createDamage(this.actor, this);
} }
async rollAmmoUsage() {
if (this.type !== "arme") return null;
return game.system.donjonEtCie.rolls.rollWeaponAmmoUsage(this);
}
async rollDamageUsage() {
if (this.type !== "arme") return null;
return game.system.donjonEtCie.rolls.rollWeaponDamageUsage(this);
}
async postToChat() { async postToChat() {
const content = await foundry.applications.handlebars.renderTemplate( const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-donjon-et-cie/templates/chat/item-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/item-card.hbs",
@@ -54,4 +87,11 @@ export class DonjonEtCieItem extends Item {
content content
}); });
} }
async resetUsageDie() {
if (this.type !== "entrainement") return this;
const deltaMax = this.usageDieMax;
if (!deltaMax) return this;
return this.update({ "system.delta": deltaMax });
}
} }
+925
View File
@@ -0,0 +1,925 @@
/**
* Donjon & Cie - Systeme FoundryVTT
*
* Donjon & Cie est un jeu de role edite par John Doe.
* Ce systeme FoundryVTT est une implementation independante et n'est pas
* affilie a John Doe.
*
* @author LeRatierBretonnien
* @copyright 20252026 LeRatierBretonnien
* @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
*/
import { DonjonEtCieUtility } from "./donjon-et-cie-utility.mjs";
export class DonjonEtCieMacros {
static MISSION_PACK_MODE_SETTING = "missionPackMode";
static MISSION_PACK_SOCKET_SCOPE = "missionPackCampaign";
static MISSION_PACK_REQUEST_TIMEOUT = 300000;
static MISSION_PACK_CAMPAIGN_DICE = [20, 12, 10, 8];
static MISSION_PACK_STEWARD_MODES = {
positive: "avantage",
neutral: "normal",
negative: "desavantage"
};
static #campaignRequests = new Map();
static #socketRegistered = false;
static MISSION_PACK_TABLES = [
{ key: "melee", name: "Armes de corps a corps", multiple: false },
{ key: "ranged", name: "Armes a distance", multiple: false },
{ key: "armor", name: "Armures", multiple: false },
{ key: "misc", name: "Encas et equipement divers", multiple: true }
];
static MISSION_PACK_UNIQUE_ITEMS = [
{
name: "L epee de Monsieur Noir",
type: "arme",
system: {
categorie: "melee",
caracteristique: "force",
degats: "1d6",
portee: "",
mains: 1,
equipee: false,
description: "<p>Elle n a rien de particulier mais confere un prestige important.</p><p>Avantage a tous les jets d interaction avec les employes de Donjon &amp; Cie.</p>",
notes: ""
}
},
{
name: "Fil a plomb d Arnezon",
type: "equipement",
system: {
quantite: 1,
equipee: false,
emplacement: "",
description: "<p>Il oscille de maniere clairement etrange en presence d un passage secret.</p>",
notes: ""
}
},
{
name: "Boussole de Drize Durban",
type: "equipement",
system: {
quantite: 1,
equipee: false,
emplacement: "",
description: "<p>Trois reglages : pointe vers le client, le resident ou l employe le plus proche.</p>",
notes: ""
}
},
{
name: "Boule de cristal de la supervision",
type: "equipement",
system: {
quantite: 1,
equipee: false,
emplacement: "",
description: "<p>Permet de voir toute personne ou lieu qu on connait deja.</p><p>Jet de SAG pour la controler. La plupart des huiles sentent quand on les observe.</p>",
notes: ""
}
},
{
name: "Dent du dragon Leogradonardicus III",
type: "equipement",
system: {
quantite: 1,
equipee: false,
emplacement: "",
description: "<p>Un jet de CHA reussi permet de controler les reptiles inintelligents et de charmer les humanoides reptiliens.</p>",
notes: ""
}
},
{
name: "Doigt d Aarcarcerax",
type: "consommable",
system: {
quantite: 1,
delta: 4,
effet: "Tue la creature vers qui on pointe le doigt.",
description: "<p>Tue la creature vers qui on pointe le doigt.</p><p>La liche sait instantanement qu on a retrouve son doigt.</p>",
notes: ""
}
},
{
name: "Cape de Vlad von Drakovitch",
type: "consommable",
system: {
quantite: 1,
delta: 6,
effet: "Permet de se transformer en 1-3 rats, 4-5 chauve-souris, 6 forme gazeuse.",
description: "<p>Delta 6 charges.</p><p>Permet de se transformer en 1-3 rats, 4-5 chauve-souris, 6 forme gazeuse.</p>",
notes: ""
}
},
{
name: "Vieux carnet de notes d Affalella",
type: "consommable",
system: {
quantite: 1,
delta: 6,
effet: "Fonctionne comme des faveurs de la Mercatique utilisables uniquement dans les aires client.",
description: "<p>Vieux carnet de notes d Affalella, directrice de la Mercatique.</p><p>Fonctionne comme Delta 6 faveurs de la Mercatique utilisables uniquement dans les aires client.</p>",
notes: ""
}
},
{
name: "Ancienne cle universelle de Paiji",
type: "consommable",
system: {
quantite: 1,
delta: 8,
effet: "Ouvre tous les coffres, portes, armoires et autres serrures du Donjon.",
description: "<p>Ancienne cle universelle de Paiji, directeur de la Maintenance.</p><p>Delta 8 usages. Ouvre tous les coffres, portes, armoires et autres serrures du Donjon.</p>",
notes: ""
}
},
{
name: "Baguette d urgence",
type: "equipement",
system: {
quantite: 1,
equipee: false,
emplacement: "",
description: "<p>Teleporte l utilisateur au palier des huiles lorsque la baguette est brisee.</p>",
notes: ""
}
}
];
static registerSocketListeners() {
if (this.#socketRegistered || !game.socket) return;
game.socket.on(`system.${game.system.id}`, (payload) => {
void this.#handleSocketMessage(payload);
});
this.#socketRegistered = true;
}
static #normalizeName(value) {
return String(value ?? "")
.normalize("NFD")
.replace(/\p{Diacritic}/gu, "")
.replace(/[']/g, " ")
.replace(/\s+/g, " ")
.trim()
.toLowerCase();
}
static #getMissionPackLabel(key) {
return game.i18n.localize(`DNC.Macro.MissionPack.${key}`);
}
static #getModeLabel(mode) {
if (mode === "avantage") return game.i18n.localize("DNC.UI.ModeAdvantage");
if (mode === "desavantage") return game.i18n.localize("DNC.UI.ModeDisadvantage");
return game.i18n.localize("DNC.UI.ModeNormal");
}
static #getMissionPackMode() {
return String(game.settings.get("fvtt-donjon-et-cie", this.MISSION_PACK_MODE_SETTING) ?? "classic");
}
static #getMissionPackModeLabel(mode) {
return game.i18n.localize(mode === "campaign"
? "DNC.Macro.MissionPack.ModeCampaign"
: "DNC.Macro.MissionPack.ModeClassic");
}
static #getMissionPackModeDescription(mode) {
return game.i18n.localize(mode === "campaign"
? "DNC.Macro.MissionPack.ModeCampaignHint"
: "DNC.Macro.MissionPack.ModeClassicHint");
}
static #getStewardRelationLabel(relation) {
return game.i18n.localize(`DNC.Macro.MissionPack.Relation.${relation ?? "neutral"}`);
}
static #getStewardRelationOptions() {
return ["positive", "neutral", "negative"].map((relation) => ({
value: relation,
label: this.#getStewardRelationLabel(relation)
}));
}
static #getCampaignDiceOptions() {
return this.MISSION_PACK_CAMPAIGN_DICE.map((sides) => ({
value: String(sides),
label: `1d${sides}`
}));
}
static #getDefaultCampaignAssignments() {
return Object.fromEntries(this.MISSION_PACK_TABLES.map((spec, index) => [spec.key, String(this.MISSION_PACK_CAMPAIGN_DICE[index])]));
}
static #getDefaultMissionPackActorId(actors) {
const controlledActor = canvas?.tokens?.controlled?.[0]?.actor ?? null;
if (controlledActor?.type === "employe") return controlledActor.id;
return actors[0]?.id ?? "";
}
static #getMissionPackActorOptions() {
return game.actors
.filter((actor) => actor.type === "employe")
.sort((a, b) => a.name.localeCompare(b.name, "fr", { sensitivity: "base" }))
.map((actor) => ({
value: actor.id,
label: actor.name
}));
}
static #getMissionPackOwnerUsers(actor) {
return game.users
.filter((user) => !user.isGM && actor.testUserPermission(user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER))
.sort((a, b) => Number(b.active) - Number(a.active) || a.name.localeCompare(b.name, "fr", { sensitivity: "base" }));
}
static #getCampaignTargetUser(actor) {
return this.#getMissionPackOwnerUsers(actor).find((user) => user.active) ?? null;
}
static async #resolveMissionPackActor(target = null) {
if (target?.documentName === "Actor") return target;
if (target?.actor?.documentName === "Actor") return target.actor;
if (typeof target === "string" && target) {
const document = await fromUuid(target);
if (document?.documentName === "Actor") return document;
if (document?.actor?.documentName === "Actor") return document.actor;
}
const controlledTokens = canvas?.tokens?.controlled ?? [];
if (controlledTokens.length === 1 && controlledTokens[0]?.actor) return controlledTokens[0].actor;
if (controlledTokens.length > 1) {
ui.notifications.warn(game.i18n.localize("DNC.Macro.MissionPack.WarnMultipleTokens"));
return null;
}
return game.user.character ?? null;
}
static async #getRandomTableDocuments() {
const worldTables = game.tables?.contents ?? [];
if (worldTables.length) return worldTables;
const pack = game.packs.get("fvtt-donjon-et-cie.random-tables");
return pack ? pack.getDocuments() : [];
}
static async #findRollTableByName(name) {
const normalizedName = this.#normalizeName(name);
const worldTable = game.tables.find((table) => this.#normalizeName(table.name) === normalizedName);
if (worldTable) return worldTable;
const packTables = await this.#getRandomTableDocuments();
return packTables.find((table) => this.#normalizeName(table.name) === normalizedName) ?? null;
}
static async #getEquipmentDocuments() {
const pack = game.packs.get("fvtt-donjon-et-cie.equipment");
const packDocuments = pack ? await pack.getDocuments() : [];
return [...(game.items?.contents ?? []), ...packDocuments];
}
static async #findItemByName(name) {
const normalizedName = this.#normalizeName(name);
const documents = await this.#getEquipmentDocuments();
return documents.find((item) => this.#normalizeName(item.name) === normalizedName) ?? null;
}
static #extractUuidTargets(text) {
const matches = [...String(text ?? "").matchAll(/@UUID\[([^\]]+)\](?:\{([^}]+)\})?/g)];
return matches.map((match) => ({
uuid: match[1],
label: match[2] ?? ""
}));
}
static #extractPlainTextEntries(text) {
const rawText = String(text ?? "")
.replace(/<[^>]+>/g, " ")
.replace(/\s+/g, " ")
.trim();
if (!rawText) return [];
return rawText
.split(",")
.map((entry) => entry.trim())
.filter(Boolean)
.filter((entry) => !/^dotation\s+\d+$/i.test(entry));
}
static async #evaluateRoll(formula) {
const roll = await (new Roll(formula)).evaluate();
const values = roll.dice
.flatMap((die) => die.results.map((result) => Number(result.result ?? result.count ?? 0)))
.filter((value) => Number.isFinite(value));
return {
roll,
total: Number(roll.total ?? 0),
values
};
}
static #formatRollValues(values, kept) {
if (!values.length) return String(kept ?? 0);
if (values.length === 1) return String(values[0]);
return `${values.join(" / ")} -> ${kept}`;
}
static async #rollPackDie(sides, { mode = "normal" } = {}) {
const formula = mode === "avantage"
? `2d${sides}kh`
: mode === "desavantage"
? `2d${sides}kl`
: `1d${sides}`;
const evaluated = await this.#evaluateRoll(formula);
return {
sides: Number(sides),
dieLabel: `1d${sides}`,
roll: evaluated.roll,
values: evaluated.values,
valuesLabel: this.#formatRollValues(evaluated.values, evaluated.total),
kept: evaluated.total,
mode,
modeLabel: this.#getModeLabel(mode)
};
}
static #createInlineItemData(spec) {
return {
name: spec.name,
type: spec.type,
img: DonjonEtCieUtility.getDefaultItemIcon(spec.type),
system: foundry.utils.deepClone(spec.system)
};
}
static #getTableResults(table) {
return table?.results?.contents ?? [];
}
static #getTableRange(result) {
const rawRange = Array.isArray(result?.range)
? result.range
: result?._source?.range ?? [];
return [Number(rawRange[0] ?? 0), Number(rawRange[1] ?? 0)];
}
static #findTableResultByTotal(table, total) {
const entries = this.#getTableResults(table)
.map((result) => ({
result,
range: this.#getTableRange(result)
}))
.filter(({ range }) => Number.isFinite(range[0]) && Number.isFinite(range[1]) && range[1] >= range[0])
.sort((a, b) => a.range[0] - b.range[0]);
if (!entries.length) {
return {
result: null,
clampedTotal: total,
range: null
};
}
const minimum = entries[0].range[0];
const maximum = entries.at(-1).range[1];
const clampedTotal = Math.max(minimum, Math.min(maximum, total));
const match = entries.find(({ range }) => clampedTotal >= range[0] && clampedTotal <= range[1]) ?? entries.at(-1);
return {
result: match.result,
clampedTotal,
range: match.range
};
}
static async #resolveUniqueMissionPackEntry({ comparisonRoll = null } = {}) {
const reference = await this.#rollPackDie(20);
const actorEvaluation = comparisonRoll == null ? await this.#rollPackDie(20) : null;
const actorRoll = Number(comparisonRoll ?? actorEvaluation?.kept ?? 0);
if (reference.kept !== actorRoll) {
return {
matched: false,
referenceRoll: reference.kept,
actorRoll,
uniqueRoll: null,
itemName: "",
itemData: null,
rolls: [reference.roll, ...(actorEvaluation ? [actorEvaluation.roll] : [])]
};
}
const uniqueRoll = await this.#rollPackDie(this.MISSION_PACK_UNIQUE_ITEMS.length);
const spec = this.MISSION_PACK_UNIQUE_ITEMS[Math.max(0, uniqueRoll.kept - 1)] ?? this.MISSION_PACK_UNIQUE_ITEMS[0];
const document = await this.#findItemByName(spec.name);
const itemData = document ? this.#toEmbeddedItemData(document) : this.#createInlineItemData(spec);
return {
matched: true,
referenceRoll: reference.kept,
actorRoll,
uniqueRoll: uniqueRoll.kept,
itemName: document?.name ?? spec.name,
itemData,
rolls: [reference.roll, ...(actorEvaluation ? [actorEvaluation.roll] : []), uniqueRoll.roll]
};
}
static async #resolveTableResultEntries(result, { multiple = false } = {}) {
if (!result) {
return {
display: game.i18n.localize("DNC.Macro.MissionPack.NoResult"),
entries: []
};
}
const source = result._source ?? {};
const resultName = String(result.name ?? source.name ?? "").trim();
const resultDescription = String(result.description ?? source.description ?? "").trim();
const resultText = String(source.text ?? "").trim();
const resultLabel = resultName || resultDescription || resultText;
const documentUuid = result.type === "document"
? result.documentUuid ?? source.documentUuid ?? null
: null;
if (documentUuid) {
const document = await fromUuid(documentUuid);
const label = document?.name ?? resultLabel;
return {
display: label,
entries: label ? [{ name: label, document }] : []
};
}
const sourceText = resultDescription || resultText || resultLabel;
const uuidTargets = this.#extractUuidTargets(sourceText);
if (uuidTargets.length) {
const entries = [];
for (const target of uuidTargets) {
const document = await fromUuid(target.uuid);
const name = document?.name ?? target.label;
if (!name) continue;
entries.push({ name, document });
}
return {
display: entries.map((entry) => entry.name).join(", "),
entries
};
}
const names = multiple
? this.#extractPlainTextEntries(sourceText || resultLabel)
: [resultLabel].filter(Boolean);
return {
display: names.join(", "),
entries: names.map((name) => ({ name, document: null }))
};
}
static #toEmbeddedItemData(item) {
const data = foundry.utils.deepClone(item.toObject());
delete data._id;
delete data.folder;
delete data.sort;
return data;
}
static async #materializeMissionPackEntries(entries) {
const embeddedItems = [];
const addedNames = [];
const missingNames = [];
for (const entry of entries) {
const item = entry.document ?? await this.#findItemByName(entry.name);
if (!item) {
missingNames.push(entry.name);
continue;
}
embeddedItems.push(this.#toEmbeddedItemData(item));
addedNames.push(item.name);
}
return {
embeddedItems,
addedNames,
missingNames
};
}
static async #openCampaignAllocationDialog(actor, { playerName = "", requesterName = "" } = {}) {
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-donjon-et-cie/templates/dialogs/mission-pack-campaign-dialog.hbs",
{
actorName: actor.name,
playerName,
requesterName,
rank: Number(actor.system.anciennete?.rang ?? actor.system.sante?.dv ?? 0),
assignments: this.MISSION_PACK_TABLES.map((spec) => ({
key: spec.key,
label: this.#getMissionPackLabel(spec.key),
fieldName: `${spec.key}Die`,
selectedDie: this.#getDefaultCampaignAssignments()[spec.key]
})),
diceOptions: this.#getCampaignDiceOptions(),
relationOptions: this.#getStewardRelationOptions(),
selectedRelation: "neutral"
}
);
return foundry.applications.api.DialogV2.wait({
window: {
title: game.i18n.localize("DNC.Macro.MissionPack.CampaignDialogTitle"),
icon: "fa-solid fa-dice"
},
classes: ["dnc-roll-dialog", "dnc-mission-pack-dialog"],
content,
modal: true,
buttons: [
{
action: "confirm",
label: game.i18n.localize("DNC.Macro.MissionPack.CampaignDialogAction"),
icon: "fa-solid fa-check",
default: true,
callback: async (event, button) => {
const assignments = Object.fromEntries(this.MISSION_PACK_TABLES.map((spec) => [
spec.key,
Number(button.form.elements[`${spec.key}Die`]?.value ?? 0)
]));
const selectedDice = Object.values(assignments).filter((value) => value > 0);
if (selectedDice.length !== this.MISSION_PACK_TABLES.length) {
ui.notifications.warn(game.i18n.localize("DNC.Macro.MissionPack.WarnDiceRequired"));
return null;
}
if (new Set(selectedDice).size !== selectedDice.length) {
ui.notifications.warn(game.i18n.localize("DNC.Macro.MissionPack.WarnDiceUnique"));
return null;
}
return {
assignments,
stewardRelation: button.form.elements.stewardRelation?.value ?? "neutral"
};
}
}
],
rejectClose: false
});
}
static async #handleSocketMessage(payload) {
if (payload?.scope !== this.MISSION_PACK_SOCKET_SCOPE) return;
if (payload.type === "campaign-response") {
const pending = this.#campaignRequests.get(payload.requestId);
if (!pending) return;
clearTimeout(pending.timeoutId);
this.#campaignRequests.delete(payload.requestId);
pending.resolve(payload.result ?? null);
return;
}
if (payload.type !== "campaign-request" || payload.targetUserId !== game.user.id) return;
const actor = await fromUuid(payload.actorUuid);
const allocation = actor
? await this.#openCampaignAllocationDialog(actor, {
playerName: game.user.name,
requesterName: payload.requesterName ?? ""
})
: null;
game.socket.emit(`system.${game.system.id}`, {
scope: this.MISSION_PACK_SOCKET_SCOPE,
type: "campaign-response",
requestId: payload.requestId,
result: allocation
? {
allocation,
responderUserId: game.user.id,
responderName: game.user.name
}
: {
allocation: null,
responderUserId: game.user.id,
responderName: game.user.name
}
});
}
static async #requestCampaignAllocation(actor, targetUser) {
if (!targetUser || targetUser.id === game.user.id) {
const allocation = await this.#openCampaignAllocationDialog(actor, {
playerName: game.user.name,
requesterName: game.user.name
});
return allocation ? { allocation, responderName: game.user.name } : null;
}
const requestId = foundry.utils.randomID();
const responsePromise = new Promise((resolve) => {
const timeoutId = globalThis.setTimeout(() => {
this.#campaignRequests.delete(requestId);
resolve(null);
}, this.MISSION_PACK_REQUEST_TIMEOUT);
this.#campaignRequests.set(requestId, { resolve, timeoutId });
});
game.socket.emit(`system.${game.system.id}`, {
scope: this.MISSION_PACK_SOCKET_SCOPE,
type: "campaign-request",
requestId,
targetUserId: targetUser.id,
actorUuid: actor.uuid,
requesterName: game.user.name
});
const response = await responsePromise;
if (!response?.allocation) {
ui.notifications.warn(game.i18n.format("DNC.Macro.MissionPack.CampaignRequestCanceled", {
actor: actor.name,
player: targetUser.name
}));
return null;
}
return {
allocation: response.allocation,
responderName: response.responderName ?? targetUser.name
};
}
static async #finalizeMissionPack(actor, drawPlans, {
generationMode = "classic",
controllerName = "",
stewardRelation = "neutral",
uniqueComparisonRoll = null
} = {}) {
const draws = [];
const embeddedItems = [];
const rolls = [];
let missingCount = 0;
for (const plan of drawPlans) {
rolls.push(...(plan.rolls ?? []).filter((roll) => roll instanceof Roll));
if (plan.failed || !plan.resolved) {
draws.push({
label: this.#getMissionPackLabel(plan.spec.key),
display: game.i18n.format("DNC.Macro.MissionPack.TableMissing", { table: plan.spec.name }),
addedNames: [],
addedSummary: "",
missingNames: [],
missingSummary: "",
failed: true
});
continue;
}
const materialized = await this.#materializeMissionPackEntries(plan.resolved.entries);
embeddedItems.push(...materialized.embeddedItems);
missingCount += materialized.missingNames.length;
draws.push({
label: this.#getMissionPackLabel(plan.spec.key),
display: plan.resolved.display || game.i18n.localize("DNC.Macro.MissionPack.NoResult"),
addedNames: materialized.addedNames,
addedSummary: materialized.addedNames.join(", "),
missingNames: materialized.missingNames,
missingSummary: materialized.missingNames.join(", "),
failed: false,
...plan.detail
});
}
const uniqueEntry = await this.#resolveUniqueMissionPackEntry({ comparisonRoll: uniqueComparisonRoll });
rolls.push(...(uniqueEntry.rolls ?? []).filter((roll) => roll instanceof Roll));
if (uniqueEntry.itemData) {
embeddedItems.push(uniqueEntry.itemData);
}
const createdItems = embeddedItems.length
? await actor.createEmbeddedDocuments("Item", embeddedItems, { renderSheet: false })
: [];
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-donjon-et-cie/templates/chat/mission-pack-card.hbs",
{
title: game.i18n.localize("DNC.Macro.MissionPack.Title"),
actorName: actor.name,
createdCount: createdItems.length,
missingCount,
draws,
uniqueEntry,
isCampaign: generationMode === "campaign",
generationModeLabel: this.#getMissionPackModeLabel(generationMode),
controllerName,
stewardRelationLabel: generationMode === "campaign" ? this.#getStewardRelationLabel(stewardRelation) : ""
}
);
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor }),
user: game.user.id,
content,
rolls
});
if (createdItems.length && !missingCount) {
ui.notifications.info(game.i18n.format("DNC.Macro.MissionPack.Success", {
actor: actor.name,
count: createdItems.length
}));
} else if (createdItems.length) {
ui.notifications.warn(game.i18n.format("DNC.Macro.MissionPack.Partial", {
actor: actor.name,
count: createdItems.length,
missing: missingCount
}));
} else {
ui.notifications.warn(game.i18n.localize("DNC.Macro.MissionPack.WarnNothingAdded"));
}
return {
actor,
createdItems,
missingCount,
draws,
uniqueEntry,
generationMode,
controllerName,
stewardRelation
};
}
static async #grantClassicMissionPack(actor) {
const drawPlans = [];
for (const spec of this.MISSION_PACK_TABLES) {
const table = await this.#findRollTableByName(spec.name);
if (!table) {
drawPlans.push({ spec, failed: true, resolved: null, rolls: [] });
continue;
}
const draw = await table.draw({ displayChat: false });
const result = draw.results?.[0] ?? null;
drawPlans.push({
spec,
failed: false,
resolved: await this.#resolveTableResultEntries(result, { multiple: spec.multiple }),
detail: null,
rolls: draw.roll instanceof Roll ? [draw.roll] : []
});
}
return this.#finalizeMissionPack(actor, drawPlans, {
generationMode: "classic"
});
}
static async #grantCampaignMissionPack(actor) {
const targetUser = this.#getCampaignTargetUser(actor);
const allocationResult = await this.#requestCampaignAllocation(actor, targetUser);
if (!allocationResult?.allocation) return null;
const rankBonus = Number(actor.system.anciennete?.rang ?? actor.system.sante?.dv ?? 0);
const stewardRelation = allocationResult.allocation.stewardRelation ?? "neutral";
const mode = this.MISSION_PACK_STEWARD_MODES[stewardRelation] ?? "normal";
const drawPlans = [];
let uniqueComparisonRoll = null;
for (const spec of this.MISSION_PACK_TABLES) {
const table = await this.#findRollTableByName(spec.name);
if (!table) {
drawPlans.push({ spec, failed: true, resolved: null, rolls: [] });
continue;
}
const sides = Number(allocationResult.allocation.assignments?.[spec.key] ?? 0);
const rollData = await this.#rollPackDie(sides, { mode });
if (sides === 20) uniqueComparisonRoll = rollData.kept;
const total = rollData.kept + rankBonus;
const tableResult = this.#findTableResultByTotal(table, total);
drawPlans.push({
spec,
failed: false,
resolved: await this.#resolveTableResultEntries(tableResult.result, { multiple: spec.multiple }),
detail: {
dieLabel: rollData.dieLabel,
modeLabel: rollData.modeLabel,
rollValuesLabel: rollData.valuesLabel,
kept: rollData.kept,
rankBonus,
total,
resolvedTotal: tableResult.clampedTotal,
clamped: tableResult.clampedTotal !== total
},
rolls: [rollData.roll]
});
}
return this.#finalizeMissionPack(actor, drawPlans, {
generationMode: "campaign",
controllerName: allocationResult.responderName ?? targetUser?.name ?? game.user.name,
stewardRelation,
uniqueComparisonRoll
});
}
/**
* Open the GM-only mission pack dialog.
* @returns {Promise<object|null>}
*/
static async openMissionPackDialog() {
if (!game.user.isGM) {
ui.notifications.warn(game.i18n.localize("DNC.Macro.MissionPack.WarnGMOnly"));
return null;
}
const actorOptions = this.#getMissionPackActorOptions();
if (!actorOptions.length) {
ui.notifications.warn(game.i18n.localize("DNC.Macro.MissionPack.WarnNoEmployees"));
return null;
}
const mode = this.#getMissionPackMode();
const selectedActorId = this.#getDefaultMissionPackActorId(actorOptions.map((option) => game.actors.get(option.value)).filter(Boolean));
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-donjon-et-cie/templates/dialogs/mission-pack-dialog.hbs",
{
actorOptions,
selectedActorId,
modeLabel: this.#getMissionPackModeLabel(mode),
modeDescription: this.#getMissionPackModeDescription(mode),
isCampaign: mode === "campaign"
}
);
return foundry.applications.api.DialogV2.wait({
window: {
title: game.i18n.localize("DNC.Macro.MissionPack.DialogTitle"),
icon: "fa-solid fa-box-open"
},
classes: ["dnc-roll-dialog"],
content,
modal: false,
buttons: [
{
action: "grant",
label: game.i18n.localize("DNC.Macro.MissionPack.DialogAction"),
icon: "fa-solid fa-box-open",
default: true,
callback: async (event, button) => {
const actorId = button.form.elements.actorId?.value ?? "";
const actor = actorId ? game.actors.get(actorId) : null;
if (!actor) {
ui.notifications.warn(game.i18n.localize("DNC.Macro.MissionPack.WarnNoActor"));
return null;
}
return this.grantMissionPack(actor);
}
}
],
rejectClose: false
});
}
/**
* Draw the initial mission pack for the resolved actor and add the resulting items.
* @param {Actor|string|null} target Resolved actor, token, or UUID. Defaults to selected token or user character.
* @returns {Promise<object|null>}
*/
static async grantMissionPack(target = null) {
const actor = await this.#resolveMissionPackActor(target);
if (!actor) {
ui.notifications.warn(game.i18n.localize("DNC.Macro.MissionPack.WarnNoActor"));
return null;
}
if (actor.type !== "employe") {
ui.notifications.warn(game.i18n.localize("DNC.Macro.MissionPack.WarnInvalidActor"));
return null;
}
return this.#getMissionPackMode() === "campaign"
? this.#grantCampaignMissionPack(actor)
: this.#grantClassicMissionPack(actor);
}
}
+191 -6
View File
@@ -18,10 +18,48 @@ 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";
const WELCOME_MESSAGE_SETTING = "welcomeMessageVersion";
function injectActorDirectoryMissionPackButton(app, element) {
if (!game.user.isGM) return;
const root = app?.element ?? element?.[0] ?? element;
if (!(root instanceof HTMLElement)) return;
const headerActions = root.querySelector(".directory-header .header-actions");
if (!(headerActions instanceof HTMLElement)) return;
if (headerActions.querySelector(".dnc-mission-pack-button")) return;
const button = document.createElement("button");
button.type = "button";
button.className = "dnc-mission-pack-button";
button.title = game.i18n.localize("DNC.Macro.MissionPack.SidebarButton");
button.setAttribute("aria-label", game.i18n.localize("DNC.Macro.MissionPack.SidebarButton"));
button.innerHTML = `<i class="fa-solid fa-box-open" inert></i><span>${game.i18n.localize("DNC.Macro.MissionPack.SidebarButton")}</span>`;
button.addEventListener("click", () => {
void game.system.donjonEtCie.macros.openMissionPackDialog();
});
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) {
const button = event.target.closest("[data-action='rollChatDamage'], [data-action='rollSpellChaos'], [data-action='applyDamage']"); const button = event.target.closest("[data-action='rollChatDamage'], [data-action='rollSpellChaos'], [data-action='applyDamage'], [data-action='rollAmmoUsage'], [data-action='rollDamageUsage']");
if (!(button instanceof HTMLElement)) return; if (!(button instanceof HTMLElement)) return;
event.preventDefault(); event.preventDefault();
@@ -60,10 +98,96 @@ function onChatActionClick(event) {
const itemUuid = button.dataset.itemUuid; const itemUuid = button.dataset.itemUuid;
if (!itemUuid) return; if (!itemUuid) return;
const item = await fromUuid(itemUuid); const item = await fromUuid(itemUuid);
if (button.dataset.action === "rollAmmoUsage") {
return item?.rollAmmoUsage?.();
}
if (button.dataset.action === "rollDamageUsage") {
return item?.rollDamageUsage?.();
}
return item?.rollDamage?.(); return item?.rollDamage?.();
})(); })();
} }
function registerSystemSettings() {
game.settings.register("fvtt-donjon-et-cie", WELCOME_MESSAGE_SETTING, {
name: "Version du message de bienvenue",
hint: "Usage interne pour eviter de republier le message de bienvenue a chaque chargement.",
scope: "world",
config: false,
type: String,
default: ""
});
game.settings.register("fvtt-donjon-et-cie", DonjonEtCieMacros.MISSION_PACK_MODE_SETTING, {
name: "Mode de paquetage de mission",
hint: "Choisit entre le tirage classique rapide et la regle optionnelle de campagne avec allocation des des par les joueurs.",
scope: "world",
config: true,
type: String,
default: "classic",
choices: {
classic: "Classique",
campaign: "Campagne"
}
});
}
async function getHelpJournalLink() {
const pack = [...game.packs.values()].find((candidate) => candidate.metadata.name === "system-help");
if (!pack) return null;
const index = await pack.getIndex();
const entry = index.find((document) => document.name === "Aide du systeme");
if (!entry?._id) return null;
const journal = await pack.getDocument(entry._id);
if (!journal?.uuid) return null;
return `@UUID[${journal.uuid}]{${game.i18n.localize("DNC.Welcome.HelpLinkLabel")}}`;
}
async function maybeCreateWelcomeMessage() {
if (!game.user.isGM) return;
const currentVersion = String(game.system.version ?? "");
const shownVersion = String(game.settings.get("fvtt-donjon-et-cie", WELCOME_MESSAGE_SETTING) ?? "");
if (shownVersion === currentVersion) return;
const helpJournalLink = await getHelpJournalLink();
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-donjon-et-cie/templates/chat/welcome-card.hbs",
{
title: game.i18n.localize("DNC.Welcome.Title"),
subtitle: game.i18n.format("DNC.Welcome.Subtitle", { version: currentVersion }),
intro: game.i18n.localize("DNC.Welcome.Intro"),
bullets: [
game.i18n.localize("DNC.Welcome.BulletActors"),
game.i18n.localize("DNC.Welcome.BulletItems"),
game.i18n.localize("DNC.Welcome.BulletMissionPack")
],
helpLabel: game.i18n.localize("DNC.Welcome.HelpLabel"),
helpLink: helpJournalLink,
helpFallback: game.i18n.localize("DNC.Welcome.HelpFallback"),
footer: game.i18n.localize("DNC.Welcome.Footer"),
creditsLabel: game.i18n.localize("DNC.Welcome.CreditsLabel"),
creditsText: game.i18n.localize("DNC.Welcome.CreditsText"),
officialLabel: game.i18n.localize("DNC.Welcome.OfficialLabel"),
officialUrl: "https://johndoe-rpg.com/catalogue/donjon-cie/",
officialLinkText: game.i18n.localize("DNC.Welcome.OfficialLinkText")
}
);
await ChatMessage.create({
speaker: {
alias: game.system.title
},
user: game.user.id,
content: await TextEditor.enrichHTML(content, { async: true })
});
await game.settings.set("fvtt-donjon-et-cie", WELCOME_MESSAGE_SETTING, currentVersion);
}
Hooks.once("init", async () => { Hooks.once("init", async () => {
const startupBanner = const startupBanner =
`▗▄▄▄ ▗▄▖ ▗▖ ▗▖ ▗▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▄▖▗▄▄▄▖ ▗▄▄▖▗▄▄▄▖▗▞▀▚▖ `▗▄▄▄ ▗▄▖ ▗▖ ▗▖ ▗▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▄▖▗▄▄▄▖ ▗▄▄▖▗▄▄▄▖▗▞▀▚▖
@@ -75,6 +199,7 @@ Hooks.once("init", async () => {
console.log(`%c${startupBanner}`, "font-family: monospace; white-space: pre; line-height: 1.1;"); console.log(`%c${startupBanner}`, "font-family: monospace; white-space: pre; line-height: 1.1;");
console.log("Initialisation du systeme Donjon & Cie"); console.log("Initialisation du systeme Donjon & Cie");
registerSystemSettings();
await DonjonEtCieUtility.preloadHandlebarsTemplates(); await DonjonEtCieUtility.preloadHandlebarsTemplates();
CONFIG.Combat.initiative = { CONFIG.Combat.initiative = {
@@ -97,7 +222,8 @@ Hooks.once("init", async () => {
arme: models.ArmeDataModel, arme: models.ArmeDataModel,
armure: models.ArmureDataModel, armure: models.ArmureDataModel,
equipement: models.EquipementDataModel, equipement: models.EquipementDataModel,
consommable: models.ConsommableDataModel consommable: models.ConsommableDataModel,
entrainement: models.EntrainementDataModel
}; };
game.system.donjonEtCie = { game.system.donjonEtCie = {
@@ -106,7 +232,8 @@ Hooks.once("init", async () => {
sheets, sheets,
rolls: DonjonEtCieRolls, rolls: DonjonEtCieRolls,
dialogs: DonjonEtCieRollDialog, dialogs: DonjonEtCieRollDialog,
utility: DonjonEtCieUtility utility: DonjonEtCieUtility,
macros: DonjonEtCieMacros
}; };
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet); foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
@@ -114,11 +241,69 @@ Hooks.once("init", async () => {
foundry.documents.collections.Actors.registerSheet("fvtt-donjon-et-cie", sheets.DonjonEtCiePNJSheet, { types: ["pnj"], makeDefault: true }); foundry.documents.collections.Actors.registerSheet("fvtt-donjon-et-cie", sheets.DonjonEtCiePNJSheet, { types: ["pnj"], makeDefault: true });
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet); foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
for (const type of ["trait", "langue", "capacite", "sortilege", "arme", "armure", "equipement", "consommable"]) { for (const type of ["trait", "langue", "capacite", "sortilege", "arme", "armure", "equipement", "consommable", "entrainement"]) {
foundry.documents.collections.Items.registerSheet("fvtt-donjon-et-cie", sheets.DonjonEtCieItemSheet, { types: [type], makeDefault: true }); foundry.documents.collections.Items.registerSheet("fvtt-donjon-et-cie", sheets.DonjonEtCieItemSheet, { types: [type], makeDefault: true });
} }
}); });
Hooks.once("ready", () => { // Gestion de la commande /employes
document.addEventListener("click", onChatActionClick); // 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", () => {
DonjonEtCieMacros.registerSocketListeners();
document.addEventListener("click", onChatActionClick);
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) => {
injectActorDirectoryMissionPackButton(app, element);
}); });
+177 -65
View File
@@ -14,12 +14,14 @@ import { DonjonEtCieUtility } from "./donjon-et-cie-utility.mjs";
import { DONJON_ET_CIE } from "./donjon-et-cie-config.mjs"; import { DONJON_ET_CIE } from "./donjon-et-cie-config.mjs";
export class DonjonEtCieRolls { export class DonjonEtCieRolls {
static async #createChatCard(actor, template, context) { static async #createChatCard(actor, template, context, { rolls = [] } = {}) {
const content = await foundry.applications.handlebars.renderTemplate(template, context); const content = await foundry.applications.handlebars.renderTemplate(template, context);
const validRolls = rolls.filter((roll) => roll instanceof Roll);
await ChatMessage.create({ await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor }), speaker: ChatMessage.getSpeaker({ actor }),
user: game.user.id, user: game.user.id,
content content,
rolls: validRolls
}); });
} }
@@ -35,8 +37,9 @@ export class DonjonEtCieRolls {
} }
static #getModeLabel(mode) { static #getModeLabel(mode) {
if (mode === "avantage") return "Avantage"; if (mode === "avantage") return game.i18n.localize("DNC.UI.ModeAdvantage");
if (mode === "desavantage") return "Desavantage"; if (mode === "desavantage") return game.i18n.localize("DNC.UI.ModeDisadvantage");
if (mode === "normal") return game.i18n.localize("DNC.UI.ModeNormal");
return null; return null;
} }
@@ -67,7 +70,7 @@ export class DonjonEtCieRolls {
const kept = this.#selectKeptValue(values, mode, "low"); const kept = this.#selectKeptValue(values, mode, "low");
const success = kept <= target; const success = kept <= target;
return { characteristic, characteristicKey, target, values, kept, success, mode, isNaturalOne: kept === 1, isNaturalTwenty: kept === 20 }; return { characteristic, characteristicKey, target, values, kept, success, mode, roll, isNaturalOne: kept === 1, isNaturalTwenty: kept === 20 };
} }
static async #resolveFavorBoost(actor, favorKey, mode = "normal") { static async #resolveFavorBoost(actor, favorKey, mode = "normal") {
@@ -77,7 +80,7 @@ export class DonjonEtCieRolls {
const path = `system.faveurs.${favorKey}.delta`; const path = `system.faveurs.${favorKey}.delta`;
const before = Number(foundry.utils.getProperty(actor, path) ?? 0); const before = Number(foundry.utils.getProperty(actor, path) ?? 0);
if (!before) { if (!before) {
ui.notifications.warn(`Aucune faveur disponible pour ${label}.`); ui.notifications.warn(game.i18n.format("DNC.Warn.NoFavorAvailable", { label }));
return null; return null;
} }
@@ -100,6 +103,7 @@ export class DonjonEtCieRolls {
effectiveMode: this.#applyFavorMode(mode), effectiveMode: this.#applyFavorMode(mode),
modeBefore: mode, modeBefore: mode,
modeAfter: this.#applyFavorMode(mode), modeAfter: this.#applyFavorMode(mode),
rolls: resolved.rolls,
note: degraded note: degraded
? "Le coup de pouce reste anonyme : un collegue du departement a donne l'info utile." ? "Le coup de pouce reste anonyme : un collegue du departement a donne l'info utile."
: "Le coup de pouce tient bon : nommez le collegue, ses trois traits et la relation pour le trombinoscope." : "Le coup de pouce tient bon : nommez le collegue, ses trois traits et la relation pour le trombinoscope."
@@ -113,7 +117,7 @@ export class DonjonEtCieRolls {
const path = `system.faveurs.${favorKey}.delta`; const path = `system.faveurs.${favorKey}.delta`;
const before = Number(foundry.utils.getProperty(actor, path) ?? 0); const before = Number(foundry.utils.getProperty(actor, path) ?? 0);
if (!before) { if (!before) {
ui.notifications.warn(`Aucune faveur disponible pour ${label}.`); ui.notifications.warn(game.i18n.format("DNC.Warn.NoFavorAvailable", { label }));
return null; return null;
} }
@@ -128,7 +132,7 @@ export class DonjonEtCieRolls {
after: DonjonEtCieUtility.formatUsageDie(after), after: DonjonEtCieUtility.formatUsageDie(after),
autoSpent: true, autoSpent: true,
note: "La faveur est brulee pour obtenir directement l'aide souhaitee, a la discretion du MJ." note: "La faveur est brulee pour obtenir directement l'aide souhaitee, a la discretion du MJ."
}); }, { rolls: [] });
return { key: favorKey, label, before, after }; return { key: favorKey, label, before, after };
} }
@@ -171,6 +175,7 @@ export class DonjonEtCieRolls {
before: focusDelta, before: focusDelta,
after, after,
degraded, degraded,
rolls: resolved.rolls,
values: resolved.values values: resolved.values
}; };
} }
@@ -182,32 +187,32 @@ export class DonjonEtCieRolls {
if (!result) return null; if (!result) return null;
await this.#createChatCard(actor, "systems/fvtt-donjon-et-cie/templates/chat/roll-card.hbs", { await this.#createChatCard(actor, "systems/fvtt-donjon-et-cie/templates/chat/roll-card.hbs", {
title: label ?? "Jet de caracteristique", title: label ?? game.i18n.localize("DNC.Roll.Characteristic"),
subtitle: result.characteristic.label, subtitle: result.characteristic.label,
formula: result.values.length > 1 ? "2d20" : "1d20", formula: result.values.length > 1 ? "2d20" : "1d20",
mode: effectiveMode, mode: effectiveMode,
modeLabel: this.#getModeLabel(effectiveMode), modeLabel: this.#getModeLabel(effectiveMode),
target: result.target, target: result.target,
targetPillLabel: "Cible", targetPillLabel: game.i18n.localize("DNC.Chat.Target"),
targetPillValue: result.target, targetPillValue: result.target,
values: result.values, values: result.values,
kept: result.kept, kept: result.kept,
keptPillLabel: "Garde", keptPillLabel: game.i18n.localize("DNC.Chat.Kept"),
keptPillValue: result.kept, keptPillValue: result.kept,
success: result.success, success: result.success,
favorLabel: favor?.label ?? null, favorLabel: favor?.label ?? null,
favorNote: favor?.note ?? null, favorNote: favor?.note ?? null,
details: [ details: [
{ label: "Caracteristique", value: result.characteristic.label }, { label: game.i18n.localize("DNC.UI.Characteristic"), value: result.characteristic.label },
{ label: "Valeur cible", value: result.target }, { label: game.i18n.localize("DNC.Chat.TargetValue"), value: result.target },
...(favor ? [ ...(favor ? [
{ label: "Faveur", value: favor.label }, { label: game.i18n.localize("DNC.Chat.Favor"), value: favor.label },
{ label: "Dé de faveur", value: favor.result }, { label: game.i18n.localize("DNC.Chat.FavorDie"), value: favor.result },
{ label: "Avant", value: DonjonEtCieUtility.formatUsageDie(favor.before) }, { label: game.i18n.localize("DNC.Chat.Before"), value: DonjonEtCieUtility.formatUsageDie(favor.before) },
{ label: "Apres", value: DonjonEtCieUtility.formatUsageDie(favor.after) } { label: game.i18n.localize("DNC.Chat.After"), value: DonjonEtCieUtility.formatUsageDie(favor.after) }
] : []) ] : [])
] ]
}); }, { rolls: [...(favor?.rolls ?? []), result.roll] });
return { ...result, favor, mode: effectiveMode }; return { ...result, favor, mode: effectiveMode };
} }
@@ -245,7 +250,7 @@ export class DonjonEtCieRolls {
mode: result.mode, mode: result.mode,
modeLabel: this.#getModeLabel(result.mode), modeLabel: this.#getModeLabel(result.mode),
syncedCombat syncedCombat
}); }, { rolls: result.rolls });
return { total: result.kept, die, dieValues, dex, bonus: sheetBonus, mode: result.mode, syncedCombat }; return { total: result.kept, die, dieValues, dex, bonus: sheetBonus, mode: result.mode, syncedCombat };
} }
@@ -270,7 +275,7 @@ export class DonjonEtCieRolls {
formula: roll.formula, formula: roll.formula,
total: roll.total, total: roll.total,
dieValues dieValues
}); }, { rolls: [roll] });
return { formula: roll.formula, total: roll.total, dieValues }; return { formula: roll.formula, total: roll.total, dieValues };
} }
@@ -297,36 +302,85 @@ export class DonjonEtCieRolls {
targetPillValue: result.target, targetPillValue: result.target,
values: result.values, values: result.values,
kept: result.kept, kept: result.kept,
keptPillLabel: "Jet", keptPillLabel: game.i18n.localize("DNC.Chat.RollValue"),
keptPillValue: result.kept, keptPillValue: result.kept,
success: result.success, success: result.success,
favorLabel: favor?.label ?? null, favorLabel: favor?.label ?? null,
favorNote: favor?.note ?? null, favorNote: favor?.note ?? null,
showDamageButton: result.success && Boolean(item.system.degats), showDamageButton: result.success && Boolean(item.system.degats),
showAmmoButton: Number(item.system.munitionsDelta ?? 0) > 0,
itemUuid: item.uuid, itemUuid: item.uuid,
details: [ details: [
{ label: "Arme", value: item.name }, { label: game.i18n.localize("DNC.UI.Weapon"), value: item.name },
{ label: "Caracteristique", value: characteristicLabel }, { label: game.i18n.localize("DNC.UI.Characteristic"), value: characteristicLabel },
{ label: `Valeur de ${characteristicLabel}`, value: result.target }, { label: `Valeur de ${characteristicLabel}`, value: result.target },
{ label: "Degats", value: item.system.degats || "—" }, { label: game.i18n.localize("DNC.UI.Damage"), value: item.system.degats || "—" },
{ label: "Portee", value: item.system.portee || "—" }, { label: game.i18n.localize("DNC.UI.Ammunition"), value: Number(item.system.munitionsDelta ?? 0) > 0 ? DonjonEtCieUtility.formatUsageDie(item.system.munitionsDelta) : "—" },
{ label: game.i18n.localize("DNC.UI.Range"), value: item.system.portee || "—" },
...(favor ? [ ...(favor ? [
{ label: "Faveur", value: favor.label }, { label: game.i18n.localize("DNC.Chat.Favor"), value: favor.label },
{ label: "Dé de faveur", value: favor.result }, { label: game.i18n.localize("DNC.Chat.FavorDie"), value: favor.result },
{ label: "Avant", value: DonjonEtCieUtility.formatUsageDie(favor.before) }, { label: game.i18n.localize("DNC.Chat.Before"), value: DonjonEtCieUtility.formatUsageDie(favor.before) },
{ label: "Apres", value: DonjonEtCieUtility.formatUsageDie(favor.after) } { label: game.i18n.localize("DNC.Chat.After"), value: DonjonEtCieUtility.formatUsageDie(favor.after) }
] : []) ] : [])
] ]
}); }, { rolls: [...(favor?.rolls ?? []), result.roll] });
return { ...result, favor, mode: effectiveMode }; return { ...result, favor, mode: effectiveMode };
} }
static async rollWeaponAmmoUsage(item, { mode = "normal" } = {}) {
const before = Number(item?.system?.munitionsDelta ?? 0);
if (!before) {
ui.notifications.warn(game.i18n.localize("DNC.Warn.NoAmmunitionAvailable"));
return null;
}
const resolved = await this.#resolveFormulaRoll(`1d${before}`, {}, { mode, favorable: "high" });
const result = resolved.kept;
const degraded = result <= 3;
const after = degraded ? DonjonEtCieUtility.degradeUsageDie(before) : before;
if (after !== before) {
await item.update({ "system.munitionsDelta": after });
}
await this.#createChatCard(item.actor, "systems/fvtt-donjon-et-cie/templates/chat/usage-card.hbs", {
title: `${game.i18n.localize("DNC.Roll.Ammunition")} : ${item.name}`,
value: result,
values: resolved.values,
mode: resolved.mode,
modeLabel: this.#getModeLabel(resolved.mode),
before: DonjonEtCieUtility.formatUsageDie(before),
after: DonjonEtCieUtility.formatUsageDie(after),
protectionStored: null,
degraded,
exhausted: after === 0,
isAmmunition: true
}, { rolls: resolved.rolls });
return { result, values: resolved.values, mode: resolved.mode, before, after, degraded };
}
static async rollDamage(actor, item, { mode = "normal" } = {}) { static async rollDamage(actor, item, { mode = "normal" } = {}) {
if (!item.system.degats) return null; const isUsageDie = Boolean(item.system.degatsEstUsageDe);
const actorBonus = Number(actor?.system?.combat?.degatsBonus ?? 0); const degatsDelta = Number(item.system.degatsDelta ?? 0);
const totalBonus = actorBonus;
const formula = totalBonus ? `${item.system.degats} + ${totalBonus}` : item.system.degats; if (isUsageDie && !degatsDelta) {
ui.notifications.warn(game.i18n.localize("DNC.Warn.DamageExhausted"));
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 effectiveDamage = damageContext.effectiveFormula || (isUsageDie ? `1d${degatsDelta}` : item.system.degats);
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) => {
@@ -334,6 +388,7 @@ export class DonjonEtCieRolls {
return dieValues.length ? dieValues.join(" + ") : String(roll.total ?? "—"); return dieValues.length ? dieValues.join(" + ") : String(roll.total ?? "—");
}); });
const keptDieLabel = rollDieLabels[result.keptIndex] ?? rollDieLabels[0] ?? String(result.kept); const keptDieLabel = rollDieLabels[result.keptIndex] ?? rollDieLabels[0] ?? String(result.kept);
const baseDamageDisplay = isUsageDie ? DonjonEtCieUtility.formatUsageDie(degatsDelta) : item.system.degats;
await this.#createChatCard(actor ?? item.actor, "systems/fvtt-donjon-et-cie/templates/chat/damage-card.hbs", { await this.#createChatCard(actor ?? item.actor, "systems/fvtt-donjon-et-cie/templates/chat/damage-card.hbs", {
title: `${game.i18n.localize("DNC.Roll.Damage")} : ${item.name}`, title: `${game.i18n.localize("DNC.Roll.Damage")} : ${item.name}`,
@@ -345,14 +400,65 @@ export class DonjonEtCieRolls {
keptDieLabel, keptDieLabel,
values: result.values, values: result.values,
total: result.kept, total: result.kept,
bonus: totalBonus, baseDamage: baseDamageDisplay,
baseDamage: item.system.degats, effectiveDamage,
damageCapped: damageContext.capped,
martialDvLabel: damageContext.martialDvSides ? `d${damageContext.martialDvSides}` : damageContext.martialDvFormula,
sourceLabel: item.name, sourceLabel: item.name,
targets, targets,
hasTargets: targets.length > 0 hasTargets: targets.length > 0,
}); showDamageUsageButton: isUsageDie && degatsDelta > 0,
itemUuid: item.uuid
}, { rolls: result.rolls });
return { total: result.kept, formula: result.formula, bonus: totalBonus, values: result.values, mode: result.mode }; return {
total: result.kept,
formula: result.formula,
baseDamage: baseDamageDisplay,
effectiveDamage,
damageCapped: damageContext.capped,
values: result.values,
mode: result.mode
};
}
static async rollWeaponDamageUsage(item, { mode = "normal" } = {}) {
const before = Number(item?.system?.degatsDelta ?? 0);
if (!before) {
ui.notifications.warn(game.i18n.localize("DNC.Warn.DamageExhausted"));
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 result = resolved.kept;
const degraded = result <= 3;
const after = degraded ? DonjonEtCieUtility.degradeUsageDie(before) : before;
if (after !== before) {
await item.update({ "system.degatsDelta": after });
}
await this.#createChatCard(item.actor, "systems/fvtt-donjon-et-cie/templates/chat/usage-card.hbs", {
title: `${game.i18n.localize("DNC.Roll.DamageUsage")} : ${item.name}`,
value: result,
values: resolved.values,
mode: resolved.mode,
modeLabel: this.#getModeLabel(resolved.mode),
before: DonjonEtCieUtility.formatUsageDie(before),
after: DonjonEtCieUtility.formatUsageDie(after),
protectionStored: null,
degraded,
exhausted: after === 0,
isDamageUsage: true
}, { rolls: resolved.rolls });
return { result, values: resolved.values, mode: resolved.mode, before, after, degraded };
} }
static async applyDamage(target, { damage = 0, useArmor = false, sourceLabel = "" } = {}) { static async applyDamage(target, { damage = 0, useArmor = false, sourceLabel = "" } = {}) {
@@ -388,6 +494,7 @@ export class DonjonEtCieRolls {
static async rollSpell(actor, item, { mode = "normal", favorKey = "" } = {}) { static async rollSpell(actor, item, { mode = "normal", favorKey = "" } = {}) {
const characteristicKey = item.system.caracteristique || "intelligence"; const characteristicKey = item.system.caracteristique || "intelligence";
const focus = await this.#ensureFocus(actor); const focus = await this.#ensureFocus(actor);
const currentSceneId = DonjonEtCieUtility.getCurrentSceneId();
const rank = Number(actor.system.anciennete?.rang ?? actor.system.sante?.dv ?? 0); const rank = Number(actor.system.anciennete?.rang ?? actor.system.sante?.dv ?? 0);
const cost = Number(item.system.coutPv ?? 0); const cost = Number(item.system.coutPv ?? 0);
const autoDisadvantage = cost > rank; const autoDisadvantage = cost > rank;
@@ -398,18 +505,23 @@ export class DonjonEtCieRolls {
if (!result) return null; if (!result) return null;
const currentPv = Number(actor.system.sante?.pv?.value ?? 0); const liveActor = game.actors.get(actor.id) ?? actor;
const availableMagicHp = currentPv + focus.activeValue; const currentPv = Number(liveActor.system.sante?.pv?.value ?? 0);
const currentFocusSceneId = liveActor.system.magie?.focus?.sceneId ?? "";
const currentFocusValue = currentFocusSceneId === currentSceneId
? Number(liveActor.system.magie?.focus?.resultat ?? 0)
: 0;
const availableMagicHp = currentPv + currentFocusValue;
if (cost > availableMagicHp) { if (cost > availableMagicHp) {
ui.notifications.warn("Le lanceur ne dispose pas d'assez de PV et de focus pour payer ce sort."); ui.notifications.warn(game.i18n.localize("DNC.Warn.SpellInsufficientResources"));
return null; return null;
} }
const characteristicShort = DONJON_ET_CIE.characteristics[characteristicKey]?.short ?? characteristicKey; const characteristicShort = DONJON_ET_CIE.characteristics[characteristicKey]?.short ?? characteristicKey;
const success = result.isNaturalTwenty ? false : result.success; const success = result.isNaturalTwenty ? false : result.success;
const focusSpent = result.isNaturalOne ? 0 : Math.min(cost, focus.activeValue); const focusSpent = result.isNaturalOne ? 0 : Math.min(cost, currentFocusValue);
const focusRemaining = Math.max(focus.activeValue - focusSpent, 0); const focusRemaining = Math.max(currentFocusValue - focusSpent, 0);
const spentPv = result.isNaturalOne ? 0 : Math.max(cost - focusSpent, 0); const spentPv = result.isNaturalOne ? 0 : Math.max(cost - focusSpent, 0);
const remainingPv = Math.max(currentPv - spentPv, 0); const remainingPv = Math.max(currentPv - spentPv, 0);
const updateData = {}; const updateData = {};
@@ -445,7 +557,7 @@ export class DonjonEtCieRolls {
targetPillValue: result.target, targetPillValue: result.target,
values: result.values, values: result.values,
kept: result.kept, kept: result.kept,
keptPillLabel: "Jet", keptPillLabel: game.i18n.localize("DNC.Chat.RollValue"),
keptPillValue: result.kept, keptPillValue: result.kept,
success, success,
specialNote, specialNote,
@@ -454,23 +566,23 @@ export class DonjonEtCieRolls {
itemUuid: item.uuid, itemUuid: item.uuid,
actorUuid: actor.uuid, actorUuid: actor.uuid,
details: [ details: [
{ label: "Sortilege", value: item.name }, { label: game.i18n.localize("DNC.UI.Spell"), value: item.name },
{ label: "Caracteristique", value: result.characteristic.label }, { label: game.i18n.localize("DNC.UI.Characteristic"), value: result.characteristic.label },
{ label: "Valeur de la caracteristique", value: result.target }, { label: game.i18n.localize("DNC.Chat.CharacteristicValue"), value: result.target },
{ label: "Cout en PV", value: cost }, { label: game.i18n.localize("DNC.Chat.HpCost"), value: cost },
{ label: "Focus", value: focus.activeValue > 0 ? `${focus.activeValue} (${DonjonEtCieUtility.formatUsageDie(focus.before)})` : "—" }, { label: game.i18n.localize("DNC.UI.Focus"), value: focus.activeValue > 0 ? `${focus.activeValue} (${DonjonEtCieUtility.formatUsageDie(focus.before)})` : "—" },
{ label: "Focus depense", value: focusSpent }, { label: game.i18n.localize("DNC.Chat.FocusSpent"), value: focusSpent },
{ label: "Focus restant", value: focusRemaining }, { label: game.i18n.localize("DNC.Chat.FocusRemaining"), value: focusRemaining },
{ label: "PV depenses", value: spentPv }, { label: game.i18n.localize("DNC.Chat.HpSpent"), value: spentPv },
{ label: "PV restants", value: remainingPv }, { label: game.i18n.localize("DNC.Chat.HpRemaining"), value: remainingPv },
{ label: "Rang du lanceur", value: rank }, { label: game.i18n.localize("DNC.Chat.CasterRank"), value: rank },
{ label: "Difficulte", value: item.system.difficulte ?? 0 }, { label: game.i18n.localize("DNC.Chat.Difficulty"), value: item.system.difficulte ?? 0 },
{ label: "Effet", value: item.system.effet || "—" }, { label: game.i18n.localize("DNC.Chat.Effect"), value: item.system.effet || "—" },
...(favor ? [ ...(favor ? [
{ label: "Faveur", value: favor.label }, { label: game.i18n.localize("DNC.Chat.Favor"), value: favor.label },
{ label: "Dé de faveur", value: favor.result }, { label: game.i18n.localize("DNC.Chat.FavorDie"), value: favor.result },
{ label: "Avant", value: DonjonEtCieUtility.formatUsageDie(favor.before) }, { label: game.i18n.localize("DNC.Chat.Before"), value: DonjonEtCieUtility.formatUsageDie(favor.before) },
{ label: "Apres", value: DonjonEtCieUtility.formatUsageDie(favor.after) } { label: game.i18n.localize("DNC.Chat.After"), value: DonjonEtCieUtility.formatUsageDie(favor.after) }
] : []) ] : [])
], ],
focusRolled: focus.rolled, focusRolled: focus.rolled,
@@ -482,7 +594,7 @@ export class DonjonEtCieRolls {
focusDegraded: focus.degraded, focusDegraded: focus.degraded,
spentPv, spentPv,
remainingPv remainingPv
}); }, { rolls: [...(favor?.rolls ?? []), ...(focus.rolls ?? []), result.roll] });
return { ...result, success, spentPv, remainingPv, cost, focus, focusSpent, focusRemaining, favor, mode: effectiveMode }; return { ...result, success, spentPv, remainingPv, cost, focus, focusSpent, focusRemaining, favor, mode: effectiveMode };
} }
@@ -490,7 +602,7 @@ export class DonjonEtCieRolls {
static async rollSpellChaos(actor, item) { static async rollSpellChaos(actor, item) {
const before = Number(actor?.system?.magie?.chaos?.delta ?? 12); const before = Number(actor?.system?.magie?.chaos?.delta ?? 12);
if (!before || before < 4) { if (!before || before < 4) {
ui.notifications.warn("Le Chaos n'est pas disponible pour ce sort."); ui.notifications.warn(game.i18n.localize("DNC.Warn.ChaosUnavailable"));
return null; return null;
} }
@@ -513,7 +625,7 @@ export class DonjonEtCieRolls {
degraded, degraded,
exhausted: after < 4, exhausted: after < 4,
itemName: item.name itemName: item.name
}); }, { rolls: resolved.rolls });
return { result, before, after, degraded, chaosEntry }; return { result, before, after, degraded, chaosEntry };
} }
@@ -551,7 +663,7 @@ export class DonjonEtCieRolls {
protectionStored: item.type === "armure" ? result : null, protectionStored: item.type === "armure" ? result : null,
degraded, degraded,
exhausted: after === 0 exhausted: after === 0
}); }, { rolls: resolved.rolls });
return { result, values: resolved.values, mode: resolved.mode, before, after, degraded }; return { result, values: resolved.values, mode: resolved.mode, before, after, degraded };
} }
+116 -5
View File
@@ -19,6 +19,7 @@ export class DonjonEtCieUtility {
trait: "systems/fvtt-donjon-et-cie/assets/icons/system/items/trait.svg", trait: "systems/fvtt-donjon-et-cie/assets/icons/system/items/trait.svg",
sortilege: "systems/fvtt-donjon-et-cie/assets/icons/system/items/sortilege.svg", sortilege: "systems/fvtt-donjon-et-cie/assets/icons/system/items/sortilege.svg",
equipement: "systems/fvtt-donjon-et-cie/assets/icons/system/items/equipement.svg", equipement: "systems/fvtt-donjon-et-cie/assets/icons/system/items/equipement.svg",
entrainement: "systems/fvtt-donjon-et-cie/assets/icons/system/items/capacite.svg",
other: "systems/fvtt-donjon-et-cie/assets/icons/system/items/autre.svg" other: "systems/fvtt-donjon-et-cie/assets/icons/system/items/autre.svg"
}; };
@@ -33,6 +34,9 @@ export class DonjonEtCieUtility {
"systems/fvtt-donjon-et-cie/templates/dialogs/damage-roll.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/damage-roll.hbs",
"systems/fvtt-donjon-et-cie/templates/dialogs/spell-roll.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/spell-roll.hbs",
"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-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",
@@ -42,7 +46,9 @@ export class DonjonEtCieUtility {
"systems/fvtt-donjon-et-cie/templates/chat/favor-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/favor-card.hbs",
"systems/fvtt-donjon-et-cie/templates/chat/initiative-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/initiative-card.hbs",
"systems/fvtt-donjon-et-cie/templates/chat/usage-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/usage-card.hbs",
"systems/fvtt-donjon-et-cie/templates/chat/item-card.hbs" "systems/fvtt-donjon-et-cie/templates/chat/item-card.hbs",
"systems/fvtt-donjon-et-cie/templates/chat/mission-pack-card.hbs",
"systems/fvtt-donjon-et-cie/templates/chat/welcome-card.hbs"
]); ]);
} }
@@ -71,6 +77,9 @@ 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 targetedTokens = game.user?.targets ?? new Set();
const targetedTokenUuid = targetedTokens?.first()?.document.uuid ?? null;
return tokens return tokens
.map((token) => { .map((token) => {
const actor = token.actor; const actor = token.actor;
@@ -84,7 +93,8 @@ export class DonjonEtCieUtility {
tokenId: token.id, tokenId: token.id,
tokenUuid: token.uuid, tokenUuid: token.uuid,
actorUuid: actor.uuid, actorUuid: actor.uuid,
label label,
isSelected: token.uuid === targetedTokenUuid
}; };
}) })
.filter(Boolean) .filter(Boolean)
@@ -114,6 +124,87 @@ export class DonjonEtCieUtility {
}; };
} }
static parseDieFormula(formula) {
const normalized = String(formula ?? "").trim();
if (!normalized) return null;
const dieMatch = normalized.match(/(?<count>\d*)d(?<sides>\d+)/i);
if (dieMatch?.groups?.sides) {
return {
formula: normalized,
count: Number(dieMatch.groups.count || 1),
countRaw: dieMatch.groups.count ?? "",
sides: Number(dieMatch.groups.sides),
match: dieMatch[0],
index: dieMatch.index ?? 0
};
}
if (/^\d+$/.test(normalized)) {
const sides = Number(normalized);
if ([4, 6, 8, 10, 12, 20].includes(sides)) {
return {
formula: normalized,
count: 1,
countRaw: "",
sides,
match: normalized,
index: 0
};
}
}
return null;
}
static getMartialDamageContext(actor, item) {
const isUsageDie = Boolean(item?.system?.degatsEstUsageDe);
const degatsDelta = Number(item?.system?.degatsDelta ?? 0);
const baseFormula = isUsageDie
? (degatsDelta > 0 ? `1d${degatsDelta}` : "")
: String(item?.system?.degats ?? "").trim();
const baseContext = {
baseFormula,
effectiveFormula: baseFormula,
capped: false,
martialDvFormula: String(actor?.system?.sante?.dv ?? "").trim(),
martialDvSides: 0,
weaponSides: 0,
isUsageDie
};
if (actor?.type !== "employe" || item?.type !== "arme" || !baseFormula) {
return baseContext;
}
const damageDie = this.parseDieFormula(baseFormula);
const martialDie = this.parseDieFormula(actor.system.sante?.dv);
if (!damageDie || !martialDie?.sides) return baseContext;
const cappedSides = Math.min(damageDie.sides, martialDie.sides);
if (cappedSides >= damageDie.sides) {
return {
...baseContext,
martialDvSides: martialDie.sides,
weaponSides: damageDie.sides
};
}
const replacement = `${damageDie.countRaw || ""}d${cappedSides}`;
const effectiveFormula = damageDie.match === damageDie.formula
? replacement
: `${damageDie.formula.slice(0, damageDie.index)}${replacement}${damageDie.formula.slice(damageDie.index + damageDie.match.length)}`;
return {
...baseContext,
effectiveFormula,
capped: true,
martialDvSides: martialDie.sides,
weaponSides: damageDie.sides
};
}
static getFavorLabel(key) { static getFavorLabel(key) {
return DONJON_ET_CIE.favorDepartments[key] ?? key; return DONJON_ET_CIE.favorDepartments[key] ?? key;
} }
@@ -167,6 +258,21 @@ export class DonjonEtCieUtility {
static enrichItemForSheet(item) { static enrichItemForSheet(item) {
const system = item.system; const system = item.system;
const delta = Number(system.delta ?? 0); const delta = Number(system.delta ?? 0);
const deltaMax = Number(system.deltaMax ?? delta ?? 0);
const ammunitionDelta = Number(system.munitionsDelta ?? 0);
const isUsageDie = Boolean(system.degatsEstUsageDe);
const degatsDelta = Number(system.degatsDelta ?? 0);
const usageLabel = item.type === "entrainement" && deltaMax > 0
? `${this.formatUsageDie(delta)} / ${this.formatUsageDie(deltaMax)}`
: delta > 0
? this.formatUsageDie(delta)
: null;
const damageUsageLabel = isUsageDie
? (degatsDelta > 0 ? this.formatUsageDie(degatsDelta) : game.i18n.localize("DNC.UI.DamageExhausted"))
: null;
const damageLabel = isUsageDie ? damageUsageLabel : (system.degats || null);
return { return {
id: item.id, id: item.id,
name: item.name, name: item.name,
@@ -174,15 +280,20 @@ export class DonjonEtCieUtility {
img: item.img, img: item.img,
system, system,
uuid: item.uuid, uuid: item.uuid,
usageLabel: delta > 0 ? this.formatUsageDie(delta) : null, usageLabel,
ammunitionUsageLabel: item.type === "arme" && ammunitionDelta > 0 ? this.formatUsageDie(ammunitionDelta) : null,
protectionLabel: item.type === "armure" && Number(system.resultatProtection ?? 0) > 0 ? `Protection ${system.resultatProtection}` : null, protectionLabel: item.type === "armure" && Number(system.resultatProtection ?? 0) > 0 ? `Protection ${system.resultatProtection}` : null,
weaponCharacteristicLabel: item.type === "arme" ? this.getWeaponCharacteristicLabel(system.categorie) : null, weaponCharacteristicLabel: item.type === "arme" ? this.getWeaponCharacteristicLabel(system.categorie) : null,
canRoll: ["arme", "sortilege"].includes(item.type), canRoll: ["arme", "sortilege"].includes(item.type),
canUse: delta > 0, canUse: delta > 0,
canRollDamage: Boolean(system.degats), hasTrackedAmmunition: item.type === "arme" && ammunitionDelta > 0,
damageUsageLabel,
damageLabel,
canRollDamage: item.type === "arme" && (isUsageDie ? degatsDelta > 0 : Boolean(system.degats)),
rollAction: item.type === "sortilege" ? "rollSpell" : "rollWeapon", rollAction: item.type === "sortilege" ? "rollSpell" : "rollWeapon",
damageAction: "rollDamage", damageAction: "rollDamage",
isEquipped: Boolean(system.equipee) isEquipped: Boolean(system.equipee),
canReset: item.type === "entrainement" && deltaMax > 0 && delta !== deltaMax
}; };
} }
+3
View File
@@ -20,6 +20,9 @@ export default class ArmeDataModel extends BaseItemDataModel {
categorie: new fields.StringField({ initial: "melee" }), categorie: new fields.StringField({ initial: "melee" }),
caracteristique: new fields.StringField({ initial: "force" }), caracteristique: new fields.StringField({ initial: "force" }),
degats: new fields.StringField({ initial: "1d6" }), degats: new fields.StringField({ initial: "1d6" }),
degatsEstUsageDe: new fields.BooleanField({ initial: false }),
degatsDelta: new fields.NumberField({ initial: 0, integer: true, min: 0 }),
munitionsDelta: new fields.NumberField({ initial: 0, integer: true, min: 0 }),
portee: new fields.StringField({ initial: "" }), portee: new fields.StringField({ initial: "" }),
mains: new fields.NumberField({ initial: 1, integer: true }), mains: new fields.NumberField({ initial: 1, integer: true }),
equipee: new fields.BooleanField({ initial: false }) equipee: new fields.BooleanField({ initial: false })
-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 })
}), }),
+25
View File
@@ -0,0 +1,25 @@
/**
* Donjon & Cie - Systeme FoundryVTT
*
* Donjon & Cie est un jeu de role edite par John Doe.
* Ce systeme FoundryVTT est une implementation independante et n'est pas
* affilie a John Doe.
*
* @author LeRatierBretonnien
* @copyright 20252026 LeRatierBretonnien
* @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
*/
import BaseItemDataModel from "./base-item.mjs";
export default class EntrainementDataModel extends BaseItemDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
delta: new fields.NumberField({ initial: 4, integer: true }),
deltaMax: new fields.NumberField({ initial: 4, integer: true }),
effet: new fields.StringField({ initial: "" })
};
}
}
+1
View File
@@ -19,5 +19,6 @@ export { default as ArmeDataModel } from "./arme.mjs";
export { default as ArmureDataModel } from "./armure.mjs"; export { default as ArmureDataModel } from "./armure.mjs";
export { default as EquipementDataModel } from "./equipement.mjs"; export { default as EquipementDataModel } from "./equipement.mjs";
export { default as ConsommableDataModel } from "./consommable.mjs"; export { default as ConsommableDataModel } from "./consommable.mjs";
export { default as EntrainementDataModel } from "./entrainement.mjs";
export { default as EmployeDataModel } from "./employe.mjs"; export { default as EmployeDataModel } from "./employe.mjs";
export { default as PnjDataModel } from "./pnj.mjs"; export { default as PnjDataModel } from "./pnj.mjs";
+8 -4
View File
@@ -13,6 +13,11 @@
export default class PnjDataModel extends foundry.abstract.TypeDataModel { export default class PnjDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() { static defineSchema() {
const fields = foundry.data.fields; const fields = foundry.data.fields;
const makeAttack = () => new fields.SchemaField({
nom: new fields.StringField({ initial: "Attaque" }),
degats: new fields.StringField({ initial: "1d6" }),
notes: new fields.StringField({ initial: "" })
});
return { return {
espece: new fields.StringField({ initial: "" }), espece: new fields.StringField({ initial: "" }),
@@ -35,10 +40,9 @@ export default class PnjDataModel extends foundry.abstract.TypeDataModel {
delta: new fields.NumberField({ initial: 0, integer: true }) delta: new fields.NumberField({ initial: 0, integer: true })
}) })
}), }),
attaque: new fields.SchemaField({ attaque: makeAttack(),
nom: new fields.StringField({ initial: "Attaque" }), attaques: new fields.ArrayField(makeAttack(), {
degats: new fields.StringField({ initial: "1d6" }), initial: [{ nom: "Attaque", degats: "1d6", notes: "" }]
notes: new fields.StringField({ initial: "" })
}), }),
pouvoirsSpeciaux: new fields.HTMLField({ initial: "" }), pouvoirsSpeciaux: new fields.HTMLField({ initial: "" }),
description: new fields.HTMLField({ initial: "" }), description: new fields.HTMLField({ initial: "" }),
+739
View File
@@ -9,12 +9,165 @@
"version": "0.1.0", "version": "0.1.0",
"license": "UNLICENSED", "license": "UNLICENSED",
"devDependencies": { "devDependencies": {
"@foundryvtt/foundryvtt-cli": "^1.1.0",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-less": "^5.0.0", "gulp-less": "^5.0.0",
"gulp-rename": "^2.0.0", "gulp-rename": "^2.0.0",
"gulp-sourcemaps": "^3.0.0" "gulp-sourcemaps": "^3.0.0"
} }
}, },
"node_modules/@foundryvtt/foundryvtt-cli": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@foundryvtt/foundryvtt-cli/-/foundryvtt-cli-1.1.0.tgz",
"integrity": "sha512-ergKZDUSgQ79168r38ORyN4v/UTliA40rxElaUh5iS27Qw9H8Ep/ll8j3/HfiikO3XUDwYxZLfDJfbcyj2i9TQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^5.4.1",
"classic-level": "^1.4.1",
"esm": "^3.2.25",
"js-yaml": "^4.1.0",
"mkdirp": "^3.0.1",
"nedb-promises": "^6.2.3",
"yargs": "^17.7.2"
},
"bin": {
"fvtt": "fvtt.mjs"
},
"engines": {
"node": ">17.0.0"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@foundryvtt/foundryvtt-cli/node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/@gulp-sourcemaps/identity-map": { "node_modules/@gulp-sourcemaps/identity-map": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz",
@@ -81,6 +234,67 @@
"xtend": "~4.0.1" "xtend": "~4.0.1"
} }
}, },
"node_modules/@seald-io/binary-search-tree": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@seald-io/binary-search-tree/-/binary-search-tree-1.0.3.tgz",
"integrity": "sha512-qv3jnwoakeax2razYaMsGI/luWdliBLHTdC6jU55hQt1hcFqzauH/HsBollQ7IR4ySTtYhT+xyHoijpA16C+tA==",
"dev": true
},
"node_modules/@seald-io/nedb": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@seald-io/nedb/-/nedb-4.1.2.tgz",
"integrity": "sha512-bDr6TqjBVS2rDyYM9CPxAnotj5FuNL9NF8o7h7YyFXM7yruqT4ddr+PkSb2mJvvw991bqdftazkEo38gykvaww==",
"dev": true,
"license": "MIT",
"dependencies": {
"@seald-io/binary-search-tree": "^1.0.3",
"localforage": "^1.10.0",
"util": "^0.12.5"
}
},
"node_modules/abstract-level": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.4.tgz",
"integrity": "sha512-eUP/6pbXBkMbXFdx4IH2fVgvB7M0JvR7/lIL33zcs0IBcwjdzSSl31TOJsaCzmKSSDF9h8QYSOJux4Nd4YJqFg==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer": "^6.0.3",
"catering": "^2.1.0",
"is-buffer": "^2.0.5",
"level-supports": "^4.0.0",
"level-transcoder": "^1.0.1",
"module-error": "^1.0.1",
"queue-microtask": "^1.2.3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/abstract-level/node_modules/is-buffer": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
"integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "6.4.2", "version": "6.4.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
@@ -130,6 +344,22 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/ansi-wrap": { "node_modules/ansi-wrap": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
@@ -184,6 +414,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/arr-diff": { "node_modules/arr-diff": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -397,6 +634,22 @@
"node": ">= 4.5.0" "node": ">= 4.5.0"
} }
}, },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"possible-typed-array-names": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/bach": { "node_modules/bach": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
@@ -457,6 +710,27 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/binary-extensions": { "node_modules/binary-extensions": {
"version": "1.13.1", "version": "1.13.1",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
@@ -511,6 +785,31 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-equal": { "node_modules/buffer-equal": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz",
@@ -612,6 +911,29 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/catering": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz",
"integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/chalk": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chokidar": { "node_modules/chokidar": {
"version": "2.1.8", "version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
@@ -678,6 +1000,24 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/classic-level": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.4.1.tgz",
"integrity": "sha512-qGx/KJl3bvtOHrGau2WklEZuXhS3zme+jf+fsu6Ej7W7IP/C49v7KNlWIsT1jZu0YnfzSIYDGcEWpCa1wKGWXQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"abstract-level": "^1.0.2",
"catering": "^2.1.0",
"module-error": "^1.0.1",
"napi-macros": "^2.2.2",
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/cliui": { "node_modules/cliui": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@@ -768,6 +1108,26 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/color-support": { "node_modules/color-support": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
@@ -1075,6 +1435,13 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/end-of-stream": { "node_modules/end-of-stream": {
"version": "1.4.5", "version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
@@ -1198,6 +1565,26 @@
"es6-symbol": "^3.1.1" "es6-symbol": "^3.1.1"
} }
}, },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/esm": {
"version": "3.2.25",
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/esniff": { "node_modules/esniff": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
@@ -1492,6 +1879,22 @@
"readable-stream": "^2.3.6" "readable-stream": "^2.3.6"
} }
}, },
"node_modules/for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-callable": "^1.2.7"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/for-in": { "node_modules/for-in": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -1590,6 +1993,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/generator-function": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
"integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/get-caller-file": { "node_modules/get-caller-file": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
@@ -1951,6 +2364,22 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-value": { "node_modules/has-value": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
@@ -2040,6 +2469,27 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/image-size": { "node_modules/image-size": {
"version": "0.5.5", "version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
@@ -2054,6 +2504,13 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"dev": true,
"license": "MIT"
},
"node_modules/inflight": { "node_modules/inflight": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -2127,6 +2584,23 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/is-arguments": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
"integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"has-tostringtag": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-arrayish": { "node_modules/is-arrayish": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -2154,6 +2628,19 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-core-module": { "node_modules/is-core-module": {
"version": "2.16.1", "version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@@ -2230,6 +2717,26 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-generator-function": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
"integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.4",
"generator-function": "^2.0.0",
"get-proto": "^1.0.1",
"has-tostringtag": "^1.0.2",
"safe-regex-test": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-glob": { "node_modules/is-glob": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -2296,6 +2803,25 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/is-regex": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"gopd": "^1.2.0",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-relative": { "node_modules/is-relative": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
@@ -2309,6 +2835,22 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-typed-array": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"which-typed-array": "^1.1.16"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-unc-path": { "node_modules/is-unc-path": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
@@ -2386,6 +2928,19 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/json-stable-stringify-without-jsonify": { "node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
@@ -2489,6 +3044,40 @@
"source-map": "~0.6.0" "source-map": "~0.6.0"
} }
}, },
"node_modules/level-supports": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz",
"integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/level-transcoder": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz",
"integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer": "^6.0.3",
"module-error": "^1.0.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
"integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
"dev": true,
"license": "MIT",
"dependencies": {
"immediate": "~3.0.5"
}
},
"node_modules/liftoff": { "node_modules/liftoff": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
@@ -2549,6 +3138,16 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/localforage": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
"integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"lie": "3.1.1"
}
},
"node_modules/lru-queue": { "node_modules/lru-queue": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
@@ -2837,6 +3436,32 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/mkdirp": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
"dev": true,
"license": "MIT",
"bin": {
"mkdirp": "dist/cjs/src/bin.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/module-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz",
"integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -2935,6 +3560,23 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/napi-macros": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz",
"integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==",
"dev": true,
"license": "MIT"
},
"node_modules/nedb-promises": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/nedb-promises/-/nedb-promises-6.2.3.tgz",
"integrity": "sha512-enq0IjNyBz9Qy9W/QPCcLGh/QORGBjXbIeZeWvIjO3OMLyAvlKT3hiJubP2BKEiFniUlR3L01o18ktqgn5jxqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@seald-io/nedb": "^4.0.2"
}
},
"node_modules/needle": { "node_modules/needle": {
"version": "3.5.0", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/needle/-/needle-3.5.0.tgz", "resolved": "https://registry.npmjs.org/needle/-/needle-3.5.0.tgz",
@@ -2960,6 +3602,18 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/node-gyp-build": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
"dev": true,
"license": "MIT",
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/normalize-package-data": { "node_modules/normalize-package-data": {
"version": "2.5.0", "version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@@ -3455,6 +4109,16 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "7.0.39", "version": "7.0.39",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
@@ -3521,6 +4185,27 @@
"pump": "^2.0.0" "pump": "^2.0.0"
} }
}, },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/read-pkg": { "node_modules/read-pkg": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
@@ -3840,6 +4525,24 @@
"ret": "~0.1.10" "ret": "~0.1.10"
} }
}, },
"node_modules/safe-regex-test": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
"integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"is-regex": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -4727,6 +5430,20 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/util": {
"version": "0.12.5",
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"is-arguments": "^1.0.4",
"is-generator-function": "^1.0.7",
"is-typed-array": "^1.1.3",
"which-typed-array": "^1.1.2"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -4908,6 +5625,28 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/which-typed-array": {
"version": "1.1.20",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
"integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
"dev": true,
"license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.8",
"call-bound": "^1.0.4",
"for-each": "^0.3.5",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-tostringtag": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/wrap-ansi": { "node_modules/wrap-ansi": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+5 -2
View File
@@ -4,11 +4,14 @@
"description": "Systeme FoundryVTT pour Donjon & Cie", "description": "Systeme FoundryVTT pour Donjon & Cie",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "gulp build", "build": "gulp build && npm run pack:compendiums",
"watch": "gulp watch" "watch": "gulp watch",
"pack:compendiums": "node ./tools/packCompendiums.mjs",
"unpack:compendiums": "node ./tools/unpackCompendiums.mjs"
}, },
"license": "UNLICENSED", "license": "UNLICENSED",
"devDependencies": { "devDependencies": {
"@foundryvtt/foundryvtt-cli": "^1.1.0",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-less": "^5.0.0", "gulp-less": "^5.0.0",
"gulp-rename": "^2.0.0", "gulp-rename": "^2.0.0",
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000019 MANIFEST-000189
+7 -3
View File
@@ -1,3 +1,7 @@
2026/04/13-20:45:52.211396 7f68497ed6c0 Recovering log #17 2026/06/03-20:02:36.286349 7f589d3fe6c0 Recovering log #187
2026/04/13-20:45:52.220887 7f68497ed6c0 Delete type=3 #15 2026/06/03-20:02:36.295607 7f589d3fe6c0 Delete type=3 #185
2026/04/13-20:45:52.220974 7f68497ed6c0 Delete type=0 #17 2026/06/03-20:02:36.295653 7f589d3fe6c0 Delete type=0 #187
2026/06/03-20:14:21.989739 7f584f7fe6c0 Level-0 table #192: started
2026/06/03-20:14:21.989753 7f584f7fe6c0 Level-0 table #192: 0 bytes OK
2026/06/03-20:14:21.995792 7f584f7fe6c0 Delete type=0 #190
2026/06/03-20:14:22.012809 7f584f7fe6c0 Manual compaction at level-0 from '!folders!K9aiFu0dE6UYiXBd' @ 72057594037927935 : 1 .. '!items!zyqLzmpbHxK3jt5q' @ 0 : 0; will stop at (end)
+7 -8
View File
@@ -1,8 +1,7 @@
2026/04/13-15:38:21.573543 7f2a6bfff6c0 Recovering log #12 2026/06/03-17:24:13.769178 7f589dbff6c0 Recovering log #183
2026/04/13-15:38:21.584944 7f2a6bfff6c0 Delete type=3 #10 2026/06/03-17:24:13.812146 7f589dbff6c0 Delete type=3 #181
2026/04/13-15:38:21.585068 7f2a6bfff6c0 Delete type=0 #12 2026/06/03-17:24:13.812199 7f589dbff6c0 Delete type=0 #183
2026/04/13-15:53:03.495112 7f2a69ffb6c0 Level-0 table #18: started 2026/06/03-19:19:52.344655 7f584f7fe6c0 Level-0 table #188: started
2026/04/13-15:53:03.495152 7f2a69ffb6c0 Level-0 table #18: 0 bytes OK 2026/06/03-19:19:52.344671 7f584f7fe6c0 Level-0 table #188: 0 bytes OK
2026/04/13-15:53:03.502660 7f2a69ffb6c0 Delete type=0 #16 2026/06/03-19:19:52.350896 7f584f7fe6c0 Delete type=0 #186
2026/04/13-15:53:03.512745 7f2a69ffb6c0 Manual compaction at level-0 from '!folders!K9aiFu0dE6UYiXBd' @ 72057594037927935 : 1 .. '!items!zyqLzmpbHxK3jt5q' @ 0 : 0; will stop at (end) 2026/06/03-19:19:52.367561 7f584f7fe6c0 Manual compaction at level-0 from '!folders!K9aiFu0dE6UYiXBd' @ 72057594037927935 : 1 .. '!items!zyqLzmpbHxK3jt5q' @ 0 : 0; will stop at (end)
2026/04/13-15:53:03.512766 7f2a69ffb6c0 Manual compaction at level-1 from '!folders!K9aiFu0dE6UYiXBd' @ 72057594037927935 : 1 .. '!items!zyqLzmpbHxK3jt5q' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
Binary file not shown.
View File
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000006 MANIFEST-000176
+7 -3
View File
@@ -1,3 +1,7 @@
2026/04/13-20:45:52.223768 7f6849fee6c0 Recovering log #4 2026/06/03-20:02:36.298130 7f584ffff6c0 Recovering log #174
2026/04/13-20:45:52.234123 7f6849fee6c0 Delete type=3 #2 2026/06/03-20:02:36.307600 7f584ffff6c0 Delete type=3 #172
2026/04/13-20:45:52.234212 7f6849fee6c0 Delete type=0 #4 2026/06/03-20:02:36.307643 7f584ffff6c0 Delete type=0 #174
2026/06/03-20:14:22.006386 7f584f7fe6c0 Level-0 table #179: started
2026/06/03-20:14:22.006405 7f584f7fe6c0 Level-0 table #179: 0 bytes OK
2026/06/03-20:14:22.012715 7f584f7fe6c0 Delete type=0 #177
2026/06/03-20:14:22.012822 7f584f7fe6c0 Manual compaction at level-0 from '!tables!PPsxQgHwLCQ2gjSW' @ 72057594037927935 : 1 .. '!tables.results!wJZXUo4q5b5vE3Dy.zFTPLMc9zOl5hISV' @ 0 : 0; will stop at (end)
+7 -5
View File
@@ -1,5 +1,7 @@
2026/04/13-15:38:21.601302 7f2a6a7fc6c0 Delete type=3 #1 2026/06/03-17:24:13.815984 7f589cbfd6c0 Recovering log #170
2026/04/13-15:53:03.477052 7f2a69ffb6c0 Level-0 table #5: started 2026/06/03-17:24:13.870566 7f589cbfd6c0 Delete type=3 #168
2026/04/13-15:53:03.481742 7f2a69ffb6c0 Level-0 table #5: 39147 bytes OK 2026/06/03-17:24:13.870643 7f589cbfd6c0 Delete type=0 #170
2026/04/13-15:53:03.488722 7f2a69ffb6c0 Delete type=0 #3 2026/06/03-19:19:52.350980 7f584f7fe6c0 Level-0 table #175: started
2026/04/13-15:53:03.512725 7f2a69ffb6c0 Manual compaction at level-0 from '!tables!PPsxQgHwLCQ2gjSW' @ 72057594037927935 : 1 .. '!tables.results!wJZXUo4q5b5vE3Dy.zFTPLMc9zOl5hISV' @ 0 : 0; will stop at (end) 2026/06/03-19:19:52.351000 7f584f7fe6c0 Level-0 table #175: 0 bytes OK
2026/06/03-19:19:52.357007 7f584f7fe6c0 Delete type=0 #173
2026/06/03-19:19:52.367567 7f584f7fe6c0 Manual compaction at level-0 from '!tables!PPsxQgHwLCQ2gjSW' @ 72057594037927935 : 1 .. '!tables.results!wJZXUo4q5b5vE3Dy.zFTPLMc9zOl5hISV' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
View File
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
MANIFEST-000002
View File
+5
View File
@@ -0,0 +1,5 @@
2026/06/03-20:12:45.450958 7f07bbfff6c0 Delete type=3 #1
2026/06/03-20:12:45.455909 7f07baffd6c0 Level-0 table #5: started
2026/06/03-20:12:45.474772 7f07baffd6c0 Level-0 table #5: 3056 bytes OK
2026/06/03-20:12:45.523202 7f07baffd6c0 Delete type=0 #3
2026/06/03-20:12:45.523559 7f07baffd6c0 Manual compaction at level-0 from '!journal!69Da9YvF9BfOV7oK' @ 72057594037927935 : 1 .. '!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65' @ 0 : 0; will stop at (end)
@@ -0,0 +1,41 @@
{
"_id": "69Da9YvF9BfOV7oK",
"name": "Aide du systeme",
"folder": null,
"categories": [],
"sort": 0,
"_key": "!journal!69Da9YvF9BfOV7oK",
"ownership": {
"default": 0
},
"flags": {},
"pages": [
{
"_id": "XM0eLkgKXPyskV65",
"name": "Guide",
"type": "text",
"title": {
"show": true,
"level": 1
},
"text": {
"format": 1,
"content": "<section>\n <h1>Donjon &amp; Cie — Aide du systeme</h1>\n <p>Ce journal presente les principaux ecrans et usages du systeme FoundryVTT pour <strong>Donjon &amp; Cie</strong>. Il suit l'organisation du jeu : employes, PNJ, objets, ressources magiques, jets et outils MJ.</p>\n</section>\n<section>\n <h2>1. Fiche Employe</h2>\n <p>La fiche <strong>Employe</strong> regroupe les informations essentielles du personnage : concept, anciennete, DV, PV, attaques, caracteristiques et inventaire. Les trois onglets organisent le jeu en <em>Combat &amp; materiel</em>, <em>Magie &amp; capacites</em> et <em>Profil &amp; langues</em>.</p>\n <ul>\n <li><strong>DV</strong> et <strong>PV</strong> sont accessibles en haut de fiche.</li>\n <li>Les sections d'objets acceptent le glisser-deposer depuis le repertoire des items ou les compendiums.</li>\n <li>Les boutons d'action permettent de lancer un jet, les degats, ou publier un objet dans le chat.</li>\n </ul>\n <figure>\n <img src=\"systems/fvtt-donjon-et-cie/assets/help/employe-sheet.png\" alt=\"Fiche employe\" />\n <figcaption>La fiche Employe centralise combat, equipement et ressources.</figcaption>\n </figure>\n</section>\n<section>\n <h2>2. Magie, focus, chaos et capacites</h2>\n <p>L'onglet <strong>Magie &amp; capacites</strong> rassemble les ressources du lanceur de sorts :</p>\n <ul>\n <li>le <strong>Rang</strong>, le <strong>Focus</strong> et le <strong>Chaos</strong> ;</li>\n <li>la <strong>Table du Chaos</strong> pour resoudre rapidement les incidents magiques ;</li>\n <li>la liste des <strong>Sortileges</strong>, avec les <strong>Capacites</strong> affichees juste en dessous.</li>\n </ul>\n <p>Les boutons de ligne permettent de lancer le sort, ses degats, ou de poster sa fiche dans le chat.</p>\n <figure>\n <img src=\"systems/fvtt-donjon-et-cie/assets/help/magie-capacites.png\" alt=\"Onglet magie et capacites\" />\n <figcaption>Les Capacites sont placees sous les Sortileges pour rester coherentes avec les regles et la lecture de fiche.</figcaption>\n </figure>\n</section>\n<section>\n <h2>3. Fiche PNJ</h2>\n <p>La fiche <strong>PNJ</strong> est optimisee pour le jeu rapide :</p>\n <ul>\n <li>valeurs de <strong>DV</strong>, <strong>PV</strong>, <strong>ARM</strong> et <strong>COU</strong> en acces direct ;</li>\n <li>liste d'<strong>attaques multiples</strong> editable ligne par ligne ;</li>\n <li>bouton de jet de degats sur chaque attaque.</li>\n </ul>\n <p>Le layout favorise le nom de l'attaque, avec des tooltips sur les champs pour garder une interface compacte.</p>\n <figure>\n <img src=\"systems/fvtt-donjon-et-cie/assets/help/pnj-sheet.png\" alt=\"Fiche PNJ\" />\n <figcaption>Un PNJ peut maintenant posseder plusieurs attaques distinctes.</figcaption>\n </figure>\n</section>\n<section>\n <h2>4. Paquetage de mission</h2>\n <p>Le MJ dispose d'un outil de <strong>Paquetage</strong> accessible depuis le haut du repertoire des acteurs. Il ouvre un dialogue permettant de choisir l'employe cible puis de lancer la dotation de debut de mission.</p>\n <ul>\n <li>Le systeme tire silencieusement sur les tables d'armes, armures et equipement divers.</li>\n <li>Les objets trouves sont ajoutes automatiquement a la fiche du personnage.</li>\n <li>Un message de synthese est publie dans le chat.</li>\n </ul>\n <figure>\n <img src=\"systems/fvtt-donjon-et-cie/assets/help/mission-pack-dialog.png\" alt=\"Dialogue de paquetage de mission\" />\n <figcaption>Le bouton Paquetage ouvre un dialogue MJ pour attribuer rapidement la dotation d'entree en mission.</figcaption>\n </figure>\n</section>\n<section>\n <h2>5. Jets et messages de chat</h2>\n <p>Les cartes de chat servent d'interface de resolution. On y retrouve selon les cas :</p>\n <ul>\n <li>les resultats de jets de caracteristique, d'attaque, de degats et de magie ;</li>\n <li>les usages de ressources a de d'usure ;</li>\n <li>les syntheses de paquetage et d'autres actions systeme.</li>\n </ul>\n <p>Quand un objet ou une action propose un bouton <em>Poster dans le chat</em>, cela permet de partager proprement l'information avec la table sans ouvrir la fiche du document.</p>\n</section>\n<section>\n <h2>6. Conseils d'utilisation</h2>\n <ul>\n <li>Utilisez le glisser-deposer pour equiper rapidement un employe.</li>\n <li>Gardez les PNJ ouverts pendant les scenes pour acceder vite a leurs attaques et defenses.</li>\n <li>Le sidebar <strong>Acteurs</strong> concentre les outils MJ utiles, notamment le paquetage de mission.</li>\n <li>Les compendiums du systeme contiennent l'equipement et les tables aleatoires servant de base au jeu.</li>\n </ul>\n</section>"
},
"system": {},
"image": {},
"video": {
"controls": true,
"volume": 0.5
},
"src": null,
"category": null,
"sort": 0,
"ownership": {
"default": -1
},
"flags": {},
"_key": "!journal.pages!69Da9YvF9BfOV7oK.XM0eLkgKXPyskV65"
}
]
}
+604 -77
View File
@@ -57,7 +57,7 @@
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
} }
.sheet-card { .dnc-sheet .sheet-card {
border: 2px solid #5b4634; border: 2px solid #5b4634;
border-radius: 10px; border-radius: 10px;
background: rgba(255, 248, 236, 0.92); background: rgba(255, 248, 236, 0.92);
@@ -73,20 +73,20 @@
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.04em; letter-spacing: 0.04em;
} }
.sheet-header { .dnc-sheet .sheet-header {
display: grid; display: grid;
grid-template-columns: 140px 1fr; grid-template-columns: 140px 1fr;
gap: 1rem; gap: 1rem;
align-items: start; align-items: start;
} }
.sheet-header.compact { .dnc-sheet .sheet-header.compact {
grid-template-columns: 110px 1fr; grid-template-columns: 110px 1fr;
gap: 0.75rem; gap: 0.75rem;
} }
.portrait { .dnc-sheet .portrait {
position: relative; position: relative;
} }
.portrait img { .dnc-sheet .portrait img {
width: 100%; width: 100%;
aspect-ratio: 1; aspect-ratio: 1;
object-fit: cover; object-fit: cover;
@@ -94,7 +94,7 @@
border: 3px solid #5b4634; border: 3px solid #5b4634;
background: #fff; background: #fff;
} }
.icon-button { .dnc-sheet .icon-button {
position: absolute; position: absolute;
right: 0.4rem; right: 0.4rem;
bottom: 0.4rem; bottom: 0.4rem;
@@ -105,13 +105,13 @@
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
} }
.identity-grid, .dnc-sheet .identity-grid,
.identity-grid.two-columns { .dnc-sheet .identity-grid.two-columns {
display: grid; display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 0.75rem; gap: 0.75rem;
} }
.identity-grid.two-columns { .dnc-sheet .identity-grid.two-columns {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
.dnc-employe-sheet .sheet-header.compact .identity-grid { .dnc-employe-sheet .sheet-header.compact .identity-grid {
@@ -121,11 +121,16 @@
.dnc-employe-sheet .sheet-header.compact label { .dnc-employe-sheet .sheet-header.compact label {
gap: 0.25rem; gap: 0.25rem;
} }
.dnc-employe-sheet .sheet-header.compact .hp-field {
grid-column: span 2;
}
.dnc-employe-sheet .sheet-header.compact input[type="number"] { .dnc-employe-sheet .sheet-header.compact input[type="number"] {
max-width: 4.75rem; max-width: 4.75rem;
} }
.dnc-employe-sheet .sheet-header.compact .counter-field input[type="number"] { .dnc-employe-sheet .sheet-header.compact .counter-field input[name="system.sante.pv.value"],
max-width: 4rem; .dnc-employe-sheet .sheet-header.compact .counter-field input[name="system.sante.pv.max"] {
width: 5.25rem;
max-width: 5.25rem;
} }
.dnc-pnj-sheet .sheet-header.compact .identity-grid { .dnc-pnj-sheet .sheet-header.compact .identity-grid {
grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-columns: repeat(4, minmax(0, 1fr));
@@ -134,13 +139,17 @@
.dnc-pnj-sheet .sheet-header.compact label { .dnc-pnj-sheet .sheet-header.compact label {
gap: 0.25rem; gap: 0.25rem;
} }
.dnc-pnj-sheet .sheet-header.compact .hp-field {
grid-column: span 2;
}
.dnc-pnj-sheet .sheet-header.compact input[type="number"] { .dnc-pnj-sheet .sheet-header.compact input[type="number"] {
width: 4.5rem; width: 4.5rem;
max-width: 4.5rem; max-width: 4.5rem;
} }
.dnc-pnj-sheet .sheet-header.compact .counter-field input[type="number"] { .dnc-pnj-sheet .sheet-header.compact .counter-field input[name="system.sante.pv.value"],
width: 3.75rem; .dnc-pnj-sheet .sheet-header.compact .counter-field input[name="system.sante.pv.max"] {
max-width: 3.75rem; width: 5.25rem;
max-width: 5.25rem;
} }
.dnc-sheet label { .dnc-sheet label {
display: flex; display: flex;
@@ -153,18 +162,18 @@
letter-spacing: 0.04em; letter-spacing: 0.04em;
color: #6d5a4f; color: #6d5a4f;
} }
.readonly-field { .dnc-sheet .readonly-field {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.4rem; gap: 0.4rem;
} }
.readonly-field > span { .dnc-sheet .readonly-field > span {
font-size: 0.8rem; font-size: 0.8rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.04em; letter-spacing: 0.04em;
color: #6d5a4f; color: #6d5a4f;
} }
.readonly-field p { .dnc-sheet .readonly-field p {
margin: 0; margin: 0;
border: 1px solid rgba(91, 70, 52, 0.55); border: 1px solid rgba(91, 70, 52, 0.55);
border-radius: 6px; border-radius: 6px;
@@ -188,29 +197,29 @@
resize: vertical; resize: vertical;
min-height: 4.5rem; min-height: 4.5rem;
} }
.sheet-columns { .dnc-sheet .sheet-columns {
display: grid; display: grid;
grid-template-columns: minmax(250px, 320px) 1fr; grid-template-columns: minmax(250px, 320px) 1fr;
gap: 1rem; gap: 1rem;
} }
.sheet-sections { .dnc-sheet .sheet-sections {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 1rem; gap: 1rem;
} }
.compact-sections { .dnc-sheet .compact-sections {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.counter-field { .dnc-sheet .counter-field {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.4rem; gap: 0.4rem;
} }
.counter-field button, .dnc-sheet .counter-field button,
.item-actions button, .dnc-sheet .item-actions button,
.sheet-footer button, .dnc-sheet .sheet-footer button,
.text-button, .dnc-sheet .text-button,
.roll-button { .dnc-sheet .roll-button {
border: 1px solid #5b4634; border: 1px solid #5b4634;
border-radius: 6px; border-radius: 6px;
background: linear-gradient(180deg, #fdf9f2 0%, #e8d5ba 100%); background: linear-gradient(180deg, #fdf9f2 0%, #e8d5ba 100%);
@@ -218,90 +227,97 @@
min-height: 2rem; min-height: 2rem;
padding: 0.35rem 0.65rem; padding: 0.35rem 0.65rem;
} }
.text-button { .dnc-sheet .text-button {
white-space: nowrap; white-space: nowrap;
} }
.roll-button { .dnc-sheet .roll-button {
width: 2rem; width: 2rem;
padding: 0; padding: 0;
} }
.with-controls { .dnc-sheet .with-controls {
gap: 0.4rem; gap: 0.4rem;
} }
.section-header { .dnc-sheet .section-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
gap: 0.75rem; gap: 0.75rem;
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.empty-state { .dnc-sheet .empty-state {
margin: 0; margin: 0;
color: #6d5a4f; color: #6d5a4f;
font-style: italic; font-style: italic;
} }
.span-two { .dnc-sheet .span-two {
grid-column: span 2; grid-column: span 2;
} }
.characteristic-list { .dnc-actor-sheet .characteristic-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.4rem; gap: 0.4rem;
} }
.characteristic-row { .dnc-actor-sheet .characteristic-row {
display: grid; display: grid;
grid-template-columns: 2rem 1fr 4rem; grid-template-columns: 2rem 1fr 4rem;
gap: 0.4rem; gap: 0.4rem;
align-items: center; align-items: center;
} }
.characteristic-label { .dnc-actor-sheet .characteristic-label {
font-weight: 700; font-weight: 700;
} }
.profile-counters { .dnc-actor-sheet .profile-counters {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: 0.75rem; gap: 0.75rem;
} }
.profile-card-wide { .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 {
width: 100%; width: 100%;
} }
.profile-layout { .dnc-actor-sheet .profile-layout {
display: grid; display: grid;
grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.85fr); grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.85fr);
gap: 1rem; gap: 1rem;
align-items: start; align-items: start;
} }
.profile-column { .dnc-actor-sheet .profile-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.75rem; gap: 0.75rem;
} }
.favor-card { .dnc-actor-sheet .favor-card {
margin: 0.75rem 0; margin: 0.75rem 0;
padding: 0.75rem; padding: 0.75rem;
border: 1px dashed rgba(91, 70, 52, 0.55); border: 1px dashed rgba(91, 70, 52, 0.55);
border-radius: 10px; border-radius: 10px;
background: rgba(226, 208, 177, 0.35); background: rgba(226, 208, 177, 0.35);
} }
.favor-header h2 { .dnc-actor-sheet .favor-header h2 {
margin-bottom: 0.4rem; margin-bottom: 0.4rem;
} }
.favor-help { .dnc-actor-sheet .favor-help {
margin: 0 0 0.75rem; margin: 0 0 0.75rem;
color: #6d5a4f; color: #6d5a4f;
font-size: 0.82rem; font-size: 0.82rem;
} }
.favor-list { .dnc-actor-sheet .favor-list {
display: grid; display: grid;
gap: 0.4rem; gap: 0.4rem;
} }
.favor-row { .dnc-actor-sheet .favor-row {
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) 4.5rem auto; grid-template-columns: minmax(0, 1fr) 4.5rem auto;
gap: 0.4rem; gap: 0.4rem;
align-items: end; align-items: end;
} }
.favor-meta { .dnc-actor-sheet .favor-meta {
min-height: 2.3rem; min-height: 2.3rem;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -311,57 +327,57 @@
border: 1px solid rgba(91, 70, 52, 0.35); border: 1px solid rgba(91, 70, 52, 0.35);
font-weight: 700; font-weight: 700;
} }
.magic-layout { .dnc-actor-sheet .magic-layout {
align-items: start; align-items: start;
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.magic-layout .sheet-column { .dnc-actor-sheet .magic-layout .sheet-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.75rem; gap: 0.75rem;
} }
.magic-resource-grid { .dnc-actor-sheet .magic-resource-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
gap: 0.75rem; gap: 0.75rem;
align-items: end; align-items: end;
} }
.chaos-table-wrapper { .dnc-actor-sheet .chaos-table-wrapper {
margin-top: 0.75rem; margin-top: 0.75rem;
} }
.chaos-table-wrapper h3 { .dnc-actor-sheet .chaos-table-wrapper h3 {
margin: 0 0 0.4rem; margin: 0 0 0.4rem;
font-size: 0.95rem; font-size: 0.95rem;
} }
.chaos-table-caption { .dnc-actor-sheet .chaos-table-caption {
margin: 0 0 0.4rem; margin: 0 0 0.4rem;
color: #6d5a4f; color: #6d5a4f;
font-size: 0.78rem; font-size: 0.78rem;
font-style: italic; font-style: italic;
} }
.chaos-table { .dnc-actor-sheet .chaos-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
font-size: 0.8rem; font-size: 0.8rem;
} }
.chaos-table th, .dnc-actor-sheet .chaos-table th,
.chaos-table td { .dnc-actor-sheet .chaos-table td {
padding: 0.4rem 0.45rem; padding: 0.4rem 0.45rem;
border: 1px solid rgba(91, 70, 52, 0.35); border: 1px solid rgba(91, 70, 52, 0.35);
vertical-align: top; vertical-align: top;
} }
.chaos-table th { .dnc-actor-sheet .chaos-table th {
text-align: left; text-align: left;
background: rgba(226, 208, 177, 0.55); background: rgba(226, 208, 177, 0.55);
} }
.sheet-tabs { .dnc-actor-sheet .sheet-tabs {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.4rem; gap: 0.4rem;
margin: 1rem 0 0.75rem; margin: 1rem 0 0.75rem;
justify-content: center; justify-content: center;
} }
.sheet-tab { .dnc-actor-sheet .sheet-tab {
border: 1px solid rgba(91, 70, 52, 0.8); border: 1px solid rgba(91, 70, 52, 0.8);
border-radius: 10px; border-radius: 10px;
background: rgba(226, 208, 177, 0.55); background: rgba(226, 208, 177, 0.55);
@@ -369,27 +385,27 @@
font-weight: 700; font-weight: 700;
padding: 0.4rem 0.75rem; padding: 0.4rem 0.75rem;
} }
.sheet-tab.active { .dnc-actor-sheet .sheet-tab.active {
background: #8b2e17; background: #8b2e17;
border-color: #561d0e; border-color: #561d0e;
color: #fff; color: #fff;
} }
.sheet-tab-panel { .dnc-actor-sheet .sheet-tab-panel {
display: none; display: none;
} }
.sheet-tab-panel.active { .dnc-actor-sheet .sheet-tab-panel.active {
display: block; display: block;
} }
.item-list { .dnc-actor-sheet .item-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.75rem; gap: 0.75rem;
} }
.item-section.is-dragover { .dnc-actor-sheet .item-section.is-dragover {
border-color: #8b2e17; border-color: #8b2e17;
box-shadow: 0 0 0 2px rgba(139, 46, 23, 0.2); box-shadow: 0 0 0 2px rgba(139, 46, 23, 0.2);
} }
.item-dropzone { .dnc-actor-sheet .item-dropzone {
border: 1px dashed rgba(91, 70, 52, 0.75); border: 1px dashed rgba(91, 70, 52, 0.75);
border-radius: 10px; border-radius: 10px;
padding: 0.75rem; padding: 0.75rem;
@@ -403,16 +419,16 @@
font-size: 0.9rem; font-size: 0.9rem;
font-weight: 600; font-weight: 600;
} }
.item-dropzone.has-items { .dnc-actor-sheet .item-dropzone.has-items {
padding: 0.4rem 0.75rem; padding: 0.4rem 0.75rem;
font-size: 0.8rem; font-size: 0.8rem;
} }
.item-section.is-dragover .item-dropzone { .dnc-actor-sheet .item-section.is-dragover .item-dropzone {
border-color: #8b2e17; border-color: #8b2e17;
background: rgba(139, 46, 23, 0.12); background: rgba(139, 46, 23, 0.12);
color: #221b18; color: #221b18;
} }
.item { .dnc-actor-sheet .item {
display: grid; display: grid;
grid-template-columns: 48px 1fr auto; grid-template-columns: 48px 1fr auto;
gap: 0.75rem; gap: 0.75rem;
@@ -420,11 +436,11 @@
border-top: 1px dashed rgba(91, 70, 52, 0.45); border-top: 1px dashed rgba(91, 70, 52, 0.45);
padding-top: 0.75rem; padding-top: 0.75rem;
} }
.item:first-child { .dnc-actor-sheet .item:first-child {
border-top: 0; border-top: 0;
padding-top: 0; padding-top: 0;
} }
.item img { .dnc-actor-sheet .item img {
width: 48px; width: 48px;
height: 48px; height: 48px;
object-fit: cover; object-fit: cover;
@@ -432,43 +448,71 @@
border: 1px solid #5b4634; border: 1px solid #5b4634;
background: #fff; background: #fff;
} }
.item-main p { .dnc-actor-sheet .item-main p {
margin: 0.4rem 0 0; margin: 0.4rem 0 0;
color: #6d5a4f; color: #6d5a4f;
} }
.item-title-row { .dnc-actor-sheet .item-title-row {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.4rem; gap: 0.4rem;
align-items: center; align-items: center;
} }
.item-meta { .dnc-actor-sheet .item-meta {
border-radius: 999px; border-radius: 999px;
background: #e2d0b1; background: #e2d0b1;
color: #221b18; color: #221b18;
font-size: 0.75rem; font-size: 0.75rem;
padding: 0.1rem 0.5rem; padding: 0.1rem 0.5rem;
} }
.item-actions { .dnc-actor-sheet .item-actions {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.4rem; gap: 0.4rem;
justify-content: flex-end; justify-content: flex-end;
} }
.pnj-layout .sheet-column { .dnc-pnj-sheet .pnj-layout .sheet-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.75rem; gap: 0.75rem;
} }
.pnj-combat-grid { .dnc-pnj-sheet .pnj-combat-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.75rem; gap: 0.75rem;
align-items: end; align-items: end;
} }
.pnj-combat-grid .span-two { .dnc-pnj-sheet .pnj-combat-grid .span-two {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
.dnc-pnj-sheet .pnj-attack-list {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.dnc-pnj-sheet .pnj-attack-list .section-header {
margin-bottom: 0;
}
.dnc-pnj-sheet .pnj-attack-list h3 {
margin: 0;
font-size: 0.95rem;
color: #221b18;
}
.dnc-pnj-sheet .pnj-attack-rows {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.dnc-pnj-sheet .pnj-attack-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 4.5rem max-content;
gap: 0.75rem;
align-items: end;
}
.dnc-pnj-sheet .pnj-attack-row .item-actions {
align-self: end;
gap: 0.4rem;
}
.application.fvtt-donjon-et-cie.item { .application.fvtt-donjon-et-cie.item {
display: flex !important; display: flex !important;
flex-direction: column; flex-direction: column;
@@ -492,7 +536,7 @@
.dnc-item-sheet .identity-grid { .dnc-item-sheet .identity-grid {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
.sheet-footer { .dnc-item-sheet .sheet-footer {
display: flex; display: flex;
gap: 0.75rem; gap: 0.75rem;
justify-content: flex-end; justify-content: flex-end;
@@ -528,6 +572,126 @@
.dnc-roll-dialog .window-content { .dnc-roll-dialog .window-content {
background: linear-gradient(180deg, #f7efe0 0%, #e3d0b1 100%); background: linear-gradient(180deg, #f7efe0 0%, #e3d0b1 100%);
} }
.dnc-mission-pack-mode,
.dnc-mission-pack-note {
font-size: 0.9rem;
}
.dnc-mission-pack-campaign {
gap: 1rem;
}
.dnc-mission-pack-hero {
padding: 1rem;
border: 1px solid rgba(91, 70, 52, 0.35);
border-radius: 10px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.65) 0%, rgba(241, 229, 208, 0.68) 100%), linear-gradient(135deg, rgba(139, 46, 23, 0.08) 0%, rgba(139, 46, 23, 0) 100%);
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.1);
}
.dnc-mission-pack-kicker {
margin: 0 0 0.25rem;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: rgba(139, 46, 23, 0.82);
}
.dnc-mission-pack-hero h2 {
margin: 0;
font-family: "IM Fell English SC", "Palatino Linotype", "Book Antiqua", Palatino, serif;
font-size: 1.4rem;
line-height: 1.1;
color: #8b2e17;
}
.dnc-mission-pack-subtitle {
margin-top: 0.2rem;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: rgba(91, 70, 52, 0.78);
}
.dnc-mission-pack-intro {
margin-top: 0.4rem;
color: #6d5a4f;
}
.dnc-mission-pack-meta-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.75rem;
}
.dnc-mission-pack-meta-card {
display: grid;
gap: 0.2rem;
padding: 0.75rem;
border: 1px solid rgba(91, 70, 52, 0.3);
border-radius: 10px;
background: rgba(255, 255, 255, 0.42);
}
.dnc-mission-pack-meta-card span {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #6d5a4f;
}
.dnc-mission-pack-meta-card strong {
font-size: 1rem;
}
.dnc-mission-pack-section {
display: grid;
gap: 0.75rem;
}
.dnc-mission-pack-select {
padding: 0.75rem;
border: 1px solid rgba(91, 70, 52, 0.32);
border-radius: 10px;
background: rgba(255, 255, 255, 0.36);
}
.dnc-mission-pack-assignments {
display: grid;
gap: 0.75rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.dnc-mission-pack-assignment {
display: grid;
gap: 0.4rem;
padding: 0.75rem;
border: 1px solid rgba(91, 70, 52, 0.35);
border-radius: 10px;
background: rgba(255, 255, 255, 0.38);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55);
}
.dnc-mission-pack-assignment span {
font-size: 0.82rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.dnc-mission-pack-note {
color: #6d5a4f;
}
.dnc-mission-pack-note-foot {
padding: 0.75rem;
border-top: 1px solid rgba(91, 70, 52, 0.24);
}
.dnc-mission-pack-dialog .window-header {
background: linear-gradient(180deg, rgba(253, 246, 231, 0.96) 0%, rgba(234, 212, 170, 0.96) 100%), linear-gradient(90deg, rgba(139, 46, 23, 0.12) 0%, rgba(139, 46, 23, 0) 100%);
border-bottom: 1px solid rgba(91, 70, 52, 0.35);
}
.dnc-mission-pack-dialog .window-title {
color: #8b2e17;
font-family: "IM Fell English SC", "Palatino Linotype", "Book Antiqua", Palatino, serif;
letter-spacing: 0.03em;
text-shadow: none;
}
.dnc-mission-pack-dialog .window-header button {
color: #221b18;
}
@media (max-width: 640px) {
.dnc-mission-pack-meta-grid,
.dnc-mission-pack-assignments {
grid-template-columns: 1fr;
}
}
.dnc-chat-card { .dnc-chat-card {
position: relative; position: relative;
border: 2px solid #5b4634; border: 2px solid #5b4634;
@@ -722,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;
@@ -835,6 +1021,24 @@
.dnc-chat-card-favor { .dnc-chat-card-favor {
background: linear-gradient(180deg, rgba(248, 251, 255, 0.8) 0%, rgba(205, 220, 238, 0.94) 100%), linear-gradient(135deg, #f7fbff 0%, #9fb8d7 100%); background: linear-gradient(180deg, rgba(248, 251, 255, 0.8) 0%, rgba(205, 220, 238, 0.94) 100%), linear-gradient(135deg, #f7fbff 0%, #9fb8d7 100%);
} }
.dnc-chat-card-welcome {
border-color: rgba(139, 46, 23, 0.34);
background: linear-gradient(180deg, rgba(255, 250, 240, 0.84) 0%, rgba(239, 219, 179, 0.94) 100%), linear-gradient(135deg, #fffaf0 0%, #dcb772 100%);
}
.chat-welcome-intro {
font-size: 0.82rem;
line-height: 1.45;
}
.chat-welcome-link {
margin-top: 0.75rem;
padding: 0.6rem 0.8rem;
border: 1px solid rgba(139, 46, 23, 0.24);
border-radius: 12px;
background: rgba(139, 46, 23, 0.08);
}
.chat-welcome-link a.content-link {
font-weight: 700;
}
.dnc-chat-card-spell .chat-actions + .chat-actions { .dnc-chat-card-spell .chat-actions + .chat-actions {
margin-top: 0.4rem; margin-top: 0.4rem;
} }
@@ -908,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
+18
View File
@@ -52,6 +52,18 @@
"PLAYER": "OBSERVER", "PLAYER": "OBSERVER",
"ASSISTANT": "OWNER" "ASSISTANT": "OWNER"
} }
},
{
"type": "JournalEntry",
"label": "Aide du système",
"name": "system-help",
"path": "packs/system-help",
"system": "fvtt-donjon-et-cie",
"flags": {},
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
}
} }
], ],
"documentTypes": { "documentTypes": {
@@ -118,6 +130,12 @@
"description", "description",
"notes" "notes"
] ]
},
"entrainement": {
"htmlFields": [
"description",
"notes"
]
} }
} }
}, },
+63 -50
View File
@@ -28,7 +28,7 @@
<button type="button" data-action="rollHitDice" aria-label="Lancer le de de vie" title="Lancer le de de vie"><i class="fa-solid fa-dice-d20"></i></button> <button type="button" data-action="rollHitDice" aria-label="Lancer le de de vie" title="Lancer le de de vie"><i class="fa-solid fa-dice-d20"></i></button>
</div> </div>
</label> </label>
<label class="with-controls"> <label class="with-controls hp-field" style="grid-column: span 2;">
<span>PV</span> <span>PV</span>
<div class="counter-field"> <div class="counter-field">
<button type="button" data-action="adjustCounter" data-path="system.sante.pv.value" data-delta="-1">-</button> <button type="button" data-action="adjustCounter" data-path="system.sante.pv.value" data-delta="-1">-</button>
@@ -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>
@@ -93,7 +89,8 @@
{{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}} {{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}}
</div> </div>
{{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}} {{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}}
{{#if this.system.degats}}<p>Degats : {{this.system.degats}}</p>{{/if}} {{#if this.damageLabel}}<p>Degats : {{this.damageLabel}}</p>{{/if}}
{{#if this.ammunitionUsageLabel}}<p>Munitions : {{this.ammunitionUsageLabel}}</p>{{/if}}
</div> </div>
<div class="item-actions"> <div class="item-actions">
{{#if this.canRoll}} {{#if this.canRoll}}
@@ -138,7 +135,8 @@
{{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}} {{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}}
</div> </div>
{{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}} {{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}}
{{#if this.system.degats}}<p>Degats : {{this.system.degats}}</p>{{/if}} {{#if this.damageLabel}}<p>Degats : {{this.damageLabel}}</p>{{/if}}
{{#if this.ammunitionUsageLabel}}<p>Munitions : {{this.ammunitionUsageLabel}}</p>{{/if}}
</div> </div>
<div class="item-actions"> <div class="item-actions">
{{#if this.canRoll}} {{#if this.canRoll}}
@@ -150,6 +148,9 @@
{{#if this.canUse}} {{#if this.canUse}}
<button type="button" data-action="rollUsage" aria-label="Utiliser {{this.name}}" title="Utiliser {{this.name}}"><i class="fa-solid fa-hourglass-half"></i></button> <button type="button" data-action="rollUsage" aria-label="Utiliser {{this.name}}" title="Utiliser {{this.name}}"><i class="fa-solid fa-hourglass-half"></i></button>
{{/if}} {{/if}}
{{#if this.canReset}}
<button type="button" data-action="resetUsage" aria-label="Reinitialiser {{this.name}}" title="Reinitialiser"><i class="fa-solid fa-arrow-rotate-left"></i></button>
{{/if}}
<button type="button" data-action="postItem" aria-label="Poster {{this.name}} dans le chat" title="Poster dans le chat"><i class="fa-solid fa-message"></i></button> <button type="button" data-action="postItem" aria-label="Poster {{this.name}} dans le chat" title="Poster dans le chat"><i class="fa-solid fa-message"></i></button>
<button type="button" data-action="editItem" aria-label="Editer {{this.name}}" title="Editer"><i class="fa-solid fa-pen"></i></button> <button type="button" data-action="editItem" aria-label="Editer {{this.name}}" title="Editer"><i class="fa-solid fa-pen"></i></button>
<button type="button" data-action="deleteItem" aria-label="Supprimer {{this.name}}" title="Supprimer"><i class="fa-solid fa-trash"></i></button> <button type="button" data-action="deleteItem" aria-label="Supprimer {{this.name}}" title="Supprimer"><i class="fa-solid fa-trash"></i></button>
@@ -212,47 +213,6 @@
</table> </table>
</div> </div>
</section> </section>
<section class="sheet-card item-section">
<header class="section-header">
<h2>{{capacitySection.label}}</h2>
<button type="button" class="text-button" data-action="createItem" data-type="{{capacitySection.createType}}">+ Ajouter</button>
</header>
<div class="item-list">
{{#if capacitySection.items.length}}
{{#each capacitySection.items}}
<article class="item" data-item-id="{{this.id}}">
<img src="{{this.img}}" alt="{{this.name}}">
<div class="item-main">
<div class="item-title-row">
<strong>{{this.name}}</strong>
{{#if this.usageLabel}}<span class="item-meta">{{this.usageLabel}}</span>{{/if}}
{{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}}
</div>
{{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}}
{{#if this.system.degats}}<p>Degats : {{this.system.degats}}</p>{{/if}}
</div>
<div class="item-actions">
{{#if this.canRoll}}
<button type="button" data-action="{{this.rollAction}}" aria-label="Lancer {{this.name}}" title="Lancer {{this.name}}"><i class="fa-solid fa-dice-d20"></i></button>
{{/if}}
{{#if this.canRollDamage}}
<button type="button" data-action="rollDamage" aria-label="Lancer les degats de {{this.name}}" title="Lancer les degats"><i class="fa-solid fa-burst"></i></button>
{{/if}}
{{#if this.canUse}}
<button type="button" data-action="rollUsage" aria-label="Utiliser {{this.name}}" title="Utiliser {{this.name}}"><i class="fa-solid fa-hourglass-half"></i></button>
{{/if}}
<button type="button" data-action="postItem" aria-label="Poster {{this.name}} dans le chat" title="Poster dans le chat"><i class="fa-solid fa-message"></i></button>
<button type="button" data-action="editItem" aria-label="Editer {{this.name}}" title="Editer"><i class="fa-solid fa-pen"></i></button>
<button type="button" data-action="deleteItem" aria-label="Supprimer {{this.name}}" title="Supprimer"><i class="fa-solid fa-trash"></i></button>
</div>
</article>
{{/each}}
{{/if}}
<div class="item-dropzone {{#if capacitySection.items.length}}has-items{{/if}}" data-item-type="{{capacitySection.createType}}">
Glissez-deposez ici les {{capacitySection.label}}.
</div>
</div>
</section>
</section> </section>
<section class="sheet-column"> <section class="sheet-column">
<section class="sheet-card item-section"> <section class="sheet-card item-section">
@@ -272,7 +232,8 @@
{{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}} {{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}}
</div> </div>
{{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}} {{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}}
{{#if this.system.degats}}<p>Degats : {{this.system.degats}}</p>{{/if}} {{#if this.damageLabel}}<p>Degats : {{this.damageLabel}}</p>{{/if}}
{{#if this.ammunitionUsageLabel}}<p>Munitions : {{this.ammunitionUsageLabel}}</p>{{/if}}
</div> </div>
<div class="item-actions"> <div class="item-actions">
{{#if this.canRoll}} {{#if this.canRoll}}
@@ -284,6 +245,9 @@
{{#if this.canUse}} {{#if this.canUse}}
<button type="button" data-action="rollUsage" aria-label="Utiliser {{this.name}}" title="Utiliser {{this.name}}"><i class="fa-solid fa-hourglass-half"></i></button> <button type="button" data-action="rollUsage" aria-label="Utiliser {{this.name}}" title="Utiliser {{this.name}}"><i class="fa-solid fa-hourglass-half"></i></button>
{{/if}} {{/if}}
{{#if this.canReset}}
<button type="button" data-action="resetUsage" aria-label="Reinitialiser {{this.name}}" title="Reinitialiser"><i class="fa-solid fa-arrow-rotate-left"></i></button>
{{/if}}
<button type="button" data-action="postItem" aria-label="Poster {{this.name}} dans le chat" title="Poster dans le chat"><i class="fa-solid fa-message"></i></button> <button type="button" data-action="postItem" aria-label="Poster {{this.name}} dans le chat" title="Poster dans le chat"><i class="fa-solid fa-message"></i></button>
<button type="button" data-action="editItem" aria-label="Editer {{this.name}}" title="Editer"><i class="fa-solid fa-pen"></i></button> <button type="button" data-action="editItem" aria-label="Editer {{this.name}}" title="Editer"><i class="fa-solid fa-pen"></i></button>
<button type="button" data-action="deleteItem" aria-label="Supprimer {{this.name}}" title="Supprimer"><i class="fa-solid fa-trash"></i></button> <button type="button" data-action="deleteItem" aria-label="Supprimer {{this.name}}" title="Supprimer"><i class="fa-solid fa-trash"></i></button>
@@ -296,6 +260,51 @@
</div> </div>
</div> </div>
</section> </section>
<section class="sheet-card item-section">
<header class="section-header">
<h2>{{capacitySection.label}}</h2>
<button type="button" class="text-button" data-action="createItem" data-type="{{capacitySection.createType}}">+ Ajouter</button>
</header>
<div class="item-list">
{{#if capacitySection.items.length}}
{{#each capacitySection.items}}
<article class="item" data-item-id="{{this.id}}">
<img src="{{this.img}}" alt="{{this.name}}">
<div class="item-main">
<div class="item-title-row">
<strong>{{this.name}}</strong>
{{#if this.usageLabel}}<span class="item-meta">{{this.usageLabel}}</span>{{/if}}
{{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}}
</div>
{{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}}
{{#if this.damageLabel}}<p>Degats : {{this.damageLabel}}</p>{{/if}}
{{#if this.ammunitionUsageLabel}}<p>Munitions : {{this.ammunitionUsageLabel}}</p>{{/if}}
</div>
<div class="item-actions">
{{#if this.canRoll}}
<button type="button" data-action="{{this.rollAction}}" aria-label="Lancer {{this.name}}" title="Lancer {{this.name}}"><i class="fa-solid fa-dice-d20"></i></button>
{{/if}}
{{#if this.canRollDamage}}
<button type="button" data-action="rollDamage" aria-label="Lancer les degats de {{this.name}}" title="Lancer les degats"><i class="fa-solid fa-burst"></i></button>
{{/if}}
{{#if this.canUse}}
<button type="button" data-action="rollUsage" aria-label="Utiliser {{this.name}}" title="Utiliser {{this.name}}"><i class="fa-solid fa-hourglass-half"></i></button>
{{/if}}
{{#if this.canReset}}
<button type="button" data-action="resetUsage" aria-label="Reinitialiser {{this.name}}" title="Reinitialiser"><i class="fa-solid fa-arrow-rotate-left"></i></button>
{{/if}}
<button type="button" data-action="postItem" aria-label="Poster {{this.name}} dans le chat" title="Poster dans le chat"><i class="fa-solid fa-message"></i></button>
<button type="button" data-action="editItem" aria-label="Editer {{this.name}}" title="Editer"><i class="fa-solid fa-pen"></i></button>
<button type="button" data-action="deleteItem" aria-label="Supprimer {{this.name}}" title="Supprimer"><i class="fa-solid fa-trash"></i></button>
</div>
</article>
{{/each}}
{{/if}}
<div class="item-dropzone {{#if capacitySection.items.length}}has-items{{/if}}" data-item-type="{{capacitySection.createType}}">
Glissez-deposez ici les {{capacitySection.label}}.
</div>
</div>
</section>
</section> </section>
</section> </section>
</section> </section>
@@ -386,7 +395,8 @@
{{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}} {{#if this.protectionLabel}}<span class="item-meta">{{this.protectionLabel}}</span>{{/if}}
</div> </div>
{{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}} {{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}}
{{#if this.system.degats}}<p>Degats : {{this.system.degats}}</p>{{/if}} {{#if this.damageLabel}}<p>Degats : {{this.damageLabel}}</p>{{/if}}
{{#if this.ammunitionUsageLabel}}<p>Munitions : {{this.ammunitionUsageLabel}}</p>{{/if}}
</div> </div>
<div class="item-actions"> <div class="item-actions">
{{#if this.canRoll}} {{#if this.canRoll}}
@@ -398,6 +408,9 @@
{{#if this.canUse}} {{#if this.canUse}}
<button type="button" data-action="rollUsage" aria-label="Utiliser {{this.name}}" title="Utiliser {{this.name}}"><i class="fa-solid fa-hourglass-half"></i></button> <button type="button" data-action="rollUsage" aria-label="Utiliser {{this.name}}" title="Utiliser {{this.name}}"><i class="fa-solid fa-hourglass-half"></i></button>
{{/if}} {{/if}}
{{#if this.canReset}}
<button type="button" data-action="resetUsage" aria-label="Reinitialiser {{this.name}}" title="Reinitialiser"><i class="fa-solid fa-arrow-rotate-left"></i></button>
{{/if}}
<button type="button" data-action="postItem" aria-label="Poster {{this.name}} dans le chat" title="Poster dans le chat"><i class="fa-solid fa-message"></i></button> <button type="button" data-action="postItem" aria-label="Poster {{this.name}} dans le chat" title="Poster dans le chat"><i class="fa-solid fa-message"></i></button>
<button type="button" data-action="editItem" aria-label="Editer {{this.name}}" title="Editer"><i class="fa-solid fa-pen"></i></button> <button type="button" data-action="editItem" aria-label="Editer {{this.name}}" title="Editer"><i class="fa-solid fa-pen"></i></button>
<button type="button" data-action="deleteItem" aria-label="Supprimer {{this.name}}" title="Supprimer"><i class="fa-solid fa-trash"></i></button> <button type="button" data-action="deleteItem" aria-label="Supprimer {{this.name}}" title="Supprimer"><i class="fa-solid fa-trash"></i></button>
+22 -14
View File
@@ -32,7 +32,7 @@
<button type="button" data-action="rollHitDice" aria-label="Lancer le de de vie" title="Lancer le de de vie"><i class="fa-solid fa-dice-d20"></i></button> <button type="button" data-action="rollHitDice" aria-label="Lancer le de de vie" title="Lancer le de de vie"><i class="fa-solid fa-dice-d20"></i></button>
</div> </div>
</label> </label>
<label class="with-controls"> <label class="with-controls hp-field" style="grid-column: span 2;">
<span>PV</span> <span>PV</span>
<div class="counter-field"> <div class="counter-field">
<button type="button" data-action="adjustCounter" data-path="system.sante.pv.value" data-delta="-1">-</button> <button type="button" data-action="adjustCounter" data-path="system.sante.pv.value" data-delta="-1">-</button>
@@ -72,23 +72,30 @@
<span>Valeurs</span> <span>Valeurs</span>
<p>ARM {{armorDisplay}} · COU {{courageDisplay}}</p> <p>ARM {{armorDisplay}} · COU {{courageDisplay}}</p>
</div> </div>
<section class="pnj-attack-list span-two">
<header class="section-header">
<h3>Attaques</h3>
<button type="button" class="text-button" data-action="createPnjAttack">+ Ajouter</button>
</header>
<div class="pnj-attack-rows">
{{#each pnjAttacks}}
<article class="pnj-attack-row" data-attack-index="{{this.index}}">
<label> <label>
<span>ATT</span> <input type="text" name="system.attaques.{{this.index}}.nom" value="{{this.nom}}" aria-label="Nom de l'attaque" title="Nom de l'attaque">
<input type="text" name="system.attaque.nom" value="{{system.attaque.nom}}">
</label> </label>
<label> <label>
<span>Degats</span> <input type="text" name="system.attaques.{{this.index}}.degats" value="{{this.degats}}" aria-label="Dégâts" title="Dégâts">
<div class="counter-field"> </label>
<input type="text" name="system.attaque.degats" value="{{system.attaque.degats}}"> <div class="item-actions">
{{#if hasAttackDamage}} {{#if this.degats}}
<button type="button" data-action="rollPnjAttackDamage" aria-label="Lancer les degats de l'attaque" title="Lancer les degats"><i class="fa-solid fa-burst"></i></button> <button type="button" data-action="rollPnjAttackDamage" aria-label="Lancer les degats de {{this.nom}}" title="Lancer les degats"><i class="fa-solid fa-burst"></i></button>
{{/if}} {{/if}}
<button type="button" data-action="deletePnjAttack" aria-label="Supprimer {{this.nom}}" title="Supprimer"><i class="fa-solid fa-trash"></i></button>
</div> </div>
</label> </article>
<label class="span-two"> {{/each}}
<span>Notes d'attaque</span> </div>
<input type="text" name="system.attaque.notes" value="{{system.attaque.notes}}"> </section>
</label>
</div> </div>
</section> </section>
@@ -115,7 +122,8 @@
{{#if this.usageLabel}}<span class="item-meta">{{this.usageLabel}}</span>{{/if}} {{#if this.usageLabel}}<span class="item-meta">{{this.usageLabel}}</span>{{/if}}
</div> </div>
{{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}} {{#if this.system.effet}}<p>{{this.system.effet}}</p>{{/if}}
{{#if this.system.degats}}<p>Degats : {{this.system.degats}}</p>{{/if}} {{#if this.damageLabel}}<p>Degats : {{this.damageLabel}}</p>{{/if}}
{{#if this.ammunitionUsageLabel}}<p>Munitions : {{this.ammunitionUsageLabel}}</p>{{/if}}
</div> </div>
<div class="item-actions"> <div class="item-actions">
{{#if this.canRoll}} {{#if this.canRoll}}
+8 -8
View File
@@ -1,19 +1,19 @@
<section class="dnc-chat-card dnc-chat-card-chaos"> <section class="dnc-chat-card dnc-chat-card-chaos">
<header class="chat-card-header"> <header class="chat-card-header">
<div class="chat-card-heading"> <div class="chat-card-heading">
<p class="chat-card-kicker">Chaos</p> <p class="chat-card-kicker">{{localize "DNC.UI.Chaos"}}</p>
<h2>{{title}}</h2> <h2>{{title}}</h2>
<p class="chat-card-subtitle">La magie opere malgre l'echec ; le MJ determine les consequences.</p> <p class="chat-card-subtitle">{{localize "DNC.Chat.ChaosSubtitle"}}</p>
</div> </div>
<div class="chat-card-total"> <div class="chat-card-total">
<span>Jet</span> <span>{{localize "DNC.Chat.RollValue"}}</span>
<strong>{{value}}</strong> <strong>{{value}}</strong>
</div> </div>
</header> </header>
<div class="chat-pill-row"> <div class="chat-pill-row">
<span class="chat-pill success">Sort maintenu</span> <span class="chat-pill success">{{localize "DNC.Chat.SpellMaintained"}}</span>
<span class="chat-pill">Avant {{before}}</span> <span class="chat-pill">{{localize "DNC.Chat.Before"}} {{before}}</span>
<span class="chat-pill">Apres {{after}}</span> <span class="chat-pill">{{localize "DNC.Chat.After"}} {{after}}</span>
</div> </div>
{{#if chaosEntry}} {{#if chaosEntry}}
<div class="chat-chaos-result"> <div class="chat-chaos-result">
@@ -22,9 +22,9 @@
</div> </div>
{{/if}} {{/if}}
{{#if degraded}} {{#if degraded}}
<p class="chat-note warning">Le de du Chaos descend d'un cran.</p> <p class="chat-note warning">{{localize "DNC.Chat.ChaosDieDrops"}}</p>
{{/if}} {{/if}}
{{#if exhausted}} {{#if exhausted}}
<p class="chat-note warning">Le de du Chaos est trop faible pour etre reutilise dans le Donjon.</p> <p class="chat-note warning">{{localize "DNC.Chat.ChaosDieExhausted"}}</p>
{{/if}} {{/if}}
</section> </section>
+10 -3
View File
@@ -13,7 +13,7 @@
<div class="chat-pill-row"> <div class="chat-pill-row">
{{#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 bonus}}<span class="chat-pill">Bonus +{{bonus}}</span>{{/if}} {{#if damageCapped}}<span class="chat-pill">{{localize "DNC.Chat.DamageCapped" damage=effectiveDamage dv=martialDvLabel}}</span>{{/if}}
</div> </div>
<p class="chat-formula">{{formula}}</p> <p class="chat-formula">{{formula}}</p>
{{#if rollDieLabels.[1]}} {{#if rollDieLabels.[1]}}
@@ -22,14 +22,14 @@
<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 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>
<select class="chat-select" data-role="damage-target" {{#unless hasTargets}}disabled{{/unless}}> <select class="chat-select" data-role="damage-target" {{#unless hasTargets}}disabled{{/unless}}>
{{#if hasTargets}} {{#if hasTargets}}
{{#each targets}} {{#each targets}}
<option value="{{this.tokenUuid}}">{{this.label}}</option> <option value="{{this.tokenUuid}}"{{#if this.isSelected}} selected{{/if}}>{{this.label}}</option>
{{/each}} {{/each}}
{{else}} {{else}}
<option value="">Aucune cible sur la scène</option> <option value="">Aucune cible sur la scène</option>
@@ -45,4 +45,11 @@
</button> </button>
</div> </div>
</div> </div>
{{#if showDamageUsageButton}}
<div class="chat-actions">
<button type="button" class="chat-action-button" data-action="rollDamageUsage" data-item-uuid="{{itemUuid}}">
<i class="fa-solid fa-hourglass-half"></i> {{localize "DNC.Chat.RollDamageUsage"}}
</button>
</div>
{{/if}}
</section> </section>
+3 -3
View File
@@ -1,7 +1,7 @@
<section class="dnc-chat-card dnc-chat-card-favor"> <section class="dnc-chat-card dnc-chat-card-favor">
<header class="chat-card-header"> <header class="chat-card-header">
<div class="chat-card-heading"> <div class="chat-card-heading">
<p class="chat-card-kicker">Reseau</p> <p class="chat-card-kicker">{{localize "DNC.Chat.Kicker.Network"}}</p>
<h2>{{title}}</h2> <h2>{{title}}</h2>
{{#if subtitle}}<p class="chat-card-subtitle">{{subtitle}}</p>{{/if}} {{#if subtitle}}<p class="chat-card-subtitle">{{subtitle}}</p>{{/if}}
</div> </div>
@@ -10,8 +10,8 @@
</div> </div>
</header> </header>
<div class="chat-pill-row"> <div class="chat-pill-row">
<span class="chat-pill">Avant {{before}}</span> <span class="chat-pill">{{localize "DNC.Chat.Before"}} {{before}}</span>
<span class="chat-pill">Apres {{after}}</span> <span class="chat-pill">{{localize "DNC.Chat.After"}} {{after}}</span>
</div> </div>
<p class="chat-note">{{note}}</p> <p class="chat-note">{{note}}</p>
</section> </section>
+5 -5
View File
@@ -1,13 +1,13 @@
<section class="dnc-chat-card dnc-chat-card-initiative"> <section class="dnc-chat-card dnc-chat-card-initiative">
<header class="initiative-header"> <header class="initiative-header">
<div class="chat-card-heading"> <div class="chat-card-heading">
<p class="chat-card-kicker">Ordre de marche</p> <p class="chat-card-kicker">{{localize "DNC.Chat.Kicker.Initiative"}}</p>
<h2>{{title}}</h2> <h2>{{title}}</h2>
<p class="chat-card-subtitle">{{actorName}}</p> <p class="chat-card-subtitle">{{actorName}}</p>
{{#if modeLabel}}<p class="chat-card-subtitle">{{modeLabel}}</p>{{/if}} {{#if modeLabel}}<p class="chat-card-subtitle">{{modeLabel}}</p>{{/if}}
</div> </div>
<div class="initiative-total"> <div class="initiative-total">
<span class="initiative-total-label">Total</span> <span class="initiative-total-label">{{localize "DNC.Chat.Total"}}</span>
<strong>{{total}}</strong> <strong>{{total}}</strong>
</div> </div>
</header> </header>
@@ -24,19 +24,19 @@
</div> </div>
<div class="initiative-operator">+</div> <div class="initiative-operator">+</div>
<div class="initiative-part"> <div class="initiative-part">
<span class="initiative-part-label">Bonus</span> <span class="initiative-part-label">{{localize "DNC.Chat.Bonus"}}</span>
<strong>{{bonus}}</strong> <strong>{{bonus}}</strong>
</div> </div>
</div> </div>
<p class="initiative-formula">{{formula}}</p> <p class="initiative-formula">{{formula}}</p>
{{#if dieValues.[1]}} {{#if dieValues.[1]}}
<p class="roll-values">Lancers : {{#each dieValues}}<span>{{this}}</span>{{/each}}</p> <p class="roll-values">{{localize "DNC.Chat.Rolls"}} : {{#each dieValues}}<span>{{this}}</span>{{/each}}</p>
{{/if}} {{/if}}
{{#if syncedCombat}} {{#if syncedCombat}}
<div class="initiative-sync"> <div class="initiative-sync">
<span class="chat-pill success">Combat synchronise</span> <span class="chat-pill success">{{localize "DNC.Chat.CombatSynced"}}</span>
<p>{{syncedCombat.name}} · rang {{syncedCombat.rank}} / {{syncedCombat.total}}</p> <p>{{syncedCombat.name}} · rang {{syncedCombat.rank}} / {{syncedCombat.total}}</p>
</div> </div>
{{/if}} {{/if}}
+62
View File
@@ -0,0 +1,62 @@
<section class="dnc-chat-card dnc-chat-card-item dnc-chat-card-mission-pack">
<header class="chat-card-header">
<div class="chat-card-heading">
<p class="chat-card-kicker">{{localize "DNC.Chat.Kicker.Logistics"}}</p>
<h2>{{title}}</h2>
<p class="chat-card-subtitle">{{actorName}}</p>
</div>
<div class="chat-card-seal {{#if missingCount}}failure{{else}}success{{/if}}">
{{createdCount}}
</div>
</header>
<div class="chat-pill-row">
<span class="chat-pill">{{localize "DNC.Macro.MissionPack.ActiveMode"}} {{generationModeLabel}}</span>
{{#if isCampaign}}<span class="chat-pill">{{localize "DNC.Macro.MissionPack.CampaignController"}} {{controllerName}}</span>{{/if}}
{{#if isCampaign}}<span class="chat-pill">{{localize "DNC.Macro.MissionPack.CampaignRelation"}} {{stewardRelationLabel}}</span>{{/if}}
<span class="chat-pill success">{{localize "DNC.Macro.MissionPack.ItemsAdded"}} {{createdCount}}</span>
{{#if missingCount}}<span class="chat-pill failure">{{localize "DNC.Macro.MissionPack.ItemsMissing"}} {{missingCount}}</span>{{/if}}
</div>
<p class="chat-note">
{{#if missingCount}}
{{localize "DNC.Macro.MissionPack.PartialChatSummary"}}
{{else}}
{{localize "DNC.Macro.MissionPack.CompleteChatSummary"}}
{{/if}}
</p>
<ul class="chat-details">
{{#each draws}}
<li>
<strong>{{this.label}}</strong> : {{this.display}}
{{#if this.dieLabel}}
<br>{{localize "DNC.Macro.MissionPack.RollDetail" die=this.dieLabel mode=this.modeLabel values=this.rollValuesLabel kept=this.kept rank=this.rankBonus total=this.total}}
{{#if this.clamped}}<br>{{localize "DNC.Macro.MissionPack.TotalClamped" total=this.total clamped=this.resolvedTotal}}{{/if}}
{{/if}}
{{#if this.missingSummary}}<br>{{localize "DNC.Macro.MissionPack.Missing"}} : {{this.missingSummary}}{{/if}}
</li>
{{/each}}
</ul>
<div class="chat-pill-row">
<span class="chat-pill">{{localize "DNC.Macro.MissionPack.UniqueReference"}} {{uniqueEntry.referenceRoll}}</span>
<span class="chat-pill">{{localize "DNC.Macro.MissionPack.UniqueActorRoll"}} {{uniqueEntry.actorRoll}}</span>
<span class="chat-pill {{#if uniqueEntry.matched}}success{{else}}failure{{/if}}">
{{#if uniqueEntry.matched}}
{{localize "DNC.Macro.MissionPack.UniqueMatch"}}
{{else}}
{{localize "DNC.Macro.MissionPack.UniqueMiss"}}
{{/if}}
</span>
</div>
{{#if uniqueEntry.matched}}
<p class="chat-note">
<strong>{{localize "DNC.Macro.MissionPack.UniqueGranted"}}</strong> {{uniqueEntry.itemName}}
{{#if uniqueEntry.uniqueRoll}}({{localize "DNC.Macro.MissionPack.UniqueTableRoll"}} {{uniqueEntry.uniqueRoll}}){{/if}}
</p>
{{else}}
<p class="chat-note">{{localize "DNC.Macro.MissionPack.UniqueRuleReminder"}}</p>
{{/if}}
</section>
+21 -8
View File
@@ -1,7 +1,7 @@
<section class="dnc-chat-card dnc-chat-card-roll"> <section class="dnc-chat-card dnc-chat-card-roll">
<header class="chat-card-header"> <header class="chat-card-header">
<div class="chat-card-heading"> <div class="chat-card-heading">
<p class="chat-card-kicker">Resolution</p> <p class="chat-card-kicker">{{localize "DNC.Chat.Kicker.Resolution"}}</p>
<h2>{{title}}</h2> <h2>{{title}}</h2>
{{#if subtitle}}<p class="chat-card-subtitle">{{subtitle}}</p>{{/if}} {{#if subtitle}}<p class="chat-card-subtitle">{{subtitle}}</p>{{/if}}
</div> </div>
@@ -10,26 +10,39 @@
</div> </div>
</header> </header>
<div class="chat-pill-row"> <div class="chat-pill-row">
<span class="chat-pill {{#if success}}success{{else}}failure{{/if}}">{{#if success}}Reussite{{else}}Echec{{/if}}</span> <span class="chat-pill {{#if success}}success{{else}}failure{{/if}}">{{#if success}}{{localize "DNC.Chat.Success"}}{{else}}{{localize "DNC.Chat.Failure"}}{{/if}}</span>
{{#if modeLabel}}<span class="chat-pill">{{modeLabel}}</span>{{/if}} {{#if modeLabel}}<span class="chat-pill">{{modeLabel}}</span>{{/if}}
{{#if favorLabel}}<span class="chat-pill">Faveur {{favorLabel}}</span>{{/if}} {{#if favorLabel}}<span class="chat-pill">{{localize "DNC.Chat.Favor"}} {{favorLabel}}</span>{{/if}}
{{#if targetPillLabel}}<span class="chat-pill">{{targetPillLabel}} {{targetPillValue}}</span>{{else}}<span class="chat-pill">Cible {{target}}</span>{{/if}} {{#if targetPillLabel}}<span class="chat-pill">{{targetPillLabel}} {{targetPillValue}}</span>{{else}}<span class="chat-pill">{{localize "DNC.Chat.Target"}} {{target}}</span>{{/if}}
{{#if keptPillLabel}}<span class="chat-pill">{{keptPillLabel}} {{keptPillValue}}</span>{{else}}<span class="chat-pill">Garde {{kept}}</span>{{/if}} {{#if keptPillLabel}}<span class="chat-pill">{{keptPillLabel}} {{keptPillValue}}</span>{{else}}<span class="chat-pill">{{localize "DNC.Chat.Kept"}} {{kept}}</span>{{/if}}
</div> </div>
<div class="chat-keyline"> <div class="chat-keyline">
<span class="chat-keyline-label">Lancers</span> <span class="chat-keyline-label">{{localize "DNC.Chat.Rolls"}}</span>
<p class="roll-values">{{#each values}}<span>{{this}}</span>{{/each}}</p> <p class="roll-values">{{#each values}}<span>{{this}}</span>{{/each}}</p>
</div> </div>
{{#if favorNote}} {{#if favorNote}}
<p class="chat-note"><strong>Faveur</strong> : {{favorNote}}</p> <p class="chat-note"><strong>{{localize "DNC.Chat.Favor"}}</strong> : {{favorNote}}</p>
{{/if}} {{/if}}
{{#if showDamageButton}} {{#if showDamageButton}}
<div class="chat-actions"> <div class="chat-actions">
<button type="button" class="chat-action-button" data-action="rollChatDamage" data-item-uuid="{{itemUuid}}"> <button type="button" class="chat-action-button" data-action="rollChatDamage" data-item-uuid="{{itemUuid}}">
<i class="fa-solid fa-burst"></i> Lancer les degats <i class="fa-solid fa-burst"></i> {{localize "DNC.Chat.RollDamage"}}
</button>
{{#if showAmmoButton}}
<button type="button" class="chat-action-button" data-action="rollAmmoUsage" data-item-uuid="{{itemUuid}}">
<i class="fa-solid fa-bullseye"></i> {{localize "DNC.Chat.RollAmmunition"}}
</button>
{{/if}}
</div>
{{else}}
{{#if showAmmoButton}}
<div class="chat-actions">
<button type="button" class="chat-action-button" data-action="rollAmmoUsage" data-item-uuid="{{itemUuid}}">
<i class="fa-solid fa-bullseye"></i> {{localize "DNC.Chat.RollAmmunition"}}
</button> </button>
</div> </div>
{{/if}} {{/if}}
{{/if}}
<ul class="chat-details chat-details-ornate"> <ul class="chat-details chat-details-ornate">
{{#each details}} {{#each details}}
<li><strong>{{this.label}}</strong> : {{this.value}}</li> <li><strong>{{this.label}}</strong> : {{this.value}}</li>
+18 -18
View File
@@ -1,7 +1,7 @@
<section class="dnc-chat-card dnc-chat-card-spell"> <section class="dnc-chat-card dnc-chat-card-spell">
<header class="chat-card-header"> <header class="chat-card-header">
<div class="chat-card-heading"> <div class="chat-card-heading">
<p class="chat-card-kicker">Magie</p> <p class="chat-card-kicker">{{localize "DNC.Chat.Kicker.Magic"}}</p>
<h2>{{title}}</h2> <h2>{{title}}</h2>
{{#if subtitle}}<p class="chat-card-subtitle">{{subtitle}}</p>{{/if}} {{#if subtitle}}<p class="chat-card-subtitle">{{subtitle}}</p>{{/if}}
</div> </div>
@@ -10,57 +10,57 @@
</div> </div>
</header> </header>
<div class="chat-pill-row"> <div class="chat-pill-row">
<span class="chat-pill {{#if success}}success{{else}}failure{{/if}}">{{#if success}}Reussite{{else}}Echec{{/if}}</span> <span class="chat-pill {{#if success}}success{{else}}failure{{/if}}">{{#if success}}{{localize "DNC.Chat.Success"}}{{else}}{{localize "DNC.Chat.Failure"}}{{/if}}</span>
{{#if modeLabel}}<span class="chat-pill">{{modeLabel}}</span>{{/if}} {{#if modeLabel}}<span class="chat-pill">{{modeLabel}}</span>{{/if}}
{{#if favorLabel}}<span class="chat-pill">Faveur {{favorLabel}}</span>{{/if}} {{#if favorLabel}}<span class="chat-pill">{{localize "DNC.Chat.Favor"}} {{favorLabel}}</span>{{/if}}
<span class="chat-pill">{{targetPillLabel}} {{targetPillValue}}</span> <span class="chat-pill">{{targetPillLabel}} {{targetPillValue}}</span>
<span class="chat-pill">Jet {{keptPillValue}}</span> <span class="chat-pill">{{localize "DNC.Chat.RollValue"}} {{keptPillValue}}</span>
</div> </div>
<div class="chat-keyline"> <div class="chat-keyline">
<span class="chat-keyline-label">Lancers</span> <span class="chat-keyline-label">{{localize "DNC.Chat.Rolls"}}</span>
<p class="roll-values">{{#each values}}<span>{{this}}</span>{{/each}}</p> <p class="roll-values">{{#each values}}<span>{{this}}</span>{{/each}}</p>
</div> </div>
{{#if autoDisadvantage}} {{#if autoDisadvantage}}
<p class="chat-note"> <p class="chat-note">
<strong>Desavantage automatique</strong> : <strong>{{localize "DNC.Chat.AutoDisadvantage"}}</strong> :
{{#if autoDisadvantageCanceled}} {{#if autoDisadvantageCanceled}}
le cout du sort depasse le rang du lanceur, mais une faveur l'a annule. {{localize "DNC.Chat.AutoDisadvantageCanceled"}}
{{else}} {{else}}
le cout du sort depasse le rang du lanceur. {{localize "DNC.Chat.AutoDisadvantageApplies"}}
{{/if}} {{/if}}
</p> </p>
{{/if}} {{/if}}
{{#if favorNote}} {{#if favorNote}}
<p class="chat-note"><strong>Faveur</strong> : {{favorNote}}</p> <p class="chat-note"><strong>{{localize "DNC.Chat.Favor"}}</strong> : {{favorNote}}</p>
{{/if}} {{/if}}
{{#if focusValue}} {{#if focusValue}}
<p class="chat-note"> <p class="chat-note">
<strong>Focus</strong> : {{focusValue}} depuis {{focusBeforeLabel}} <strong>{{localize "DNC.UI.Focus"}}</strong> : {{focusValue}} {{localize "DNC.Chat.FocusFrom"}} {{focusBeforeLabel}}
{{#if focusSpent}} · {{focusSpent}} utilise{{/if}} {{#if focusSpent}} · {{focusSpent}} {{localize "DNC.Chat.FocusUsed"}}{{/if}}
· {{focusRemaining}} restant · {{focusRemaining}} {{localize "DNC.Chat.FocusLeft"}}
{{#if focusDegraded}} · le focus descend a {{focusAfterLabel}}{{/if}} {{#if focusDegraded}} · {{localize "DNC.Chat.FocusDropsTo"}} {{focusAfterLabel}}{{/if}}
{{#if focusRolled}} · premier sort de la scene{{/if}} {{#if focusRolled}} · {{localize "DNC.Chat.FirstSpellScene"}}{{/if}}
</p> </p>
{{else}} {{else}}
{{#if focusRolled}} {{#if focusRolled}}
<p class="chat-note"><strong>Focus</strong> : aucun apport pour cette scene.</p> <p class="chat-note"><strong>{{localize "DNC.UI.Focus"}}</strong> : {{localize "DNC.Chat.FocusNoBenefit"}}</p>
{{/if}} {{/if}}
{{/if}} {{/if}}
<p class="chat-note"><strong>PV depenses</strong> : {{spentPv}} · <strong>PV restants</strong> : {{remainingPv}}</p> <p class="chat-note"><strong>{{localize "DNC.Chat.HpSpent"}}</strong> : {{spentPv}} · <strong>{{localize "DNC.Chat.HpRemaining"}}</strong> : {{remainingPv}}</p>
{{#if specialNote}} {{#if specialNote}}
<p class="chat-note warning">{{specialNote}}</p> <p class="chat-note warning">{{specialNote}}</p>
{{/if}} {{/if}}
{{#if showDamageButton}} {{#if showDamageButton}}
<div class="chat-actions"> <div class="chat-actions">
<button type="button" class="chat-action-button" data-action="rollChatDamage" data-item-uuid="{{itemUuid}}"> <button type="button" class="chat-action-button" data-action="rollChatDamage" data-item-uuid="{{itemUuid}}">
<i class="fa-solid fa-burst"></i> Lancer les degats <i class="fa-solid fa-burst"></i> {{localize "DNC.Chat.RollDamage"}}
</button> </button>
</div> </div>
{{/if}} {{/if}}
{{#if showChaosButton}} {{#if showChaosButton}}
<div class="chat-actions"> <div class="chat-actions">
<button type="button" class="chat-action-button" data-action="rollSpellChaos" data-actor-uuid="{{actorUuid}}" data-item-uuid="{{itemUuid}}"> <button type="button" class="chat-action-button" data-action="rollSpellChaos" data-actor-uuid="{{actorUuid}}" data-item-uuid="{{itemUuid}}">
<i class="fa-solid fa-dice-d20"></i> Risquer le Chaos <i class="fa-solid fa-dice-d20"></i> {{localize "DNC.Chat.RiskChaos"}}
</button> </button>
</div> </div>
{{/if}} {{/if}}
+16 -8
View File
@@ -1,7 +1,7 @@
<section class="dnc-chat-card dnc-chat-card-usage"> <section class="dnc-chat-card dnc-chat-card-usage">
<header class="chat-card-header"> <header class="chat-card-header">
<div class="chat-card-heading"> <div class="chat-card-heading">
<p class="chat-card-kicker">Usure</p> <p class="chat-card-kicker">{{localize "DNC.Chat.Kicker.Wear"}}</p>
<h2>{{title}}</h2> <h2>{{title}}</h2>
</div> </div>
<div class="chat-card-seal {{#if degraded}}failure{{else}}success{{/if}}"> <div class="chat-card-seal {{#if degraded}}failure{{else}}success{{/if}}">
@@ -10,17 +10,25 @@
</header> </header>
<div class="chat-pill-row"> <div class="chat-pill-row">
{{#if modeLabel}}<span class="chat-pill">{{modeLabel}}</span>{{/if}} {{#if modeLabel}}<span class="chat-pill">{{modeLabel}}</span>{{/if}}
<span class="chat-pill">Resultat {{value}}</span> <span class="chat-pill">{{localize "DNC.Chat.Result"}} {{value}}</span>
<span class="chat-pill">Avant {{before}}</span> <span class="chat-pill">{{localize "DNC.Chat.Before"}} {{before}}</span>
<span class="chat-pill">Apres {{after}}</span> <span class="chat-pill">{{localize "DNC.Chat.After"}} {{after}}</span>
</div> </div>
{{#if values.[1]}} {{#if values.[1]}}
<div class="chat-keyline"> <div class="chat-keyline">
<span class="chat-keyline-label">Lancers</span> <span class="chat-keyline-label">{{localize "DNC.Chat.Rolls"}}</span>
<p class="roll-values">{{#each values}}<span>{{this}}</span>{{/each}}</p> <p class="roll-values">{{#each values}}<span>{{this}}</span>{{/each}}</p>
</div> </div>
{{/if}} {{/if}}
{{#if protectionStored}}<p class="chat-note">Protection stockee pour ce combat : <strong>{{protectionStored}}</strong>.</p>{{/if}} {{#if protectionStored}}<p class="chat-note">{{localize "DNC.Chat.StoredProtection"}} : <strong>{{protectionStored}}</strong>.</p>{{/if}}
{{#if degraded}}<p class="chat-note">La ressource s'amenuise.</p>{{else}}<p class="chat-note">La ressource tient bon.</p>{{/if}} {{#if isAmmunition}}
{{#if exhausted}}<p class="warning">La ressource est epuisee.</p>{{/if}} {{#if degraded}}<p class="chat-note">{{localize "DNC.Chat.AmmunitionWearsDown"}}</p>{{else}}<p class="chat-note">{{localize "DNC.Chat.AmmunitionStable"}}</p>{{/if}}
{{#if exhausted}}<p class="warning">{{localize "DNC.Chat.AmmunitionExhausted"}}</p>{{/if}}
{{else if isDamageUsage}}
{{#if degraded}}<p class="chat-note">{{localize "DNC.Chat.DamageUsageWearsDown"}}</p>{{else}}<p class="chat-note">{{localize "DNC.Chat.DamageUsageStable"}}</p>{{/if}}
{{#if exhausted}}<p class="warning">{{localize "DNC.Chat.DamageUsageExhausted"}}</p>{{/if}}
{{else}}
{{#if degraded}}<p class="chat-note">{{localize "DNC.Chat.ResourceWearsDown"}}</p>{{else}}<p class="chat-note">{{localize "DNC.Chat.ResourceStable"}}</p>{{/if}}
{{#if exhausted}}<p class="warning">{{localize "DNC.Chat.ResourceExhausted"}}</p>{{/if}}
{{/if}}
</section> </section>
+40
View File
@@ -0,0 +1,40 @@
<section class="dnc-chat-card dnc-chat-card-welcome">
<header class="chat-card-header">
<div class="chat-card-heading">
<p class="chat-card-kicker">{{localize "DNC.Welcome.Kicker"}}</p>
<h2>{{title}}</h2>
<p class="chat-card-subtitle">{{subtitle}}</p>
</div>
<div class="chat-card-seal success">
{{localize "DNC.Welcome.Seal"}}
</div>
</header>
<p class="chat-note chat-welcome-intro">{{intro}}</p>
<ul class="chat-details chat-details-ornate">
{{#each bullets}}
<li>{{this}}</li>
{{/each}}
</ul>
<p class="chat-note chat-welcome-link">
<strong>{{helpLabel}}</strong>
{{#if helpLink}}
{{{helpLink}}}
{{else}}
{{helpFallback}}
{{/if}}
</p>
<p class="chat-note">
<strong>{{creditsLabel}}</strong> {{creditsText}}
</p>
<p class="chat-note">
<strong>{{officialLabel}}</strong>
<a href="{{officialUrl}}" target="_blank" rel="noopener">{{officialLinkText}}</a>
</p>
<p class="chat-note">{{footer}}</p>
</section>
+6 -6
View File
@@ -1,18 +1,18 @@
<div class="dnc-dialog-form"> <div class="dnc-dialog-form">
<p><strong>{{actorName}}</strong> effectue un jet de <strong>{{characteristic.label}}</strong>.</p> <p><strong>{{actorName}}</strong> effectue un jet de <strong>{{characteristic.label}}</strong>.</p>
<label> <label>
<span>Mode</span> <span>{{localize "DNC.UI.Mode"}}</span>
<select name="mode"> <select name="mode">
<option value="normal">Normal</option> <option value="normal">{{localize "DNC.UI.ModeNormal"}}</option>
<option value="avantage">Avantage</option> <option value="avantage">{{localize "DNC.UI.ModeAdvantage"}}</option>
<option value="desavantage">Desavantage</option> <option value="desavantage">{{localize "DNC.UI.ModeDisadvantage"}}</option>
</select> </select>
</label> </label>
{{#if hasFavorOptions}} {{#if hasFavorOptions}}
<label> <label>
<span>Coup de pouce</span> <span>{{localize "DNC.UI.FavorBoost"}}</span>
<select name="favorDepartment"> <select name="favorDepartment">
<option value="">Aucune faveur</option> <option value="">{{localize "DNC.UI.NoFavor"}}</option>
{{selectOptions favorOptions selected="" labelAttr="label" valueAttr="value" localize=false}} {{selectOptions favorOptions selected="" labelAttr="label" valueAttr="value" localize=false}}
</select> </select>
</label> </label>
+8 -6
View File
@@ -1,12 +1,14 @@
<div class="dnc-dialog-form"> <div class="dnc-dialog-form">
<p><strong>{{item.name}}</strong> inflige <strong>{{item.system.degats}}</strong>.</p> <p><strong>{{item.name}}</strong> inflige <strong>{{damageFormula}}</strong>.</p>
<p>Bonus de degats de l'acteur : <strong>{{actorBonus}}</strong></p> {{#if damageCapped}}
<p>{{localize "DNC.Dialog.DamageCappedByDv" dv=martialDvLabel damage=damageFormula base=damageBase}}</p>
{{/if}}
<label> <label>
<span>Mode</span> <span>{{localize "DNC.UI.Mode"}}</span>
<select name="mode"> <select name="mode">
<option value="normal">Normal</option> <option value="normal" {{#if (eq defaultMode "normal")}}selected{{/if}}>{{localize "DNC.UI.ModeNormal"}}</option>
<option value="avantage">Avantage</option> <option value="avantage" {{#if (eq defaultMode "avantage")}}selected{{/if}}>{{localize "DNC.UI.ModeAdvantage"}}</option>
<option value="desavantage">Desavantage</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>
+6 -6
View File
@@ -1,12 +1,12 @@
<div class="dnc-dialog-form"> <div class="dnc-dialog-form">
<p><strong>{{actorName}}</strong> lance l'initiative.</p> <p>{{localize "DNC.Dialog.InitiativeIntro" actorName=actorName}}</p>
<p>DEX actuelle : <strong>{{dex}}</strong>, bonus de fiche : <strong>{{initiativeBonus}}</strong></p> <p>{{localize "DNC.Dialog.InitiativeCurrent" dex=dex initiativeBonus=initiativeBonus}}</p>
<label> <label>
<span>Mode</span> <span>{{localize "DNC.UI.Mode"}}</span>
<select name="mode"> <select name="mode">
<option value="normal">Normal</option> <option value="normal">{{localize "DNC.UI.ModeNormal"}}</option>
<option value="avantage">Avantage</option> <option value="avantage">{{localize "DNC.UI.ModeAdvantage"}}</option>
<option value="desavantage">Desavantage</option> <option value="desavantage">{{localize "DNC.UI.ModeDisadvantage"}}</option>
</select> </select>
</label> </label>
</div> </div>
@@ -0,0 +1,54 @@
<div class="dnc-dialog-form dnc-mission-pack-campaign">
<header class="dnc-mission-pack-hero">
<p class="dnc-mission-pack-kicker">{{localize "DNC.Chat.Kicker.Logistics"}}</p>
<h2>{{localize "DNC.Macro.MissionPack.CampaignDialogTitle"}}</h2>
<p class="dnc-mission-pack-subtitle">{{localize "DNC.Macro.MissionPack.CampaignDialogSubtitle"}}</p>
<p class="dnc-mission-pack-intro">{{localize "DNC.Macro.MissionPack.CampaignDialogIntro" actor=actorName}}</p>
</header>
<section class="dnc-mission-pack-meta-grid">
<article class="dnc-mission-pack-meta-card">
<span>{{localize "DNC.Macro.MissionPack.DialogActor"}}</span>
<strong>{{actorName}}</strong>
</article>
<article class="dnc-mission-pack-meta-card">
<span>{{localize "DNC.Macro.MissionPack.CampaignDialogPlayer"}}</span>
<strong>{{playerName}}</strong>
</article>
{{#if requesterName}}
<article class="dnc-mission-pack-meta-card">
<span>{{localize "DNC.Macro.MissionPack.CampaignDialogRequester"}}</span>
<strong>{{requesterName}}</strong>
</article>
{{/if}}
<article class="dnc-mission-pack-meta-card">
<span>{{localize "DNC.Macro.MissionPack.CampaignDialogRank"}}</span>
<strong>+{{rank}}</strong>
</article>
</section>
<section class="dnc-mission-pack-section">
<label class="dnc-mission-pack-select">
<span>{{localize "DNC.Macro.MissionPack.CampaignDialogRelation"}}</span>
<select name="stewardRelation">
{{selectOptions relationOptions selected=selectedRelation labelAttr="label" valueAttr="value" localize=false}}
</select>
</label>
</section>
<section class="dnc-mission-pack-section">
<p class="dnc-mission-pack-note">{{localize "DNC.Macro.MissionPack.CampaignDialogAssignHelp"}}</p>
<div class="dnc-mission-pack-assignments">
{{#each assignments}}
<label class="dnc-mission-pack-assignment">
<span>{{this.label}}</span>
<select name="{{this.fieldName}}">
{{selectOptions ../diceOptions selected=this.selectedDie labelAttr="label" valueAttr="value" localize=false}}
</select>
</label>
{{/each}}
</div>
</section>
<p class="dnc-mission-pack-note dnc-mission-pack-note-foot">{{localize "DNC.Macro.MissionPack.CampaignDialogHelp"}}</p>
</div>
+14
View File
@@ -0,0 +1,14 @@
<div class="dnc-dialog-form">
<p>{{localize "DNC.Macro.MissionPack.DialogIntro"}}</p>
<p class="dnc-mission-pack-mode"><strong>{{localize "DNC.Macro.MissionPack.ActiveMode"}}</strong> {{modeLabel}}</p>
<p>{{modeDescription}}</p>
<label>
<span>{{localize "DNC.Macro.MissionPack.DialogActor"}}</span>
<select name="actorId">
{{selectOptions actorOptions selected=selectedActorId labelAttr="label" valueAttr="value" localize=false}}
</select>
</label>
{{#if isCampaign}}
<p class="dnc-mission-pack-note">{{localize "DNC.Macro.MissionPack.CampaignDialogLead"}}</p>
{{/if}}
</div>
+13 -13
View File
@@ -1,47 +1,47 @@
<div class="dnc-dialog-form"> <div class="dnc-dialog-form">
<p><strong>{{actorName}}</strong> lance <strong>{{item.name}}</strong>.</p> <p><strong>{{actorName}}</strong> lance <strong>{{item.name}}</strong>.</p>
<label> <label>
<span>Caracteristique</span> <span>{{localize "DNC.UI.Characteristic"}}</span>
<input type="text" value="{{characteristic.label}} ({{characteristic.value}})" readonly> <input type="text" value="{{characteristic.label}} ({{characteristic.value}})" readonly>
</label> </label>
<label> <label>
<span>Cout</span> <span>{{localize "DNC.UI.Cost"}}</span>
<input type="text" value="{{item.system.coutPv}} PV" readonly> <input type="text" value="{{item.system.coutPv}} PV" readonly>
</label> </label>
<label> <label>
<span>PV actuels</span> <span>{{localize "DNC.UI.CurrentHp"}}</span>
<input type="text" value="{{currentPv}}" readonly> <input type="text" value="{{currentPv}}" readonly>
</label> </label>
<label> <label>
<span>Rang</span> <span>{{localize "DNC.UI.Rank"}}</span>
<input type="text" value="{{rank}}" readonly> <input type="text" value="{{rank}}" readonly>
</label> </label>
<label> <label>
<span>Focus</span> <span>{{localize "DNC.UI.Focus"}}</span>
<input type="text" value="{{#if focusIsActive}}{{focusDisplay}}{{else}}{{focusLabel}}{{/if}}" readonly> <input type="text" value="{{#if focusIsActive}}{{focusDisplay}}{{else}}{{focusLabel}}{{/if}}" readonly>
</label> </label>
<label> <label>
<span>Chaos</span> <span>{{localize "DNC.UI.Chaos"}}</span>
<input type="text" value="{{chaosLabel}}" readonly> <input type="text" value="{{chaosLabel}}" readonly>
</label> </label>
<label> <label>
<span>Mode</span> <span>{{localize "DNC.UI.Mode"}}</span>
<select name="mode"> <select name="mode">
<option value="normal">Normal</option> <option value="normal">{{localize "DNC.UI.ModeNormal"}}</option>
<option value="avantage">Avantage</option> <option value="avantage">{{localize "DNC.UI.ModeAdvantage"}}</option>
<option value="desavantage">Desavantage</option> <option value="desavantage">{{localize "DNC.UI.ModeDisadvantage"}}</option>
</select> </select>
</label> </label>
{{#if hasFavorOptions}} {{#if hasFavorOptions}}
<label> <label>
<span>Coup de pouce</span> <span>{{localize "DNC.UI.FavorBoost"}}</span>
<select name="favorDepartment"> <select name="favorDepartment">
<option value="">Aucune faveur</option> <option value="">{{localize "DNC.UI.NoFavor"}}</option>
{{selectOptions favorOptions selected="" labelAttr="label" valueAttr="value" localize=false}} {{selectOptions favorOptions selected="" labelAttr="label" valueAttr="value" localize=false}}
</select> </select>
</label> </label>
{{/if}} {{/if}}
{{#if autoDisadvantage}} {{#if autoDisadvantage}}
<p class="dialog-help warning">Le cout depasse le rang du lanceur : le jet se fera automatiquement avec desavantage.</p> <p class="dialog-help warning">{{localize "DNC.Dialog.SpellAutoDisadvantage"}}</p>
{{/if}} {{/if}}
</div> </div>
+6 -6
View File
@@ -1,12 +1,12 @@
<div class="dnc-dialog-form"> <div class="dnc-dialog-form">
<p>Utiliser <strong>{{item.name}}</strong> et lancer son de d'usage actuel.</p> <p>{{localize "DNC.Dialog.UseUsageDie" itemName=item.name}}</p>
<p>Valeur actuelle : <strong>Δ{{item.system.delta}}</strong></p> <p>{{localize "DNC.Dialog.CurrentValue"}} : <strong>Δ{{item.system.delta}}</strong></p>
<label> <label>
<span>Mode</span> <span>{{localize "DNC.UI.Mode"}}</span>
<select name="mode"> <select name="mode">
<option value="normal">Normal</option> <option value="normal">{{localize "DNC.UI.ModeNormal"}}</option>
<option value="avantage">Avantage</option> <option value="avantage">{{localize "DNC.UI.ModeAdvantage"}}</option>
<option value="desavantage">Desavantage</option> <option value="desavantage">{{localize "DNC.UI.ModeDisadvantage"}}</option>
</select> </select>
</label> </label>
</div> </div>
+7 -7
View File
@@ -1,19 +1,19 @@
<div class="dnc-dialog-form"> <div class="dnc-dialog-form">
<p><strong>{{actorName}}</strong> attaque avec <strong>{{item.name}}</strong>.</p> <p><strong>{{actorName}}</strong> attaque avec <strong>{{item.name}}</strong>.</p>
<p>Caracteristique utilisee : <strong>{{characteristicLabel}}</strong> (<strong>{{characteristicValue}}</strong>).</p> <p>{{localize "DNC.Dialog.CharacteristicUsed"}} : <strong>{{characteristicLabel}}</strong> (<strong>{{characteristicValue}}</strong>).</p>
<label> <label>
<span>Mode</span> <span>{{localize "DNC.UI.Mode"}}</span>
<select name="mode"> <select name="mode">
<option value="normal">Normal</option> <option value="normal">{{localize "DNC.UI.ModeNormal"}}</option>
<option value="avantage">Avantage</option> <option value="avantage">{{localize "DNC.UI.ModeAdvantage"}}</option>
<option value="desavantage">Desavantage</option> <option value="desavantage">{{localize "DNC.UI.ModeDisadvantage"}}</option>
</select> </select>
</label> </label>
{{#if hasFavorOptions}} {{#if hasFavorOptions}}
<label> <label>
<span>Coup de pouce</span> <span>{{localize "DNC.UI.FavorBoost"}}</span>
<select name="favorDepartment"> <select name="favorDepartment">
<option value="">Aucune faveur</option> <option value="">{{localize "DNC.UI.NoFavor"}}</option>
{{selectOptions favorOptions selected="" labelAttr="label" valueAttr="value" localize=false}} {{selectOptions favorOptions selected="" labelAttr="label" valueAttr="value" localize=false}}
</select> </select>
</label> </label>
+34 -1
View File
@@ -18,10 +18,25 @@
<span>Caracteristique</span> <span>Caracteristique</span>
<p>{{weaponCharacteristicLabel}}</p> <p>{{weaponCharacteristicLabel}}</p>
</div> </div>
<label>
<span>Degats usage die (Δ)</span>
<input type="checkbox" name="system.degatsEstUsageDe" {{checked system.degatsEstUsageDe}}>
</label>
{{#if system.degatsEstUsageDe}}
<label>
<span>Δ Degats courant</span>
<select name="system.degatsDelta">{{selectOptions config.usageDieOptions selected=system.degatsDelta localize=false blank="Epuise"}}</select>
</label>
{{else}}
<label> <label>
<span>Degats</span> <span>Degats</span>
<input type="text" name="system.degats" value="{{system.degats}}"> <input type="text" name="system.degats" value="{{system.degats}}">
</label> </label>
{{/if}}
<label>
<span>Munitions</span>
<select name="system.munitionsDelta">{{selectOptions config.usageDieOptions selected=system.munitionsDelta localize=false blank="Aucune"}}</select>
</label>
<label> <label>
<span>Portee</span> <span>Portee</span>
<input type="text" name="system.portee" value="{{system.portee}}"> <input type="text" name="system.portee" value="{{system.portee}}">
@@ -53,7 +68,7 @@
<input type="number" name="system.quantite" value="{{system.quantite}}"> <input type="number" name="system.quantite" value="{{system.quantite}}">
</label> </label>
<label> <label>
<span>Delta</span> <span>De d'usure</span>
<select name="system.delta">{{selectOptions config.usageDieOptions selected=system.delta localize=false}}</select> <select name="system.delta">{{selectOptions config.usageDieOptions selected=system.delta localize=false}}</select>
</label> </label>
<label class="span-two"> <label class="span-two">
@@ -73,6 +88,21 @@
</label> </label>
{{/if}} {{/if}}
{{#if isTraining}}
<label>
<span>De d'usure courant</span>
<select name="system.delta">{{selectOptions config.usageDieOptions selected=system.delta localize=false blank="Epuise"}}</select>
</label>
<label>
<span>De d'usure max</span>
<select name="system.deltaMax">{{selectOptions config.usageDieOptions selected=system.deltaMax localize=false}}</select>
</label>
<label class="span-two">
<span>Effet</span>
<input type="text" name="system.effet" value="{{system.effet}}">
</label>
{{/if}}
{{#if isSpell}} {{#if isSpell}}
<label> <label>
<span>Caracteristique</span> <span>Caracteristique</span>
@@ -144,6 +174,9 @@
<footer class="sheet-footer"> <footer class="sheet-footer">
<button type="button" data-action="postItem" aria-label="Poster dans le chat"><i class="fa-solid fa-message"></i> Poster dans le chat</button> <button type="button" data-action="postItem" aria-label="Poster dans le chat"><i class="fa-solid fa-message"></i> Poster dans le chat</button>
<button type="button" data-action="rollItem" aria-label="Utiliser ou lancer l'objet"><i class="fa-solid fa-dice-d20"></i> Utiliser / lancer</button> <button type="button" data-action="rollItem" aria-label="Utiliser ou lancer l'objet"><i class="fa-solid fa-dice-d20"></i> Utiliser / lancer</button>
{{#if canResetUsage}}
<button type="button" data-action="resetUsageItem" aria-label="Reinitialiser l'usage"><i class="fa-solid fa-arrow-rotate-left"></i> Reinitialiser</button>
{{/if}}
{{#if canRollDamage}} {{#if canRollDamage}}
<button type="button" data-action="rollDamageItem" aria-label="Lancer les degats"><i class="fa-solid fa-burst"></i> Lancer les degats</button> <button type="button" data-action="rollDamageItem" aria-label="Lancer les degats"><i class="fa-solid fa-burst"></i> Lancer les degats</button>
{{/if}} {{/if}}
+51
View File
@@ -0,0 +1,51 @@
import { compilePack, extractPack } from "@foundryvtt/foundryvtt-cli";
import { promises as fs } from "node:fs";
import path from "node:path";
const ROOT_DIR = process.cwd();
export class CompendiumsManager {
static async packToDistDir(srcDir = "packs_src", distDir = "packs", mode = "json") {
const yaml = mode === "yaml";
const packs = await fs.readdir(path.join(ROOT_DIR, srcDir));
for (const pack of packs) {
if (pack.startsWith(".")) continue;
const sourcePath = path.join(ROOT_DIR, srcDir, pack);
const targetPath = path.join(ROOT_DIR, distDir, pack);
await fs.rm(targetPath, { recursive: true, force: true });
console.log(`Packing ${pack}`);
await compilePack(sourcePath, targetPath, { yaml });
}
}
static async unpackToSrcDir(srcDir = "packs_src", distDir = "packs", mode = "json") {
const yaml = mode === "yaml";
const packs = await fs.readdir(path.join(ROOT_DIR, distDir));
for (const pack of packs) {
if (pack.startsWith(".")) continue;
const sourcePath = path.join(ROOT_DIR, distDir, pack);
const targetPath = path.join(ROOT_DIR, srcDir, pack);
await fs.mkdir(targetPath, { recursive: true });
const existingFiles = await fs.readdir(targetPath);
for (const file of existingFiles) {
await fs.unlink(path.join(targetPath, file));
}
console.log(`Unpacking ${pack}`);
await extractPack(sourcePath, targetPath, {
yaml,
transformName: (doc) => CompendiumsManager.transformName(doc, yaml)
});
}
}
static transformName(doc, yaml) {
const safeName = (doc.name ?? doc._id ?? "document").replace(/[^a-zA-Z0-9_-]+/g, "_");
const type = doc._key?.split("!")[1] ?? doc.type ?? "document";
const extension = yaml ? "yml" : "json";
return `${type}_${safeName}_${doc._id}.${extension}`;
}
}
+3
View File
@@ -0,0 +1,3 @@
import { CompendiumsManager } from "./CompendiumsManager.mjs";
await CompendiumsManager.packToDistDir();
+3
View File
@@ -0,0 +1,3 @@
import { CompendiumsManager } from "./CompendiumsManager.mjs";
await CompendiumsManager.unpackToSrcDir();