mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-11 19:25:21 +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": {
|
||||
"title": "Ownership Selection - {name}",
|
||||
"default": "Default Ownership"
|
||||
},
|
||||
"ResourceDice": {
|
||||
"title": "{name} Resource",
|
||||
"rerollDice": "Reroll Dice"
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -632,6 +636,10 @@
|
|||
"abbreviation": "AS"
|
||||
}
|
||||
},
|
||||
"ItemResourceType": {
|
||||
"simple": "Simple",
|
||||
"diceValue": "Dice Value"
|
||||
},
|
||||
"Range": {
|
||||
"self": {
|
||||
"name": "Self",
|
||||
|
|
@ -1068,6 +1076,10 @@
|
|||
"shortrest": "Short Rest",
|
||||
"longrest": "Long Rest"
|
||||
},
|
||||
"Resource": {
|
||||
"single": "Resource",
|
||||
"plural": "Resources"
|
||||
},
|
||||
"Tabs": {
|
||||
"details": "Details",
|
||||
"attack": "Attack",
|
||||
|
|
@ -1154,6 +1166,17 @@
|
|||
"value": "Value"
|
||||
},
|
||||
"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": {
|
||||
"baseScore": "Base Score",
|
||||
"baseThresholds": {
|
||||
|
|
@ -1331,8 +1354,8 @@
|
|||
},
|
||||
"UI": {
|
||||
"Chat": {
|
||||
"dualityRoll": {
|
||||
"abilityCheckTitle": "{ability} Check"
|
||||
"applyEffect": {
|
||||
"title": "Apply Effects - {name}"
|
||||
},
|
||||
"attackRoll": {
|
||||
"title": "Attack - {attack}",
|
||||
|
|
@ -1348,25 +1371,28 @@
|
|||
"hitTarget": "Hit Targets",
|
||||
"selectedTarget": "Selected"
|
||||
},
|
||||
"applyEffect": {
|
||||
"title": "Apply Effects - {name}"
|
||||
},
|
||||
"healingRoll": {
|
||||
"title": "Heal - {healing}",
|
||||
"heal": "Heal"
|
||||
},
|
||||
"deathMove": {
|
||||
"title": "Death Move"
|
||||
},
|
||||
"domainCard": {
|
||||
"title": "Domain Card"
|
||||
},
|
||||
"dualityRoll": {
|
||||
"abilityCheckTitle": "{ability} Check"
|
||||
},
|
||||
"featureTitle": "Class Feature",
|
||||
"foundationCard": {
|
||||
"ancestryTitle": "Ancestry Card",
|
||||
"communityTitle": "Community Card",
|
||||
"subclassFeatureTitle": "Subclass Feature"
|
||||
},
|
||||
"featureTitle": "Class Feature"
|
||||
"healingRoll": {
|
||||
"title": "Heal - {healing}",
|
||||
"heal": "Heal"
|
||||
},
|
||||
"resourceRoll": {
|
||||
"playerMessage": "{user} rerolled their {name}"
|
||||
}
|
||||
},
|
||||
"Notifications": {
|
||||
"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 Downtime } from './downtime.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;
|
||||
if (this.config.costs?.length) {
|
||||
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);
|
||||
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.canRoll = context.canRoll && this.action.hasUses(context.uses);
|
||||
}
|
||||
if(this.roll) {
|
||||
if (this.roll) {
|
||||
context.roll = this.roll;
|
||||
context.rollType = this.roll?.constructor.name;
|
||||
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.getRealIndex = this.getRealIndex.bind(this);
|
||||
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||
context.costOptions = this.getCostOptions();
|
||||
context.disableOption = this.disableOption.bind(this);
|
||||
context.isNPC = this.action.actor && this.action.actor.type !== 'character';
|
||||
context.hasRoll = this.action.hasRoll;
|
||||
|
|
@ -125,8 +126,21 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|||
this.render(true);
|
||||
}
|
||||
|
||||
disableOption(index, options, choices) {
|
||||
const filtered = foundry.utils.deepClone(options);
|
||||
getCostOptions() {
|
||||
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 => {
|
||||
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);
|
||||
}
|
||||
|
||||
_prepareSubmitData(event, formData) {
|
||||
_prepareSubmitData(_event, formData) {
|
||||
const submitData = foundry.utils.expandObject(formData.object);
|
||||
for (const keyPath of this.constructor.CLEAN_ARRAYS) {
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { abilities } from '../../../config/actorConfig.mjs';
|
|||
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
|
||||
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
||||
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||
import { itemAbleRollParse } from '../../../helpers/utils.mjs';
|
||||
|
||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||
|
||||
|
|
@ -25,6 +26,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
toggleEquipItem: CharacterSheet.#toggleEquipItem,
|
||||
useItem: this.useItem, //TODO Fix this
|
||||
useAction: this.useAction,
|
||||
toggleResourceDice: this.toggleResourceDice,
|
||||
handleResourceDice: this.handleResourceDice,
|
||||
toChat: this.toChat
|
||||
},
|
||||
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 */
|
||||
async _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 */
|
||||
/* -------------------------------------------- */
|
||||
|
|
@ -640,6 +675,41 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
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
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
@ -672,14 +742,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
|
||||
async _onDragStart(event) {
|
||||
const item = this.getItem(event);
|
||||
|
||||
|
||||
const dragData = {
|
||||
type: item.documentName,
|
||||
uuid: item.uuid
|
||||
};
|
||||
|
||||
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
|
||||
|
||||
|
||||
super._onDragStart(event);
|
||||
}
|
||||
|
||||
|
|
@ -687,7 +757,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
// Prevent event bubbling to avoid duplicate handling
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
|
||||
super._onDrop(event);
|
||||
this._onDropItem(event, TextEditor.getDragEventData(event));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
removeAction: DHBaseItemSheet.#removeAction,
|
||||
addFeature: DHBaseItemSheet.#addFeature,
|
||||
editFeature: DHBaseItemSheet.#editFeature,
|
||||
removeFeature: DHBaseItemSheet.#removeFeature
|
||||
removeFeature: DHBaseItemSheet.#removeFeature,
|
||||
addResource: DHBaseItemSheet.#addResource,
|
||||
removeResource: DHBaseItemSheet.#removeResource
|
||||
},
|
||||
dragDrop: [
|
||||
{ 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 */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
export default function ItemAttachmentSheet(Base) {
|
||||
return class extends Base {
|
||||
static DEFAULT_OPTIONS = {
|
||||
|
|
@ -25,10 +24,7 @@ export default function ItemAttachmentSheet(Base) {
|
|||
...super.TABS,
|
||||
primary: {
|
||||
...super.TABS?.primary,
|
||||
tabs: [
|
||||
...(super.TABS?.primary?.tabs || []),
|
||||
{ id: 'attachments' }
|
||||
],
|
||||
tabs: [...(super.TABS?.primary?.tabs || []), { id: 'attachments' }],
|
||||
initial: super.TABS?.primary?.initial || 'description',
|
||||
labelPrefix: super.TABS?.primary?.labelPrefix || 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
|
|
@ -46,29 +42,28 @@ export default function ItemAttachmentSheet(Base) {
|
|||
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
|
||||
|
||||
const attachmentsSection = event.target.closest('.attachments-section');
|
||||
if (!attachmentsSection) return super._onDrop(event);
|
||||
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
|
||||
const item = await Item.implementation.fromDropData(data);
|
||||
if (!item) return;
|
||||
|
||||
|
||||
// Call the data model's public method
|
||||
await this.document.system.addAttachment(item);
|
||||
}
|
||||
|
||||
|
||||
static async #removeAttachment(event, target) {
|
||||
// Call the data model's public method
|
||||
await this.document.system.removeAttachment(target.dataset.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
async _preparePartContext(partId, context) {
|
||||
await super._preparePartContext(partId, context);
|
||||
|
||||
|
||||
if (partId === 'attachments') {
|
||||
// Keep this simple UI preparation in the mixin
|
||||
const attachedUUIDs = this.document.system.attached;
|
||||
|
|
@ -83,8 +78,8 @@ export default function ItemAttachmentSheet(Base) {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return context;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
|||
template: 'systems/daggerheart/templates/sheets/items/armor/settings.hbs',
|
||||
scrollable: ['.settings']
|
||||
},
|
||||
...super.PARTS,
|
||||
...super.PARTS
|
||||
};
|
||||
|
||||
/**@inheritdoc */
|
||||
|
|
|
|||
|
|
@ -85,6 +85,16 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
await this.document.update({
|
||||
'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') {
|
||||
if (target.classList.contains('primary-weapon-section')) {
|
||||
if (!this.document.system.characterGuide.suggestedPrimaryWeapon && !item.system.secondary)
|
||||
|
|
@ -144,7 +154,7 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
static async #removeItemFromCollection(_event, element) {
|
||||
const { uuid, target } = element.dataset;
|
||||
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' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
||||
settings: { template: 'systems/daggerheart/templates/sheets/items/feature/settings.hbs' },
|
||||
actions: {
|
||||
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
||||
scrollable: ['.actions']
|
||||
|
|
@ -34,7 +35,7 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
|||
/**@override */
|
||||
static TABS = {
|
||||
primary: {
|
||||
tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'effects' }],
|
||||
tabs: [{ id: 'description' }, { id: 'settings' }, { id: 'actions' }, { id: 'effects' }],
|
||||
initial: 'description',
|
||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
|
|
@ -67,7 +68,6 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
|||
}
|
||||
),
|
||||
title = game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType');
|
||||
console.log(this.document);
|
||||
|
||||
return foundry.applications.api.DialogV2.prompt({
|
||||
window: { title },
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
|||
template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs',
|
||||
scrollable: ['.settings']
|
||||
},
|
||||
...super.PARTS,
|
||||
...super.PARTS
|
||||
};
|
||||
|
||||
/**@inheritdoc */
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ export const encounterCountdown = {
|
|||
position: 'countdown-encounter-position'
|
||||
};
|
||||
|
||||
export const itemAttachmentSource = 'attachmentSource';
|
||||
export const itemAttachmentSource = 'attachmentSource';
|
||||
|
|
|
|||
|
|
@ -366,31 +366,6 @@ export const abilityCosts = {
|
|||
label: 'Armor Stack',
|
||||
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: {
|
||||
id: 'fear',
|
||||
label: 'Fear',
|
||||
|
|
|
|||
|
|
@ -1334,3 +1334,14 @@ export const actionTypes = {
|
|||
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',
|
||||
initial: 'physical',
|
||||
initial: 'physical'
|
||||
}
|
||||
),
|
||||
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 D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
|
||||
|
||||
|
|
@ -35,12 +35,12 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
}),
|
||||
cost: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
type: new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.abilityCosts,
|
||||
key: new fields.StringField({
|
||||
nullable: false,
|
||||
required: true,
|
||||
initial: 'hope'
|
||||
}),
|
||||
keyIsID: new fields.BooleanField(),
|
||||
value: new fields.NumberField({ nullable: true, initial: 1 }),
|
||||
scalable: new fields.BooleanField({ initial: false }),
|
||||
step: new fields.NumberField({ nullable: true, initial: null })
|
||||
|
|
@ -181,7 +181,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
|
||||
// Add Roll results to RollDatas
|
||||
actorData.result = data.roll?.total ?? 1;
|
||||
|
||||
|
||||
actorData.scale = data.costs?.length // Right now only return the first scalable cost.
|
||||
? (data.costs.find(c => c.scalable)?.total ?? 1)
|
||||
: 1;
|
||||
|
|
@ -204,7 +204,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
|
||||
// Prepare Costs
|
||||
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.");
|
||||
|
||||
// Prepare Uses
|
||||
|
|
@ -278,7 +278,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
|
||||
prepareCost() {
|
||||
const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : [];
|
||||
return costs;
|
||||
return this.calcCosts(costs);
|
||||
}
|
||||
|
||||
prepareUse() {
|
||||
|
|
@ -327,11 +327,26 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
}
|
||||
|
||||
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
|
||||
.filter(c => c.enabled !== false)
|
||||
.map(c => {
|
||||
const resource = this.actor.system.resources[c.type];
|
||||
return { type: c.type, value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1) };
|
||||
const resource = usefulResources[c.key];
|
||||
return {
|
||||
key: c.key,
|
||||
value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1),
|
||||
target: resource.target,
|
||||
keyIsID: resource.keyIsID
|
||||
};
|
||||
});
|
||||
|
||||
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),
|
||||
hasFearCost = realCosts.findIndex(c => c.type === 'fear');
|
||||
hasFearCost = realCosts.findIndex(c => c.key === 'fear');
|
||||
if (hasFearCost > -1) {
|
||||
const fearCost = realCosts.splice(hasFearCost, 1)[0];
|
||||
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 */
|
||||
const resources = this.actor.system.resources;
|
||||
const resources = await this.getResources(realCosts);
|
||||
return realCosts.reduce(
|
||||
(a, c) =>
|
||||
a && resources[c.type].isReversed
|
||||
? resources[c.type].value + (c.total ?? c.value) <= resources[c.type].max
|
||||
: resources[c.type]?.value >= (c.total ?? c.value),
|
||||
a && resources[c.key].isReversed
|
||||
? resources[c.key].value + (c.total ?? c.value) <= resources[c.key].max
|
||||
: resources[c.key]?.value >= (c.total ?? c.value),
|
||||
true
|
||||
);
|
||||
}
|
||||
/* COST */
|
||||
|
||||
/* USES */
|
||||
calcUses(uses) {
|
||||
|
|
@ -409,7 +441,6 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
if (!uses) return true;
|
||||
return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max;
|
||||
}
|
||||
/* USES */
|
||||
|
||||
/* TARGET */
|
||||
isTargetFriendly(target) {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
|
||||
async rollDamage(event, data) {
|
||||
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;
|
||||
|
||||
if (!formula || formula == '') return;
|
||||
|
|
@ -36,7 +36,7 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
config.source.message = data._id;
|
||||
config.directDamage = false;
|
||||
}
|
||||
|
||||
|
||||
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import DHBaseActorSettings from "../../applications/sheets/api/actor-setting.mjs";
|
||||
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
|
||||
|
||||
const resistanceField = () =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
|
|
@ -37,13 +37,12 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
const fields = foundry.data.fields;
|
||||
const schema = {};
|
||||
|
||||
if(this.metadata.isNPC)
|
||||
schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
if(this.metadata.hasResistances)
|
||||
if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
if (this.metadata.hasResistances)
|
||||
schema.resistance = new fields.SchemaField({
|
||||
physical: resistanceField(),
|
||||
magical: resistanceField()
|
||||
})
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,9 +39,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(0, true),
|
||||
stress: resourceField(6, true),
|
||||
hope: resourceField(6),
|
||||
tokens: new fields.ObjectField(),
|
||||
dice: new fields.ObjectField()
|
||||
hope: resourceField(6)
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField(),
|
||||
|
|
@ -292,9 +290,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}
|
||||
|
||||
get deathMoveViable() {
|
||||
return (
|
||||
this.resources.hitPoints.max > 0 && this.resources.hitPoints.value >= this.resources.hitPoints.max
|
||||
);
|
||||
return this.resources.hitPoints.max > 0 && this.resources.hitPoints.value >= this.resources.hitPoints.max;
|
||||
}
|
||||
|
||||
get armorApplicableDamageTypes() {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ export default class DHArmor extends AttachableItem {
|
|||
label: 'TYPES.Item.armor',
|
||||
type: 'armor',
|
||||
hasDescription: true,
|
||||
isQuantifiable: true,
|
||||
isInventoryItem: true
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default class AttachableItem extends BaseDataItem {
|
|||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...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) {
|
||||
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 effectsToRemove = actor.effects.filter(effect => {
|
||||
const attachmentSource = effect.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.itemAttachmentSource);
|
||||
return attachmentSource &&
|
||||
return (
|
||||
attachmentSource &&
|
||||
attachmentSource[parentUuidProperty] === this.parent.uuid &&
|
||||
attachmentSource.itemUuid === attachedUuid;
|
||||
attachmentSource.itemUuid === attachedUuid
|
||||
);
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ITEMS'];
|
||||
|
||||
/** @returns {ItemDataModelMetadata}*/
|
||||
static get metadata() {
|
||||
return {
|
||||
label: 'Base Item',
|
||||
type: 'base',
|
||||
hasDescription: false,
|
||||
hasResource: false,
|
||||
isQuantifiable: 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.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)
|
||||
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;
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _preCreate(data, options, user) {
|
||||
// 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 actionType = { weapon: 'attack' }[metadataType];
|
||||
const ActionClass = game.system.api.models.actions.actionsTypes[actionType];
|
||||
const action = new ActionClass(
|
||||
{
|
||||
_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(
|
||||
{
|
||||
_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] });
|
||||
this.updateSource({ actions: [action] });
|
||||
}
|
||||
}
|
||||
|
||||
_onCreate(data) {
|
||||
|
|
@ -95,7 +124,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
...feature,
|
||||
system: {
|
||||
...feature.system,
|
||||
type: this.parent.type,
|
||||
originItemType: this.parent.type,
|
||||
originId: data._id,
|
||||
identifier: feature.identifier
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Item.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, {
|
||||
label: 'TYPES.Item.feature',
|
||||
type: 'feature',
|
||||
hasDescription: true
|
||||
hasDescription: true,
|
||||
hasResource: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -16,10 +17,32 @@ export default class DHFeature extends BaseDataItem {
|
|||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...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 }),
|
||||
identifier: new fields.StringField(),
|
||||
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',
|
||||
type: 'weapon',
|
||||
hasDescription: true,
|
||||
isQuantifiable: true,
|
||||
isInventoryItem: true
|
||||
// hasInitialAction: true
|
||||
});
|
||||
|
|
@ -37,7 +36,7 @@ export default class DHWeapon extends AttachableItem {
|
|||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||
})
|
||||
),
|
||||
attack: new ActionField({
|
||||
attack: new ActionField({
|
||||
initial: {
|
||||
name: 'Attack',
|
||||
img: 'icons/skills/melee/blood-slash-foam-red.webp',
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export default class DHRoll extends Roll {
|
|||
|
||||
// Create Chat 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 {
|
||||
config.message = await this.toMessage(roll, config);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { itemAbleRollParse } from '../helpers/utils.mjs';
|
||||
|
||||
export default class DhActiveEffect extends ActiveEffect {
|
||||
get isSuppressed() {
|
||||
// If this is a copied effect from an attachment, never suppress it
|
||||
|
|
@ -24,16 +26,18 @@ export default class DhActiveEffect extends ActiveEffect {
|
|||
*/
|
||||
get isAttached() {
|
||||
if (!this.parent || !this.parent.parent) return false;
|
||||
|
||||
|
||||
// Check if this item's UUID is in any actor's armor or weapon attachment lists
|
||||
const actor = this.parent.parent;
|
||||
if (!actor || !actor.items) return false;
|
||||
|
||||
|
||||
return actor.items.some(item => {
|
||||
return (item.type === 'armor' || item.type === 'weapon') &&
|
||||
item.system?.attached &&
|
||||
Array.isArray(item.system.attached) &&
|
||||
item.system.attached.includes(this.parent.uuid);
|
||||
return (
|
||||
(item.type === 'armor' || item.type === 'weapon') &&
|
||||
item.system?.attached &&
|
||||
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) {
|
||||
const isItemTarget = change.value.toLowerCase().startsWith('item.');
|
||||
change.value = isItemTarget ? change.value.slice(5) : change.value;
|
||||
change.value = Roll.safeEval(
|
||||
Roll.replaceFormulaData(change.value, isItemTarget ? change.effect.parent : model)
|
||||
);
|
||||
change.value = itemAbleRollParse(change.value, model, change.effect.parent);
|
||||
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 (this.type === 'companion') {
|
||||
await this.modifyResource([{ value: 1, type: 'stress' }]);
|
||||
await this.modifyResource([{ value: 1, key: 'stress' }]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -418,8 +418,8 @@ export default class DhpActor extends Actor {
|
|||
const { modifiedDamage, armorSpent, stressSpent } = armorStackResult;
|
||||
updates.find(u => u.type === 'hitPoints').value = modifiedDamage;
|
||||
updates.push(
|
||||
...(armorSpent ? [{ value: armorSpent, type: 'armorStack' }] : []),
|
||||
...(stressSpent ? [{ value: stressSpent, type: 'stress' }] : [])
|
||||
...(armorSpent ? [{ value: armorSpent, key: 'armorStack' }] : []),
|
||||
...(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]?.resistance) baseDamage = Math.ceil(baseDamage / 2); */
|
||||
if(this.canResist(type, 'immunity')) return 0;
|
||||
if(this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
|
||||
if (this.canResist(type, 'immunity')) return 0;
|
||||
if (this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
|
||||
|
||||
// const flatReduction = this.system.resistance[type].reduction;
|
||||
const flatReduction = this.getDamageTypeReduction(type);
|
||||
|
|
@ -448,13 +448,16 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
|
||||
canResist(type, resistance) {
|
||||
if(!type) return 0;
|
||||
if (!type) return 0;
|
||||
return type.every(t => this.system.resistance[t]?.[resistance] === true);
|
||||
}
|
||||
|
||||
getDamageTypeReduction(type) {
|
||||
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);
|
||||
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
|
||||
);
|
||||
return reduction === Infinity ? 0 : reduction;
|
||||
}
|
||||
|
||||
|
|
@ -467,39 +470,61 @@ export default class DhpActor extends Actor {
|
|||
if (!resources.length) return;
|
||||
|
||||
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 => {
|
||||
switch (r.type) {
|
||||
case 'fear':
|
||||
ui.resources.updateFear(
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear) + r.value
|
||||
);
|
||||
break;
|
||||
case 'armorStack':
|
||||
updates.armor.resources['system.marks.value'] = Math.max(
|
||||
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
||||
0
|
||||
);
|
||||
break;
|
||||
default:
|
||||
updates.actor.resources[`system.resources.${r.type}.value`] = Math.max(
|
||||
Math.min(
|
||||
this.system.resources[r.type].value + r.value,
|
||||
this.system.resources[r.type].max
|
||||
),
|
||||
0
|
||||
);
|
||||
break;
|
||||
if (r.keyIsID) {
|
||||
updates.items[r.key] = {
|
||||
target: r.target,
|
||||
resources: {
|
||||
'system.resource.value': r.target.system.resource.value + r.value
|
||||
}
|
||||
};
|
||||
} else {
|
||||
switch (r.key) {
|
||||
case 'fear':
|
||||
ui.resources.updateFear(
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear) + r.value
|
||||
);
|
||||
break;
|
||||
case 'armorStack':
|
||||
updates.armor.resources['system.marks.value'] = Math.max(
|
||||
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
||||
0
|
||||
);
|
||||
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 => {
|
||||
if (Object.keys(u.resources).length > 0) {
|
||||
await emitAsGM(
|
||||
GMUpdateEvent.UpdateDocument,
|
||||
u.target.update.bind(u.target),
|
||||
u.resources,
|
||||
u.target.uuid
|
||||
);
|
||||
Object.keys(updates).forEach(async key => {
|
||||
const u = updates[key];
|
||||
if (key === 'items') {
|
||||
Object.values(u).forEach(async item => {
|
||||
await emitAsGM(
|
||||
GMUpdateEvent.UpdateDocument,
|
||||
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 {
|
||||
static registerHelpers() {
|
||||
Handlebars.registerHelper({
|
||||
|
|
@ -6,8 +8,7 @@ export default class RegisterHandlebarsHelpers {
|
|||
times: this.times,
|
||||
damageFormula: this.damageFormula,
|
||||
damageSymbols: this.damageSymbols,
|
||||
tertiary: this.tertiary,
|
||||
signedNumber: this.signedNumber
|
||||
rollParsed: this.rollParsed
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +44,9 @@ export default class RegisterHandlebarsHelpers {
|
|||
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
|
||||
}
|
||||
|
||||
static tertiary(a, b) {
|
||||
return a ?? b;
|
||||
static rollParsed(value, actor, item, numerical) {
|
||||
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 => {
|
||||
return {
|
||||
'none': 0,
|
||||
'minor': 1,
|
||||
'major': 2,
|
||||
'severe': 3
|
||||
none: 0,
|
||||
minor: 1,
|
||||
major: 2,
|
||||
severe: 3
|
||||
}[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/domain-card-item.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/levelup/parts/selectable-card-preview.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
@import './level-up/summary-container.less';
|
||||
@import './level-up/tiers-container.less';
|
||||
|
||||
@import './resource-dice/sheet.less';
|
||||
|
||||
@import './actions/action-list.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;
|
||||
height: inherit;
|
||||
.tag {
|
||||
box-shadow: 0 0 0 1.1em #E5E5E5 inset;
|
||||
box-shadow: 0 0 0 1.1em @beige inset;
|
||||
vertical-align: top;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
|
|
@ -120,9 +120,9 @@
|
|||
color: black;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
transition: .13s ease-out;
|
||||
transition: 0.13s ease-out;
|
||||
height: 22px;
|
||||
font-size: .9rem;
|
||||
font-size: 0.9rem;
|
||||
gap: 0.5em;
|
||||
z-index: 1;
|
||||
.remove {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
@import './tab-actions.less';
|
||||
@import './tab-features.less';
|
||||
@import './tab-effects.less';
|
||||
@import './tab-settings.less';
|
||||
@import './item-header.less';
|
||||
@import './feature-section.less';
|
||||
@import './inventory-item.less';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
@import '../utils/colors.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 {
|
||||
.inventory-item {
|
||||
display: grid;
|
||||
|
|
@ -21,10 +32,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
.item-label-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 60px;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-family: @font-body;
|
||||
align-self: center;
|
||||
|
||||
&.fullWidth {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
|
@ -84,11 +104,27 @@
|
|||
&:hover {
|
||||
.card-label {
|
||||
padding-top: 15px;
|
||||
.controls {
|
||||
.menu {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: all 0.3s ease;
|
||||
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%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
|
|
@ -123,15 +160,88 @@
|
|||
color: @beige;
|
||||
}
|
||||
|
||||
.controls {
|
||||
.menu {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-height: 0px;
|
||||
opacity: 0;
|
||||
visibility: collapse;
|
||||
transition: all 0.3s ease;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@
|
|||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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 {
|
||||
.dice-flavor {
|
||||
text-align: center;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
{{#each source as |cost index|}}
|
||||
<div class="nest-inputs">
|
||||
{{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.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>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
{{#each costs as | cost index |}}
|
||||
<div class="form-group">
|
||||
<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}}>
|
||||
{{#if scalable}}
|
||||
<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"
|
||||
>
|
||||
{{> '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)}}
|
||||
</section>
|
||||
|
|
@ -1,31 +1,35 @@
|
|||
<li class="card-item" data-item-id="{{item.id}}" data-item-id="{{item.id}}">
|
||||
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
||||
<div class="card-label">
|
||||
<div class="controls">
|
||||
{{#if (eq type 'weapon')}}
|
||||
<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-hands"></i>
|
||||
</a>
|
||||
<div class="menu {{#if item.system.resource}}resource-menu{{/if}} {{#if (eq item.system.resource.type 'diceValue')}}dice-menu{{/if}}">
|
||||
{{#if item.system.resource}}
|
||||
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
|
||||
{{/if}}
|
||||
{{#if (eq type 'armor')}}
|
||||
<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>
|
||||
{{/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>
|
||||
<div class="controls">
|
||||
{{#if (eq type 'weapon')}}
|
||||
<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-hands"></i>
|
||||
</a>
|
||||
{{else}}
|
||||
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToLoadout'}}">
|
||||
<i class="fa-solid fa-arrow-up"></i>
|
||||
{{/if}}
|
||||
{{#if (eq type 'armor')}}
|
||||
<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>
|
||||
{{/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>
|
||||
{{/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>
|
||||
{{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 class="card-name">{{item.name}}</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">
|
||||
<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">
|
||||
{{#if isCompanion}}
|
||||
<a class="item-name" data-action="attackRoll">{{item.name}}</a>
|
||||
{{else}}
|
||||
<div class="item-name">{{item.name}}</div>
|
||||
{{/if}}
|
||||
{{#if (eq type 'weapon')}}
|
||||
<div class="item-tags">
|
||||
<div class="item-label-wrapper">
|
||||
<div class="item-label {{#unless (and (not isSidebar) (or (eq item.system.resource.type 'simple') item.system.quantity))}}fullWidth{{/unless}}">
|
||||
{{#if isCompanion}}
|
||||
<a class="item-name" data-action="attackRoll">{{item.name}}</a>
|
||||
{{else}}
|
||||
<div class="item-name">{{item.name}}</div>
|
||||
{{/if}}
|
||||
{{#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}}
|
||||
<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}}
|
||||
{{!-- ({{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}}
|
||||
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
||||
{{item.system.baseScore}}
|
||||
</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 class="item-tags">
|
||||
<div class="tag">
|
||||
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
||||
{{item.system.baseScore}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.base"}}:
|
||||
{{item.system.baseThresholds.major}}
|
||||
<span>/</span>
|
||||
{{item.system.baseThresholds.severe}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq type 'armor')}}
|
||||
{{#if isSidebar}}
|
||||
<div class="item-labels">
|
||||
<div class="label">
|
||||
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
||||
{{item.system.baseScore}}
|
||||
{{/if}}
|
||||
{{#if (eq type 'domainCard')}}
|
||||
{{#if isSidebar}}
|
||||
<div class="item-labels">
|
||||
<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>
|
||||
</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="tag">
|
||||
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
||||
{{item.system.baseScore}}
|
||||
{{localize (concat 'TYPES.Item.' item.parent.type)}}
|
||||
<span>: </span>
|
||||
{{item.parent.name}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.base"}}:
|
||||
{{item.system.baseThresholds.major}}
|
||||
<span>/</span>
|
||||
{{item.system.baseThresholds.severe}}
|
||||
{{#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>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if (and (not isSidebar) (eq item.system.resource.type 'simple'))}}
|
||||
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
|
||||
{{/if}}
|
||||
{{#if (eq type 'domainCard')}}
|
||||
{{#if isSidebar}}
|
||||
<div class="item-labels">
|
||||
<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>
|
||||
{{#if (and (not isSidebar) item.system.quantity)}}
|
||||
<div class="item-resource">
|
||||
<input type="number" class="inventory-item-quantity" value="{{item.system.quantity}}" step="1" />
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
@ -175,7 +184,9 @@
|
|||
<span></span>
|
||||
{{/unless}}
|
||||
<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}}
|
||||
<div class="item-buttons">
|
||||
{{#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}}'
|
||||
>
|
||||
<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>
|
||||
<div class="feature-list">
|
||||
{{#each source.system.hopeFeatures as |feature|}}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
</div>
|
||||
</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>
|
||||
<div class="feature-list">
|
||||
{{#each source.system.classFeatures as |feature|}}
|
||||
|
|
|
|||
|
|
@ -17,4 +17,6 @@
|
|||
<span>{{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}</span>
|
||||
{{formField systemFields.recallCost value=source.system.recallCost data-dtype="Number"}}
|
||||
</fieldset>
|
||||
|
||||
{{> "systems/daggerheart/templates/sheets/global/partials/resource-section.hbs" }}
|
||||
</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>
|
||||
{{#if feature.img}}<img class="tooltip-tag-image" src="{{feature.img}}" />{{/if}}
|
||||
</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>
|
||||
{{/each}}
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue