mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
[Feature] Item Resource Support (#328)
* Initial * Resource setup finished * Fixed so that costs can be used * Corrected standard resources * Actions can only use item resources from their parent item * Fixed up dice * Fixed resource dice positioning * Fixed parsing of resource.max * Fixed styling on settings tab * Added manual input for Dice Resources * Lightmode fixes * Fixed Feature spellcasting modifier * Bugfix for item input to resourceDiceDialog * Item fix for TokenInput * PR Fixes
This commit is contained in:
parent
eefa116d9a
commit
4be3e6179c
53 changed files with 972 additions and 329 deletions
46
lang/en.json
46
lang/en.json
|
|
@ -387,6 +387,10 @@
|
||||||
"OwnershipSelection": {
|
"OwnershipSelection": {
|
||||||
"title": "Ownership Selection - {name}",
|
"title": "Ownership Selection - {name}",
|
||||||
"default": "Default Ownership"
|
"default": "Default Ownership"
|
||||||
|
},
|
||||||
|
"ResourceDice": {
|
||||||
|
"title": "{name} Resource",
|
||||||
|
"rerollDice": "Reroll Dice"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -632,6 +636,10 @@
|
||||||
"abbreviation": "AS"
|
"abbreviation": "AS"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ItemResourceType": {
|
||||||
|
"simple": "Simple",
|
||||||
|
"diceValue": "Dice Value"
|
||||||
|
},
|
||||||
"Range": {
|
"Range": {
|
||||||
"self": {
|
"self": {
|
||||||
"name": "Self",
|
"name": "Self",
|
||||||
|
|
@ -1068,6 +1076,10 @@
|
||||||
"shortrest": "Short Rest",
|
"shortrest": "Short Rest",
|
||||||
"longrest": "Long Rest"
|
"longrest": "Long Rest"
|
||||||
},
|
},
|
||||||
|
"Resource": {
|
||||||
|
"single": "Resource",
|
||||||
|
"plural": "Resources"
|
||||||
|
},
|
||||||
"Tabs": {
|
"Tabs": {
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"attack": "Attack",
|
"attack": "Attack",
|
||||||
|
|
@ -1154,6 +1166,17 @@
|
||||||
"value": "Value"
|
"value": "Value"
|
||||||
},
|
},
|
||||||
"ITEMS": {
|
"ITEMS": {
|
||||||
|
"FIELDS": {
|
||||||
|
"resource": {
|
||||||
|
"amount": { "label": "Amount" },
|
||||||
|
"dieFaces": { "label": "Die Faces" },
|
||||||
|
"icon": { "label": "Icon" },
|
||||||
|
"max": { "label": "Max" },
|
||||||
|
"recovery": { "label": "Recovery" },
|
||||||
|
"type": { "label": "Type" },
|
||||||
|
"value": { "label": "Value" }
|
||||||
|
}
|
||||||
|
},
|
||||||
"Armor": {
|
"Armor": {
|
||||||
"baseScore": "Base Score",
|
"baseScore": "Base Score",
|
||||||
"baseThresholds": {
|
"baseThresholds": {
|
||||||
|
|
@ -1331,8 +1354,8 @@
|
||||||
},
|
},
|
||||||
"UI": {
|
"UI": {
|
||||||
"Chat": {
|
"Chat": {
|
||||||
"dualityRoll": {
|
"applyEffect": {
|
||||||
"abilityCheckTitle": "{ability} Check"
|
"title": "Apply Effects - {name}"
|
||||||
},
|
},
|
||||||
"attackRoll": {
|
"attackRoll": {
|
||||||
"title": "Attack - {attack}",
|
"title": "Attack - {attack}",
|
||||||
|
|
@ -1348,25 +1371,28 @@
|
||||||
"hitTarget": "Hit Targets",
|
"hitTarget": "Hit Targets",
|
||||||
"selectedTarget": "Selected"
|
"selectedTarget": "Selected"
|
||||||
},
|
},
|
||||||
"applyEffect": {
|
|
||||||
"title": "Apply Effects - {name}"
|
|
||||||
},
|
|
||||||
"healingRoll": {
|
|
||||||
"title": "Heal - {healing}",
|
|
||||||
"heal": "Heal"
|
|
||||||
},
|
|
||||||
"deathMove": {
|
"deathMove": {
|
||||||
"title": "Death Move"
|
"title": "Death Move"
|
||||||
},
|
},
|
||||||
"domainCard": {
|
"domainCard": {
|
||||||
"title": "Domain Card"
|
"title": "Domain Card"
|
||||||
},
|
},
|
||||||
|
"dualityRoll": {
|
||||||
|
"abilityCheckTitle": "{ability} Check"
|
||||||
|
},
|
||||||
|
"featureTitle": "Class Feature",
|
||||||
"foundationCard": {
|
"foundationCard": {
|
||||||
"ancestryTitle": "Ancestry Card",
|
"ancestryTitle": "Ancestry Card",
|
||||||
"communityTitle": "Community Card",
|
"communityTitle": "Community Card",
|
||||||
"subclassFeatureTitle": "Subclass Feature"
|
"subclassFeatureTitle": "Subclass Feature"
|
||||||
},
|
},
|
||||||
"featureTitle": "Class Feature"
|
"healingRoll": {
|
||||||
|
"title": "Heal - {healing}",
|
||||||
|
"heal": "Heal"
|
||||||
|
},
|
||||||
|
"resourceRoll": {
|
||||||
|
"playerMessage": "{user} rerolled their {name}"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Notifications": {
|
"Notifications": {
|
||||||
"adversaryMissing": "The linked adversary doesn't exist in the world.",
|
"adversaryMissing": "The linked adversary doesn't exist in the world.",
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@ export { default as DamageSelectionDialog } from './damageSelectionDialog.mjs';
|
||||||
export { default as DeathMove } from './deathMove.mjs';
|
export { default as DeathMove } from './deathMove.mjs';
|
||||||
export { default as Downtime } from './downtime.mjs';
|
export { default as Downtime } from './downtime.mjs';
|
||||||
export { default as OwnershipSelection } from './ownershipSelection.mjs';
|
export { default as OwnershipSelection } from './ownershipSelection.mjs';
|
||||||
|
export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs';
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,12 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
context.canRoll = true;
|
context.canRoll = true;
|
||||||
if (this.config.costs?.length) {
|
if (this.config.costs?.length) {
|
||||||
const updatedCosts = this.action.calcCosts(this.config.costs);
|
const updatedCosts = this.action.calcCosts(this.config.costs);
|
||||||
context.costs = updatedCosts;
|
context.costs = updatedCosts.map(x => ({
|
||||||
|
...x,
|
||||||
|
label: x.keyIsID
|
||||||
|
? this.action.parent.parent.name
|
||||||
|
: game.i18n.localize(CONFIG.DH.GENERAL.abilityCosts[x.key].label)
|
||||||
|
}));
|
||||||
context.canRoll = this.action.hasCost(updatedCosts);
|
context.canRoll = this.action.hasCost(updatedCosts);
|
||||||
this.config.data.scale = this.config.costs[0].total;
|
this.config.data.scale = this.config.costs[0].total;
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +79,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
context.uses = this.action.calcUses(this.config.uses);
|
context.uses = this.action.calcUses(this.config.uses);
|
||||||
context.canRoll = context.canRoll && this.action.hasUses(context.uses);
|
context.canRoll = context.canRoll && this.action.hasUses(context.uses);
|
||||||
}
|
}
|
||||||
if(this.roll) {
|
if (this.roll) {
|
||||||
context.roll = this.roll;
|
context.roll = this.roll;
|
||||||
context.rollType = this.roll?.constructor.name;
|
context.rollType = this.roll?.constructor.name;
|
||||||
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
|
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
|
||||||
|
|
|
||||||
99
module/applications/dialogs/resourceDiceDialog.mjs
Normal file
99
module/applications/dialogs/resourceDiceDialog.mjs
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { itemAbleRollParse } from '../../helpers/utils.mjs';
|
||||||
|
|
||||||
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class ResourceDiceDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(item, actor, options = {}) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
this.item = item;
|
||||||
|
this.actor = actor;
|
||||||
|
this.diceStates = foundry.utils.deepClone(item.system.resource.diceStates);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'resource-dice'],
|
||||||
|
window: {
|
||||||
|
icon: 'fa-solid fa-dice'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
rerollDice: this.rerollDice,
|
||||||
|
save: this.save
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateResourceDice,
|
||||||
|
submitOnChange: true,
|
||||||
|
submitOnClose: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
resourceDice: {
|
||||||
|
id: 'resourceDice',
|
||||||
|
template: 'systems/daggerheart/templates/dialogs/dice-roll/resourceDice.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return game.i18n.format('DAGGERHEART.APPLICATIONS.ResourceDice.title', { name: this.item.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.item = this.item;
|
||||||
|
context.actor = this.actor;
|
||||||
|
context.diceStates = this.diceStates;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateResourceDice(event, _, formData) {
|
||||||
|
const { diceStates } = foundry.utils.expandObject(formData.object);
|
||||||
|
this.diceStates = Object.keys(diceStates).reduce((acc, key) => {
|
||||||
|
const resourceState = this.item.system.resource.diceStates[key];
|
||||||
|
acc[key] = { ...diceStates[key], used: Boolean(resourceState?.used) };
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async save() {
|
||||||
|
this.rollValues = Object.values(this.diceStates);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async rerollDice() {
|
||||||
|
const max = itemAbleRollParse(this.item.system.resource.max, this.actor, this.item);
|
||||||
|
const diceFormula = `${max}d${this.item.system.resource.dieFaces}`;
|
||||||
|
const roll = await new Roll(diceFormula).evaluate();
|
||||||
|
if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
|
||||||
|
this.rollValues = roll.terms[0].results.map(x => ({ value: x.result, used: false }));
|
||||||
|
this.resetUsed = true;
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const msg = new cls({
|
||||||
|
user: game.user.id,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/ui/chat/resource-roll.hbs',
|
||||||
|
{
|
||||||
|
user: this.actor.name,
|
||||||
|
name: this.item.name
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create(item, actor, options = {}) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const app = new this(item, actor, options);
|
||||||
|
app.addEventListener('close', () => resolve(app.rollValues), { once: true });
|
||||||
|
app.render({ force: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -107,6 +107,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
context.hasBaseDamage = !!this.action.parent.attack;
|
context.hasBaseDamage = !!this.action.parent.attack;
|
||||||
context.getRealIndex = this.getRealIndex.bind(this);
|
context.getRealIndex = this.getRealIndex.bind(this);
|
||||||
context.getEffectDetails = this.getEffectDetails.bind(this);
|
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||||
|
context.costOptions = this.getCostOptions();
|
||||||
context.disableOption = this.disableOption.bind(this);
|
context.disableOption = this.disableOption.bind(this);
|
||||||
context.isNPC = this.action.actor && this.action.actor.type !== 'character';
|
context.isNPC = this.action.actor && this.action.actor.type !== 'character';
|
||||||
context.hasRoll = this.action.hasRoll;
|
context.hasRoll = this.action.hasRoll;
|
||||||
|
|
@ -125,8 +126,21 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
this.render(true);
|
this.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
disableOption(index, options, choices) {
|
getCostOptions() {
|
||||||
const filtered = foundry.utils.deepClone(options);
|
const options = foundry.utils.deepClone(CONFIG.DH.GENERAL.abilityCosts);
|
||||||
|
const resource = this.action.parent.resource;
|
||||||
|
if (resource) {
|
||||||
|
options[this.action.parent.parent.id] = {
|
||||||
|
label: this.action.parent.parent.name,
|
||||||
|
group: 'TYPES.Actor.character'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
disableOption(index, costOptions, choices) {
|
||||||
|
const filtered = foundry.utils.deepClone(costOptions);
|
||||||
Object.keys(filtered).forEach(o => {
|
Object.keys(filtered).forEach(o => {
|
||||||
if (choices.find((c, idx) => c.type === o && index !== idx)) filtered[o].disabled = true;
|
if (choices.find((c, idx) => c.type === o && index !== idx)) filtered[o].disabled = true;
|
||||||
});
|
});
|
||||||
|
|
@ -142,11 +156,19 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
return this.action.item.effects.get(id);
|
return this.action.item.effects.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
_prepareSubmitData(event, formData) {
|
_prepareSubmitData(_event, formData) {
|
||||||
const submitData = foundry.utils.expandObject(formData.object);
|
const submitData = foundry.utils.expandObject(formData.object);
|
||||||
for (const keyPath of this.constructor.CLEAN_ARRAYS) {
|
for (const keyPath of this.constructor.CLEAN_ARRAYS) {
|
||||||
const data = foundry.utils.getProperty(submitData, keyPath);
|
const data = foundry.utils.getProperty(submitData, keyPath);
|
||||||
if (data) foundry.utils.setProperty(submitData, keyPath, Object.values(data));
|
const dataValues = data ? Object.values(data) : [];
|
||||||
|
if (keyPath === 'cost') {
|
||||||
|
for (var value of dataValues) {
|
||||||
|
const item = this.action.parent.parent.id === value.key;
|
||||||
|
value.keyIsID = Boolean(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) foundry.utils.setProperty(submitData, keyPath, dataValues);
|
||||||
}
|
}
|
||||||
return submitData;
|
return submitData;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { abilities } from '../../../config/actorConfig.mjs';
|
||||||
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
|
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
|
||||||
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
||||||
import FilterMenu from '../../ux/filter-menu.mjs';
|
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||||
|
import { itemAbleRollParse } from '../../../helpers/utils.mjs';
|
||||||
|
|
||||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||||
|
|
||||||
|
|
@ -25,6 +26,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
toggleEquipItem: CharacterSheet.#toggleEquipItem,
|
toggleEquipItem: CharacterSheet.#toggleEquipItem,
|
||||||
useItem: this.useItem, //TODO Fix this
|
useItem: this.useItem, //TODO Fix this
|
||||||
useAction: this.useAction,
|
useAction: this.useAction,
|
||||||
|
toggleResourceDice: this.toggleResourceDice,
|
||||||
|
handleResourceDice: this.handleResourceDice,
|
||||||
toChat: this.toChat
|
toChat: this.toChat
|
||||||
},
|
},
|
||||||
window: {
|
window: {
|
||||||
|
|
@ -91,6 +94,17 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
|
htmlElement.querySelectorAll('.inventory-item-resource').forEach(element => {
|
||||||
|
element.addEventListener('change', this.updateItemResource.bind(this));
|
||||||
|
});
|
||||||
|
htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => {
|
||||||
|
element.addEventListener('change', this.updateItemQuantity.bind(this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
async _onRender(context, options) {
|
async _onRender(context, options) {
|
||||||
await super._onRender(context, options);
|
await super._onRender(context, options);
|
||||||
|
|
@ -480,6 +494,27 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Application Listener Actions */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async updateItemResource(event) {
|
||||||
|
const item = this.getItem(event.currentTarget);
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
const max = item.system.resource.max ? itemAbleRollParse(item.system.resource.max, this.document, item) : null;
|
||||||
|
const value = max ? Math.min(Number(event.currentTarget.value), max) : event.currentTarget.value;
|
||||||
|
await item.update({ 'system.resource.value': value });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateItemQuantity(event) {
|
||||||
|
const item = this.getItem(event.currentTarget);
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
await item.update({ 'system.quantity': event.currentTarget.value });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Application Clicks Actions */
|
/* Application Clicks Actions */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -640,6 +675,41 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
action.use(event);
|
action.use(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the used state of a resource dice.
|
||||||
|
* @type {ApplicationClickAction}
|
||||||
|
*/
|
||||||
|
static async toggleResourceDice(event) {
|
||||||
|
const target = event.target.closest('.item-resource');
|
||||||
|
const item = this.getItem(event);
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
const diceState = item.system.resource.diceStates[target.dataset.dice];
|
||||||
|
await item.update({
|
||||||
|
[`system.resource.diceStates.${target.dataset.dice}.used`]: diceState?.used ? !diceState.used : true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the roll values of resource dice.
|
||||||
|
* @type {ApplicationClickAction}
|
||||||
|
*/
|
||||||
|
static async handleResourceDice(event) {
|
||||||
|
const item = this.getItem(event);
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
const rollValues = await game.system.api.applications.dialogs.ResourceDiceDialog.create(item, this.document);
|
||||||
|
if (!rollValues) return;
|
||||||
|
|
||||||
|
await item.update({
|
||||||
|
'system.resource.diceStates': rollValues.reduce((acc, state, index) => {
|
||||||
|
acc[index] = { value: state.value, used: state.used };
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send item to Chat
|
* Send item to Chat
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
removeAction: DHBaseItemSheet.#removeAction,
|
removeAction: DHBaseItemSheet.#removeAction,
|
||||||
addFeature: DHBaseItemSheet.#addFeature,
|
addFeature: DHBaseItemSheet.#addFeature,
|
||||||
editFeature: DHBaseItemSheet.#editFeature,
|
editFeature: DHBaseItemSheet.#editFeature,
|
||||||
removeFeature: DHBaseItemSheet.#removeFeature
|
removeFeature: DHBaseItemSheet.#removeFeature,
|
||||||
|
addResource: DHBaseItemSheet.#addResource,
|
||||||
|
removeResource: DHBaseItemSheet.#removeResource
|
||||||
},
|
},
|
||||||
dragDrop: [
|
dragDrop: [
|
||||||
{ dragSelector: null, dropSelector: '.tab.features .drop-section' },
|
{ dragSelector: null, dropSelector: '.tab.features .drop-section' },
|
||||||
|
|
@ -215,6 +217,26 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a resource to the item.
|
||||||
|
* @type {ApplicationClickAction}
|
||||||
|
*/
|
||||||
|
static async #addResource() {
|
||||||
|
await this.document.update({
|
||||||
|
'system.resource': { type: 'simple', value: 0 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the resource from the item.
|
||||||
|
* @type {ApplicationClickAction}
|
||||||
|
*/
|
||||||
|
static async #removeResource() {
|
||||||
|
await this.document.update({
|
||||||
|
'system.resource': null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Application Drag/Drop */
|
/* Application Drag/Drop */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
export default function ItemAttachmentSheet(Base) {
|
export default function ItemAttachmentSheet(Base) {
|
||||||
return class extends Base {
|
return class extends Base {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
|
|
@ -25,10 +24,7 @@ export default function ItemAttachmentSheet(Base) {
|
||||||
...super.TABS,
|
...super.TABS,
|
||||||
primary: {
|
primary: {
|
||||||
...super.TABS?.primary,
|
...super.TABS?.primary,
|
||||||
tabs: [
|
tabs: [...(super.TABS?.primary?.tabs || []), { id: 'attachments' }],
|
||||||
...(super.TABS?.primary?.tabs || []),
|
|
||||||
{ id: 'attachments' }
|
|
||||||
],
|
|
||||||
initial: super.TABS?.primary?.initial || 'description',
|
initial: super.TABS?.primary?.initial || 'description',
|
||||||
labelPrefix: super.TABS?.primary?.labelPrefix || 'DAGGERHEART.GENERAL.Tabs'
|
labelPrefix: super.TABS?.primary?.labelPrefix || 'DAGGERHEART.GENERAL.Tabs'
|
||||||
}
|
}
|
||||||
|
|
@ -60,11 +56,10 @@ export default function ItemAttachmentSheet(Base) {
|
||||||
await this.document.system.addAttachment(item);
|
await this.document.system.addAttachment(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static async #removeAttachment(event, target) {
|
static async #removeAttachment(event, target) {
|
||||||
// Call the data model's public method
|
// Call the data model's public method
|
||||||
await this.document.system.removeAttachment(target.dataset.uuid);
|
await this.document.system.removeAttachment(target.dataset.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preparePartContext(partId, context) {
|
async _preparePartContext(partId, context) {
|
||||||
await super._preparePartContext(partId, context);
|
await super._preparePartContext(partId, context);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
||||||
template: 'systems/daggerheart/templates/sheets/items/armor/settings.hbs',
|
template: 'systems/daggerheart/templates/sheets/items/armor/settings.hbs',
|
||||||
scrollable: ['.settings']
|
scrollable: ['.settings']
|
||||||
},
|
},
|
||||||
...super.PARTS,
|
...super.PARTS
|
||||||
};
|
};
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,16 @@ export default class ClassSheet extends DHBaseItemSheet {
|
||||||
await this.document.update({
|
await this.document.update({
|
||||||
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
|
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
|
||||||
});
|
});
|
||||||
|
} else if (item.type === 'feature') {
|
||||||
|
if (target.classList.contains('hope-feature')) {
|
||||||
|
await this.document.update({
|
||||||
|
'system.hopeFeatures': [...this.document.system.hopeFeatures.map(x => x.uuid), item.uuid]
|
||||||
|
});
|
||||||
|
} else if (target.classList.contains('class-feature')) {
|
||||||
|
await this.document.update({
|
||||||
|
'system.classFeatures': [...this.document.system.classFeatures.map(x => x.uuid), item.uuid]
|
||||||
|
});
|
||||||
|
}
|
||||||
} else if (item.type === 'weapon') {
|
} else if (item.type === 'weapon') {
|
||||||
if (target.classList.contains('primary-weapon-section')) {
|
if (target.classList.contains('primary-weapon-section')) {
|
||||||
if (!this.document.system.characterGuide.suggestedPrimaryWeapon && !item.system.secondary)
|
if (!this.document.system.characterGuide.suggestedPrimaryWeapon && !item.system.secondary)
|
||||||
|
|
@ -144,7 +154,7 @@ export default class ClassSheet extends DHBaseItemSheet {
|
||||||
static async #removeItemFromCollection(_event, element) {
|
static async #removeItemFromCollection(_event, element) {
|
||||||
const { uuid, target } = element.dataset;
|
const { uuid, target } = element.dataset;
|
||||||
const prop = foundry.utils.getProperty(this.document.system, target);
|
const prop = foundry.utils.getProperty(this.document.system, target);
|
||||||
await this.document.update({ [target]: prop.filter(i => i.uuid !== uuid) });
|
await this.document.update({ [`system.${target}`]: prop.filter(i => i.uuid !== uuid) });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
||||||
header: { template: 'systems/daggerheart/templates/sheets/items/feature/header.hbs' },
|
header: { template: 'systems/daggerheart/templates/sheets/items/feature/header.hbs' },
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
||||||
|
settings: { template: 'systems/daggerheart/templates/sheets/items/feature/settings.hbs' },
|
||||||
actions: {
|
actions: {
|
||||||
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
||||||
scrollable: ['.actions']
|
scrollable: ['.actions']
|
||||||
|
|
@ -34,7 +35,7 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
||||||
/**@override */
|
/**@override */
|
||||||
static TABS = {
|
static TABS = {
|
||||||
primary: {
|
primary: {
|
||||||
tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'effects' }],
|
tabs: [{ id: 'description' }, { id: 'settings' }, { id: 'actions' }, { id: 'effects' }],
|
||||||
initial: 'description',
|
initial: 'description',
|
||||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +68,6 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
title = game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType');
|
title = game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType');
|
||||||
console.log(this.document);
|
|
||||||
|
|
||||||
return foundry.applications.api.DialogV2.prompt({
|
return foundry.applications.api.DialogV2.prompt({
|
||||||
window: { title },
|
window: { title },
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
||||||
template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs',
|
template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs',
|
||||||
scrollable: ['.settings']
|
scrollable: ['.settings']
|
||||||
},
|
},
|
||||||
...super.PARTS,
|
...super.PARTS
|
||||||
};
|
};
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
|
|
|
||||||
|
|
@ -366,31 +366,6 @@ export const abilityCosts = {
|
||||||
label: 'Armor Stack',
|
label: 'Armor Stack',
|
||||||
group: 'TYPES.Actor.character'
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
prayer: {
|
|
||||||
id: 'prayer',
|
|
||||||
label: 'Prayer Dice',
|
|
||||||
group: 'TYPES.Actor.character'
|
|
||||||
},
|
|
||||||
favor: {
|
|
||||||
id: 'favor',
|
|
||||||
label: 'Favor Points',
|
|
||||||
group: 'TYPES.Actor.character'
|
|
||||||
},
|
|
||||||
slayer: {
|
|
||||||
id: 'slayer',
|
|
||||||
label: 'Slayer Dice',
|
|
||||||
group: 'TYPES.Actor.character'
|
|
||||||
},
|
|
||||||
tide: {
|
|
||||||
id: 'tide',
|
|
||||||
label: 'Tide',
|
|
||||||
group: 'TYPES.Actor.character'
|
|
||||||
},
|
|
||||||
chaos: {
|
|
||||||
id: 'chaos',
|
|
||||||
label: 'Chaos',
|
|
||||||
group: 'TYPES.Actor.character'
|
|
||||||
},
|
|
||||||
fear: {
|
fear: {
|
||||||
id: 'fear',
|
id: 'fear',
|
||||||
label: 'Fear',
|
label: 'Fear',
|
||||||
|
|
|
||||||
|
|
@ -1334,3 +1334,14 @@ export const actionTypes = {
|
||||||
label: 'DAGGERHEART.CONFIG.ActionType.reaction'
|
label: 'DAGGERHEART.CONFIG.ActionType.reaction'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const itemResourceTypes = {
|
||||||
|
simple: {
|
||||||
|
id: 'simple',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ItemResourceType.simple'
|
||||||
|
},
|
||||||
|
diceValue: {
|
||||||
|
id: 'diceValue',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ItemResourceType.diceValue'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ export class DHDamageData extends foundry.abstract.DataModel {
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
label: 'Type',
|
label: 'Type',
|
||||||
initial: 'physical',
|
initial: 'physical'
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
resultBased: new fields.BooleanField({
|
resultBased: new fields.BooleanField({
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { DHActionDiceData, DHActionRollData, DHDamageData, DHDamageField } from './actionDice.mjs';
|
import { DHActionDiceData, DHActionRollData, DHDamageField } from './actionDice.mjs';
|
||||||
import DhpActor from '../../documents/actor.mjs';
|
import DhpActor from '../../documents/actor.mjs';
|
||||||
import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
|
import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
|
||||||
|
|
||||||
|
|
@ -35,12 +35,12 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}),
|
}),
|
||||||
cost: new fields.ArrayField(
|
cost: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
type: new fields.StringField({
|
key: new fields.StringField({
|
||||||
choices: CONFIG.DH.GENERAL.abilityCosts,
|
|
||||||
nullable: false,
|
nullable: false,
|
||||||
required: true,
|
required: true,
|
||||||
initial: 'hope'
|
initial: 'hope'
|
||||||
}),
|
}),
|
||||||
|
keyIsID: new fields.BooleanField(),
|
||||||
value: new fields.NumberField({ nullable: true, initial: 1 }),
|
value: new fields.NumberField({ nullable: true, initial: 1 }),
|
||||||
scalable: new fields.BooleanField({ initial: false }),
|
scalable: new fields.BooleanField({ initial: false }),
|
||||||
step: new fields.NumberField({ nullable: true, initial: null })
|
step: new fields.NumberField({ nullable: true, initial: null })
|
||||||
|
|
@ -204,7 +204,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
|
|
||||||
// Prepare Costs
|
// Prepare Costs
|
||||||
const costsConfig = this.prepareCost();
|
const costsConfig = this.prepareCost();
|
||||||
if (isFastForward && !this.hasCost(costsConfig))
|
if (isFastForward && !(await this.hasCost(costsConfig)))
|
||||||
return ui.notifications.warn("You don't have the resources to use that action.");
|
return ui.notifications.warn("You don't have the resources to use that action.");
|
||||||
|
|
||||||
// Prepare Uses
|
// Prepare Uses
|
||||||
|
|
@ -278,7 +278,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
|
|
||||||
prepareCost() {
|
prepareCost() {
|
||||||
const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : [];
|
const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : [];
|
||||||
return costs;
|
return this.calcCosts(costs);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareUse() {
|
prepareUse() {
|
||||||
|
|
@ -327,11 +327,26 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
async consume(config) {
|
async consume(config) {
|
||||||
|
const usefulResources = foundry.utils.deepClone(this.actor.system.resources);
|
||||||
|
for (var cost of config.costs) {
|
||||||
|
if (cost.keyIsID) {
|
||||||
|
usefulResources[cost.key] = {
|
||||||
|
value: cost.value,
|
||||||
|
target: this.parent.parent,
|
||||||
|
keyIsID: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
const resources = config.costs
|
const resources = config.costs
|
||||||
.filter(c => c.enabled !== false)
|
.filter(c => c.enabled !== false)
|
||||||
.map(c => {
|
.map(c => {
|
||||||
const resource = this.actor.system.resources[c.type];
|
const resource = usefulResources[c.key];
|
||||||
return { type: c.type, value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1) };
|
return {
|
||||||
|
key: c.key,
|
||||||
|
value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1),
|
||||||
|
target: resource.target,
|
||||||
|
keyIsID: resource.keyIsID
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.actor.modifyResource(resources);
|
await this.actor.modifyResource(resources);
|
||||||
|
|
@ -372,9 +387,27 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
hasCost(costs) {
|
async getResources(costs) {
|
||||||
|
const actorResources = this.actor.system.resources;
|
||||||
|
const itemResources = {};
|
||||||
|
for (var itemResource of costs) {
|
||||||
|
if (itemResource.keyIsID) {
|
||||||
|
itemResources[itemResource.key] = {
|
||||||
|
value: this.parent.resource.value ?? 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...actorResources,
|
||||||
|
...itemResources
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* COST */
|
||||||
|
async hasCost(costs) {
|
||||||
const realCosts = this.getRealCosts(costs),
|
const realCosts = this.getRealCosts(costs),
|
||||||
hasFearCost = realCosts.findIndex(c => c.type === 'fear');
|
hasFearCost = realCosts.findIndex(c => c.key === 'fear');
|
||||||
if (hasFearCost > -1) {
|
if (hasFearCost > -1) {
|
||||||
const fearCost = realCosts.splice(hasFearCost, 1)[0];
|
const fearCost = realCosts.splice(hasFearCost, 1)[0];
|
||||||
if (
|
if (
|
||||||
|
|
@ -385,16 +418,15 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* isReversed is a sign that the resource is inverted, IE it counts upwards instead of down */
|
/* isReversed is a sign that the resource is inverted, IE it counts upwards instead of down */
|
||||||
const resources = this.actor.system.resources;
|
const resources = await this.getResources(realCosts);
|
||||||
return realCosts.reduce(
|
return realCosts.reduce(
|
||||||
(a, c) =>
|
(a, c) =>
|
||||||
a && resources[c.type].isReversed
|
a && resources[c.key].isReversed
|
||||||
? resources[c.type].value + (c.total ?? c.value) <= resources[c.type].max
|
? resources[c.key].value + (c.total ?? c.value) <= resources[c.key].max
|
||||||
: resources[c.type]?.value >= (c.total ?? c.value),
|
: resources[c.key]?.value >= (c.total ?? c.value),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/* COST */
|
|
||||||
|
|
||||||
/* USES */
|
/* USES */
|
||||||
calcUses(uses) {
|
calcUses(uses) {
|
||||||
|
|
@ -409,7 +441,6 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
if (!uses) return true;
|
if (!uses) return true;
|
||||||
return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max;
|
return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max;
|
||||||
}
|
}
|
||||||
/* USES */
|
|
||||||
|
|
||||||
/* TARGET */
|
/* TARGET */
|
||||||
isTargetFriendly(target) {
|
isTargetFriendly(target) {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export default class DHDamageAction extends DHBaseAction {
|
||||||
|
|
||||||
async rollDamage(event, data) {
|
async rollDamage(event, data) {
|
||||||
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + '),
|
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + '),
|
||||||
damageTypes = [...new Set(this.damage.parts.reduce((a,c) => a.concat([...c.type]), []))];
|
damageTypes = [...new Set(this.damage.parts.reduce((a, c) => a.concat([...c.type]), []))];
|
||||||
|
|
||||||
damageTypes = !damageTypes.length ? ['physical'] : damageTypes;
|
damageTypes = !damageTypes.length ? ['physical'] : damageTypes;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import DHBaseActorSettings from "../../applications/sheets/api/actor-setting.mjs";
|
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
|
||||||
|
|
||||||
const resistanceField = () =>
|
const resistanceField = () =>
|
||||||
new foundry.data.fields.SchemaField({
|
new foundry.data.fields.SchemaField({
|
||||||
|
|
@ -37,13 +37,12 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
const schema = {};
|
const schema = {};
|
||||||
|
|
||||||
if(this.metadata.isNPC)
|
if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||||
schema.description = new fields.HTMLField({ required: true, nullable: true });
|
if (this.metadata.hasResistances)
|
||||||
if(this.metadata.hasResistances)
|
|
||||||
schema.resistance = new fields.SchemaField({
|
schema.resistance = new fields.SchemaField({
|
||||||
physical: resistanceField(),
|
physical: resistanceField(),
|
||||||
magical: resistanceField()
|
magical: resistanceField()
|
||||||
})
|
});
|
||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,7 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
resources: new fields.SchemaField({
|
resources: new fields.SchemaField({
|
||||||
hitPoints: resourceField(0, true),
|
hitPoints: resourceField(0, true),
|
||||||
stress: resourceField(6, true),
|
stress: resourceField(6, true),
|
||||||
hope: resourceField(6),
|
hope: resourceField(6)
|
||||||
tokens: new fields.ObjectField(),
|
|
||||||
dice: new fields.ObjectField()
|
|
||||||
}),
|
}),
|
||||||
traits: new fields.SchemaField({
|
traits: new fields.SchemaField({
|
||||||
agility: attributeField(),
|
agility: attributeField(),
|
||||||
|
|
@ -292,9 +290,7 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
}
|
}
|
||||||
|
|
||||||
get deathMoveViable() {
|
get deathMoveViable() {
|
||||||
return (
|
return this.resources.hitPoints.max > 0 && this.resources.hitPoints.value >= this.resources.hitPoints.max;
|
||||||
this.resources.hitPoints.max > 0 && this.resources.hitPoints.value >= this.resources.hitPoints.max
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get armorApplicableDamageTypes() {
|
get armorApplicableDamageTypes() {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ export default class DHArmor extends AttachableItem {
|
||||||
label: 'TYPES.Item.armor',
|
label: 'TYPES.Item.armor',
|
||||||
type: 'armor',
|
type: 'armor',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
|
||||||
isInventoryItem: true
|
isInventoryItem: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export default class AttachableItem extends BaseDataItem {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
attached: new fields.ArrayField(new fields.DocumentUUIDField({ type: "Item", nullable: true }))
|
attached: new fields.ArrayField(new fields.DocumentUUIDField({ type: 'Item', nullable: true }))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +90,10 @@ export default class AttachableItem extends BaseDataItem {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (effectsToRemove.length > 0) {
|
if (effectsToRemove.length > 0) {
|
||||||
await actor.deleteEmbeddedDocuments('ActiveEffect', effectsToRemove.map(e => e.id));
|
await actor.deleteEmbeddedDocuments(
|
||||||
|
'ActiveEffect',
|
||||||
|
effectsToRemove.map(e => e.id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,13 +143,18 @@ export default class AttachableItem extends BaseDataItem {
|
||||||
const parentUuidProperty = `${parentType}Uuid`;
|
const parentUuidProperty = `${parentType}Uuid`;
|
||||||
const effectsToRemove = actor.effects.filter(effect => {
|
const effectsToRemove = actor.effects.filter(effect => {
|
||||||
const attachmentSource = effect.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.itemAttachmentSource);
|
const attachmentSource = effect.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.itemAttachmentSource);
|
||||||
return attachmentSource &&
|
return (
|
||||||
|
attachmentSource &&
|
||||||
attachmentSource[parentUuidProperty] === this.parent.uuid &&
|
attachmentSource[parentUuidProperty] === this.parent.uuid &&
|
||||||
attachmentSource.itemUuid === attachedUuid;
|
attachmentSource.itemUuid === attachedUuid
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (effectsToRemove.length > 0) {
|
if (effectsToRemove.length > 0) {
|
||||||
await actor.deleteEmbeddedDocuments('ActiveEffect', effectsToRemove.map(e => e.id));
|
await actor.deleteEmbeddedDocuments(
|
||||||
|
'ActiveEffect',
|
||||||
|
effectsToRemove.map(e => e.id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,12 +11,15 @@
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
|
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ITEMS'];
|
||||||
|
|
||||||
/** @returns {ItemDataModelMetadata}*/
|
/** @returns {ItemDataModelMetadata}*/
|
||||||
static get metadata() {
|
static get metadata() {
|
||||||
return {
|
return {
|
||||||
label: 'Base Item',
|
label: 'Base Item',
|
||||||
type: 'base',
|
type: 'base',
|
||||||
hasDescription: false,
|
hasDescription: false,
|
||||||
|
hasResource: false,
|
||||||
isQuantifiable: false,
|
isQuantifiable: false,
|
||||||
isInventoryItem: false
|
isInventoryItem: false
|
||||||
};
|
};
|
||||||
|
|
@ -33,6 +36,33 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
|
|
||||||
if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||||
|
|
||||||
|
if (this.metadata.hasResource) {
|
||||||
|
schema.resource = new fields.SchemaField(
|
||||||
|
{
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: CONFIG.DH.ITEM.itemResourceTypes,
|
||||||
|
initial: CONFIG.DH.ITEM.itemResourceTypes.simple
|
||||||
|
}),
|
||||||
|
value: new fields.NumberField({ integer: true, min: 0, initial: 0 }),
|
||||||
|
max: new fields.StringField({ nullable: true, initial: null }),
|
||||||
|
icon: new fields.StringField(),
|
||||||
|
recovery: new fields.StringField({
|
||||||
|
choices: CONFIG.DH.GENERAL.refreshTypes,
|
||||||
|
initial: null,
|
||||||
|
nullable: true
|
||||||
|
}),
|
||||||
|
diceStates: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ integer: true, nullable: true, initial: null }),
|
||||||
|
used: new fields.BooleanField({ initial: false })
|
||||||
|
})
|
||||||
|
),
|
||||||
|
dieFaces: new fields.StringField({ initial: '4' })
|
||||||
|
},
|
||||||
|
{ nullable: true, initial: null }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.metadata.isQuantifiable)
|
if (this.metadata.isQuantifiable)
|
||||||
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
|
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
|
||||||
|
|
||||||
|
|
@ -62,28 +92,27 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**@inheritdoc */
|
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
// Skip if no initial action is required or actions already exist
|
// Skip if no initial action is required or actions already exist
|
||||||
if (!this.metadata.hasInitialAction || !foundry.utils.isEmpty(this.actions)) return;
|
if (this.metadata.hasInitialAction && foundry.utils.isEmpty(this.actions)) {
|
||||||
|
const metadataType = this.metadata.type;
|
||||||
|
const actionType = { weapon: 'attack' }[metadataType];
|
||||||
|
const ActionClass = game.system.api.models.actions.actionsTypes[actionType];
|
||||||
|
|
||||||
const metadataType = this.metadata.type;
|
const action = new ActionClass(
|
||||||
const actionType = { weapon: 'attack' }[metadataType];
|
{
|
||||||
const ActionClass = game.system.api.models.actions.actionsTypes[actionType];
|
_id: foundry.utils.randomID(),
|
||||||
|
type: actionType,
|
||||||
|
name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name),
|
||||||
|
...ActionClass.getSourceConfig(this.parent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parent: this.parent
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const action = new ActionClass(
|
this.updateSource({ actions: [action] });
|
||||||
{
|
}
|
||||||
_id: foundry.utils.randomID(),
|
|
||||||
type: actionType,
|
|
||||||
name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name),
|
|
||||||
...ActionClass.getSourceConfig(this.parent)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
parent: this.parent
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.updateSource({ actions: [action] });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onCreate(data) {
|
_onCreate(data) {
|
||||||
|
|
@ -95,7 +124,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
...feature,
|
...feature,
|
||||||
system: {
|
system: {
|
||||||
...feature.system,
|
...feature.system,
|
||||||
type: this.parent.type,
|
originItemType: this.parent.type,
|
||||||
originId: data._id,
|
originId: data._id,
|
||||||
identifier: feature.identifier
|
identifier: feature.identifier
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ export default class DHDomainCard extends BaseDataItem {
|
||||||
return foundry.utils.mergeObject(super.metadata, {
|
return foundry.utils.mergeObject(super.metadata, {
|
||||||
label: 'TYPES.Item.domainCard',
|
label: 'TYPES.Item.domainCard',
|
||||||
type: 'domainCard',
|
type: 'domainCard',
|
||||||
hasDescription: true
|
hasDescription: true,
|
||||||
|
hasResource: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ export default class DHFeature extends BaseDataItem {
|
||||||
return foundry.utils.mergeObject(super.metadata, {
|
return foundry.utils.mergeObject(super.metadata, {
|
||||||
label: 'TYPES.Item.feature',
|
label: 'TYPES.Item.feature',
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
hasDescription: true
|
hasDescription: true,
|
||||||
|
hasResource: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,10 +17,32 @@ export default class DHFeature extends BaseDataItem {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
type: new fields.StringField({ choices: CONFIG.DH.ITEM.featureTypes, nullable: true, initial: null }),
|
originItemType: new fields.StringField({
|
||||||
|
choices: CONFIG.DH.ITEM.featureTypes,
|
||||||
|
nullable: true,
|
||||||
|
initial: null
|
||||||
|
}),
|
||||||
originId: new fields.StringField({ nullable: true, initial: null }),
|
originId: new fields.StringField({ nullable: true, initial: null }),
|
||||||
identifier: new fields.StringField(),
|
identifier: new fields.StringField(),
|
||||||
actions: new fields.ArrayField(new ActionField())
|
actions: new fields.ArrayField(new ActionField())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get spellcastingModifier() {
|
||||||
|
let traitValue = 0;
|
||||||
|
if (this.actor && this.originId && ['class', 'subclass'].includes(this.originItemType)) {
|
||||||
|
if (this.originItemType === 'subclass') {
|
||||||
|
traitValue =
|
||||||
|
this.actor.system.traits[this.actor.items.get(this.originId).system.spellcastingTrait]?.value ?? 0;
|
||||||
|
} else {
|
||||||
|
const subclass =
|
||||||
|
this.actor.system.multiclass.value?.id === this.originId
|
||||||
|
? this.actor.system.multiclass.subclass
|
||||||
|
: this.actor.system.class.subclass;
|
||||||
|
traitValue = this.actor.system.traits[subclass.system.spellcastingTrait]?.value ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return traitValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ export default class DHWeapon extends AttachableItem {
|
||||||
label: 'TYPES.Item.weapon',
|
label: 'TYPES.Item.weapon',
|
||||||
type: 'weapon',
|
type: 'weapon',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
|
||||||
isInventoryItem: true
|
isInventoryItem: true
|
||||||
// hasInitialAction: true
|
// hasInitialAction: true
|
||||||
});
|
});
|
||||||
|
|
@ -37,7 +36,7 @@ export default class DHWeapon extends AttachableItem {
|
||||||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
attack: new ActionField({
|
attack: new ActionField({
|
||||||
initial: {
|
initial: {
|
||||||
name: 'Attack',
|
name: 'Attack',
|
||||||
img: 'icons/skills/melee/blood-slash-foam-red.webp',
|
img: 'icons/skills/melee/blood-slash-foam-red.webp',
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export default class DHRoll extends Roll {
|
||||||
|
|
||||||
// Create Chat Message
|
// Create Chat Message
|
||||||
if (config.source?.message) {
|
if (config.source?.message) {
|
||||||
if(game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
|
if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
|
||||||
} else {
|
} else {
|
||||||
config.message = await this.toMessage(roll, config);
|
config.message = await this.toMessage(roll, config);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { itemAbleRollParse } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
export default class DhActiveEffect extends ActiveEffect {
|
export default class DhActiveEffect extends ActiveEffect {
|
||||||
get isSuppressed() {
|
get isSuppressed() {
|
||||||
// If this is a copied effect from an attachment, never suppress it
|
// If this is a copied effect from an attachment, never suppress it
|
||||||
|
|
@ -30,10 +32,12 @@ export default class DhActiveEffect extends ActiveEffect {
|
||||||
if (!actor || !actor.items) return false;
|
if (!actor || !actor.items) return false;
|
||||||
|
|
||||||
return actor.items.some(item => {
|
return actor.items.some(item => {
|
||||||
return (item.type === 'armor' || item.type === 'weapon') &&
|
return (
|
||||||
item.system?.attached &&
|
(item.type === 'armor' || item.type === 'weapon') &&
|
||||||
Array.isArray(item.system.attached) &&
|
item.system?.attached &&
|
||||||
item.system.attached.includes(this.parent.uuid);
|
Array.isArray(item.system.attached) &&
|
||||||
|
item.system.attached.includes(this.parent.uuid)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,11 +55,7 @@ export default class DhActiveEffect extends ActiveEffect {
|
||||||
}
|
}
|
||||||
|
|
||||||
static applyField(model, change, field) {
|
static applyField(model, change, field) {
|
||||||
const isItemTarget = change.value.toLowerCase().startsWith('item.');
|
change.value = itemAbleRollParse(change.value, model, change.effect.parent);
|
||||||
change.value = isItemTarget ? change.value.slice(5) : change.value;
|
|
||||||
change.value = Roll.safeEval(
|
|
||||||
Roll.replaceFormulaData(change.value, isItemTarget ? change.effect.parent : model)
|
|
||||||
);
|
|
||||||
super.applyField(model, change, field);
|
super.applyField(model, change, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -396,7 +396,7 @@ export default class DhpActor extends Actor {
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, baseDamage, type) === false) return null;
|
if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, baseDamage, type) === false) return null;
|
||||||
|
|
||||||
if (this.type === 'companion') {
|
if (this.type === 'companion') {
|
||||||
await this.modifyResource([{ value: 1, type: 'stress' }]);
|
await this.modifyResource([{ value: 1, key: 'stress' }]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -418,8 +418,8 @@ export default class DhpActor extends Actor {
|
||||||
const { modifiedDamage, armorSpent, stressSpent } = armorStackResult;
|
const { modifiedDamage, armorSpent, stressSpent } = armorStackResult;
|
||||||
updates.find(u => u.type === 'hitPoints').value = modifiedDamage;
|
updates.find(u => u.type === 'hitPoints').value = modifiedDamage;
|
||||||
updates.push(
|
updates.push(
|
||||||
...(armorSpent ? [{ value: armorSpent, type: 'armorStack' }] : []),
|
...(armorSpent ? [{ value: armorSpent, key: 'armorStack' }] : []),
|
||||||
...(stressSpent ? [{ value: stressSpent, type: 'stress' }] : [])
|
...(stressSpent ? [{ value: stressSpent, key: 'stress' }] : [])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -434,8 +434,8 @@ export default class DhpActor extends Actor {
|
||||||
|
|
||||||
/* if(this.system.resistance[type]?.immunity) return 0;
|
/* if(this.system.resistance[type]?.immunity) return 0;
|
||||||
if(this.system.resistance[type]?.resistance) baseDamage = Math.ceil(baseDamage / 2); */
|
if(this.system.resistance[type]?.resistance) baseDamage = Math.ceil(baseDamage / 2); */
|
||||||
if(this.canResist(type, 'immunity')) return 0;
|
if (this.canResist(type, 'immunity')) return 0;
|
||||||
if(this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
|
if (this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
|
||||||
|
|
||||||
// const flatReduction = this.system.resistance[type].reduction;
|
// const flatReduction = this.system.resistance[type].reduction;
|
||||||
const flatReduction = this.getDamageTypeReduction(type);
|
const flatReduction = this.getDamageTypeReduction(type);
|
||||||
|
|
@ -448,13 +448,16 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
canResist(type, resistance) {
|
canResist(type, resistance) {
|
||||||
if(!type) return 0;
|
if (!type) return 0;
|
||||||
return type.every(t => this.system.resistance[t]?.[resistance] === true);
|
return type.every(t => this.system.resistance[t]?.[resistance] === true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDamageTypeReduction(type) {
|
getDamageTypeReduction(type) {
|
||||||
if(!type) return 0;
|
if (!type) return 0;
|
||||||
const reduction = Object.entries(this.system.resistance).reduce((a, [index, value]) => type.includes(index) ? Math.min(value.reduction, a) : a, Infinity);
|
const reduction = Object.entries(this.system.resistance).reduce(
|
||||||
|
(a, [index, value]) => (type.includes(index) ? Math.min(value.reduction, a) : a),
|
||||||
|
Infinity
|
||||||
|
);
|
||||||
return reduction === Infinity ? 0 : reduction;
|
return reduction === Infinity ? 0 : reduction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -467,39 +470,61 @@ export default class DhpActor extends Actor {
|
||||||
if (!resources.length) return;
|
if (!resources.length) return;
|
||||||
|
|
||||||
if (resources.find(r => r.type === 'stress')) this.convertStressDamageToHP(resources);
|
if (resources.find(r => r.type === 'stress')) this.convertStressDamageToHP(resources);
|
||||||
let updates = { actor: { target: this, resources: {} }, armor: { target: this.system.armor, resources: {} } };
|
let updates = {
|
||||||
|
actor: { target: this, resources: {} },
|
||||||
|
armor: { target: this.system.armor, resources: {} },
|
||||||
|
items: {}
|
||||||
|
};
|
||||||
resources.forEach(r => {
|
resources.forEach(r => {
|
||||||
switch (r.type) {
|
if (r.keyIsID) {
|
||||||
case 'fear':
|
updates.items[r.key] = {
|
||||||
ui.resources.updateFear(
|
target: r.target,
|
||||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear) + r.value
|
resources: {
|
||||||
);
|
'system.resource.value': r.target.system.resource.value + r.value
|
||||||
break;
|
}
|
||||||
case 'armorStack':
|
};
|
||||||
updates.armor.resources['system.marks.value'] = Math.max(
|
} else {
|
||||||
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
switch (r.key) {
|
||||||
0
|
case 'fear':
|
||||||
);
|
ui.resources.updateFear(
|
||||||
break;
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear) + r.value
|
||||||
default:
|
);
|
||||||
updates.actor.resources[`system.resources.${r.type}.value`] = Math.max(
|
break;
|
||||||
Math.min(
|
case 'armorStack':
|
||||||
this.system.resources[r.type].value + r.value,
|
updates.armor.resources['system.marks.value'] = Math.max(
|
||||||
this.system.resources[r.type].max
|
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
||||||
),
|
0
|
||||||
0
|
);
|
||||||
);
|
break;
|
||||||
break;
|
default:
|
||||||
|
updates.actor.resources[`system.resources.${r.key}.value`] = Math.max(
|
||||||
|
Math.min(this.system.resources[r.key].value + r.value, this.system.resources[r.key].max),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Object.values(updates).forEach(async u => {
|
Object.keys(updates).forEach(async key => {
|
||||||
if (Object.keys(u.resources).length > 0) {
|
const u = updates[key];
|
||||||
await emitAsGM(
|
if (key === 'items') {
|
||||||
GMUpdateEvent.UpdateDocument,
|
Object.values(u).forEach(async item => {
|
||||||
u.target.update.bind(u.target),
|
await emitAsGM(
|
||||||
u.resources,
|
GMUpdateEvent.UpdateDocument,
|
||||||
u.target.uuid
|
item.target.update.bind(item.target),
|
||||||
);
|
item.resources,
|
||||||
|
item.target.uuid
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (Object.keys(u.resources).length > 0) {
|
||||||
|
await emitAsGM(
|
||||||
|
GMUpdateEvent.UpdateDocument,
|
||||||
|
u.target.update.bind(u.target),
|
||||||
|
u.resources,
|
||||||
|
u.target.uuid
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { itemAbleRollParse } from './utils.mjs';
|
||||||
|
|
||||||
export default class RegisterHandlebarsHelpers {
|
export default class RegisterHandlebarsHelpers {
|
||||||
static registerHelpers() {
|
static registerHelpers() {
|
||||||
Handlebars.registerHelper({
|
Handlebars.registerHelper({
|
||||||
|
|
@ -6,8 +8,7 @@ export default class RegisterHandlebarsHelpers {
|
||||||
times: this.times,
|
times: this.times,
|
||||||
damageFormula: this.damageFormula,
|
damageFormula: this.damageFormula,
|
||||||
damageSymbols: this.damageSymbols,
|
damageSymbols: this.damageSymbols,
|
||||||
tertiary: this.tertiary,
|
rollParsed: this.rollParsed
|
||||||
signedNumber: this.signedNumber
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,7 +44,9 @@ export default class RegisterHandlebarsHelpers {
|
||||||
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
|
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
|
||||||
}
|
}
|
||||||
|
|
||||||
static tertiary(a, b) {
|
static rollParsed(value, actor, item, numerical) {
|
||||||
return a ?? b;
|
const isNumerical = typeof numerical === 'boolean' ? numerical : false;
|
||||||
|
const result = itemAbleRollParse(value, actor, item);
|
||||||
|
return isNumerical && !result ? 0 : result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,10 +245,10 @@ export const getDamageLabel = damage => {
|
||||||
|
|
||||||
export const damageKeyToNumber = key => {
|
export const damageKeyToNumber = key => {
|
||||||
return {
|
return {
|
||||||
'none': 0,
|
none: 0,
|
||||||
'minor': 1,
|
minor: 1,
|
||||||
'major': 2,
|
major: 2,
|
||||||
'severe': 3
|
severe: 3
|
||||||
}[key];
|
}[key];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -299,3 +299,15 @@ export const updateActorTokens = async (actor, update) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const itemAbleRollParse = (value, actor, item) => {
|
||||||
|
if (!value) return value;
|
||||||
|
|
||||||
|
const isItemTarget = value.toLowerCase().startsWith('item.');
|
||||||
|
const slicedValue = isItemTarget ? value.slice(5) : value;
|
||||||
|
try {
|
||||||
|
return Roll.safeEval(Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor));
|
||||||
|
} catch (_) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/sheets/global/partials/action-item.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/action-item.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs',
|
||||||
|
'systems/daggerheart/templates/sheets/global/partials/item-resource.hbs',
|
||||||
|
'systems/daggerheart/templates/sheets/global/partials/resource-section.hbs',
|
||||||
'systems/daggerheart/templates/components/card-preview.hbs',
|
'systems/daggerheart/templates/components/card-preview.hbs',
|
||||||
'systems/daggerheart/templates/levelup/parts/selectable-card-preview.hbs',
|
'systems/daggerheart/templates/levelup/parts/selectable-card-preview.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
@import './level-up/summary-container.less';
|
@import './level-up/summary-container.less';
|
||||||
@import './level-up/tiers-container.less';
|
@import './level-up/tiers-container.less';
|
||||||
|
|
||||||
|
@import './resource-dice/sheet.less';
|
||||||
|
|
||||||
@import './actions/action-list.less';
|
@import './actions/action-list.less';
|
||||||
|
|
||||||
@import './damage-selection/sheet.less';
|
@import './damage-selection/sheet.less';
|
||||||
|
|
|
||||||
58
styles/less/dialog/resource-dice/sheet.less
Normal file
58
styles/less/dialog/resource-dice/sheet.less
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
.theme-light .daggerheart.dialog.dh-style.views.resource-dice {
|
||||||
|
.resource-items .resource-item {
|
||||||
|
input {
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
filter: brightness(0) saturate(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.daggerheart.dialog.dh-style.views.resource-dice {
|
||||||
|
.reroll-confirmation {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-items {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.resource-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
input {
|
||||||
|
position: absolute;
|
||||||
|
border-color: light-dark(@dark-blue, @golden);
|
||||||
|
color: light-dark(black, white);
|
||||||
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
z-index: 2;
|
||||||
|
line-height: 22px;
|
||||||
|
height: unset;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(97%) sepia(7%) saturate(580%) hue-rotate(332deg)
|
||||||
|
brightness(96%) contrast(95%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -112,7 +112,7 @@
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
height: inherit;
|
height: inherit;
|
||||||
.tag {
|
.tag {
|
||||||
box-shadow: 0 0 0 1.1em #E5E5E5 inset;
|
box-shadow: 0 0 0 1.1em @beige inset;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
@ -120,9 +120,9 @@
|
||||||
color: black;
|
color: black;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
transition: .13s ease-out;
|
transition: 0.13s ease-out;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
font-size: .9rem;
|
font-size: 0.9rem;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
.remove {
|
.remove {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
@import './tab-actions.less';
|
@import './tab-actions.less';
|
||||||
@import './tab-features.less';
|
@import './tab-features.less';
|
||||||
@import './tab-effects.less';
|
@import './tab-effects.less';
|
||||||
|
@import './tab-settings.less';
|
||||||
@import './item-header.less';
|
@import './item-header.less';
|
||||||
@import './feature-section.less';
|
@import './feature-section.less';
|
||||||
@import './inventory-item.less';
|
@import './inventory-item.less';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,17 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
|
.theme-light .application.daggerheart.dh-style {
|
||||||
|
.inventory-item,
|
||||||
|
.card-item {
|
||||||
|
.item-resource .item-dice-resource {
|
||||||
|
img {
|
||||||
|
filter: brightness(0) saturate(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.application.daggerheart.dh-style {
|
.application.daggerheart.dh-style {
|
||||||
.inventory-item {
|
.inventory-item {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
@ -21,10 +32,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-label-wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 60px;
|
||||||
|
}
|
||||||
|
|
||||||
.item-label {
|
.item-label {
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
|
|
||||||
|
&.fullWidth {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
.item-name {
|
.item-name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
@ -84,11 +104,27 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
.card-label {
|
.card-label {
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
.controls {
|
.menu {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
max-height: 16px;
|
max-height: 16px;
|
||||||
|
|
||||||
|
&.resource-menu {
|
||||||
|
max-height: 55px;
|
||||||
|
|
||||||
|
&.dice-menu {
|
||||||
|
max-height: 118px;
|
||||||
|
|
||||||
|
.item-resources {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-resource {
|
||||||
|
width: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +133,7 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-label {
|
.card-label {
|
||||||
|
|
@ -123,15 +160,88 @@
|
||||||
color: @beige;
|
color: @beige;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
.menu {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
flex-direction: column;
|
||||||
align-items: center;
|
gap: 8px;
|
||||||
max-height: 0px;
|
max-height: 0px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: collapse;
|
visibility: collapse;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
color: @beige;
|
color: @beige;
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
gap: 15px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-resources {
|
||||||
|
width: 92px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-resource {
|
||||||
|
width: 92px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inventory-item,
|
||||||
|
.card-item {
|
||||||
|
.item-resources {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.resource-edit {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-resource {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: end;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
flex: none;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-dice-resource {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 26px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
position: absolute;
|
||||||
|
color: light-dark(white, black);
|
||||||
|
filter: drop-shadow(0 0 1px light-dark(@dark-blue, @golden));
|
||||||
|
z-index: 2;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(97%) sepia(7%) saturate(580%) hue-rotate(332deg)
|
||||||
|
brightness(96%) contrast(95%);
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
position: absolute;
|
||||||
|
text-shadow: 0 0 3px white;
|
||||||
|
filter: drop-shadow(0 1px white);
|
||||||
|
color: black;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
8
styles/less/global/tab-settings.less
Normal file
8
styles/less/global/tab-settings.less
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
@import '../utils/colors.less';
|
||||||
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
|
.sheet.daggerheart.dh-style {
|
||||||
|
.tab.settings {
|
||||||
|
margin-bottom: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.resource-roll {
|
||||||
|
.reroll-message {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.roll {
|
&.roll {
|
||||||
.dice-flavor {
|
.dice-flavor {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
{{#each source as |cost index|}}
|
{{#each source as |cost index|}}
|
||||||
<div class="nest-inputs">
|
<div class="nest-inputs">
|
||||||
{{formField ../fields.scalable label="Scalable" value=cost.scalable name=(concat "cost." index ".scalable") classes="checkbox"}}
|
{{formField ../fields.scalable label="Scalable" value=cost.scalable name=(concat "cost." index ".scalable") classes="checkbox"}}
|
||||||
{{formField ../fields.type choices=(@root.disableOption index ../fields.type.choices ../source) label="Resource" value=cost.type name=(concat "cost." index ".type") localize=true}}
|
{{formField ../fields.key choices=(@root.disableOption index @root.costOptions ../source) label="Resource" value=cost.key name=(concat "cost." index ".key") localize=true}}
|
||||||
{{formField ../fields.value label="Amount" value=cost.value name=(concat "cost." index ".value")}}
|
{{formField ../fields.value label="Amount" value=cost.value name=(concat "cost." index ".value")}}
|
||||||
{{formField ../fields.step label="Step" value=cost.step name=(concat "cost." index ".step") disabled=(not cost.scalable)}}
|
{{formField ../fields.step label="Step" value=cost.step name=(concat "cost." index ".step") disabled=(not cost.scalable)}}
|
||||||
<a class="btn" data-tooltip="{{localize "CONTROLS.CommonDelete"}}" data-action="removeElement" data-index="{{index}}"><i class="fas fa-trash"></i></a>
|
<a class="btn" data-tooltip="{{localize "CONTROLS.CommonDelete"}}" data-action="removeElement" data-index="{{index}}"><i class="fas fa-trash"></i></a>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
{{#each costs as | cost index |}}
|
{{#each costs as | cost index |}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
<label for="{{type}}">{{type}}: {{total}}</label>
|
<label>{{label}}: {{total}}</label>
|
||||||
<input name="costs.{{index}}.enabled" type="checkbox"{{#if enabled}} checked{{/if}}>
|
<input name="costs.{{index}}.enabled" type="checkbox"{{#if enabled}} checked{{/if}}>
|
||||||
{{#if scalable}}
|
{{#if scalable}}
|
||||||
<input type="range" value="{{scale}}" min="1" max="10" step="{{step}}" name="costs.{{index}}.scale">
|
<input type="range" value="{{scale}}" min="1" max="10" step="{{step}}" name="costs.{{index}}.scale">
|
||||||
|
|
|
||||||
16
templates/dialogs/dice-roll/resourceDice.hbs
Normal file
16
templates/dialogs/dice-roll/resourceDice.hbs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<section>
|
||||||
|
<div class="resource-items">
|
||||||
|
{{#times (rollParsed item.system.resource.max actor item numerical=true)}}
|
||||||
|
{{#with (ifThen (lookup ../diceStates this) (lookup ../diceStates this) this) as | state |}}
|
||||||
|
<div class="resource-item" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}">
|
||||||
|
<input type="number" data-dtype="Number" name={{concat "diceStates." (ifThen ../../this ../this state) ".value" }} value="{{state.value}}" />
|
||||||
|
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/d" (ifThen ../../item.system.resource.dieFaces ../../item.system.resource.dieFaces ../item.system.resource.dieFaces) ".svg"}}" />
|
||||||
|
</div>
|
||||||
|
{{/with}}
|
||||||
|
{{/times}}
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<button data-action="save">{{localize 'Save'}}</button>
|
||||||
|
<button data-action="rerollDice">{{localize "DAGGERHEART.APPLICATIONS.ResourceDice.rerollDice"}}</button>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
|
|
@ -4,6 +4,6 @@
|
||||||
data-tab="config"
|
data-tab="config"
|
||||||
>
|
>
|
||||||
{{> 'systems/daggerheart/templates/actionTypes/uses.hbs' fields=fields.uses.fields source=source.uses}}
|
{{> 'systems/daggerheart/templates/actionTypes/uses.hbs' fields=fields.uses.fields source=source.uses}}
|
||||||
{{> 'systems/daggerheart/templates/actionTypes/cost.hbs' fields=fields.cost.element.fields source=source.cost}}
|
{{> 'systems/daggerheart/templates/actionTypes/cost.hbs' fields=fields.cost.element.fields source=source.cost costOptions=costOptions}}
|
||||||
{{> 'systems/daggerheart/templates/actionTypes/range-target.hbs' fields=(object range=fields.range target=fields.target.fields) source=(object target=source.target range=source.range)}}
|
{{> 'systems/daggerheart/templates/actionTypes/range-target.hbs' fields=(object range=fields.range target=fields.target.fields) source=(object target=source.target range=source.range)}}
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -1,31 +1,35 @@
|
||||||
<li class="card-item" data-item-id="{{item.id}}" data-item-id="{{item.id}}">
|
<li class="card-item" data-item-id="{{item.id}}" data-item-id="{{item.id}}">
|
||||||
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
||||||
<div class="card-label">
|
<div class="card-label">
|
||||||
<div class="controls">
|
<div class="menu {{#if item.system.resource}}resource-menu{{/if}} {{#if (eq item.system.resource.type 'diceValue')}}dice-menu{{/if}}">
|
||||||
{{#if (eq type 'weapon')}}
|
{{#if item.system.resource}}
|
||||||
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.UI.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.UI.Tooltip.unequip'}}{{/unless}}">
|
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
|
||||||
<i class="fa-solid fa-hands"></i>
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq type 'armor')}}
|
<div class="controls">
|
||||||
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.UI.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.UI.Tooltip.unequip'}}{{/unless}}">
|
{{#if (eq type 'weapon')}}
|
||||||
<i class="fa-solid fa-shield"></i>
|
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.UI.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.UI.Tooltip.unequip'}}{{/unless}}">
|
||||||
</a>
|
<i class="fa-solid fa-hands"></i>
|
||||||
{{/if}}
|
|
||||||
{{#if (eq type 'domainCard')}}
|
|
||||||
{{#unless item.system.inVault}}
|
|
||||||
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToVault'}}">
|
|
||||||
<i class="fa-solid fa-arrow-down"></i>
|
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{/if}}
|
||||||
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToLoadout'}}">
|
{{#if (eq type 'armor')}}
|
||||||
<i class="fa-solid fa-arrow-up"></i>
|
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.UI.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.UI.Tooltip.unequip'}}{{/unless}}">
|
||||||
|
<i class="fa-solid fa-shield"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/unless}}
|
{{/if}}
|
||||||
|
{{#if (eq type 'domainCard')}}
|
||||||
{{/if}}
|
{{#unless item.system.inVault}}
|
||||||
<a data-action="toChat" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
|
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToVault'}}">
|
||||||
<a data-action="triggerContextMenu" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
|
<i class="fa-solid fa-arrow-down"></i>
|
||||||
|
</a>
|
||||||
|
{{else}}
|
||||||
|
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToLoadout'}}">
|
||||||
|
<i class="fa-solid fa-arrow-up"></i>
|
||||||
|
</a>
|
||||||
|
{{/unless}}
|
||||||
|
{{/if}}
|
||||||
|
<a data-action="toChat" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
|
||||||
|
<a data-action="triggerContextMenu" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-name">{{item.name}}</div>
|
<div class="card-name">{{item.name}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,128 +1,137 @@
|
||||||
<li class="inventory-item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-type="{{type}}" draggable="true">
|
<li class="inventory-item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-type="{{type}}" draggable="true">
|
||||||
<img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" data-action="useItem" {{#if (not noTooltip)}}data-tooltip="{{concat "#item#" item.uuid}}"{{/if}} />
|
<img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" data-action="useItem" {{#if (not noTooltip)}}data-tooltip="{{concat "#item#" item.uuid}}"{{/if}} />
|
||||||
<div class="item-label">
|
<div class="item-label-wrapper">
|
||||||
{{#if isCompanion}}
|
<div class="item-label {{#unless (and (not isSidebar) (or (eq item.system.resource.type 'simple') item.system.quantity))}}fullWidth{{/unless}}">
|
||||||
<a class="item-name" data-action="attackRoll">{{item.name}}</a>
|
{{#if isCompanion}}
|
||||||
{{else}}
|
<a class="item-name" data-action="attackRoll">{{item.name}}</a>
|
||||||
<div class="item-name">{{item.name}}</div>
|
{{else}}
|
||||||
{{/if}}
|
<div class="item-name">{{item.name}}</div>
|
||||||
{{#if (eq type 'weapon')}}
|
{{/if}}
|
||||||
<div class="item-tags">
|
{{#if (eq type 'weapon')}}
|
||||||
|
<div class="item-tags">
|
||||||
|
{{#if isSidebar}}
|
||||||
|
<div class="item-labels">
|
||||||
|
<div class="label">
|
||||||
|
{{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.short')}}
|
||||||
|
{{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.short')}}
|
||||||
|
<span> - </span>
|
||||||
|
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
|
||||||
|
{{#each item.system.attack.damage.parts.0.type as | type | }}
|
||||||
|
{{#with (lookup @root.config.GENERAL.damageTypes type)}}
|
||||||
|
<i class="fa-solid {{icon}}"></i>
|
||||||
|
{{/with}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="tag">
|
||||||
|
{{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.name')}}
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
{{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.name')}}
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
|
||||||
|
(
|
||||||
|
{{#each item.system.attack.damage.parts.0.type}}
|
||||||
|
{{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}}
|
||||||
|
{{/each}}
|
||||||
|
)
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
{{localize (concat 'DAGGERHEART.CONFIG.Burden.' item.system.burden)}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (eq type 'armor')}}
|
||||||
{{#if isSidebar}}
|
{{#if isSidebar}}
|
||||||
<div class="item-labels">
|
<div class="item-labels">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
{{!-- {{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.short')}} --}}
|
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
||||||
{{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.short')}}
|
{{item.system.baseScore}}
|
||||||
<span> - </span>
|
|
||||||
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
|
|
||||||
{{!-- ({{localize (concat 'DAGGERHEART.CONFIG.DamageType.' item.system.attack.damage.parts.0.type '.abbreviation')}}) --}}
|
|
||||||
{{#each item.system.attack.damage.parts.0.type as | type | }}
|
|
||||||
{{#with (lookup @root.config.GENERAL.damageTypes type)}}
|
|
||||||
<i class="fa-solid {{icon}}"></i>
|
|
||||||
{{/with}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="tag">
|
<div class="item-tags">
|
||||||
{{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.name')}}
|
<div class="tag">
|
||||||
</div>
|
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
||||||
<div class="tag">
|
{{item.system.baseScore}}
|
||||||
{{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.name')}}
|
</div>
|
||||||
</div>
|
<div class="tag">
|
||||||
<div class="tag">
|
{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.base"}}:
|
||||||
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
|
{{item.system.baseThresholds.major}}
|
||||||
(
|
<span>/</span>
|
||||||
{{#each item.system.attack.damage.parts.0.type}}
|
{{item.system.baseThresholds.severe}}
|
||||||
{{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}}
|
</div>
|
||||||
{{/each}}
|
|
||||||
)
|
|
||||||
</div>
|
|
||||||
<div class="tag">
|
|
||||||
{{localize (concat 'DAGGERHEART.CONFIG.Burden.' item.system.burden)}}
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
{{/if}}
|
||||||
{{/if}}
|
{{#if (eq type 'domainCard')}}
|
||||||
{{#if (eq type 'armor')}}
|
{{#if isSidebar}}
|
||||||
{{#if isSidebar}}
|
<div class="item-labels">
|
||||||
<div class="item-labels">
|
<div class="label">
|
||||||
<div class="label">
|
{{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}}
|
||||||
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
<span> - </span>
|
||||||
{{item.system.baseScore}}
|
{{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}}
|
||||||
|
<span> - </span>
|
||||||
|
<span class="recall-value">{{item.system.recallCost}}</span>
|
||||||
|
<i class="fa-solid fa-bolt"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{{else}}
|
||||||
{{else}}
|
<div class="item-tags">
|
||||||
|
<div class="tag">
|
||||||
|
{{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}}
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
{{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}}
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
<span class="recall-label">{{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}: </span>
|
||||||
|
<span class="recall-value">{{item.system.recallCost}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if (eq type 'effect')}}
|
||||||
<div class="item-tags">
|
<div class="item-tags">
|
||||||
<div class="tag">
|
<div class="tag">
|
||||||
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
{{localize (concat 'TYPES.Item.' item.parent.type)}}
|
||||||
{{item.system.baseScore}}
|
<span>: </span>
|
||||||
|
{{item.parent.name}}
|
||||||
</div>
|
</div>
|
||||||
<div class="tag">
|
<div class="tag">
|
||||||
{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.base"}}:
|
{{#if item.duration.duration}}
|
||||||
{{item.system.baseThresholds.major}}
|
{{localize 'DAGGERHEART.EFFECTS.Duration.temporary'}}
|
||||||
<span>/</span>
|
{{else}}
|
||||||
{{item.system.baseThresholds.severe}}
|
{{localize 'DAGGERHEART.EFFECTS.Duration.passive'}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{#each item.statuses as |status|}}
|
||||||
|
<div class="tag">
|
||||||
|
{{localize (concat 'DAGGERHEART.CONFIG.Condition.' status '.name')}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (eq type 'action')}}
|
||||||
|
<div class="item-tags">
|
||||||
|
<div class="tag">
|
||||||
|
{{localize (concat 'DAGGERHEART.ACTIONS.TYPES.' item.type '.name')}}
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
{{localize (concat 'DAGGERHEART.CONFIG.ActionType.' item.actionType)}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{#if (and (not isSidebar) (eq item.system.resource.type 'simple'))}}
|
||||||
|
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq type 'domainCard')}}
|
{{#if (and (not isSidebar) item.system.quantity)}}
|
||||||
{{#if isSidebar}}
|
<div class="item-resource">
|
||||||
<div class="item-labels">
|
<input type="number" class="inventory-item-quantity" value="{{item.system.quantity}}" step="1" />
|
||||||
<div class="label">
|
|
||||||
{{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}}
|
|
||||||
<span> - </span>
|
|
||||||
{{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}}
|
|
||||||
<span> - </span>
|
|
||||||
<span class="recall-value">{{item.system.recallCost}}</span>
|
|
||||||
<i class="fa-solid fa-bolt"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="item-tags">
|
|
||||||
<div class="tag">
|
|
||||||
{{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}}
|
|
||||||
</div>
|
|
||||||
<div class="tag">
|
|
||||||
{{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}}
|
|
||||||
</div>
|
|
||||||
<div class="tag">
|
|
||||||
<span class="recall-label">{{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}: </span>
|
|
||||||
<span class="recall-value">{{item.system.recallCost}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{#if (eq type 'effect')}}
|
|
||||||
<div class="item-tags">
|
|
||||||
<div class="tag">
|
|
||||||
{{localize (concat 'TYPES.Item.' item.parent.type)}}
|
|
||||||
<span>: </span>
|
|
||||||
{{item.parent.name}}
|
|
||||||
</div>
|
|
||||||
<div class="tag">
|
|
||||||
{{#if item.duration.duration}}
|
|
||||||
{{localize 'DAGGERHEART.EFFECTS.Duration.temporary'}}
|
|
||||||
{{else}}
|
|
||||||
{{localize 'DAGGERHEART.EFFECTS.Duration.passive'}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{#each item.statuses as |status|}}
|
|
||||||
<div class="tag">
|
|
||||||
{{localize (concat 'DAGGERHEART.CONFIG.Condition.' status '.name')}}
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{#if (eq type 'action')}}
|
|
||||||
<div class="item-tags">
|
|
||||||
<div class="tag">
|
|
||||||
{{localize (concat 'DAGGERHEART.ACTIONS.TYPES.' item.type '.name')}}
|
|
||||||
</div>
|
|
||||||
<div class="tag">
|
|
||||||
{{localize (concat 'DAGGERHEART.CONFIG.ActionType.' item.actionType)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -175,7 +184,9 @@
|
||||||
<span></span>
|
<span></span>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
<div class="item-description">{{#unless isSidebar}}{{{item.system.description}}}{{/unless}}</div>
|
<div class="item-description">{{#unless isSidebar}}{{{item.system.description}}}{{/unless}}</div>
|
||||||
|
{{#if (and (not isSidebar) (eq item.system.resource.type 'diceValue'))}}
|
||||||
|
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
|
||||||
|
{{/if}}
|
||||||
{{#if featureType}}
|
{{#if featureType}}
|
||||||
<div class="item-buttons">
|
<div class="item-buttons">
|
||||||
{{#each item.system.actions as | action |}}
|
{{#each item.system.actions as | action |}}
|
||||||
|
|
|
||||||
21
templates/sheets/global/partials/item-resource.hbs
Normal file
21
templates/sheets/global/partials/item-resource.hbs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{{#if (eq item.system.resource.type 'simple')}}
|
||||||
|
<div class="item-resource">
|
||||||
|
<i class="{{#if item.system.resource.icon}}{{item.system.resource.icon}}{{else}}fa-solid fa-hashtag{{/if}}"></i>
|
||||||
|
<input type="number" class="inventory-item-resource" value="{{item.system.resource.value}}" step="1" />
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="item-resources">
|
||||||
|
{{#times (rollParsed item.system.resource.max item.parent item numerical=true)}}
|
||||||
|
{{#with (ifThen (lookup ../item.system.resource.diceStates this) (lookup ../item.system.resource.diceStates this) this) as | state |}}
|
||||||
|
<a class="item-resource" data-action="toggleResourceDice" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}">
|
||||||
|
<div class="item-dice-resource">
|
||||||
|
<label>{{ifThen state.value state.value '?'}}</label>
|
||||||
|
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/d" (ifThen ../../item.system.resource.dieFaces ../../item.system.resource.dieFaces ../item.system.resource.dieFaces) ".svg"}}" />
|
||||||
|
{{#if state.used}}<i class="fa-solid fa-x"></i>{{/if}}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{/with}}
|
||||||
|
{{/times}}
|
||||||
|
<a data-action="handleResourceDice" data-tooltip="DAGGERHEART.APPLICATIONS.ResourceDice.rerollDice"><i class="fa-solid fa-dice resource-edit"></i></a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
29
templates/sheets/global/partials/resource-section.hbs
Normal file
29
templates/sheets/global/partials/resource-section.hbs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
{{localize "DAGGERHEART.GENERAL.Resource.single"}}
|
||||||
|
{{#unless source.system.resource}}
|
||||||
|
<a data-action="addResource"><i class="fa-solid fa-plus icon-button"></i></a>
|
||||||
|
{{else}}
|
||||||
|
<a data-action="removeResource"><i class="fa-solid fa-trash"></i></a>
|
||||||
|
{{/unless}}
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
{{#if source.system.resource}}
|
||||||
|
<div class="two-columns even">
|
||||||
|
{{formGroup systemFields.resource.fields.type value=source.system.resource.type localize=true blank=false}}
|
||||||
|
{{formGroup systemFields.resource.fields.recovery value=source.system.resource.recovery localize=true}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="two-columns even">
|
||||||
|
{{#if (eq source.system.resource.type 'simple')}}
|
||||||
|
{{formGroup systemFields.resource.fields.value value=source.system.resource.value localize=true}}
|
||||||
|
{{formGroup systemFields.resource.fields.max value=source.system.resource.max localize=true}}
|
||||||
|
{{else}}
|
||||||
|
{{formGroup systemFields.resource.fields.dieFaces value=source.system.resource.dieFaces localize=true}}
|
||||||
|
{{formGroup systemFields.resource.fields.max value=source.system.resource.max label="DAGGERHEART.ITEMS.FIELDS.resource.amount.label" localize=true}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if (eq source.system.resource.type 'simple')}}{{formGroup systemFields.resource.fields.icon value=source.system.resource.icon localize=true placeholder="fa-solid fa-hashtag"}}{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</fieldset>
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
data-group='{{tabs.features.group}}'
|
data-group='{{tabs.features.group}}'
|
||||||
>
|
>
|
||||||
<div class="two-columns even">
|
<div class="two-columns even">
|
||||||
<fieldset>
|
<fieldset class="drop-section hope-feature">
|
||||||
<legend>{{localize "DAGGERHEART.ITEMS.Class.hopeFeatures"}} <a><i class="fa-solid fa-plus icon-button" data-type="hope" data-action="addFeature"></i></a></legend>
|
<legend>{{localize "DAGGERHEART.ITEMS.Class.hopeFeatures"}} <a><i class="fa-solid fa-plus icon-button" data-type="hope" data-action="addFeature"></i></a></legend>
|
||||||
<div class="feature-list">
|
<div class="feature-list">
|
||||||
{{#each source.system.hopeFeatures as |feature|}}
|
{{#each source.system.hopeFeatures as |feature|}}
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset class="drop-section class-feature">
|
||||||
<legend>{{localize "DAGGERHEART.ITEMS.Class.classFeatures"}} <a><i class="fa-solid fa-plus icon-button" data-type="class" data-action="addFeature"></i></a></legend>
|
<legend>{{localize "DAGGERHEART.ITEMS.Class.classFeatures"}} <a><i class="fa-solid fa-plus icon-button" data-type="class" data-action="addFeature"></i></a></legend>
|
||||||
<div class="feature-list">
|
<div class="feature-list">
|
||||||
{{#each source.system.classFeatures as |feature|}}
|
{{#each source.system.classFeatures as |feature|}}
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,6 @@
|
||||||
<span>{{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}</span>
|
<span>{{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}</span>
|
||||||
{{formField systemFields.recallCost value=source.system.recallCost data-dtype="Number"}}
|
{{formField systemFields.recallCost value=source.system.recallCost data-dtype="Number"}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
{{> "systems/daggerheart/templates/sheets/global/partials/resource-section.hbs" }}
|
||||||
</section>
|
</section>
|
||||||
7
templates/sheets/items/feature/settings.hbs
Normal file
7
templates/sheets/items/feature/settings.hbs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<section
|
||||||
|
class='tab {{tabs.settings.cssClass}} {{tabs.settings.id}}'
|
||||||
|
data-tab='{{tabs.settings.id}}'
|
||||||
|
data-group='{{tabs.settings.group}}'
|
||||||
|
>
|
||||||
|
{{> "systems/daggerheart/templates/sheets/global/partials/resource-section.hbs" }}
|
||||||
|
</section>
|
||||||
3
templates/ui/chat/resource-roll.hbs
Normal file
3
templates/ui/chat/resource-roll.hbs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="daggerheart chat resource-roll">
|
||||||
|
<h5 class="reroll-message">{{localize "DAGGERHEART.UI.Chat.resourceRoll.playerMessage" user=user name=name }}</h5>
|
||||||
|
</div>
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="tooltip-tag-label">{{localize feature.name}}</div>
|
<div class="tooltip-tag-label">{{localize feature.name}}</div>
|
||||||
{{#if feature.img}}<img class="tooltip-tag-image" src="{{feature.img}}" />{{/if}}
|
{{#if feature.img}}<img class="tooltip-tag-image" src="{{feature.img}}" />{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="tooltip-tag-description">{{{localize (tertiary feature.description feature.system.description)}}}</div>
|
<div class="tooltip-tag-description">{{{localize (ifThen feature.description feature.description feature.system.description)}}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue