mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-03-07 06:26:13 +01:00
Compare commits
17 commits
aa85eb89bf
...
f004f575a5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f004f575a5 | ||
|
|
154612ca7d | ||
|
|
5732639391 | ||
|
|
9bfe3505bf | ||
|
|
54ee029d02 | ||
|
|
d7386a93fe | ||
|
|
9cba77ec11 | ||
|
|
0675e1f019 | ||
|
|
1212bd01f8 | ||
|
|
3267f3f531 | ||
|
|
986544a653 | ||
|
|
5459581f7f | ||
|
|
0d0b5125ba | ||
|
|
c48842dd2d | ||
|
|
e79ccd34e9 | ||
|
|
4324c3abf2 | ||
|
|
1b09b44d6c |
30 changed files with 232 additions and 302 deletions
|
|
@ -420,10 +420,7 @@ const updateActorsRangeDependentEffects = async token => {
|
||||||
// Get required distance and special case 5 feet to test adjacency
|
// Get required distance and special case 5 feet to test adjacency
|
||||||
const required = rangeMeasurement[range];
|
const required = rangeMeasurement[range];
|
||||||
const reverse = type === CONFIG.DH.GENERAL.rangeInclusion.outsideRange.id;
|
const reverse = type === CONFIG.DH.GENERAL.rangeInclusion.outsideRange.id;
|
||||||
const inRange =
|
const inRange = userTarget.distanceTo(token.object) <= required;
|
||||||
required === 5
|
|
||||||
? userTarget.isAdjacentWith(token.object)
|
|
||||||
: userTarget.distanceTo(token.object) <= required;
|
|
||||||
if (reverse ? inRange : !inRange) {
|
if (reverse ? inRange : !inRange) {
|
||||||
enabledEffect = false;
|
enabledEffect = false;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
12
lang/en.json
12
lang/en.json
|
|
@ -1166,12 +1166,12 @@
|
||||||
},
|
},
|
||||||
"far": {
|
"far": {
|
||||||
"name": "Far",
|
"name": "Far",
|
||||||
"description": "means a distance where one can see the appearance of a person or object, but probably not in great detail-- across a small battlefield or down a large corridor. This is usually about 30-100 feet away. While under danger, a PC will likely have to make an Agility check to get here safely. Anything on a battle map that is within the length of a standard piece of paper (~10-11 inches) can usually be considered far.",
|
"description": "means a distance where one can see the appearance of a person or object, but probably not in great detail-- across a small battlefield or down a large corridor. This is usually about 30-100 feet away. While under danger, a PC will likely have to make an Agility roll to get here safely. Anything on a battle map that is within the length of a standard piece of paper (~10-11 inches) can usually be considered far.",
|
||||||
"short": "Far"
|
"short": "Far"
|
||||||
},
|
},
|
||||||
"veryFar": {
|
"veryFar": {
|
||||||
"name": "Very Far",
|
"name": "Very Far",
|
||||||
"description": "means a distance where you can see the shape of a person or object, but probably not make outany details-- across a large battlefield or down a long street, generally about 100-300 feet away. While under danger, a PC likely has to make an Agility check to get here safely. Anything on a battle map that is beyond far distance, but still within sight of the characters can usually be considered very far.",
|
"description": "means a distance where you can see the shape of a person or object, but probably not make outany details-- across a large battlefield or down a long street, generally about 100-300 feet away. While under danger, a PC likely has to make an Agility roll to get here safely. Anything on a battle map that is beyond far distance, but still within sight of the characters can usually be considered very far.",
|
||||||
"short": "V. Far"
|
"short": "V. Far"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1294,6 +1294,7 @@
|
||||||
"triggerTexts": {
|
"triggerTexts": {
|
||||||
"strangePatternsContentTitle": "Matched {nr} times.",
|
"strangePatternsContentTitle": "Matched {nr} times.",
|
||||||
"strangePatternsContentSubTitle": "Increase hope and stress to a total of {nr}.",
|
"strangePatternsContentSubTitle": "Increase hope and stress to a total of {nr}.",
|
||||||
|
"strangePatternsActionExplanation": "Left click to increase, right click to decrease",
|
||||||
"ferocityContent": "Spend 2 Hope to gain {bonus} bonus Evasion until after the next attack against you?",
|
"ferocityContent": "Spend 2 Hope to gain {bonus} bonus Evasion until after the next attack against you?",
|
||||||
"ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you."
|
"ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you."
|
||||||
},
|
},
|
||||||
|
|
@ -2805,7 +2806,7 @@
|
||||||
"title": "Domain Card"
|
"title": "Domain Card"
|
||||||
},
|
},
|
||||||
"dualityRoll": {
|
"dualityRoll": {
|
||||||
"abilityCheckTitle": "{ability} Check"
|
"abilityCheckTitle": "{ability} Roll"
|
||||||
},
|
},
|
||||||
"effectSummary": {
|
"effectSummary": {
|
||||||
"title": "Effects Applied",
|
"title": "Effects Applied",
|
||||||
|
|
@ -2820,7 +2821,7 @@
|
||||||
"selectLeader": "Select a Leader",
|
"selectLeader": "Select a Leader",
|
||||||
"selectMember": "Select a Member",
|
"selectMember": "Select a Member",
|
||||||
"rerollTitle": "Reroll Group Roll",
|
"rerollTitle": "Reroll Group Roll",
|
||||||
"rerollContent": "Are you sure you want to reroll your {trait} check?",
|
"rerollContent": "Are you sure you want to reroll your {trait} roll?",
|
||||||
"rerollTooltip": "Reroll",
|
"rerollTooltip": "Reroll",
|
||||||
"wholePartySelected": "The whole party is selected"
|
"wholePartySelected": "The whole party is selected"
|
||||||
},
|
},
|
||||||
|
|
@ -2986,7 +2987,8 @@
|
||||||
"tokenActorMissing": "{name} is missing an Actor",
|
"tokenActorMissing": "{name} is missing an Actor",
|
||||||
"tokenActorsMissing": "[{names}] missing Actors",
|
"tokenActorsMissing": "[{names}] missing Actors",
|
||||||
"domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
|
"domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
|
||||||
"knowTheTide": "Know The Tide gained a token"
|
"knowTheTide": "Know The Tide gained a token",
|
||||||
|
"lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}"
|
||||||
},
|
},
|
||||||
"Sidebar": {
|
"Sidebar": {
|
||||||
"actorDirectory": {
|
"actorDirectory": {
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,6 +228,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,6 +248,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
await this.settings.updateSource({
|
await this.settings.updateSource({
|
||||||
[`${path}.-=${id}`]: null
|
[`${path}.-=${id}`]: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,42 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
this.changeChoices = DhActiveEffectConfig.getChangeChoices();
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ['daggerheart', 'sheet', 'dh-style']
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' },
|
||||||
|
tabs: { template: 'templates/generic/tab-navigation.hbs' },
|
||||||
|
details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] },
|
||||||
|
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' },
|
||||||
|
changes: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs',
|
||||||
|
scrollable: ['ol[data-changes]']
|
||||||
|
},
|
||||||
|
footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' }
|
||||||
|
};
|
||||||
|
|
||||||
|
static TABS = {
|
||||||
|
sheet: {
|
||||||
|
tabs: [
|
||||||
|
{ id: 'details', icon: 'fa-solid fa-book' },
|
||||||
|
{ id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' },
|
||||||
|
{ id: 'changes', icon: 'fa-solid fa-gears' }
|
||||||
|
],
|
||||||
|
initial: 'details',
|
||||||
|
labelPrefix: 'EFFECT.TABS'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ChangeChoices for the changes autocomplete. Static for use in this class aswell as in settings-active-effect-config.mjs
|
||||||
|
* @returns {ChangeChoice { value: string, label: string, hint: string, group: string }[]}
|
||||||
|
*/
|
||||||
|
static getChangeChoices() {
|
||||||
const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty'];
|
const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty'];
|
||||||
|
|
||||||
const getAllLeaves = (root, group, parentPath = '') => {
|
const getAllLeaves = (root, group, parentPath = '') => {
|
||||||
|
|
@ -23,7 +59,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
|
|
||||||
return leaves;
|
return leaves;
|
||||||
};
|
};
|
||||||
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
|
return Object.keys(game.system.api.models.actors).reduce((acc, key) => {
|
||||||
if (ignoredActorKeys.includes(key)) return acc;
|
if (ignoredActorKeys.includes(key)) return acc;
|
||||||
|
|
||||||
const model = game.system.api.models.actors[key];
|
const model = game.system.api.models.actors[key];
|
||||||
|
|
@ -62,34 +98,6 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
|
||||||
classes: ['daggerheart', 'sheet', 'dh-style']
|
|
||||||
};
|
|
||||||
|
|
||||||
static PARTS = {
|
|
||||||
header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' },
|
|
||||||
tabs: { template: 'templates/generic/tab-navigation.hbs' },
|
|
||||||
details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] },
|
|
||||||
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' },
|
|
||||||
changes: {
|
|
||||||
template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs',
|
|
||||||
scrollable: ['ol[data-changes]']
|
|
||||||
},
|
|
||||||
footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' }
|
|
||||||
};
|
|
||||||
|
|
||||||
static TABS = {
|
|
||||||
sheet: {
|
|
||||||
tabs: [
|
|
||||||
{ id: 'details', icon: 'fa-solid fa-book' },
|
|
||||||
{ id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' },
|
|
||||||
{ id: 'changes', icon: 'fa-solid fa-gears' }
|
|
||||||
],
|
|
||||||
initial: 'details',
|
|
||||||
labelPrefix: 'EFFECT.TABS'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_attachPartListeners(partId, htmlElement, options) {
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
const changeChoices = this.changeChoices;
|
const changeChoices = this.changeChoices;
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
||||||
super({});
|
super({});
|
||||||
|
|
||||||
this.effect = foundry.utils.deepClone(effect);
|
this.effect = foundry.utils.deepClone(effect);
|
||||||
const ignoredActorKeys = ['config', 'DhEnvironment'];
|
this.changeChoices = game.system.api.applications.sheetConfigs.ActiveEffectConfig.getChangeChoices();
|
||||||
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
|
|
||||||
if (!ignoredActorKeys.includes(key)) {
|
|
||||||
const model = game.system.api.models.actors[key];
|
|
||||||
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
|
|
||||||
const group = game.i18n.localize(model.metadata.label);
|
|
||||||
const choices = CONFIG.Token.documentClass
|
|
||||||
.getTrackedAttributeChoices(attributes, model)
|
|
||||||
.map(x => ({ ...x, group: group }));
|
|
||||||
acc.push(...choices);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,11 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateData(event, element, formData) {
|
static async updateData(_event, _element, formData) {
|
||||||
const data = foundry.utils.expandObject(formData.object);
|
const data = foundry.utils.expandObject(formData.object);
|
||||||
foundry.utils.mergeObject(this.move, data);
|
await this.updateMove({
|
||||||
|
[`${this.movePath}`]: data
|
||||||
|
});
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
@ -135,9 +137,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.settings.updateSource({ [`${this.actionsPath}.${action.id}`]: action });
|
await this.updateMove({ [`${this.actionsPath}.${action.id}`]: action });
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,13 +150,12 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect);
|
await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect);
|
||||||
if (!updatedEffect) return;
|
if (!updatedEffect) return;
|
||||||
|
|
||||||
await this.settings.updateSource({
|
await this.updateMove({
|
||||||
[`${this.movePath}.effects`]: this.move.effects.reduce((acc, effect, index) => {
|
[`${this.movePath}.effects`]: this.move.effects.reduce((acc, effect, index) => {
|
||||||
acc.push(index === effectIndex ? { ...updatedEffect, id: effect.id } : effect);
|
acc.push(index === effectIndex ? { ...updatedEffect, id: effect.id } : effect);
|
||||||
return acc;
|
return acc;
|
||||||
}, [])
|
}, [])
|
||||||
});
|
});
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
this.render();
|
this.render();
|
||||||
} else {
|
} else {
|
||||||
const action = this.move.actions.get(id);
|
const action = this.move.actions.get(id);
|
||||||
|
|
@ -171,13 +170,13 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
: existingEffectIndex === -1
|
: existingEffectIndex === -1
|
||||||
? [...currentEffects, effectData]
|
? [...currentEffects, effectData]
|
||||||
: currentEffects.with(existingEffectIndex, effectData);
|
: currentEffects.with(existingEffectIndex, effectData);
|
||||||
await this.settings.updateSource({
|
await this.updateMove({
|
||||||
[`${this.movePath}.effects`]: updatedEffects
|
[`${this.movePath}.effects`]: updatedEffects
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.settings.updateSource({ [`${this.actionsPath}.${id}`]: updatedMove });
|
await this.updateMove({ [`${this.actionsPath}.${id}`]: updatedMove });
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
this.render();
|
this.render();
|
||||||
return updatedEffects;
|
return updatedEffects;
|
||||||
}).render(true);
|
}).render(true);
|
||||||
|
|
@ -199,33 +198,36 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.settings.updateSource({
|
await this.updateMove({
|
||||||
[this.movePath]: {
|
[this.movePath]: {
|
||||||
effects: move.effects.filter(x => x.id !== id),
|
effects: move.effects.filter(x => x.id !== id),
|
||||||
actions: move.actions
|
actions: move.actions
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
|
await this.updateMove({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addEffect(_, target) {
|
static async addEffect() {
|
||||||
const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`);
|
const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`);
|
||||||
await this.settings.updateSource({
|
|
||||||
|
await this.updateMove({
|
||||||
[`${this.movePath}.effects`]: [
|
[`${this.movePath}.effects`]: [
|
||||||
...currentEffects,
|
...currentEffects,
|
||||||
game.system.api.data.activeEffects.BaseEffect.getDefaultObject()
|
game.system.api.data.activeEffects.BaseEffect.getDefaultObject()
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateMove(update) {
|
||||||
|
await this.settings.updateSource(update);
|
||||||
|
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||||
|
}
|
||||||
|
|
||||||
static resetMoves() {}
|
static resetMoves() {}
|
||||||
|
|
||||||
_filterTabs(tabs) {
|
_filterTabs(tabs) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import DaggerheartMenu from '../../sidebar/tabs/daggerheartMenu.mjs';
|
||||||
import { socketEvent } from '../../../systemRegistration/socket.mjs';
|
import { socketEvent } from '../../../systemRegistration/socket.mjs';
|
||||||
import GroupRollDialog from '../../dialogs/group-roll-dialog.mjs';
|
import GroupRollDialog from '../../dialogs/group-roll-dialog.mjs';
|
||||||
import DhpActor from '../../../documents/actor.mjs';
|
import DhpActor from '../../../documents/actor.mjs';
|
||||||
import DHItem from '../../../documents/item.mjs';
|
|
||||||
|
|
||||||
export default class Party extends DHBaseActorSheet {
|
export default class Party extends DHBaseActorSheet {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
|
|
@ -269,15 +268,6 @@ export default class Party extends DHBaseActorSheet {
|
||||||
).render({ force: true });
|
).render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the set of ContextMenu options for Consumable and Loot.
|
|
||||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
|
||||||
* @this {CharacterSheet}
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
static #getItemContextOptions() {
|
|
||||||
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
|
|
||||||
}
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Filter Tracking */
|
/* Filter Tracking */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
||||||
|
|
@ -431,18 +431,18 @@ export default function DHApplicationMixin(Base) {
|
||||||
{
|
{
|
||||||
name: 'disableEffect',
|
name: 'disableEffect',
|
||||||
icon: 'fa-solid fa-lightbulb',
|
icon: 'fa-solid fa-lightbulb',
|
||||||
condition: target => {
|
condition: element => {
|
||||||
const doc = getDocFromElementSync(target);
|
const target = element.closest('[data-item-uuid]');
|
||||||
return doc && !doc.disabled && doc.type !== 'beastform';
|
return !target.dataset.disabled && target.dataset.itemType !== 'beastform';
|
||||||
},
|
},
|
||||||
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
|
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enableEffect',
|
name: 'enableEffect',
|
||||||
icon: 'fa-regular fa-lightbulb',
|
icon: 'fa-regular fa-lightbulb',
|
||||||
condition: target => {
|
condition: element => {
|
||||||
const doc = getDocFromElementSync(target);
|
const target = element.closest('[data-item-uuid]');
|
||||||
return doc && doc.disabled && doc.type !== 'beastform';
|
return target.dataset.disabled && target.dataset.itemType !== 'beastform';
|
||||||
},
|
},
|
||||||
callback: async target => (await getDocFromElement(target)).update({ disabled: false })
|
callback: async target => (await getDocFromElement(target)).update({ disabled: false })
|
||||||
}
|
}
|
||||||
|
|
@ -539,9 +539,9 @@ export default function DHApplicationMixin(Base) {
|
||||||
options.push({
|
options.push({
|
||||||
name: 'CONTROLS.CommonDelete',
|
name: 'CONTROLS.CommonDelete',
|
||||||
icon: 'fa-solid fa-trash',
|
icon: 'fa-solid fa-trash',
|
||||||
condition: target => {
|
condition: element => {
|
||||||
const doc = getDocFromElementSync(target);
|
const target = element.closest('[data-item-uuid]');
|
||||||
return doc && doc.type !== 'beastform';
|
return target.dataset.itemType !== 'beastform';
|
||||||
},
|
},
|
||||||
callback: async (target, event) => {
|
callback: async (target, event) => {
|
||||||
const doc = await getDocFromElement(target);
|
const doc = await getDocFromElement(target);
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
],
|
],
|
||||||
dragDrop: [
|
dragDrop: [
|
||||||
{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null },
|
{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null },
|
||||||
{ dragSelector: ".currency[data-currency] .drag-handle", dropSelector: null }
|
{ dragSelector: '.currency[data-currency] .drag-handle', dropSelector: null }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
value: context.source.system.gold[key]
|
value: context.source.system.gold[key]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
context.inventory.hasCurrency = Object.values(context.inventory.currencies).some((c) => c.enabled);
|
context.inventory.hasCurrency = Object.values(context.inventory.currencies).some(c => c.enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
|
@ -270,7 +270,9 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
currency
|
currency
|
||||||
});
|
});
|
||||||
if (quantity) {
|
if (quantity) {
|
||||||
originActor.update({ [`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity) });
|
originActor.update({
|
||||||
|
[`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity)
|
||||||
|
});
|
||||||
this.document.update({ [`system.gold.${currency}`]: this.document.system.gold[currency] + quantity });
|
this.document.update({ [`system.gold.${currency}`]: this.document.system.gold[currency] + quantity });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -292,6 +294,15 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
|
|
||||||
/* Handling transfer of inventoryItems */
|
/* Handling transfer of inventoryItems */
|
||||||
if (item.system.metadata.isInventoryItem) {
|
if (item.system.metadata.isInventoryItem) {
|
||||||
|
if (!this.document.testUserPermission(game.user, 'OWNER', { exact: true })) {
|
||||||
|
return ui.notifications.error(
|
||||||
|
game.i18n.format('DAGGERHEART.UI.Notifications.lackingItemTransferPermission', {
|
||||||
|
user: game.user.name,
|
||||||
|
target: this.document.name
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (item.system.metadata.isQuantifiable) {
|
if (item.system.metadata.isQuantifiable) {
|
||||||
const actorItem = originActor.items.get(data.originId);
|
const actorItem = originActor.items.get(data.originId);
|
||||||
const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
|
const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
|
||||||
|
|
@ -300,14 +311,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (quantityTransfered) {
|
if (quantityTransfered) {
|
||||||
if (quantityTransfered === actorItem.system.quantity) {
|
|
||||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
|
||||||
} else {
|
|
||||||
await actorItem.update({
|
|
||||||
'system.quantity': actorItem.system.quantity - quantityTransfered
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingItem = this.document.items.find(x => itemIsIdentical(x, item));
|
const existingItem = this.document.items.find(x => itemIsIdentical(x, item));
|
||||||
if (existingItem) {
|
if (existingItem) {
|
||||||
await existingItem.update({
|
await existingItem.update({
|
||||||
|
|
@ -325,10 +328,18 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quantityTransfered === actorItem.system.quantity) {
|
||||||
|
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
||||||
|
} else {
|
||||||
|
await actorItem.update({
|
||||||
|
'system.quantity': actorItem.system.quantity - quantityTransfered
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
|
||||||
await this.document.createEmbeddedDocuments('Item', [item.toObject()]);
|
await this.document.createEmbeddedDocuments('Item', [item.toObject()]);
|
||||||
|
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +350,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
*/
|
*/
|
||||||
async _onDragStart(event) {
|
async _onDragStart(event) {
|
||||||
// Handle drag/dropping currencies
|
// Handle drag/dropping currencies
|
||||||
const currencyEl = event.currentTarget.closest(".currency[data-currency]");
|
const currencyEl = event.currentTarget.closest('.currency[data-currency]');
|
||||||
if (currencyEl) {
|
if (currencyEl) {
|
||||||
const currency = currencyEl.dataset.currency;
|
const currency = currencyEl.dataset.currency;
|
||||||
const data = { type: 'Currency', currency, originActor: this.document.uuid };
|
const data = { type: 'Currency', currency, originActor: this.document.uuid };
|
||||||
|
|
@ -359,8 +370,8 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
|
event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
|
||||||
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
|
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = await getDocFromElement(event.target);
|
const item = await getDocFromElement(event.target);
|
||||||
if (item) {
|
if (item) {
|
||||||
const dragData = {
|
const dragData = {
|
||||||
|
|
|
||||||
|
|
@ -54,30 +54,58 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
if (this === target) return 0;
|
if (this === target) return 0;
|
||||||
|
|
||||||
const originPoint = this.center;
|
const originPoint = this.center;
|
||||||
const destinationPoint = target.center;
|
const targetPoint = target.center;
|
||||||
|
const thisBounds = this.bounds;
|
||||||
|
const targetBounds = target.bounds;
|
||||||
|
const adjacencyBuffer = canvas.grid.distance * 1.75; // handles diagonals with one square elevation difference
|
||||||
|
|
||||||
|
// Figure out the elevation difference.
|
||||||
|
// This intends to return "grid distance" for adjacent ones, so we add that number if not overlapping.
|
||||||
|
const sizePerUnit = canvas.grid.size / canvas.grid.distance;
|
||||||
|
const thisHeight = Math.max(thisBounds.width, thisBounds.height) / sizePerUnit;
|
||||||
|
const targetHeight = Math.max(targetBounds.width, targetBounds.height) / sizePerUnit;
|
||||||
|
const thisElevation = [this.document.elevation, this.document.elevation + thisHeight];
|
||||||
|
const targetElevation = [target.document.elevation, target.document.elevation + targetHeight];
|
||||||
|
const isSameAltitude =
|
||||||
|
thisElevation[0] < targetElevation[1] && // bottom of this must be at or below the top of target
|
||||||
|
thisElevation[1] > targetElevation[0]; // top of this must be at or above the bottom of target
|
||||||
|
const [lower, higher] = [targetElevation, thisElevation].sort((a, b) => a[1] - b[1]);
|
||||||
|
const elevation = isSameAltitude ? 0 : higher[0] - lower[1] + canvas.grid.distance;
|
||||||
|
|
||||||
// Compute for gridless. This version returns circular edge to edge + grid distance,
|
// Compute for gridless. This version returns circular edge to edge + grid distance,
|
||||||
// so that tokens that are touching return 5.
|
// so that tokens that are touching return 5.
|
||||||
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
||||||
const boundsCorrection = canvas.grid.distance / canvas.grid.size;
|
const boundsCorrection = canvas.grid.distance / canvas.grid.size;
|
||||||
const originRadius = (this.bounds.width * boundsCorrection) / 2;
|
const originRadius = (thisBounds.width * boundsCorrection) / 2;
|
||||||
const targetRadius = (target.bounds.width * boundsCorrection) / 2;
|
const targetRadius = (targetBounds.width * boundsCorrection) / 2;
|
||||||
const distance = canvas.grid.measurePath([originPoint, destinationPoint]).distance;
|
const measuredDistance = canvas.grid.measurePath([
|
||||||
return Math.floor(distance - originRadius - targetRadius + canvas.grid.distance);
|
{ ...originPoint, elevation: 0 },
|
||||||
|
{ ...targetPoint, elevation }
|
||||||
|
]).distance;
|
||||||
|
const distance = Math.floor(measuredDistance - originRadius - targetRadius + canvas.grid.distance);
|
||||||
|
return Math.min(distance, distance > adjacencyBuffer ? Infinity : canvas.grid.distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute what the closest grid space of each token is, then compute that distance
|
// Compute what the closest grid space of each token is, then compute that distance
|
||||||
const originEdge = this.#getEdgeBoundary(this.bounds, originPoint, destinationPoint);
|
const originEdge = this.#getEdgeBoundary(thisBounds, originPoint, targetPoint);
|
||||||
const targetEdge = this.#getEdgeBoundary(target.bounds, originPoint, destinationPoint);
|
const targetEdge = this.#getEdgeBoundary(targetBounds, originPoint, targetPoint);
|
||||||
const adjustedOriginPoint = canvas.grid.getTopLeftPoint({
|
const adjustedOriginPoint = originEdge
|
||||||
x: originEdge.x + Math.sign(originPoint.x - originEdge.x),
|
? canvas.grid.getTopLeftPoint({
|
||||||
y: originEdge.y + Math.sign(originPoint.y - originEdge.y)
|
x: originEdge.x + Math.sign(originPoint.x - originEdge.x),
|
||||||
});
|
y: originEdge.y + Math.sign(originPoint.y - originEdge.y)
|
||||||
const adjustDestinationPoint = canvas.grid.getTopLeftPoint({
|
})
|
||||||
x: targetEdge.x + Math.sign(destinationPoint.x - targetEdge.x),
|
: originPoint;
|
||||||
y: targetEdge.y + Math.sign(destinationPoint.y - targetEdge.y)
|
const adjustDestinationPoint = targetEdge
|
||||||
});
|
? canvas.grid.getTopLeftPoint({
|
||||||
return canvas.grid.measurePath([adjustedOriginPoint, adjustDestinationPoint]).distance;
|
x: targetEdge.x + Math.sign(targetPoint.x - targetEdge.x),
|
||||||
|
y: targetEdge.y + Math.sign(targetPoint.y - targetEdge.y)
|
||||||
|
})
|
||||||
|
: targetPoint;
|
||||||
|
const distance = canvas.grid.measurePath([
|
||||||
|
{ ...adjustedOriginPoint, elevation: 0 },
|
||||||
|
{ ...adjustDestinationPoint, elevation }
|
||||||
|
]).distance;
|
||||||
|
return Math.min(distance, distance > adjacencyBuffer ? Infinity : canvas.grid.distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onHoverIn(event, options) {
|
_onHoverIn(event, options) {
|
||||||
|
|
@ -103,8 +131,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
|
|
||||||
// Determine the actual range
|
// Determine the actual range
|
||||||
const ranges = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
|
const ranges = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
|
||||||
const distanceNum = originToken.distanceTo(this);
|
const distanceResult = DhMeasuredTemplate.getRangeLabels(originToken.distanceTo(this), ranges);
|
||||||
const distanceResult = DhMeasuredTemplate.getRangeLabels(distanceNum, ranges);
|
|
||||||
const distanceLabel = `${distanceResult.distance} ${distanceResult.units}`.trim();
|
const distanceLabel = `${distanceResult.distance} ${distanceResult.units}`.trim();
|
||||||
|
|
||||||
// Create the element
|
// Create the element
|
||||||
|
|
@ -156,11 +183,6 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tests if the token is at least adjacent with another, with some leeway for diagonals */
|
|
||||||
isAdjacentWith(token) {
|
|
||||||
return this.distanceTo(token) <= canvas.grid.distance * 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
_drawBar(number, bar, data) {
|
_drawBar(number, bar, data) {
|
||||||
const val = Number(data.value);
|
const val = Number(data.value);
|
||||||
|
|
|
||||||
|
|
@ -467,9 +467,7 @@ export const allArmorFeatures = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const orderedArmorFeatures = () => {
|
export const orderedArmorFeatures = () => {
|
||||||
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
const allFeatures = allArmorFeatures();
|
||||||
.armorFeatures;
|
|
||||||
const allFeatures = { ...armorFeatures, ...homebrewFeatures };
|
|
||||||
const all = Object.keys(allFeatures).map(key => {
|
const all = Object.keys(allFeatures).map(key => {
|
||||||
const feature = allFeatures[key];
|
const feature = allFeatures[key];
|
||||||
return {
|
return {
|
||||||
|
|
@ -1404,9 +1402,7 @@ export const allWeaponFeatures = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const orderedWeaponFeatures = () => {
|
export const orderedWeaponFeatures = () => {
|
||||||
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
const allFeatures = allWeaponFeatures();
|
||||||
.weaponFeatures;
|
|
||||||
const allFeatures = { ...weaponFeatures, ...homebrewFeatures };
|
|
||||||
const all = Object.keys(allFeatures).map(key => {
|
const all = Object.keys(allFeatures).map(key => {
|
||||||
const feature = allFeatures[key];
|
const feature = allFeatures[key];
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,10 @@ export default class DhpAdversary extends DhCreature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareDerivedData() {
|
||||||
|
this.attack.roll.isStandardAttack = true;
|
||||||
|
}
|
||||||
|
|
||||||
_getTags() {
|
_getTags() {
|
||||||
const tags = [
|
const tags = [
|
||||||
game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`),
|
game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`),
|
||||||
|
|
@ -264,12 +268,12 @@ export default class DhpAdversary extends DhCreature {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update damage in item actions
|
// Update damage in item actions
|
||||||
|
// Parse damage, and convert all formula matches in the descriptions to the new damage
|
||||||
for (const action of Object.values(item.system.actions)) {
|
for (const action of Object.values(item.system.actions)) {
|
||||||
if (!action.damage) continue;
|
|
||||||
|
|
||||||
// Parse damage, and convert all formula matches in the descriptions to the new damage
|
|
||||||
try {
|
try {
|
||||||
const result = this.#adjustActionDamage(action, { ...damageMeta, type: 'action' });
|
const result = this.#adjustActionDamage(action, { ...damageMeta, type: 'action' });
|
||||||
|
if (!result) continue;
|
||||||
|
|
||||||
for (const { previousFormula, formula } of Object.values(result)) {
|
for (const { previousFormula, formula } of Object.values(result)) {
|
||||||
const oldFormulaRegexp = new RegExp(
|
const oldFormulaRegexp = new RegExp(
|
||||||
previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?')
|
previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?')
|
||||||
|
|
@ -371,9 +375,11 @@ export default class DhpAdversary extends DhCreature {
|
||||||
/**
|
/**
|
||||||
* Updates damage to reflect a specific value.
|
* Updates damage to reflect a specific value.
|
||||||
* @throws if damage structure is invalid for conversion
|
* @throws if damage structure is invalid for conversion
|
||||||
* @returns the converted formula and value as a simplified term
|
* @returns the converted formula and value as a simplified term, or null if it doesn't deal HP damage
|
||||||
*/
|
*/
|
||||||
#adjustActionDamage(action, damageMeta) {
|
#adjustActionDamage(action, damageMeta) {
|
||||||
|
if (!action.damage?.parts.hitPoints) return null;
|
||||||
|
|
||||||
const result = {};
|
const result = {};
|
||||||
for (const property of ['value', 'valueAlt']) {
|
for (const property of ['value', 'valueAlt']) {
|
||||||
const data = action.damage.parts.hitPoints[property];
|
const data = action.damage.parts.hitPoints[property];
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,4 @@ export { default as FormulaField } from './formulaField.mjs';
|
||||||
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
||||||
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
|
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
|
||||||
export { default as TriggerField } from './triggerField.mjs';
|
export { default as TriggerField } from './triggerField.mjs';
|
||||||
export { default as MappingField } from './mappingField.mjs';
|
|
||||||
export * as ActionFields from './action/_module.mjs';
|
export * as ActionFields from './action/_module.mjs';
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,8 @@ export default class DamageField extends fields.SchemaField {
|
||||||
if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt;
|
if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt;
|
||||||
|
|
||||||
const isAdversary = this.actor.type === 'adversary';
|
const isAdversary = this.actor.type === 'adversary';
|
||||||
if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) {
|
const isHorde = this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id;
|
||||||
|
if (isAdversary && isHorde && this.roll?.isStandardAttack) {
|
||||||
const hasHordeDamage = this.actor.effects.find(x => x.type === 'horde');
|
const hasHordeDamage = this.actor.effects.find(x => x.type === 'horde');
|
||||||
if (hasHordeDamage && !hasHordeDamage.disabled) return part.valueAlt;
|
if (hasHordeDamage && !hasHordeDamage.disabled) return part.valueAlt;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import DHActionConfig from '../../applications/sheets-configs/action-config.mjs';
|
import DHActionConfig from '../../applications/sheets-configs/action-config.mjs';
|
||||||
import { itemAbleRollParse } from '../../helpers/utils.mjs';
|
import { itemAbleRollParse } from '../../helpers/utils.mjs';
|
||||||
import MappingField from './mappingField.mjs';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized collection type for stored actions.
|
* Specialized collection type for stored actions.
|
||||||
|
|
@ -61,7 +60,7 @@ export class ActionCollection extends Collection {
|
||||||
/**
|
/**
|
||||||
* Field that stores actions.
|
* Field that stores actions.
|
||||||
*/
|
*/
|
||||||
export class ActionsField extends MappingField {
|
export class ActionsField extends foundry.data.fields.TypedObjectField {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(new ActionField(), options);
|
super(new ActionField(), options);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
/**
|
|
||||||
* A subclass of ObjectField that represents a mapping of keys to the provided DataField type.
|
|
||||||
*
|
|
||||||
* @param {DataField} model The class of DataField which should be embedded in this field.
|
|
||||||
* @param {MappingFieldOptions} [options={}] Options which configure the behavior of the field.
|
|
||||||
* @property {string[]} [initialKeys] Keys that will be created if no data is provided.
|
|
||||||
* @property {MappingFieldInitialValueBuilder} [initialValue] Function to calculate the initial value for a key.
|
|
||||||
* @property {boolean} [initialKeysOnly=false] Should the keys in the initialized data be limited to the keys provided
|
|
||||||
* by `options.initialKeys`?
|
|
||||||
*/
|
|
||||||
export default class MappingField extends foundry.data.fields.ObjectField {
|
|
||||||
constructor(model, options) {
|
|
||||||
if (!(model instanceof foundry.data.fields.DataField)) {
|
|
||||||
throw new Error('MappingField must have a DataField as its contained element');
|
|
||||||
}
|
|
||||||
super(options);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The embedded DataField definition which is contained in this field.
|
|
||||||
* @type {DataField}
|
|
||||||
*/
|
|
||||||
this.model = model;
|
|
||||||
model.parent = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
static get _defaults() {
|
|
||||||
return foundry.utils.mergeObject(super._defaults, {
|
|
||||||
initialKeys: null,
|
|
||||||
initialValue: null,
|
|
||||||
initialKeysOnly: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
_cleanType(value, options) {
|
|
||||||
Object.entries(value).forEach(([k, v]) => {
|
|
||||||
if (k.startsWith('-=')) return;
|
|
||||||
value[k] = this.model.clean(v, options);
|
|
||||||
});
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
getInitialValue(data) {
|
|
||||||
let keys = this.initialKeys;
|
|
||||||
const initial = super.getInitialValue(data);
|
|
||||||
if (!keys || !foundry.utils.isEmpty(initial)) return initial;
|
|
||||||
if (!(keys instanceof Array)) keys = Object.keys(keys);
|
|
||||||
for (const key of keys) initial[key] = this._getInitialValueForKey(key);
|
|
||||||
return initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the initial value for the provided key.
|
|
||||||
* @param {string} key Key within the object being built.
|
|
||||||
* @param {object} [object] Any existing mapping data.
|
|
||||||
* @returns {*} Initial value based on provided field type.
|
|
||||||
*/
|
|
||||||
_getInitialValueForKey(key, object) {
|
|
||||||
const initial = this.model.getInitialValue();
|
|
||||||
return this.initialValue?.(key, initial, object) ?? initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
_validateType(value, options = {}) {
|
|
||||||
if (foundry.utils.getType(value) !== 'Object') throw new Error('must be an Object');
|
|
||||||
const errors = this._validateValues(value, options);
|
|
||||||
if (!foundry.utils.isEmpty(errors)) {
|
|
||||||
const failure = new foundry.data.validation.DataModelValidationFailure();
|
|
||||||
failure.elements = Object.entries(errors).map(([id, failure]) => ({ id, failure }));
|
|
||||||
throw failure.asError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate each value of the object.
|
|
||||||
* @param {object} value The object to validate.
|
|
||||||
* @param {object} options Validation options.
|
|
||||||
* @returns {Record<string, Error>} An object of value-specific errors by key.
|
|
||||||
*/
|
|
||||||
_validateValues(value, options) {
|
|
||||||
const errors = {};
|
|
||||||
for (const [k, v] of Object.entries(value)) {
|
|
||||||
if (k.startsWith('-=')) continue;
|
|
||||||
const error = this.model.validate(v, options);
|
|
||||||
if (error) errors[k] = error;
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
initialize(value, model, options = {}) {
|
|
||||||
if (!value) return value;
|
|
||||||
const obj = {};
|
|
||||||
const initialKeys = this.initialKeys instanceof Array ? this.initialKeys : Object.keys(this.initialKeys ?? {});
|
|
||||||
const keys = this.initialKeysOnly ? initialKeys : Object.keys(value);
|
|
||||||
for (const key of keys) {
|
|
||||||
const data = value[key] ?? this._getInitialValueForKey(key, value);
|
|
||||||
obj[key] = this.model.initialize(data, model, options);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
_getField(path) {
|
|
||||||
if (path.length === 0) return this;
|
|
||||||
else if (path.length === 1) return this.model;
|
|
||||||
path.shift();
|
|
||||||
return this.model._getField(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -23,9 +23,7 @@ export default class DHArmor extends AttachableItem {
|
||||||
armorFeatures: new fields.ArrayField(
|
armorFeatures: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.StringField({
|
value: new fields.StringField({
|
||||||
required: true,
|
required: true
|
||||||
choices: CONFIG.DH.ITEM.allArmorFeatures,
|
|
||||||
blank: true
|
|
||||||
}),
|
}),
|
||||||
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||||
|
|
@ -58,7 +56,7 @@ export default class DHArmor extends AttachableItem {
|
||||||
async getDescriptionData() {
|
async getDescriptionData() {
|
||||||
const baseDescription = this.description;
|
const baseDescription = this.description;
|
||||||
const allFeatures = CONFIG.DH.ITEM.allArmorFeatures();
|
const allFeatures = CONFIG.DH.ITEM.allArmorFeatures();
|
||||||
const features = this.armorFeatures.map(x => allFeatures[x.value]);
|
const features = this.armorFeatures.map(x => allFeatures[x.value]).filter(x => x);
|
||||||
|
|
||||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/sheets/items/armor/description.hbs',
|
'systems/daggerheart/templates/sheets/items/armor/description.hbs',
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,7 @@ export default class DHWeapon extends AttachableItem {
|
||||||
weaponFeatures: new fields.ArrayField(
|
weaponFeatures: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.StringField({
|
value: new fields.StringField({
|
||||||
required: true,
|
required: true
|
||||||
choices: CONFIG.DH.ITEM.allWeaponFeatures,
|
|
||||||
blank: true
|
|
||||||
}),
|
}),
|
||||||
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||||
|
|
@ -121,7 +119,7 @@ export default class DHWeapon extends AttachableItem {
|
||||||
const burden = game.i18n.localize(CONFIG.DH.GENERAL.burden[this.burden].label);
|
const burden = game.i18n.localize(CONFIG.DH.GENERAL.burden[this.burden].label);
|
||||||
|
|
||||||
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
||||||
const features = this.weaponFeatures.map(x => allFeatures[x.value]);
|
const features = this.weaponFeatures.map(x => allFeatures[x.value]).filter(x => x);
|
||||||
|
|
||||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
|
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ export default class RegisteredTriggers extends Map {
|
||||||
unregisterSceneEnvironmentTriggers(flagSystemData) {
|
unregisterSceneEnvironmentTriggers(flagSystemData) {
|
||||||
const sceneData = new game.system.api.data.scenes.DHScene(flagSystemData);
|
const sceneData = new game.system.api.data.scenes.DHScene(flagSystemData);
|
||||||
for (const environment of sceneData.sceneEnvironments) {
|
for (const environment of sceneData.sceneEnvironments) {
|
||||||
if (environment.pack) continue;
|
if (!environment || environment.pack) continue;
|
||||||
this.unregisterItemTriggers(environment.system.features);
|
this.unregisterItemTriggers(environment.system.features);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,8 @@ export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {}
|
||||||
}),
|
}),
|
||||||
maxTags: typeof maxTags === 'function' ? maxTags() : maxTags,
|
maxTags: typeof maxTags === 'function' ? maxTags() : maxTags,
|
||||||
dropdown: {
|
dropdown: {
|
||||||
|
searchKeys: ['value', 'name'],
|
||||||
mapValueTo: 'name',
|
mapValueTo: 'name',
|
||||||
searchKeys: ['value'],
|
|
||||||
enabled: 0,
|
enabled: 0,
|
||||||
maxItems: 100,
|
maxItems: 100,
|
||||||
closeOnSelect: true,
|
closeOnSelect: true,
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
{
|
{
|
||||||
"trigger": "dualityRoll",
|
"trigger": "dualityRoll",
|
||||||
"triggeringActorType": "self",
|
"triggeringActorType": "self",
|
||||||
"command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n <div><div>${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}</div>\n <div>${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}</div>\n <div class=\"flexrow\" style=\"gap: 8px;\">\n <button type=\"button\" id=\"hopeButton\">\n <i class=\"fa-solid fa-hands-holding\"></i>\n <label>0</label>\n </button>\n <button type=\"button\" id=\"stressButton\">\n <i class=\"fa-solid fa-bolt-lightning\"></i>\n <label>0</label>\n </button>\n </div>\n</div>`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;"
|
"command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n <div><div>${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}</div>\n <div>${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}</div>\n<div>${game.i18n.localize('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsActionExplanation')}</div>\n <div class=\"flexrow\" style=\"gap: 8px;\">\n <button type=\"button\" id=\"hopeButton\">\n <i class=\"fa-solid fa-hands-holding\"></i>\n <label>0</label>\n </button>\n <button type=\"button\" id=\"stressButton\">\n <i class=\"fa-solid fa-bolt-lightning\"></i>\n <label>0</label>\n </button>\n </div>\n</div>`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
"difficulty": null,
|
"difficulty": null,
|
||||||
"damageMod": "none"
|
"damageMod": "none"
|
||||||
},
|
},
|
||||||
"name": "Agility Check",
|
"name": "Agility Roll",
|
||||||
"img": "icons/skills/melee/sword-engraved-glow-purple.webp",
|
"img": "icons/skills/melee/sword-engraved-glow-purple.webp",
|
||||||
"range": "close"
|
"range": "close"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,10 +103,9 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
gap: 8px;
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
width: 15px;
|
width: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,8 +274,10 @@
|
||||||
grid-area: controls;
|
grid-area: controls;
|
||||||
align-self: start;
|
align-self: start;
|
||||||
padding-top: 0.3125rem;
|
padding-top: 0.3125rem;
|
||||||
gap: 4px;
|
|
||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
|
a {
|
||||||
|
width: 18px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
> .item-labels {
|
> .item-labels {
|
||||||
align-self: start;
|
align-self: start;
|
||||||
|
|
@ -334,6 +335,27 @@
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recall-cost {
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
top: 4px;
|
||||||
|
width: 1.75em;
|
||||||
|
height: 1.75em;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
background: @dark-blue;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid @golden;
|
||||||
|
color: @golden;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 0.1em; // compensate for font
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 0.68em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card-label {
|
.card-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.domain-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.level-details {
|
.level-details {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@
|
||||||
.daggerheart,
|
.daggerheart,
|
||||||
#chat-notifications {
|
#chat-notifications {
|
||||||
.chat-message {
|
.chat-message {
|
||||||
--text-color: @golden;
|
--text-color: light-dark(@dark-blue, @golden);
|
||||||
--bg-color: @golden-40;
|
--bg-color: @golden-40;
|
||||||
|
|
||||||
[data-use-perm='false'] {
|
[data-use-perm='false'] {
|
||||||
|
|
@ -233,7 +233,7 @@
|
||||||
font-family: @font-subtitle;
|
font-family: @font-subtitle;
|
||||||
font-size: var(--font-size-18);
|
font-size: var(--font-size-18);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: light-dark(@dark-blue, var(--text-color));
|
color: var(--text-color);
|
||||||
margin-bottom: -2px;
|
margin-bottom: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "daggerheart",
|
"id": "daggerheart",
|
||||||
"title": "Daggerheart",
|
"title": "Daggerheart",
|
||||||
"description": "An unofficial implementation of the Daggerheart system",
|
"description": "An unofficial implementation of the Daggerheart system",
|
||||||
"version": "1.7.2",
|
"version": "1.7.3",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "13.346",
|
"minimum": "13.346",
|
||||||
"verified": "13.351",
|
"verified": "13.351",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
<li class="card-item" data-item-uuid="{{item.uuid}}" data-type="domainCard">
|
<li class="card-item" data-item-uuid="{{item.uuid}}" data-type="domainCard">
|
||||||
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
||||||
|
<span class="item-icon recall-cost">
|
||||||
|
<span class="recall-value">{{item.system.recallCost}}</span>
|
||||||
|
<i class="fa-solid fa-bolt"></i>
|
||||||
|
</span>
|
||||||
<div class="card-label">
|
<div class="card-label">
|
||||||
<div
|
<div
|
||||||
class="menu {{#if item.system.resource}}resource-menu{{/if}} {{#if (eq item.system.resource.type 'diceValue')}}dice-menu{{/if}}">
|
class="menu {{#if item.system.resource}}resource-menu{{/if}} {{#if (eq item.system.resource.type 'diceValue')}}dice-menu{{/if}}">
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ Parameters:
|
||||||
{{> 'daggerheart.inventory-item'
|
{{> 'daggerheart.inventory-item'
|
||||||
item=item
|
item=item
|
||||||
type=../type
|
type=../type
|
||||||
|
disabledEffect=../disabledEffect
|
||||||
actorType=../actorType
|
actorType=../actorType
|
||||||
hideControls=../hideControls
|
hideControls=../hideControls
|
||||||
hideContextMenu=../hideContextMenu
|
hideContextMenu=../hideContextMenu
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,12 @@ Parameters:
|
||||||
- showActions {boolean} : If true show feature's actions.
|
- showActions {boolean} : If true show feature's actions.
|
||||||
--}}
|
--}}
|
||||||
|
|
||||||
<li class="inventory-item" data-item-id="{{item.id}}" {{#if (or (eq type 'action' ) (eq type 'attack' ))}}
|
<li class="inventory-item" data-item-id="{{item.id}}"
|
||||||
data-action-id="{{item.id}}" {{/if}} data-item-uuid="{{item.uuid}}" data-type="{{type}}" data-no-compendium-edit="{{noCompendiumEdit}}">
|
{{#if (or (eq type 'action' ) (eq type 'attack' ))}}data-action-id="{{item.id}}" {{/if}}
|
||||||
|
{{#if disabledEffect}}data-disabled="true"{{/if}}
|
||||||
|
data-type="{{type}}" data-item-type="{{item.type}}"
|
||||||
|
data-item-uuid="{{item.uuid}}" data-no-compendium-edit="{{noCompendiumEdit}}"
|
||||||
|
>
|
||||||
<div class="inventory-item-header {{#if hideContextMenu}}padded{{/if}}" {{#unless noExtensible}}data-action="toggleExtended" {{/unless}}>
|
<div class="inventory-item-header {{#if hideContextMenu}}padded{{/if}}" {{#unless noExtensible}}data-action="toggleExtended" {{/unless}}>
|
||||||
{{!-- Image --}}
|
{{!-- Image --}}
|
||||||
<div class="img-portait" data-action='{{ifThen (or (hasProperty item "use") (eq type "attack")) "useItem" (ifThen
|
<div class="img-portait" data-action='{{ifThen (or (hasProperty item "use") (eq type "attack")) "useItem" (ifThen
|
||||||
|
|
@ -105,7 +109,7 @@ Parameters:
|
||||||
{{else if (eq type 'armor')}}
|
{{else if (eq type 'armor')}}
|
||||||
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem"
|
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem"
|
||||||
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.equipped 'unequip' 'equip' }}">
|
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.equipped 'unequip' 'equip' }}">
|
||||||
<i class="fa-solid fa-shield"></i>
|
<i class="fa-solid fa-fw fa-shield"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq type 'domainCard')}}
|
{{#if (eq type 'domainCard')}}
|
||||||
|
|
@ -121,7 +125,7 @@ Parameters:
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (hasProperty item "toChat")}}
|
{{#if (hasProperty item "toChat")}}
|
||||||
<a data-action="toChat" data-tooltip="DAGGERHEART.UI.Tooltip.sendToChat">
|
<a data-action="toChat" data-tooltip="DAGGERHEART.UI.Tooltip.sendToChat">
|
||||||
<i class="fa-regular fa-message"></i>
|
<i class="fa-regular fa-fw fa-message"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
@ -134,7 +138,7 @@ Parameters:
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{#unless hideContextMenu}}
|
{{#unless hideContextMenu}}
|
||||||
<a data-action="triggerContextMenu" data-tooltip="DAGGERHEART.UI.Tooltip.moreOptions">
|
<a data-action="triggerContextMenu" data-tooltip="DAGGERHEART.UI.Tooltip.moreOptions">
|
||||||
<i class="fa-solid fa-ellipsis-vertical"></i>
|
<i class="fa-solid fa-fw fa-ellipsis-vertical"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,21 @@
|
||||||
data-group='{{tabs.effects.group}}'>
|
data-group='{{tabs.effects.group}}'>
|
||||||
|
|
||||||
{{> 'daggerheart.inventory-items'
|
{{> 'daggerheart.inventory-items'
|
||||||
title='DAGGERHEART.GENERAL.activeEffects'
|
title='DAGGERHEART.GENERAL.activeEffects'
|
||||||
type='effect'
|
type='effect'
|
||||||
isGlassy=true
|
isGlassy=true
|
||||||
collection=effects.actives
|
collection=effects.actives
|
||||||
canCreate=true
|
canCreate=true
|
||||||
hideResources=true
|
hideResources=true
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{{> 'daggerheart.inventory-items'
|
{{> 'daggerheart.inventory-items'
|
||||||
title='DAGGERHEART.GENERAL.inactiveEffects'
|
title='DAGGERHEART.GENERAL.inactiveEffects'
|
||||||
type='effect'
|
type='effect'
|
||||||
isGlassy=true
|
disabledEffect=true
|
||||||
collection=effects.inactives
|
isGlassy=true
|
||||||
canCreate=true
|
collection=effects.inactives
|
||||||
hideResources=true
|
canCreate=true
|
||||||
|
hideResources=true
|
||||||
}}
|
}}
|
||||||
</section>
|
</section>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue