diff --git a/src/assets/icons/game-icons/alarm-clock.svg b/src/assets/icons/game-icons/alarm-clock.svg
new file mode 100644
index 0000000..26a4024
--- /dev/null
+++ b/src/assets/icons/game-icons/alarm-clock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/awareness.svg b/src/assets/icons/game-icons/awareness.svg
new file mode 100644
index 0000000..e39fcf0
--- /dev/null
+++ b/src/assets/icons/game-icons/awareness.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/biceps.svg b/src/assets/icons/game-icons/biceps.svg
new file mode 100644
index 0000000..18c0c44
--- /dev/null
+++ b/src/assets/icons/game-icons/biceps.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/bookshelf.svg b/src/assets/icons/game-icons/bookshelf.svg
new file mode 100644
index 0000000..3e0cfc1
--- /dev/null
+++ b/src/assets/icons/game-icons/bookshelf.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/campfire.svg b/src/assets/icons/game-icons/campfire.svg
new file mode 100644
index 0000000..2edc022
--- /dev/null
+++ b/src/assets/icons/game-icons/campfire.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/card-exchange.svg b/src/assets/icons/game-icons/card-exchange.svg
new file mode 100644
index 0000000..9de4a14
--- /dev/null
+++ b/src/assets/icons/game-icons/card-exchange.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/cash.svg b/src/assets/icons/game-icons/cash.svg
new file mode 100644
index 0000000..883cb3e
--- /dev/null
+++ b/src/assets/icons/game-icons/cash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/cavalry.svg b/src/assets/icons/game-icons/cavalry.svg
new file mode 100644
index 0000000..6c578bf
--- /dev/null
+++ b/src/assets/icons/game-icons/cavalry.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/charm.svg b/src/assets/icons/game-icons/charm.svg
new file mode 100644
index 0000000..2e7a4d9
--- /dev/null
+++ b/src/assets/icons/game-icons/charm.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/deer-track.svg b/src/assets/icons/game-icons/deer-track.svg
new file mode 100644
index 0000000..c1c55ba
--- /dev/null
+++ b/src/assets/icons/game-icons/deer-track.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/discussion.svg b/src/assets/icons/game-icons/discussion.svg
new file mode 100644
index 0000000..99c39fd
--- /dev/null
+++ b/src/assets/icons/game-icons/discussion.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/hidden.svg b/src/assets/icons/game-icons/hidden.svg
new file mode 100644
index 0000000..65007db
--- /dev/null
+++ b/src/assets/icons/game-icons/hidden.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/jump-across.svg b/src/assets/icons/game-icons/jump-across.svg
new file mode 100644
index 0000000..d80da97
--- /dev/null
+++ b/src/assets/icons/game-icons/jump-across.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/lever.svg b/src/assets/icons/game-icons/lever.svg
new file mode 100644
index 0000000..9b1eb17
--- /dev/null
+++ b/src/assets/icons/game-icons/lever.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/magnifying-glass.svg b/src/assets/icons/game-icons/magnifying-glass.svg
new file mode 100644
index 0000000..140fdbf
--- /dev/null
+++ b/src/assets/icons/game-icons/magnifying-glass.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/mountain-climbing.svg b/src/assets/icons/game-icons/mountain-climbing.svg
new file mode 100644
index 0000000..71a7565
--- /dev/null
+++ b/src/assets/icons/game-icons/mountain-climbing.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/mute.svg b/src/assets/icons/game-icons/mute.svg
new file mode 100644
index 0000000..21bbb11
--- /dev/null
+++ b/src/assets/icons/game-icons/mute.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/padlock-open.svg b/src/assets/icons/game-icons/padlock-open.svg
new file mode 100644
index 0000000..af907af
--- /dev/null
+++ b/src/assets/icons/game-icons/padlock-open.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/poison-bottle.svg b/src/assets/icons/game-icons/poison-bottle.svg
new file mode 100644
index 0000000..a350ee7
--- /dev/null
+++ b/src/assets/icons/game-icons/poison-bottle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/pool-dive.svg b/src/assets/icons/game-icons/pool-dive.svg
new file mode 100644
index 0000000..83a4607
--- /dev/null
+++ b/src/assets/icons/game-icons/pool-dive.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/robber-hand.svg b/src/assets/icons/game-icons/robber-hand.svg
new file mode 100644
index 0000000..0181390
--- /dev/null
+++ b/src/assets/icons/game-icons/robber-hand.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/rune-stone.svg b/src/assets/icons/game-icons/rune-stone.svg
new file mode 100644
index 0000000..8508581
--- /dev/null
+++ b/src/assets/icons/game-icons/rune-stone.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/shield.svg b/src/assets/icons/game-icons/shield.svg
new file mode 100644
index 0000000..595de04
--- /dev/null
+++ b/src/assets/icons/game-icons/shield.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/sparkles.svg b/src/assets/icons/game-icons/sparkles.svg
new file mode 100644
index 0000000..7260362
--- /dev/null
+++ b/src/assets/icons/game-icons/sparkles.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/two-coins.svg b/src/assets/icons/game-icons/two-coins.svg
new file mode 100644
index 0000000..28f0bb7
--- /dev/null
+++ b/src/assets/icons/game-icons/two-coins.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/uncertainty.svg b/src/assets/icons/game-icons/uncertainty.svg
new file mode 100644
index 0000000..5b0625b
--- /dev/null
+++ b/src/assets/icons/game-icons/uncertainty.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/virus.svg b/src/assets/icons/game-icons/virus.svg
new file mode 100644
index 0000000..1ef38f4
--- /dev/null
+++ b/src/assets/icons/game-icons/virus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/game-icons/wolf-trap.svg b/src/assets/icons/game-icons/wolf-trap.svg
new file mode 100644
index 0000000..a2c545f
--- /dev/null
+++ b/src/assets/icons/game-icons/wolf-trap.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/lang/de.json b/src/lang/de.json
index 91f8a7e..2e98807 100644
--- a/src/lang/de.json
+++ b/src/lang/de.json
@@ -9,6 +9,7 @@
"DS4.RollableImageRollableTitle": "Für {name} würfeln",
"DS4.DiceOverlayImageAltText": "Bild eines W20",
"DS4.NotOwned": "Nicht besessen",
+ "DS4.HeadingValues": "Werte",
"DS4.HeadingBiography": "Biografie",
"DS4.HeadingDetails": "Details",
"DS4.HeadingEffects": "Effekte",
@@ -102,7 +103,7 @@
"DS4.SpellCategoryIce": "Eis",
"DS4.SpellCategoryLight": "Licht",
"DS4.SpellCategoryDarkness": "Schatten",
- "DS4.SpellCategoryMindAffecting": "Geistensbeeinflussend",
+ "DS4.SpellCategoryMindAffecting": "Geistesbeeinflussend",
"DS4.SpellCategoryElectricity": "Elektrizität",
"DS4.SpellCategoryNone": "Keine",
"DS4.SpellCategoryUnset": "Nicht gesetzt",
@@ -194,15 +195,17 @@
"DS4.ErrorCannotRollUnownedItem": "Für das Item '{name}' ({id}) kann nicht gewürfelt werden, da es keinem Aktor gehört.",
"DS4.ErrorRollingForItemTypeNotPossible": "Würfeln ist für Items vom Typ '{type}' nicht möglich.",
"DS4.ErrorWrongItemType": "Ein Item vom Type '{expectedType}' wurde erwartet aber das Item '{name}' ({id}) ist vom Typ '{actualType}'.",
- "DS4.ErrorUnexpectedAttackType": "Unerwartete Angriffsart '{actualType}', erwartete Angriffarten: {expectedTypes}",
+ "DS4.ErrorUnexpectedAttackType": "Unerwartete Angriffsart '{actualType}', erwartete Angriffsarten: {expectedTypes}",
"DS4.ErrorCanvasIsNotInitialized": "Canvas ist noch nicht initialisiert.",
+ "DS4.ErrorCannotDragMissingCheck": "Die Probe '{check}' per Drag & Drop zu ziehen ist nicht möglich, denn sie existiert nicht.",
"DS4.WarningItemMustBeEquippedToBeRolled": "Um für das Item '{name}' ({id}) vom Typ '{type}' zu würfeln, muss es ausgerüstet sein.",
"DS4.WarningMustControlActorToUseRollItemMacro": "Um ein Item-Würfel-Makro zu nutzen muss ein Aktor kontrolliert werden.",
"DS4.WarningMustControlActorToUseRollCheckMacro": "Um ein Proben-Würfel-Makro zu nutzen muss ein Aktor kontrolliert werden.",
"DS4.WarningControlledActorDoesNotHaveItem": "Der kontrollierte Aktor '{actorName}' ({actorId}) hat kein Item mit der ID '{itemId}'.",
"DS4.WarningItemIsNotRollable": "Für das Item '{name}' ({id}) vom Typ '{type}' kann nicht gewürfelt werden.",
"DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems": "Makros können nur für besessene Items angelegt werden.",
- "DS4.InfoManuallyEnterSpellBonus": "Der korrekte Wert für den Zauberbonus '{spellBonus}' des Zaubers '{name}' musss manuell angegeben werden.",
+ "DS4.WarningInvalidCheckDropped": "Eine ungültige Probe wurde auf die Hotbar gezogen.",
+ "DS4.InfoManuallyEnterSpellBonus": "Der korrekte Wert für den Zauberbonus '{spellBonus}' des Zaubers '{name}' muss manuell angegeben werden.",
"DS4.InfoSystemUpdateStart": "Aktualisiere DS4 System von Migrationsversion {currentVersion} auf {targetVersion}. Bitte haben Sie etwas Geduld, schließen Sie nicht das Spiel und fahren Sie nicht den Server herunter.",
"DS4.InfoSystemUpdateCompleted": "Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion} erfolgreich!",
"DS4.UnitRounds": "Runden",
@@ -254,16 +257,17 @@
"DS4.ChecksKnowledge": "Wissen",
"DS4.ChecksOpenLock": "Schlösser Öffnen",
"DS4.ChecksPerception": "Bemerken",
- "DS4.ChecksPickPocket": "Tschendiebstahl",
+ "DS4.ChecksPickPocket": "Taschendiebstahl",
"DS4.ChecksReadTracks": "Spuren Lesen",
"DS4.ChecksResistDisease": "Krankheit Trotzen",
"DS4.ChecksRide": "Reiten",
"DS4.ChecksSearch": "Suchen",
"DS4.ChecksSenseMagic": "Magie Erspüren",
- "DS4.ChecksSneak": "Sneak",
+ "DS4.ChecksSneak": "Schleichen",
"DS4.ChecksStartFire": "Feuer Machen",
"DS4.ChecksSwim": "Schwimmen",
"DS4.ChecksWakeUp": "Erwachen",
"DS4.ChecksWorkMechanism": "Mechanismus Öffnen",
- "DS4.ActorCheckFlavor": "{actor} würfelt eine {check} Probe."
+ "DS4.ActorCheckFlavor": "{actor} würfelt eine {check} Probe.",
+ "DS4.CheckTooltip": "{check} Probe würfeln"
}
diff --git a/src/lang/en.json b/src/lang/en.json
index 26d950c..e19f88e 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -9,6 +9,7 @@
"DS4.RollableImageRollableTitle": "Roll for {name}",
"DS4.DiceOverlayImageAltText": "Image of a d20",
"DS4.NotOwned": "No owner",
+ "DS4.HeadingValues": "Values",
"DS4.HeadingBiography": "Biography",
"DS4.HeadingDetails": "Details",
"DS4.HeadingEffects": "Effects",
@@ -196,12 +197,14 @@
"DS4.ErrorWrongItemType": "Expected an item of type '{expectedType}' but item '{name}' ({id}) is of type '{actualType}'.",
"DS4.ErrorUnexpectedAttackType": "Unexpected attack type '{actualType}', expected it to be one of: {expectedTypes}",
"DS4.ErrorCanvasIsNotInitialized": "Canvas is not initialized yet.",
+ "DS4.ErrorCannotDragMissingCheck": "Trying to drag the check '{check}' but no such check exists.",
"DS4.WarningItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.",
"DS4.WarningMustControlActorToUseRollItemMacro": "You must control an actor to be able to use a roll item macro.",
"DS4.WarningMustControlActorToUseRollCheckMacro": "You must control an actor to be able to use a roll check macro.",
"DS4.WarningControlledActorDoesNotHaveItem": "Your controlled actor '{actorName}' ({actorId}) does not have any item with the id '{itemId}'.",
"DS4.WarningItemIsNotRollable": "Item '{name}' ({id}) of type '{type}' is not rollable.",
"DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems": "Macros can only be created for owned items.",
+ "DS4.WarningInvalidCheckDropped": "An invalid check was dropped on the Hotbar.",
"DS4.InfoManuallyEnterSpellBonus": "The correct value of the spell bonus '{spellBonus}' of the spell '{name}' needs to be entered by manually.",
"DS4.InfoSystemUpdateStart": "Migrating DS4 system from migration version {currentVersion} to {targetVersion}. Please be patient and do not close your game or shut down your server.",
"DS4.InfoSystemUpdateCompleted": "Migration of DS4 system from migration version {currentVersion} to {targetVersion} successful!",
@@ -265,5 +268,6 @@
"DS4.ChecksSwim": "Swim",
"DS4.ChecksWakeUp": "Wake Up",
"DS4.ChecksWorkMechanism": "Work Mechanism",
- "DS4.ActorCheckFlavor": "{actor} rolls a {check} check."
+ "DS4.ActorCheckFlavor": "{actor} rolls a {check} check.",
+ "DS4.CheckTooltip": "Roll a {check} check"
}
diff --git a/src/module/actor/actor-prepared-data.ts b/src/module/actor/actor-prepared-data.ts
index 85080da..ab23163 100644
--- a/src/module/actor/actor-prepared-data.ts
+++ b/src/module/actor/actor-prepared-data.ts
@@ -58,6 +58,10 @@ interface DS4ActorPreparedDataDataRolling {
export type Check = keyof typeof DS4.i18n.checks;
+export function isCheck(value: string): value is Check {
+ return Object.keys(DS4.i18n.checks).includes(value);
+}
+
type DS4ActorPreparedDataDataChecks = {
[key in Check]: number;
};
diff --git a/src/module/actor/sheets/actor-sheet.ts b/src/module/actor/sheets/actor-sheet.ts
index dd8dfd4..34a8d77 100644
--- a/src/module/actor/sheets/actor-sheet.ts
+++ b/src/module/actor/sheets/actor-sheet.ts
@@ -1,9 +1,11 @@
import { ModifiableDataBaseTotal } from "../../common/common-data";
import { DS4 } from "../../config";
+import { getCanvas } from "../../helpers";
import { DS4Item } from "../../item/item";
import { DS4ItemData } from "../../item/item-data";
import notifications from "../../ui/notifications";
import { DS4Actor } from "../actor";
+import { isCheck } from "../actor-prepared-data";
/**
* The base Sheet class for all DS4 Actors
@@ -14,10 +16,11 @@ export class DS4ActorSheet extends ActorSheet> {
static get defaultOptions(): BaseEntitySheet.Options {
const superDefaultOptions = super.defaultOptions;
return mergeObject(superDefaultOptions, {
+ ...superDefaultOptions,
classes: ["ds4", "sheet", "actor"],
- width: 650,
- height: 600,
+ height: 620,
scrollY: [
+ ".values",
".inventory",
".spells",
".talents-abilities",
@@ -25,30 +28,19 @@ export class DS4ActorSheet extends ActorSheet> {
".biography",
".special-creature-abilities",
],
- template: superDefaultOptions.template,
- viewPermission: superDefaultOptions.viewPermission,
- closeOnSubmit: superDefaultOptions.closeOnSubmit,
- submitOnChange: superDefaultOptions.submitOnChange,
- submitOnClose: superDefaultOptions.submitOnClose,
- editable: superDefaultOptions.editable,
- baseApplication: superDefaultOptions.baseApplication,
- top: superDefaultOptions.top,
- left: superDefaultOptions.left,
- popOut: superDefaultOptions.popOut,
- minimizable: superDefaultOptions.minimizable,
- resizable: superDefaultOptions.resizable,
- id: superDefaultOptions.id,
- dragDrop: superDefaultOptions.dragDrop,
- filters: superDefaultOptions.filters,
- title: superDefaultOptions.title,
- tabs: superDefaultOptions.tabs,
+ tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "values" }],
+ dragDrop: [
+ { dragSelector: ".item-list .item", dropSelector: null },
+ { dragSelector: ".ds4-check", dropSelector: null },
+ ],
+ width: 650,
});
}
/** @override */
get template(): string {
- const path = "systems/ds4/templates/actor";
- return `${path}/${this.actor.data.type}-sheet.hbs`;
+ const basePath = "systems/ds4/templates/sheets/actor";
+ return `${basePath}/${this.actor.data.type}-sheet.hbs`;
}
/**
@@ -123,7 +115,9 @@ export class DS4ActorSheet extends ActorSheet> {
html.find(".item-change").on("change", this._onItemChange.bind(this));
- html.find(".rollable-item").on("click", this._onRoll.bind(this));
+ html.find(".rollable-item").on("click", this._onRollItem.bind(this));
+
+ html.find(".rollable-check").on("click", this._onRollCheck.bind(this));
}
/**
@@ -232,16 +226,47 @@ export class DS4ActorSheet extends ActorSheet> {
}
/**
- * Handle clickable rolls.
+ * Handle clickable item rolls.
* @param event - The originating click event
*/
- protected _onRoll(event: JQuery.ClickEvent): void {
+ protected _onRollItem(event: JQuery.ClickEvent): void {
event.preventDefault();
const id = $(event.currentTarget).parents(".item").data("itemId");
const item = this.actor.getOwnedItem(id);
item.roll();
}
+ /**
+ * Handle clickable check rolls.
+ * @param event - The originating click event
+ */
+ protected _onRollCheck(event: JQuery.ClickEvent): void {
+ event.preventDefault();
+ const check = event.currentTarget.dataset["check"];
+ this.actor.rollCheck(check);
+ }
+
+ /** @override */
+ _onDragStart(event: DragEvent): void {
+ const target = event.currentTarget as HTMLElement;
+ if (!(target instanceof HTMLElement)) return super._onDragStart(event);
+
+ const check = target.dataset.check;
+ if (!check) return super._onDragStart(event);
+
+ if (!isCheck(check)) throw new Error(game.i18n.format("DS4.ErrorCannotDragMissingCheck", { check }));
+
+ const dragData = {
+ actorId: this.actor.id,
+ sceneId: this.actor.isToken ? getCanvas().scene?.id : null,
+ tokenId: this.actor.isToken ? this.actor.token?.id : null,
+ type: "Check",
+ data: check,
+ };
+
+ event.dataTransfer?.setData("text/plain", JSON.stringify(dragData));
+ }
+
/** @override */
protected async _onDropItem(
event: DragEvent,
diff --git a/src/module/actor/sheets/character-sheet.ts b/src/module/actor/sheets/character-sheet.ts
index 2e077ce..437de82 100644
--- a/src/module/actor/sheets/character-sheet.ts
+++ b/src/module/actor/sheets/character-sheet.ts
@@ -8,27 +8,8 @@ export class DS4CharacterActorSheet extends DS4ActorSheet {
static get defaultOptions(): BaseEntitySheet.Options {
const superDefaultOptions = super.defaultOptions;
return mergeObject(superDefaultOptions, {
+ ...superDefaultOptions,
classes: ["ds4", "sheet", "actor", "character"],
- tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "inventory" }],
- template: superDefaultOptions.template,
- viewPermission: superDefaultOptions.viewPermission,
- closeOnSubmit: superDefaultOptions.closeOnSubmit,
- submitOnChange: superDefaultOptions.submitOnChange,
- submitOnClose: superDefaultOptions.submitOnClose,
- editable: superDefaultOptions.editable,
- baseApplication: superDefaultOptions.baseApplication,
- top: superDefaultOptions.top,
- left: superDefaultOptions.left,
- popOut: superDefaultOptions.popOut,
- minimizable: superDefaultOptions.minimizable,
- resizable: superDefaultOptions.resizable,
- id: superDefaultOptions.id,
- dragDrop: superDefaultOptions.dragDrop,
- filters: superDefaultOptions.filters,
- title: superDefaultOptions.title,
- width: superDefaultOptions.width,
- height: superDefaultOptions.height,
- scrollY: superDefaultOptions.scrollY,
});
}
}
diff --git a/src/module/actor/sheets/creature-sheet.ts b/src/module/actor/sheets/creature-sheet.ts
index 91af61d..8e032e8 100644
--- a/src/module/actor/sheets/creature-sheet.ts
+++ b/src/module/actor/sheets/creature-sheet.ts
@@ -8,27 +8,8 @@ export class DS4CreatureActorSheet extends DS4ActorSheet {
static get defaultOptions(): BaseEntitySheet.Options {
const superDefaultOptions = super.defaultOptions;
return mergeObject(superDefaultOptions, {
+ ...superDefaultOptions,
classes: ["ds4", "sheet", "actor", "creature"],
- tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "inventory" }],
- template: superDefaultOptions.template,
- viewPermission: superDefaultOptions.viewPermission,
- closeOnSubmit: superDefaultOptions.closeOnSubmit,
- submitOnChange: superDefaultOptions.submitOnChange,
- submitOnClose: superDefaultOptions.submitOnClose,
- editable: superDefaultOptions.editable,
- baseApplication: superDefaultOptions.baseApplication,
- top: superDefaultOptions.top,
- left: superDefaultOptions.left,
- popOut: superDefaultOptions.popOut,
- minimizable: superDefaultOptions.minimizable,
- resizable: superDefaultOptions.resizable,
- id: superDefaultOptions.id,
- dragDrop: superDefaultOptions.dragDrop,
- filters: superDefaultOptions.filters,
- title: superDefaultOptions.title,
- width: superDefaultOptions.width,
- height: superDefaultOptions.height,
- scrollY: superDefaultOptions.scrollY,
});
}
}
diff --git a/src/module/config.ts b/src/module/config.ts
index 2b314cd..8b21668 100644
--- a/src/module/config.ts
+++ b/src/module/config.ts
@@ -334,6 +334,40 @@ export const DS4 = {
spellcasting: "systems/ds4/assets/icons/official/combat-values/spellcasting.png",
targetedSpellcasting: "systems/ds4/assets/icons/official/combat-values/targeted-spellcasting.png",
},
+
+ /**
+ * Define the file paths to check images
+ */
+ checks: {
+ appraise: "systems/ds4/assets/icons/game-icons/two-coins.svg",
+ changeSpell: "systems/ds4/assets/icons/game-icons/card-exchange.svg",
+ climb: "systems/ds4/assets/icons/game-icons/mountain-climbing.svg",
+ communicate: "systems/ds4/assets/icons/game-icons/discussion.svg",
+ decipherScript: "systems/ds4/assets/icons/game-icons/rune-stone.svg",
+ defend: "systems/ds4/assets/icons/game-icons/shield.svg",
+ defyPoison: "systems/ds4/assets/icons/game-icons/poison-bottle.svg",
+ disableTraps: "systems/ds4/assets/icons/game-icons/wolf-trap.svg",
+ featOfStrength: "systems/ds4/assets/icons/game-icons/biceps.svg",
+ flirt: "systems/ds4/assets/icons/game-icons/charm.svg",
+ haggle: "systems/ds4/assets/icons/game-icons/cash.svg",
+ hide: "systems/ds4/assets/icons/game-icons/hidden.svg",
+ identifyMagic: "systems/ds4/assets/icons/game-icons/uncertainty.svg",
+ jump: "systems/ds4/assets/icons/game-icons/jump-across.svg",
+ knowledge: "systems/ds4/assets/icons/game-icons/bookshelf.svg",
+ openLock: "systems/ds4/assets/icons/game-icons/padlock-open.svg",
+ perception: "systems/ds4/assets/icons/game-icons/awareness.svg",
+ pickPocket: "systems/ds4/assets/icons/game-icons/robber-hand.svg",
+ readTracks: "systems/ds4/assets/icons/game-icons/deer-track.svg",
+ resistDisease: "systems/ds4/assets/icons/game-icons/virus.svg",
+ ride: "systems/ds4/assets/icons/game-icons/cavalry.svg",
+ search: "systems/ds4/assets/icons/game-icons/magnifying-glass.svg",
+ senseMagic: "systems/ds4/assets/icons/game-icons/sparkles.svg",
+ sneak: "systems/ds4/assets/icons/game-icons/mute.svg",
+ startFire: "systems/ds4/assets/icons/game-icons/campfire.svg",
+ swim: "systems/ds4/assets/icons/game-icons/pool-dive.svg",
+ wakeUp: "systems/ds4/assets/icons/game-icons/alarm-clock.svg",
+ workMechanism: "systems/ds4/assets/icons/game-icons/lever.svg",
+ },
},
/**
diff --git a/src/module/handlebars/handlebars-partials.ts b/src/module/handlebars/handlebars-partials.ts
index 81945eb..64a5f5a 100644
--- a/src/module/handlebars/handlebars-partials.ts
+++ b/src/module/handlebars/handlebars-partials.ts
@@ -1,27 +1,34 @@
export default async function registerHandlebarsPartials(): Promise {
const templatePaths = [
- "systems/ds4/templates/item/partials/sheet-header.hbs",
- "systems/ds4/templates/item/partials/description.hbs",
- "systems/ds4/templates/item/partials/details.hbs",
- "systems/ds4/templates/item/partials/effects.hbs",
- "systems/ds4/templates/item/partials/body.hbs",
- "systems/ds4/templates/actor/partials/items-overview.hbs",
- "systems/ds4/templates/actor/partials/talents-abilities-overview.hbs",
- "systems/ds4/templates/actor/partials/spells-overview.hbs",
- "systems/ds4/templates/actor/partials/overview-add-button.hbs",
- "systems/ds4/templates/actor/partials/overview-control-buttons.hbs",
- "systems/ds4/templates/actor/partials/attributes-traits.hbs",
- "systems/ds4/templates/actor/partials/combat-values.hbs",
- "systems/ds4/templates/actor/partials/profile.hbs",
- "systems/ds4/templates/actor/partials/character-progression.hbs",
- "systems/ds4/templates/actor/partials/special-creature-abilities-overview.hbs",
- "systems/ds4/templates/actor/partials/character-inventory.hbs",
- "systems/ds4/templates/actor/partials/creature-inventory.hbs",
- "systems/ds4/templates/actor/partials/talent-rank-equation.hbs",
- "systems/ds4/templates/actor/partials/item-list-header.hbs",
- "systems/ds4/templates/actor/partials/item-list-entry.hbs",
- "systems/ds4/templates/actor/partials/currency.hbs",
- "systems/ds4/templates/common/partials/rollable-image.hbs",
+ "systems/ds4/templates/sheets/actor/components/character-progression.hbs",
+ "systems/ds4/templates/sheets/actor/components/check.hbs",
+ "systems/ds4/templates/sheets/actor/components/checks.hbs",
+ "systems/ds4/templates/sheets/actor/components/combat-value.hbs",
+ "systems/ds4/templates/sheets/actor/components/combat-values.hbs",
+ "systems/ds4/templates/sheets/actor/components/core-value.hbs",
+ "systems/ds4/templates/sheets/actor/components/core-values.hbs",
+ "systems/ds4/templates/sheets/actor/components/currency.hbs",
+ "systems/ds4/templates/sheets/actor/components/item-list-entry.hbs",
+ "systems/ds4/templates/sheets/actor/components/item-list-header.hbs",
+ "systems/ds4/templates/sheets/actor/components/items-overview.hbs",
+ "systems/ds4/templates/sheets/actor/components/overview-add-button.hbs",
+ "systems/ds4/templates/sheets/actor/components/overview-control-buttons.hbs",
+ "systems/ds4/templates/sheets/actor/components/rollable-image.hbs",
+ "systems/ds4/templates/sheets/actor/components/talent-rank-equation.hbs",
+ "systems/ds4/templates/sheets/actor/tabs/biography.hbs",
+ "systems/ds4/templates/sheets/actor/tabs/character-inventory.hbs",
+ "systems/ds4/templates/sheets/actor/tabs/creature-inventory.hbs",
+ "systems/ds4/templates/sheets/actor/tabs/description.hbs",
+ "systems/ds4/templates/sheets/actor/tabs/profile.hbs",
+ "systems/ds4/templates/sheets/actor/tabs/special-creature-abilities.hbs",
+ "systems/ds4/templates/sheets/actor/tabs/spells.hbs",
+ "systems/ds4/templates/sheets/actor/tabs/talents-abilities.hbs",
+ "systems/ds4/templates/sheets/actor/tabs/values.hbs",
+ "systems/ds4/templates/sheets/item/components/body.hbs",
+ "systems/ds4/templates/sheets/item/components/sheet-header.hbs",
+ "systems/ds4/templates/sheets/item/tabs/description.hbs",
+ "systems/ds4/templates/sheets/item/tabs/details.hbs",
+ "systems/ds4/templates/sheets/item/tabs/effects.hbs",
];
await loadTemplates(templatePaths);
}
diff --git a/src/module/hooks/hotbar-drop.ts b/src/module/hooks/hotbar-drop.ts
index 8263181..725b870 100644
--- a/src/module/hooks/hotbar-drop.ts
+++ b/src/module/hooks/hotbar-drop.ts
@@ -1,5 +1,7 @@
+import { isCheck } from "../actor/actor-prepared-data";
import { DS4Item } from "../item/item";
import { DS4ItemData } from "../item/item-data";
+import { createRollCheckMacro } from "../macros/roll-check";
import { createRollItemMacro } from "../macros/roll-item";
import notifications from "../ui/notifications";
@@ -21,7 +23,13 @@ export default function registerForHotbarDropHook(): void {
}),
);
}
- await createRollItemMacro(itemData, slot);
+ return createRollItemMacro(itemData, slot);
+ }
+ case "Check": {
+ if (!("data" in data) || typeof data.data !== "string" || !isCheck(data.data)) {
+ return notifications.warn(game.i18n.localize("DS4.WarningInvalidCheckDropped"));
+ }
+ return createRollCheckMacro(data.data, slot);
}
}
});
diff --git a/src/module/item/item-sheet.ts b/src/module/item/item-sheet.ts
index 1ccbffc..2906e9d 100644
--- a/src/module/item/item-sheet.ts
+++ b/src/module/item/item-sheet.ts
@@ -37,8 +37,8 @@ export class DS4ItemSheet extends ItemSheet> {
/** @override */
get template(): string {
- const path = "systems/ds4/templates/item";
- return `${path}/${this.item.data.type}-sheet.hbs`;
+ const basePath = "systems/ds4/templates/sheets/item";
+ return `${basePath}/${this.item.data.type}-sheet.hbs`;
}
/** @override */
diff --git a/src/module/item/item.ts b/src/module/item/item.ts
index 68a1ffb..00816b7 100644
--- a/src/module/item/item.ts
+++ b/src/module/item/item.ts
@@ -155,7 +155,7 @@ export class DS4Item extends Item {
const label = game.i18n.localize("DS4.AttackType");
const answer = Dialog.prompt({
title: game.i18n.localize("DS4.AttackTypeSelection"),
- content: await renderTemplate("systems/ds4/templates/common/simple-select-form.hbs", {
+ content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", {
label,
identifier,
options: { melee, ranged },
diff --git a/src/module/macros/roll-check.ts b/src/module/macros/roll-check.ts
index e6d191b..f40ad75 100644
--- a/src/module/macros/roll-check.ts
+++ b/src/module/macros/roll-check.ts
@@ -29,7 +29,7 @@ async function getOrCreateRollCheckMacro(check: Check): Promise {
command,
name: DS4.i18n.checks[check],
type: "script",
- // TODO: img, should be addressed in https://git.f3l.de/dungeonslayers/ds4/-/issues/79
+ img: DS4.icons.checks[check],
flags: { "ds4.checkMacro": true },
},
{ displaySheet: false },
diff --git a/src/module/rolls/check-factory.ts b/src/module/rolls/check-factory.ts
index 475c50f..86b2977 100644
--- a/src/module/rolls/check-factory.ts
+++ b/src/module/rolls/check-factory.ts
@@ -106,7 +106,7 @@ async function askGmModifier(
options: Partial = {},
{ template, title }: { template?: string; title?: string } = {},
): Promise> {
- const usedTemplate = template ?? "systems/ds4/templates/roll/roll-options.hbs";
+ const usedTemplate = template ?? "systems/ds4/templates/dialogs/roll-options.hbs";
const usedTitle = title ?? game.i18n.localize("DS4.RollDialogDefaultTitle");
const templateData = {
title: usedTitle,
diff --git a/src/scss/components/_actor_sheet.scss b/src/scss/components/_actor_sheet.scss
index 55fbf56..037e31d 100644
--- a/src/scss/components/_actor_sheet.scss
+++ b/src/scss/components/_actor_sheet.scss
@@ -1,4 +1,4 @@
.ds4.sheet.actor {
min-width: 650px;
- min-height: 500px;
+ min-height: 620px;
}
diff --git a/src/scss/components/_attributes_traits.scss b/src/scss/components/_attributes_traits.scss
deleted file mode 100644
index fc29d36..0000000
--- a/src/scss/components/_attributes_traits.scss
+++ /dev/null
@@ -1,56 +0,0 @@
-@use "../utils/colors";
-@use "../utils/typography";
-@use "../utils/variables";
-
-.attributes-traits {
- margin-top: variables.$margin-sm;
- .attribute {
- .attribute-label {
- @include typography.font-heading-upper;
- font-size: 2em;
- text-align: center;
- }
- .attribute-value {
- border: variables.$border-groove;
- font-size: 1.5em;
- text-align: center;
- padding-left: 2px;
- padding-right: 2px;
- gap: 0;
- input,
- .attribute-value-total {
- grid-column: span 2;
- line-height: variables.$default-input-height;
- }
- .attribute-value-arrow {
- padding: 0 5px;
- }
- }
- }
- .trait {
- .trait-label {
- color: transparent;
- @include typography.font-heading-upper;
- font-size: 2em;
- text-align: center;
- //text-shadow: -1px 1px 0 $c-black, 1px 1px 0 $c-black, 1px -1px 0 $c-black, -1px -1px 0 $c-black;
- -webkit-text-stroke: 1px colors.$c-black;
- }
- .trait-value {
- border: variables.$border-groove;
- font-size: 1.5em;
- text-align: center;
- padding-left: 2px;
- padding-right: 2px;
- gap: 0;
- input,
- .trait-value-total {
- grid-column: span 2;
- line-height: variables.$default-input-height;
- }
- .trait-value-arrow {
- padding: 0 5px;
- }
- }
- }
-}
diff --git a/src/scss/components/_check.scss b/src/scss/components/_check.scss
new file mode 100644
index 0000000..796959d
--- /dev/null
+++ b/src/scss/components/_check.scss
@@ -0,0 +1,11 @@
+@use "../utils/mixins";
+
+.ds4-check {
+ cursor: pointer;
+ display: flex;
+ justify-content: space-between;
+
+ &:hover {
+ @include mixins.foundry-highlight-text-shadow;
+ }
+}
diff --git a/src/scss/components/_checks.scss b/src/scss/components/_checks.scss
new file mode 100644
index 0000000..9ea6750
--- /dev/null
+++ b/src/scss/components/_checks.scss
@@ -0,0 +1,7 @@
+.ds4-checks {
+ column-gap: 2em;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(11em, 1fr));
+ row-gap: 0.25em;
+ white-space: nowrap;
+}
diff --git a/src/scss/components/_combat_value.scss b/src/scss/components/_combat_value.scss
new file mode 100644
index 0000000..81014b0
--- /dev/null
+++ b/src/scss/components/_combat_value.scss
@@ -0,0 +1,61 @@
+@use "../utils/mixins";
+@use "../utils/variables";
+
+.ds4-combat-value {
+ $size: 3.75rem;
+
+ display: grid;
+ place-items: center;
+ row-gap: 0.5em;
+
+ &__value {
+ $combat-values-icons-path: "#{variables.$official-icons-path}/combat-values";
+ @include mixins.centered-content;
+
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: contain;
+ font-size: 1.5em;
+ height: $size;
+ width: $size;
+
+ &--hitPoints {
+ background-image: url("#{$combat-values-icons-path}/hit-points.png");
+ }
+ &--defense {
+ background-image: url("#{$combat-values-icons-path}/defense.png");
+ }
+ &--initiative {
+ background-image: url("#{$combat-values-icons-path}/initiative.png");
+ }
+ &--movement {
+ background-image: url("#{$combat-values-icons-path}/movement-rate.png");
+ }
+ &--meleeAttack {
+ background-image: url("#{$combat-values-icons-path}/melee-attack.png");
+ }
+ &--rangedAttack {
+ background-image: url("#{$combat-values-icons-path}/ranged-attack.png");
+ }
+ &--spellcasting {
+ background-image: url("#{$combat-values-icons-path}/spellcasting.png");
+ }
+ &--targetedSpellcasting {
+ background-image: url("#{$combat-values-icons-path}/targeted-spellcasting.png");
+ }
+ }
+
+ &__formula {
+ align-items: center;
+ justify-content: space-between;
+ display: flex;
+ gap: 0.15em;
+ text-align: center;
+ width: $size;
+ }
+
+ &__formula-base,
+ &__formula-modifier {
+ flex: 1 1 4em;
+ }
+}
diff --git a/src/scss/components/_combat_values.scss b/src/scss/components/_combat_values.scss
index a0bea35..067a3f0 100644
--- a/src/scss/components/_combat_values.scss
+++ b/src/scss/components/_combat_values.scss
@@ -1,53 +1,9 @@
-@use "../utils/mixins";
@use "../utils/variables";
-.combat-values {
- margin-top: variables.$margin-sm;
- .combat-value-with-formula {
- $size: 60px;
- display: grid;
- place-items: center;
- row-gap: variables.$margin-sm;
- .combat-value {
- $combat-values-icons-path: "#{variables.$official-icons-path}/combat-values";
- @include mixins.centered-content;
- height: $size;
- width: $size;
- flex: 0 0 auto;
- background-size: contain;
- font-size: 1.5em;
- &.hitPoints {
- background-image: url("#{$combat-values-icons-path}/hit-points.png");
- }
- &.defense {
- background-image: url("#{$combat-values-icons-path}/defense.png");
- }
- &.initiative {
- background-image: url("#{$combat-values-icons-path}/initiative.png");
- }
- &.movement {
- background-image: url("#{$combat-values-icons-path}/movement-rate.png");
- }
- &.meleeAttack {
- background-image: url("#{$combat-values-icons-path}/melee-attack.png");
- }
- &.rangedAttack {
- background-image: url("#{$combat-values-icons-path}/ranged-attack.png");
- }
- &.spellcasting {
- background-image: url("#{$combat-values-icons-path}/spellcasting.png");
- }
- &.targetedSpellcasting {
- background-image: url("#{$combat-values-icons-path}/targeted-spellcasting.png");
- }
- }
-
- .combat-value-formula {
- width: $size;
- text-align: center;
- span {
- line-height: variables.$default-input-height;
- }
- }
- }
+.ds4-combat-values {
+ border-bottom: variables.$border-groove;
+ display: flex;
+ margin: variables.$margin-sm 0;
+ padding-bottom: variables.$margin-sm;
+ justify-content: space-between;
}
diff --git a/src/scss/components/_core_value.scss b/src/scss/components/_core_value.scss
new file mode 100644
index 0000000..b2fbd15
--- /dev/null
+++ b/src/scss/components/_core_value.scss
@@ -0,0 +1,36 @@
+@use "../utils/colors";
+@use "../utils/typography";
+@use "../utils/variables";
+
+.ds4-core-value {
+ align-items: center;
+ display: flex;
+
+ &__label {
+ @include typography.font-heading-upper;
+ flex: 1;
+ font-size: 2em;
+ text-align: center;
+ }
+
+ &__value {
+ align-items: center;
+ display: flex;
+ flex: 1.1;
+ font-size: 1.2em;
+ gap: 0.15em;
+ text-align: center;
+ }
+
+ &__value-input,
+ &__value-total {
+ flex: 1 1 4em;
+ }
+
+ &--trait {
+ .ds4-core-value__label {
+ -webkit-text-stroke: 1px colors.$c-black;
+ color: transparent;
+ }
+ }
+}
diff --git a/src/scss/components/_core_values.scss b/src/scss/components/_core_values.scss
new file mode 100644
index 0000000..be68751
--- /dev/null
+++ b/src/scss/components/_core_values.scss
@@ -0,0 +1,10 @@
+@use "../utils/colors";
+@use "../utils/typography";
+@use "../utils/variables";
+
+.ds4-core-values {
+ column-gap: 0.5em;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ row-gap: 0.5em;
+}
diff --git a/src/scss/ds4.scss b/src/scss/ds4.scss
index 1d06a5f..69f44da 100644
--- a/src/scss/ds4.scss
+++ b/src/scss/ds4.scss
@@ -6,22 +6,27 @@
@include meta.load-css("global/fonts");
@include meta.load-css("global/grid");
@include meta.load-css("global/window");
+
@include meta.load-css("components/actor_sheet");
@include meta.load-css("components/dice_total");
/* Styles limited to ds4 sheets */
.ds4 {
- @include meta.load-css("components/attributes_traits");
@include meta.load-css("components/apps");
@include meta.load-css("components/basic_property");
@include meta.load-css("components/character_progression");
@include meta.load-css("components/character_values");
+ @include meta.load-css("components/check");
+ @include meta.load-css("components/checks");
+ @include meta.load-css("components/combat_value");
@include meta.load-css("components/combat_values");
+ @include meta.load-css("components/core_value");
+ @include meta.load-css("components/core_values");
+ @include meta.load-css("components/currency");
@include meta.load-css("components/description");
@include meta.load-css("components/forms");
@include meta.load-css("components/item_list");
+ @include meta.load-css("components/rollable_image");
@include meta.load-css("components/tabs");
@include meta.load-css("components/talent_rank_equation");
- @include meta.load-css("components/currency");
- @include meta.load-css("components/rollable_image");
}
diff --git a/src/scss/global/_fonts.scss b/src/scss/global/_fonts.scss
index 8f85a58..3f931a2 100644
--- a/src/scss/global/_fonts.scss
+++ b/src/scss/global/_fonts.scss
@@ -2,33 +2,33 @@
font-family: "Lora";
font-style: normal;
font-weight: normal;
- src: local("Lora"), url("/systems/ds4/fonts/Lora/Lora.woff") format("woff");
+ src: local("Lora"), url("../fonts/Lora/Lora.woff") format("woff");
}
@font-face {
font-family: "Lora";
font-style: normal;
font-weight: bold;
- src: local("Lora"), url("/systems/ds4/fonts/Lora/Lora-Bold.woff") format("woff");
+ src: local("Lora"), url("../fonts/Lora/Lora-Bold.woff") format("woff");
}
@font-face {
font-family: "Lora";
font-style: italic;
font-weight: normal;
- src: local("Lora"), url("/systems/ds4/fonts/Lora/Lora-Italic.woff") format("woff");
+ src: local("Lora"), url("../fonts/Lora/Lora-Italic.woff") format("woff");
}
@font-face {
font-family: "Lora";
font-style: italic;
font-weight: bold;
- src: local("Lora"), url("/systems/ds4/fonts/Lora/Lora-BoldItalic.woff") format("woff");
+ src: local("Lora"), url("../fonts/Lora/Lora-BoldItalic.woff") format("woff");
}
@font-face {
font-family: "Wood Stamp";
font-style: normal;
font-weight: normal;
- src: local("Wood Stamp"), url("/systems/ds4/fonts/Woodstamp/Woodstamp.woff") format("woff");
+ src: local("Wood Stamp"), url("../fonts/Woodstamp/Woodstamp.woff") format("woff");
}
diff --git a/src/scss/global/_window.scss b/src/scss/global/_window.scss
index eeeb4fb..2d22fe5 100644
--- a/src/scss/global/_window.scss
+++ b/src/scss/global/_window.scss
@@ -10,12 +10,3 @@
width: 100%;
}
}
-
-.rollable {
- &:hover,
- &:focus {
- color: #000;
- text-shadow: 0 0 10px red;
- cursor: pointer;
- }
-}
diff --git a/src/scss/utils/_mixins.scss b/src/scss/utils/_mixins.scss
index 0c35484..9f0db34 100644
--- a/src/scss/utils/_mixins.scss
+++ b/src/scss/utils/_mixins.scss
@@ -30,3 +30,7 @@
background-color: transparent;
}
}
+
+@mixin foundry-highlight-text-shadow {
+ text-shadow: 0 0 10px red;
+}
diff --git a/src/scss/utils/_variables.scss b/src/scss/utils/_variables.scss
index 0a8d4fc..2a47a3b 100644
--- a/src/scss/utils/_variables.scss
+++ b/src/scss/utils/_variables.scss
@@ -9,6 +9,6 @@ $margin-lg: $padding-lg;
$default-input-height: 26px;
-$official-icons-path: "/systems/ds4/assets/icons/official";
+$official-icons-path: "../assets/icons/official";
$border-groove: 2px groove colors.$c-border-groove;
diff --git a/src/templates/actor/partials/attributes-traits.hbs b/src/templates/actor/partials/attributes-traits.hbs
deleted file mode 100644
index 1f58bd0..0000000
--- a/src/templates/actor/partials/attributes-traits.hbs
+++ /dev/null
@@ -1,56 +0,0 @@
-{{!-- ======================================================================== --}}
-{{!-- INLINE PARTIAL DEFINITIONS --}}
-{{!-- ======================================================================== --}}
-
-{{!--
-!-- Render an attribute.
-!--
-!-- @param attribute-label: The label to display for the attribute
-!-- @param attribute-key: The key of the attribute
-!-- @param attribute-data: The data for the attribute
---}}
-
-{{#*inline "attribute"}}
-
-
+
- ➞{{attribute-data.total}}
-
-
-{{/inline}}
-
-{{!--
-!-- Render a trait.
-!--
-!-- @param trait-label: The label to display for the trait
-!-- @param trait-key: The key of the trait
-!-- @param trait-data: The data for the trait
---}}
-
-{{#*inline "trait"}}
-