Merge branch '020-weapon-rolling' into 'master'
Make weapons & spells rollable from the character sheet Closes #20 See merge request dungeonslayers/ds4!88
This commit is contained in:
commit
dcf6579c53
21 changed files with 339 additions and 90 deletions
|
@ -21,4 +21,5 @@
|
||||||
@include meta.load-css("scss/components/tabs");
|
@include meta.load-css("scss/components/tabs");
|
||||||
@include meta.load-css("scss/components/talent_rank_equation");
|
@include meta.load-css("scss/components/talent_rank_equation");
|
||||||
@include meta.load-css("scss/components/currency");
|
@include meta.load-css("scss/components/currency");
|
||||||
|
@include meta.load-css("scss/components/rollable_image");
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
"DS4.UserInteractionAddEffect": "Neuer Effekt",
|
"DS4.UserInteractionAddEffect": "Neuer Effekt",
|
||||||
"DS4.UserInteractionEditEffect": "Effekt bearbeiten",
|
"DS4.UserInteractionEditEffect": "Effekt bearbeiten",
|
||||||
"DS4.UserInteractionDeleteEffect": "Effekt löschen",
|
"DS4.UserInteractionDeleteEffect": "Effekt löschen",
|
||||||
|
"DS4.EntityImageAltText": "Bild von {name}",
|
||||||
|
"DS4.RollableImageRollableTitle": "Für {name} würfeln",
|
||||||
|
"DS4.DiceOverlayImageAltText": "Bild eines W20",
|
||||||
"DS4.NotOwned": "Nicht besessen",
|
"DS4.NotOwned": "Nicht besessen",
|
||||||
"DS4.HeadingBiography": "Biografie",
|
"DS4.HeadingBiography": "Biografie",
|
||||||
"DS4.HeadingDetails": "Details",
|
"DS4.HeadingDetails": "Details",
|
||||||
|
@ -15,8 +18,9 @@
|
||||||
"DS4.HeadingSpells": "Zaubersprüche",
|
"DS4.HeadingSpells": "Zaubersprüche",
|
||||||
"DS4.HeadingDescription": "Beschreibung",
|
"DS4.HeadingDescription": "Beschreibung",
|
||||||
"DS4.HeadingSpecialCreatureAbilities": "Besondere Fähigkeiten",
|
"DS4.HeadingSpecialCreatureAbilities": "Besondere Fähigkeiten",
|
||||||
"DS4.AttackType": "Angriffstyp",
|
"DS4.AttackType": "Angriffsart",
|
||||||
"DS4.AttackTypeAbbr": "AT",
|
"DS4.AttackTypeAbbr": "AA",
|
||||||
|
"DS4.AttackTypeSelection": "Welche Angriffsart?",
|
||||||
"DS4.WeaponBonus": "Waffenbonus",
|
"DS4.WeaponBonus": "Waffenbonus",
|
||||||
"DS4.WeaponBonusAbbr": "WB",
|
"DS4.WeaponBonusAbbr": "WB",
|
||||||
"DS4.OpponentDefense": "Gegnerabwehr",
|
"DS4.OpponentDefense": "Gegnerabwehr",
|
||||||
|
@ -184,6 +188,12 @@
|
||||||
"DS4.ErrorDiceCritOverlap": "Es gibt eine Überlappung zwischen Patzern und Immersiegen.",
|
"DS4.ErrorDiceCritOverlap": "Es gibt eine Überlappung zwischen Patzern und Immersiegen.",
|
||||||
"DS4.ErrorExplodingRecursionLimitExceeded": "Die maximale Rekursionstiefe für slayende Würfelwürfe wurde überschritten.",
|
"DS4.ErrorExplodingRecursionLimitExceeded": "Die maximale Rekursionstiefe für slayende Würfelwürfe wurde überschritten.",
|
||||||
"DS4.ErrorDuringMigration": "Fehler während der Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion}. Der Fehler trat während der Ausführung des Migrationsskripts mit der Version {migrationVersion} auf. Spätere Migrationsskripte wurden nicht ausgeführt. Mehr Details finden Sie in der Entwicklerkonsole (F12).",
|
"DS4.ErrorDuringMigration": "Fehler während der Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion}. Der Fehler trat während der Ausführung des Migrationsskripts mit der Version {migrationVersion} auf. Spätere Migrationsskripte wurden nicht ausgeführt. Mehr Details finden Sie in der Entwicklerkonsole (F12).",
|
||||||
|
"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.ErrorItemMustBeEquippedToBeRolled": "Um für das Item '{name}' ({id}) vom Typ '{type}' zu würfeln, muss es ausgerüstet sein.",
|
||||||
|
"DS4.InfoManuallyEnterSpellBonus": "Der korrekte Wert für den Zauberbonus '{spellBonus}' des Zaubers '{name}' musss 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.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.InfoSystemUpdateCompleted": "Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion} erfolgreich!",
|
||||||
"DS4.UnitRounds": "Runden",
|
"DS4.UnitRounds": "Runden",
|
||||||
|
@ -200,9 +210,9 @@
|
||||||
"DS4.UnitKilometersAbbr": "km",
|
"DS4.UnitKilometersAbbr": "km",
|
||||||
"DS4.UnitCustom": "individuell",
|
"DS4.UnitCustom": "individuell",
|
||||||
"DS4.UnitCustomAbbr": " ",
|
"DS4.UnitCustomAbbr": " ",
|
||||||
|
"DS4.GenericOkButton": "OK",
|
||||||
|
"DS4.GenericCancelButton": "Abbrechen",
|
||||||
"DS4.RollDialogDefaultTitle": "Proben-Optionen",
|
"DS4.RollDialogDefaultTitle": "Proben-Optionen",
|
||||||
"DS4.RollDialogOkButton": "OK",
|
|
||||||
"DS4.RollDialogCancelButton": "Abbrechen",
|
|
||||||
"DS4.ErrorUnexpectedHtmlType": "Typfehler: Erwartet wurde '{exType}', tatsächlich erhalten wurde '{realType}'.",
|
"DS4.ErrorUnexpectedHtmlType": "Typfehler: Erwartet wurde '{exType}', tatsächlich erhalten wurde '{realType}'.",
|
||||||
"DS4.ErrorCouldNotFindForm": "Konnte HTML Element '{htmlElement}' nicht finden.",
|
"DS4.ErrorCouldNotFindForm": "Konnte HTML Element '{htmlElement}' nicht finden.",
|
||||||
"DS4.ErrorActorDoesNotHaveItem": "Der Aktor '{actor}' hat kein Item mit der ID '{id}'.",
|
"DS4.ErrorActorDoesNotHaveItem": "Der Aktor '{actor}' hat kein Item mit der ID '{id}'.",
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
"DS4.UserInteractionAddEffect": "Add Effect",
|
"DS4.UserInteractionAddEffect": "Add Effect",
|
||||||
"DS4.UserInteractionEditEffect": "Edit Effect",
|
"DS4.UserInteractionEditEffect": "Edit Effect",
|
||||||
"DS4.UserInteractionDeleteEffect": "Delete Effect",
|
"DS4.UserInteractionDeleteEffect": "Delete Effect",
|
||||||
|
"DS4.EntityImageAltText": "Image of {name}",
|
||||||
|
"DS4.RollableImageRollableTitle": "Roll for {name}",
|
||||||
|
"DS4.DiceOverlayImageAltText": "Image of a d20",
|
||||||
"DS4.NotOwned": "No owner",
|
"DS4.NotOwned": "No owner",
|
||||||
"DS4.HeadingBiography": "Biography",
|
"DS4.HeadingBiography": "Biography",
|
||||||
"DS4.HeadingDetails": "Details",
|
"DS4.HeadingDetails": "Details",
|
||||||
|
@ -17,6 +20,7 @@
|
||||||
"DS4.HeadingSpecialCreatureAbilities": "Special Abilities",
|
"DS4.HeadingSpecialCreatureAbilities": "Special Abilities",
|
||||||
"DS4.AttackType": "Attack Type",
|
"DS4.AttackType": "Attack Type",
|
||||||
"DS4.AttackTypeAbbr": "AT",
|
"DS4.AttackTypeAbbr": "AT",
|
||||||
|
"DS4.AttackTypeSelection": "Which Attack Type?",
|
||||||
"DS4.WeaponBonus": "Weapon Bonus",
|
"DS4.WeaponBonus": "Weapon Bonus",
|
||||||
"DS4.WeaponBonusAbbr": "WB",
|
"DS4.WeaponBonusAbbr": "WB",
|
||||||
"DS4.OpponentDefense": "Opponent Defense",
|
"DS4.OpponentDefense": "Opponent Defense",
|
||||||
|
@ -184,6 +188,12 @@
|
||||||
"DS4.ErrorDiceCritOverlap": "There's an overlap between Fumbles and Coups",
|
"DS4.ErrorDiceCritOverlap": "There's an overlap between Fumbles and Coups",
|
||||||
"DS4.ErrorExplodingRecursionLimitExceeded": "Maximum recursion depth for exploding dice roll exceeded",
|
"DS4.ErrorExplodingRecursionLimitExceeded": "Maximum recursion depth for exploding dice roll exceeded",
|
||||||
"DS4.ErrorDuringMigration": "Error while migrating DS4 system from migration version {currentVersion} to {targetVersion}. The error occurred during execution of migration script with version {migrationVersion}. Later migrations have not been executed. For more details, please look at the development console (F12).",
|
"DS4.ErrorDuringMigration": "Error while migrating DS4 system from migration version {currentVersion} to {targetVersion}. The error occurred during execution of migration script with version {migrationVersion}. Later migrations have not been executed. For more details, please look at the development console (F12).",
|
||||||
|
"DS4.ErrorCannotRollUnownedItem": "Rolling for item '{name}' ({id})is not possible because it is not owned.",
|
||||||
|
"DS4.ErrorRollingForItemTypeNotPossible": "Rolling is not possible for items of type '{type}'.",
|
||||||
|
"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.ErrorItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.",
|
||||||
|
"DS4.InfoManuallyEnterSpellBonus": "The correct value of the spell bons '{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.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!",
|
"DS4.InfoSystemUpdateCompleted": "Migration of DS4 system from migration version {currentVersion} to {targetVersion} successful!",
|
||||||
"DS4.UnitRounds": "Rounds",
|
"DS4.UnitRounds": "Rounds",
|
||||||
|
@ -200,9 +210,9 @@
|
||||||
"DS4.UnitKilometersAbbr": "km",
|
"DS4.UnitKilometersAbbr": "km",
|
||||||
"DS4.UnitCustom": "Custom Unit",
|
"DS4.UnitCustom": "Custom Unit",
|
||||||
"DS4.UnitCustomAbbr": " ",
|
"DS4.UnitCustomAbbr": " ",
|
||||||
|
"DS4.GenericOkButton": "Ok",
|
||||||
|
"DS4.GenericCancelButton": "Cancel",
|
||||||
"DS4.RollDialogDefaultTitle": "Roll Options",
|
"DS4.RollDialogDefaultTitle": "Roll Options",
|
||||||
"DS4.RollDialogOkButton": "Ok",
|
|
||||||
"DS4.RollDialogCancelButton": "Cancel",
|
|
||||||
"DS4.ErrorUnexpectedHtmlType": "Type Error: Expected '{exType}' but got '{realType}'.",
|
"DS4.ErrorUnexpectedHtmlType": "Type Error: Expected '{exType}' but got '{realType}'.",
|
||||||
"DS4.ErrorCouldNotFindForm": "Could not find HTML element '{htmlElement}'.",
|
"DS4.ErrorCouldNotFindForm": "Could not find HTML element '{htmlElement}'.",
|
||||||
"DS4.ErrorActorDoesNotHaveItem": "The actor '{actor}' does not have any item with the id '{id}'.",
|
"DS4.ErrorActorDoesNotHaveItem": "The actor '{actor}' does not have any item with the id '{id}'.",
|
||||||
|
|
|
@ -17,6 +17,7 @@ interface DS4ActorDataDataBase {
|
||||||
attributes: DS4ActorDataDataAttributes;
|
attributes: DS4ActorDataDataAttributes;
|
||||||
traits: DS4ActorDataDataTraits;
|
traits: DS4ActorDataDataTraits;
|
||||||
combatValues: DS4ActorDataDataCombatValues;
|
combatValues: DS4ActorDataDataCombatValues;
|
||||||
|
rolling: DS4ActorDataDataRolling;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DS4ActorDataDataAttributes {
|
interface DS4ActorDataDataAttributes {
|
||||||
|
@ -45,6 +46,11 @@ interface DS4ActorDataDataCombatValues {
|
||||||
targetedSpellcasting: ModifiableData<number>;
|
targetedSpellcasting: ModifiableData<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DS4ActorDataDataRolling {
|
||||||
|
maximumCoupResult?: number;
|
||||||
|
minimumFumbleResult?: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface DS4CharacterDataData extends DS4ActorDataDataBase {
|
interface DS4CharacterDataData extends DS4ActorDataDataBase {
|
||||||
baseInfo: DS4CharacterDataDataBaseInfo;
|
baseInfo: DS4CharacterDataDataBaseInfo;
|
||||||
progression: DS4CharacterDataDataProgression;
|
progression: DS4CharacterDataDataProgression;
|
||||||
|
|
|
@ -25,6 +25,11 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
|
||||||
prepareBaseData(): void {
|
prepareBaseData(): void {
|
||||||
const data = this.data;
|
const data = this.data;
|
||||||
|
|
||||||
|
data.data.rolling = {
|
||||||
|
minimumFumbleResult: 20,
|
||||||
|
maximumCoupResult: 1,
|
||||||
|
};
|
||||||
|
|
||||||
const attributes = data.data.attributes;
|
const attributes = data.data.attributes;
|
||||||
Object.values(attributes).forEach(
|
Object.values(attributes).forEach(
|
||||||
(attribute: ModifiableData<number>) => (attribute.total = attribute.base + attribute.mod),
|
(attribute: ModifiableData<number>) => (attribute.total = attribute.base + attribute.mod),
|
||||||
|
|
|
@ -124,8 +124,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
|
||||||
|
|
||||||
html.find(".item-change").on("change", this._onItemChange.bind(this));
|
html.find(".item-change").on("change", this._onItemChange.bind(this));
|
||||||
|
|
||||||
// Rollable abilities.
|
html.find(".rollable-item").on("click", this._onRoll.bind(this));
|
||||||
html.find(".rollable").click(this._onRoll.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -239,17 +238,9 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
|
||||||
*/
|
*/
|
||||||
protected _onRoll(event: JQuery.ClickEvent): void {
|
protected _onRoll(event: JQuery.ClickEvent): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const element = event.currentTarget;
|
const id = $(event.currentTarget).parents(".item").data("itemId");
|
||||||
const dataset = element.dataset;
|
const item = this.actor.getOwnedItem(id);
|
||||||
|
item.roll();
|
||||||
if (dataset.roll) {
|
|
||||||
const roll = new Roll(dataset.roll, this.actor.data.data);
|
|
||||||
const label = dataset.label ? `Rolling ${dataset.label}` : "";
|
|
||||||
roll.roll().toMessage({
|
|
||||||
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
|
||||||
flavor: label,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
|
|
|
@ -8,7 +8,8 @@ import { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet";
|
||||||
import { createCheckRoll } from "./rolls/check-factory";
|
import { createCheckRoll } from "./rolls/check-factory";
|
||||||
import { registerSystemSettings } from "./settings";
|
import { registerSystemSettings } from "./settings";
|
||||||
import { migration } from "./migrations";
|
import { migration } from "./migrations";
|
||||||
import handlebarsHelpers from "./handlebars-helpers";
|
import registerHandlebarsHelpers from "./handlebars/handlebars-helpers";
|
||||||
|
import registerHandlebarsPartials from "./handlebars/handlebars-partials";
|
||||||
|
|
||||||
Hooks.once("init", async () => {
|
Hooks.once("init", async () => {
|
||||||
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
||||||
|
@ -44,37 +45,6 @@ Hooks.once("init", async () => {
|
||||||
registerHandlebarsHelpers();
|
registerHandlebarsHelpers();
|
||||||
});
|
});
|
||||||
|
|
||||||
async function registerHandlebarsPartials() {
|
|
||||||
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",
|
|
||||||
];
|
|
||||||
return loadTemplates(templatePaths);
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerHandlebarsHelpers() {
|
|
||||||
Object.entries(handlebarsHelpers).forEach(([key, helper]) => Handlebars.registerHelper(key, helper));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
export default { htmlToPlainText, isEmpty };
|
|
||||||
|
|
||||||
function htmlToPlainText(input: string | null | undefined): string | null | undefined {
|
|
||||||
if (!input) return;
|
|
||||||
return $(input).text();
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmpty(input: Array<unknown> | null | undefined): boolean {
|
|
||||||
return (input?.length ?? 0) === 0;
|
|
||||||
}
|
|
12
src/module/handlebars/handlebars-helpers.ts
Normal file
12
src/module/handlebars/handlebars-helpers.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export default function registerHandlebarsHelpers(): void {
|
||||||
|
Object.entries(helpers).forEach(([key, helper]) => Handlebars.registerHelper(key, helper));
|
||||||
|
}
|
||||||
|
|
||||||
|
const helpers = {
|
||||||
|
htmlToPlainText: (input: string | null | undefined): string | null | undefined => {
|
||||||
|
if (!input) return;
|
||||||
|
return $(input).text();
|
||||||
|
},
|
||||||
|
|
||||||
|
isEmpty: (input: Array<unknown> | null | undefined): boolean => (input?.length ?? 0) === 0,
|
||||||
|
};
|
27
src/module/handlebars/handlebars-partials.ts
Normal file
27
src/module/handlebars/handlebars-partials.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
export default async function registerHandlebarsPartials(): Promise<void> {
|
||||||
|
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",
|
||||||
|
];
|
||||||
|
await loadTemplates(templatePaths);
|
||||||
|
}
|
|
@ -32,8 +32,14 @@ type DS4LanguageData = DS4ItemDataHelper<DS4LanguageDataData, "language">;
|
||||||
type DS4AlphabetData = DS4ItemDataHelper<DS4AlphabetDataData, "alphabet">;
|
type DS4AlphabetData = DS4ItemDataHelper<DS4AlphabetDataData, "alphabet">;
|
||||||
type DS4SpecialCreatureAbilityData = DS4ItemDataHelper<DS4SpecialCreatureAbilityDataData, "specialCreatureAbility">;
|
type DS4SpecialCreatureAbilityData = DS4ItemDataHelper<DS4SpecialCreatureAbilityDataData, "specialCreatureAbility">;
|
||||||
|
|
||||||
interface DS4WeaponDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {
|
export type AttackType = keyof typeof DS4["i18n"]["attackTypes"];
|
||||||
attackType: "melee" | "ranged" | "meleeRanged";
|
|
||||||
|
interface DS4WeaponDataData
|
||||||
|
extends DS4ItemDataDataBase,
|
||||||
|
DS4ItemDataDataPhysical,
|
||||||
|
DS4ItemDataDataEquipable,
|
||||||
|
DS4ItemDataDataRollable {
|
||||||
|
attackType: AttackType;
|
||||||
weaponBonus: number;
|
weaponBonus: number;
|
||||||
opponentDefense: number;
|
opponentDefense: number;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +61,7 @@ interface DS4TalentRank extends ModifiableData<number> {
|
||||||
max: number;
|
max: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DS4SpellDataData extends DS4ItemDataDataBase, DS4ItemDataDataEquipable {
|
interface DS4SpellDataData extends DS4ItemDataDataBase, DS4ItemDataDataEquipable, DS4ItemDataDataRollable {
|
||||||
spellType: "spellcasting" | "targetedSpellcasting";
|
spellType: "spellcasting" | "targetedSpellcasting";
|
||||||
bonus: string;
|
bonus: string;
|
||||||
spellCategory:
|
spellCategory:
|
||||||
|
@ -109,6 +115,10 @@ interface DS4ItemDataDataEquipable {
|
||||||
equipped: boolean;
|
equipped: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DS4ItemDataDataRollable {
|
||||||
|
rollable?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface DS4ItemDataDataProtective {
|
interface DS4ItemDataDataProtective {
|
||||||
armorValue: number;
|
armorValue: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { DS4ItemData } from "./item-data";
|
import { DS4Actor } from "../actor/actor";
|
||||||
|
import { DS4 } from "../config";
|
||||||
|
import { createCheckRoll } from "../rolls/check-factory";
|
||||||
|
import notifications from "../ui/notifications";
|
||||||
|
import { AttackType, DS4ItemData } from "./item-data";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Item class for DS4
|
* The Item class for DS4
|
||||||
|
@ -17,6 +21,9 @@ export class DS4Item extends Item<DS4ItemData> {
|
||||||
const data = this.data.data;
|
const data = this.data.data;
|
||||||
data.rank.total = data.rank.base + data.rank.mod;
|
data.rank.total = data.rank.base + data.rank.mod;
|
||||||
}
|
}
|
||||||
|
if (this.data.type === "weapon" || this.data.type === "spell") {
|
||||||
|
this.data.data.rollable = this.data.data.equipped;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isNonEquippedEuipable(): boolean {
|
isNonEquippedEuipable(): boolean {
|
||||||
|
@ -32,4 +39,138 @@ export class DS4Item extends Item<DS4ItemData> {
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll a check for a action with this item.
|
||||||
|
*/
|
||||||
|
async roll(): Promise<void> {
|
||||||
|
if (!this.isOwnedItem()) {
|
||||||
|
throw new Error(game.i18n.format("DS4.ErrorCannotRollUnownedItem", { name: this.name, id: this.id }));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.data.type) {
|
||||||
|
case "weapon":
|
||||||
|
return this.rollWeapon();
|
||||||
|
case "spell":
|
||||||
|
return this.rollSpell();
|
||||||
|
default:
|
||||||
|
throw new Error(game.i18n.format("DS4.ErrorRollingForItemTypeNotPossible", { type: this.data.type }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async rollWeapon(this: this & { readonly isOwned: true }): Promise<void> {
|
||||||
|
if (!(this.data.type === "weapon")) {
|
||||||
|
throw new Error(
|
||||||
|
game.i18n.format("DS4.ErrorWrongItemType", {
|
||||||
|
actualType: this.data.type,
|
||||||
|
expectedType: "weapon",
|
||||||
|
id: this.id,
|
||||||
|
name: this.name,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.data.data.equipped) {
|
||||||
|
throw new Error(
|
||||||
|
game.i18n.format("DS4.ErrorItemMustBeEquippedToBeRolled", {
|
||||||
|
name: this.name,
|
||||||
|
id: this.id,
|
||||||
|
type: this.data.type,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ownerDataData = ((this.actor as unknown) as DS4Actor).data.data; // TODO(types): Improve so that the concrete Actor type is known here
|
||||||
|
const weaponBonus = this.data.data.weaponBonus;
|
||||||
|
const combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType);
|
||||||
|
const checkTargetValue = (ownerDataData.combatValues[combatValue].total as number) + weaponBonus;
|
||||||
|
await createCheckRoll(checkTargetValue, {
|
||||||
|
rollMode: game.settings.get("core", "rollMode"),
|
||||||
|
maxCritSuccess: ownerDataData.rolling.maximumCoupResult,
|
||||||
|
minCritFailure: ownerDataData.rolling.minimumFumbleResult,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async rollSpell(): Promise<void> {
|
||||||
|
if (!(this.data.type === "spell")) {
|
||||||
|
throw new Error(
|
||||||
|
game.i18n.format("DS4.ErrorWrongItemType", {
|
||||||
|
actualType: this.data.type,
|
||||||
|
expectedType: "spell",
|
||||||
|
id: this.id,
|
||||||
|
name: this.name,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.data.data.equipped) {
|
||||||
|
throw new Error(
|
||||||
|
game.i18n.format("DS4.ErrorItemMustBeEquippedToBeRolled", {
|
||||||
|
name: this.name,
|
||||||
|
id: this.id,
|
||||||
|
type: this.data.type,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ownerDataData = ((this.actor as unknown) as DS4Actor).data.data; // TODO(types): Improve so that the concrete Actor type is known here
|
||||||
|
const spellBonus = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined;
|
||||||
|
if (spellBonus === undefined) {
|
||||||
|
notifications.info(
|
||||||
|
game.i18n.format("DS4.InfoManuallyEnterSpellBonus", {
|
||||||
|
name: this.name,
|
||||||
|
spellBonus: this.data.data.bonus,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const spellType = this.data.data.spellType;
|
||||||
|
const checkTargetValue = (ownerDataData.combatValues[spellType].total as number) + (spellBonus ?? 0);
|
||||||
|
|
||||||
|
await createCheckRoll(checkTargetValue, {
|
||||||
|
rollMode: game.settings.get("core", "rollMode"),
|
||||||
|
maxCritSuccess: ownerDataData.rolling.maximumCoupResult,
|
||||||
|
minCritFailure: ownerDataData.rolling.minimumFumbleResult,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async getCombatValueKeyForAttackType(attackType: AttackType): Promise<"meleeAttack" | "rangedAttack"> {
|
||||||
|
if (attackType === "meleeRanged") {
|
||||||
|
const { melee, ranged } = { ...DS4.i18n.attackTypes };
|
||||||
|
const identifier = "attack-type-selection";
|
||||||
|
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", {
|
||||||
|
label,
|
||||||
|
identifier,
|
||||||
|
options: { melee, ranged },
|
||||||
|
}),
|
||||||
|
label: game.i18n.localize("DS4.GenericOkButton"),
|
||||||
|
callback: (html) => {
|
||||||
|
const selectedAttackType = html.find(`#${identifier}`).val();
|
||||||
|
if (selectedAttackType !== "melee" && selectedAttackType !== "ranged") {
|
||||||
|
throw new Error(
|
||||||
|
game.i18n.format("DS4.ErrorUnexpectedAttackType", {
|
||||||
|
actualType: selectedAttackType,
|
||||||
|
expectedTypes: "'melee', 'ranged'",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return `${selectedAttackType}Attack` as const;
|
||||||
|
},
|
||||||
|
render: () => undefined, // TODO(types): This is actually optional, remove when types are updated )
|
||||||
|
options: { jQuery: true },
|
||||||
|
});
|
||||||
|
return answer;
|
||||||
|
} else {
|
||||||
|
return `${attackType}Attack` as const;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type-guarding variant to check if the item is owned.
|
||||||
|
*/
|
||||||
|
isOwnedItem(): this is this & { readonly isOwned: true } {
|
||||||
|
return this.isOwned;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@ async function askGmModifier(
|
||||||
buttons: {
|
buttons: {
|
||||||
ok: {
|
ok: {
|
||||||
icon: '<i class="fas fa-check"></i>',
|
icon: '<i class="fas fa-check"></i>',
|
||||||
label: game.i18n.localize("DS4.RollDialogOkButton"),
|
label: game.i18n.localize("DS4.GenericOkButton"),
|
||||||
callback: (html) => {
|
callback: (html) => {
|
||||||
if (!("jquery" in html)) {
|
if (!("jquery" in html)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -160,7 +160,7 @@ async function askGmModifier(
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-times"></i>',
|
icon: '<i class="fas fa-times"></i>',
|
||||||
label: game.i18n.localize("DS4.RollDialogCancelButton"),
|
label: game.i18n.localize("DS4.GenericCancelButton"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: "ok",
|
default: "ok",
|
||||||
|
|
|
@ -15,31 +15,31 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
&--weapon {
|
&--weapon {
|
||||||
grid-template-columns: $row-height $row-height 3ch 3fr $row-height 1fr 3ch 5fr 4ch;
|
grid-template-columns: $row-height $row-height 3ch 3fr $row-height 1fr 3ch 5fr 5ch;
|
||||||
}
|
}
|
||||||
&--armor {
|
&--armor {
|
||||||
grid-template-columns: $row-height $row-height 3ch 3fr 1fr 1fr 3ch 5fr 4ch;
|
grid-template-columns: $row-height $row-height 3ch 3fr 1fr 1fr 3ch 5fr 5ch;
|
||||||
}
|
}
|
||||||
&--shield {
|
&--shield {
|
||||||
grid-template-columns: $row-height $row-height 3ch 1fr 3ch 3fr 4ch;
|
grid-template-columns: $row-height $row-height 3ch 1fr 3ch 3fr 5ch;
|
||||||
}
|
}
|
||||||
&--equipment {
|
&--equipment {
|
||||||
grid-template-columns: $row-height $row-height 3ch 1fr 10ch 3fr 4ch;
|
grid-template-columns: $row-height $row-height 3ch 1fr 10ch 3fr 5ch;
|
||||||
}
|
}
|
||||||
&--loot {
|
&--loot {
|
||||||
grid-template-columns: $row-height 3ch 1fr 10ch 3fr 4ch;
|
grid-template-columns: $row-height 3ch 1fr 10ch 3fr 5ch;
|
||||||
}
|
}
|
||||||
&--spell {
|
&--spell {
|
||||||
grid-template-columns: $row-height $row-height 2fr $row-height 1fr 1fr 1fr 1fr 4ch;
|
grid-template-columns: $row-height $row-height 2fr $row-height 1fr 1fr 1fr 1fr 5ch;
|
||||||
}
|
}
|
||||||
&--talent {
|
&--talent {
|
||||||
grid-template-columns: $row-height 1fr 1fr 3fr 4ch;
|
grid-template-columns: $row-height 1fr 21ch 3fr 5ch;
|
||||||
}
|
}
|
||||||
&--racial-ability,
|
&--racial-ability,
|
||||||
&--language,
|
&--language,
|
||||||
&--alphabet,
|
&--alphabet,
|
||||||
&--special-creature-ability {
|
&--special-creature-ability {
|
||||||
grid-template-columns: $row-height 1fr 3fr 4ch;
|
grid-template-columns: $row-height 1fr 3fr 5ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__row {
|
&__row {
|
||||||
|
@ -53,13 +53,12 @@
|
||||||
height: $row-height;
|
height: $row-height;
|
||||||
line-height: $row-height;
|
line-height: $row-height;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__image {
|
&__image {
|
||||||
background-position: center;
|
border: none;
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__editable {
|
&__editable {
|
||||||
|
@ -89,6 +88,14 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__control-buttons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 calc(1em / 3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ds4-item-list-title {
|
.ds4-item-list-title {
|
||||||
|
|
33
src/scss/components/_rollable_image.scss
Normal file
33
src/scss/components/_rollable_image.scss
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
.ds4-rollable-image {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&--rollable {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.ds4-rollable-image__image {
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ds4-rollable-image__overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
border: none;
|
||||||
|
transition: 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__overlay {
|
||||||
|
border: none;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
transition: 0.1s ease;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,9 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{!-- image --}}
|
{{!-- image --}}
|
||||||
<div class="ds4-item-list__image" style="background-image: url('{{itemData.img}}')" title="{{itemData.name}}"></div>
|
{{> systems/ds4/templates/common/partials/rollable-image.hbs rollable=itemData.data.rollable src=itemData.img
|
||||||
|
alt=(localize "DS4.EntityImageAltText" name=itemData.name) title=itemData.name rollableTitle=(localize
|
||||||
|
"DS4.RollableImageRollableTitle" name=itemData.name) rollableClass="rollable-item"}}
|
||||||
|
|
||||||
{{!-- amount --}}
|
{{!-- amount --}}
|
||||||
{{#if hasQuantity}}
|
{{#if hasQuantity}}
|
||||||
|
@ -41,5 +43,5 @@
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{!-- control buttons --}}
|
{{!-- control buttons --}}
|
||||||
{{> systems/ds4/templates/actor/partials/overview-control-buttons.hbs }}
|
{{> systems/ds4/templates/actor/partials/overview-control-buttons.hbs class="ds4-item-list__control-buttons" }}
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -20,10 +20,8 @@
|
||||||
{{#each itemsByType.weapon as |itemData id|}}
|
{{#each itemsByType.weapon as |itemData id|}}
|
||||||
{{#> systems/ds4/templates/actor/partials/item-list-entry.hbs itemData=itemData isEquipable=true hasQuantity=true}}
|
{{#> systems/ds4/templates/actor/partials/item-list-entry.hbs itemData=itemData isEquipable=true hasQuantity=true}}
|
||||||
{{!-- attack type --}}
|
{{!-- attack type --}}
|
||||||
<div class="ds4-item-list__image"
|
<img class="ds4-item-list__image" src="{{lookup ../../config.icons.attackTypes itemData.data.attackType}}"
|
||||||
style="background-image: url('{{lookup ../../config.icons.attackTypes itemData.data.attackType}}')"
|
title="{{lookup ../../config.i18n.attackTypes itemData.data.attackType}}" />
|
||||||
title="{{lookup ../../config.i18n.attackTypes itemData.data.attackType}}">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{!-- weapon bonus --}}
|
{{!-- weapon bonus --}}
|
||||||
<div>{{ itemData.data.weaponBonus}}</div>
|
<div>{{ itemData.data.weaponBonus}}</div>
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
{{!--
|
{{!--
|
||||||
!-- Render a group of an "edit" and a "delete" button for the current item.
|
!-- Render a group of an "edit" and a "delete" button for the current item.
|
||||||
!-- The current item is defined by the data-item-id HTML property of the parent li element.
|
!-- The current item is defined by the data-item-id HTML property of the parent li element.
|
||||||
|
!-- @param class: Additional CSS class(es) for the controls
|
||||||
--}}
|
--}}
|
||||||
<div class="item-controls">
|
<div class="item-controls {{class}}">
|
||||||
<a class="item-control item-edit" title="{{localize 'DS4.UserInteractionEditItem'}}"><i class="fas fa-edit"></i></a>
|
<a class="item-control item-edit" title="{{localize 'DS4.UserInteractionEditItem'}}"><i class="fas fa-edit"></i></a>
|
||||||
<a class="item-control item-delete" title="{{localize 'DS4.UserInteractionDeleteItem'}}"><i class="fas fa-trash"></i></a>
|
<a class="item-control item-delete" title="{{localize 'DS4.UserInteractionDeleteItem'}}"><i
|
||||||
|
class="fas fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -60,10 +60,8 @@ titleKey=titleKey}}
|
||||||
{{#> systems/ds4/templates/actor/partials/item-list-entry.hbs itemData=itemData isEquipable=true
|
{{#> systems/ds4/templates/actor/partials/item-list-entry.hbs itemData=itemData isEquipable=true
|
||||||
hideDescription=true}}
|
hideDescription=true}}
|
||||||
{{!-- spell type --}}
|
{{!-- spell type --}}
|
||||||
<div class="ds4-item-list__image"
|
<img class="ds4-item-list__image" src="{{lookup ../../config.icons.spellTypes itemData.data.spellType}}"
|
||||||
style="background-image: url('{{lookup ../../config.icons.spellTypes itemData.data.spellType}}')"
|
title="{{lookup ../../config.i18n.spellTypes itemData.data.spellType}}" />
|
||||||
title="{{lookup ../../config.i18n.spellTypes itemData.data.spellType}}">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{!-- spell bonus --}}
|
{{!-- spell bonus --}}
|
||||||
<input class="ds4-item-list__editable item-change" type="text" data-dtype="String" data-property="data.bonus"
|
<input class="ds4-item-list__editable item-change" type="text" data-dtype="String" data-property="data.bonus"
|
||||||
|
|
17
src/templates/common/partials/rollable-image.hbs
Normal file
17
src/templates/common/partials/rollable-image.hbs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{{!--
|
||||||
|
!-- Render an image that has a dice overlay image.
|
||||||
|
!-- @param rollable: A flag indicating whether or not the image is actually rollable.
|
||||||
|
!-- @param rollableClass: The CSS class(es) to add if the image is rollable.
|
||||||
|
!-- @param title: The title for the rollable image if it is not actually rollable.
|
||||||
|
!-- @param rollableTitle: The title for the rollable image if it is rollable.
|
||||||
|
!-- @param src: The path to the image.
|
||||||
|
!-- @param alt: An alternate text for the image.
|
||||||
|
--}}
|
||||||
|
<div class="ds4-rollable-image{{#if rollable}} ds4-rollable-image--rollable {{rollableClass}}{{/if}}"
|
||||||
|
title="{{#if rollable}}{{rollableTitle}}{{else}}{{title}}{{/if}}">
|
||||||
|
<img class="ds4-rollable-image__image" alt="{{alt}}" src="{{src}}" />
|
||||||
|
{{#if rollable}}
|
||||||
|
<img class="ds4-rollable-image__overlay" alt="{{localize 'DS4.DiceOverlayImageAltText'}}"
|
||||||
|
src="icons/svg/d20-black.svg" />
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
19
src/templates/common/simple-select-form.hbs
Normal file
19
src/templates/common/simple-select-form.hbs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{{!--
|
||||||
|
!-- Render a simple form with a single select element. It uses the default form classes of Foundry VTT.
|
||||||
|
!-- @param identifier: The identifier to use as id for the select element. Can be used to query the value later on.
|
||||||
|
!-- @param label: Text to display as the label for the select element.
|
||||||
|
!-- @param options: Key-value pairs that describe the options. The keys are used for the value attribute of the
|
||||||
|
options, the values are used as content.
|
||||||
|
--}}
|
||||||
|
<form class="ds4-simple-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{identifier}}">{{label}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<select name="{{identifier}}" id="{{identifier}}">
|
||||||
|
{{#each options as | value key |}}
|
||||||
|
<option value="{{key}}">{{value}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
Loading…
Reference in a new issue