mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-11 19:25:21 +01:00
[Fix] Homebrew Item Features (#1365)
* Fixed adding and editing effects on a homebrew feature * . * Fixed effect removal
This commit is contained in:
parent
cb10b18e06
commit
9fa4627b19
11 changed files with 411 additions and 252 deletions
|
|
@ -2094,6 +2094,7 @@
|
||||||
"missingDragDropThing": "Drop {thing} here",
|
"missingDragDropThing": "Drop {thing} here",
|
||||||
"multiclass": "Multiclass",
|
"multiclass": "Multiclass",
|
||||||
"newCategory": "New Category",
|
"newCategory": "New Category",
|
||||||
|
"newThing": "New {thing}",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"noTarget": "No current target",
|
"noTarget": "No current target",
|
||||||
"partner": "Partner",
|
"partner": "Partner",
|
||||||
|
|
@ -2383,8 +2384,8 @@
|
||||||
"newDowntimeMove": "Downtime Move",
|
"newDowntimeMove": "Downtime Move",
|
||||||
"downtimeMove": "Downtime Move",
|
"downtimeMove": "Downtime Move",
|
||||||
"armorFeature": "Armor Feature",
|
"armorFeature": "Armor Feature",
|
||||||
"weaponFeature": "Weapon Feaure",
|
"weaponFeature": "Weapon Feature",
|
||||||
"newFeature": "New ItemFeature",
|
"newFeature": "New Item Feature",
|
||||||
"downtimeMoves": "Downtime Moves",
|
"downtimeMoves": "Downtime Moves",
|
||||||
"itemFeatures": "Item Features",
|
"itemFeatures": "Item Features",
|
||||||
"nrChoices": "# Moves Per Rest",
|
"nrChoices": "# Moves Per Rest",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export { default as ActionConfig } from './action-config.mjs';
|
export { default as ActionConfig } from './action-config.mjs';
|
||||||
|
export { default as ActionSettingsConfig } from './action-settings-config.mjs';
|
||||||
export { default as CharacterSettings } from './character-settings.mjs';
|
export { default as CharacterSettings } from './character-settings.mjs';
|
||||||
export { default as AdversarySettings } from './adversary-settings.mjs';
|
export { default as AdversarySettings } from './adversary-settings.mjs';
|
||||||
export { default as CompanionSettings } from './companion-settings.mjs';
|
export { default as CompanionSettings } from './companion-settings.mjs';
|
||||||
|
|
|
||||||
236
module/applications/sheets-configs/action-base-config.mjs
Normal file
236
module/applications/sheets-configs/action-base-config.mjs
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
||||||
|
|
||||||
|
const { ApplicationV2 } = foundry.applications.api;
|
||||||
|
export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
|
constructor(action) {
|
||||||
|
super({});
|
||||||
|
|
||||||
|
this.action = action;
|
||||||
|
this.openSection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return `${game.i18n.localize('DAGGERHEART.GENERAL.Tabs.settings')}: ${this.action.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'dh-style', 'dialog', 'max-800'],
|
||||||
|
window: {
|
||||||
|
icon: 'fa-solid fa-wrench',
|
||||||
|
resizable: false
|
||||||
|
},
|
||||||
|
position: { width: 600, height: 'auto' },
|
||||||
|
actions: {
|
||||||
|
toggleSection: this.toggleSection,
|
||||||
|
addEffect: this.addEffect,
|
||||||
|
removeEffect: this.removeEffect,
|
||||||
|
addElement: this.addElement,
|
||||||
|
removeElement: this.removeElement,
|
||||||
|
editEffect: this.editEffect,
|
||||||
|
addDamage: this.addDamage,
|
||||||
|
removeDamage: this.removeDamage
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateForm,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
header: {
|
||||||
|
id: 'header',
|
||||||
|
template: 'systems/daggerheart/templates/sheets-settings/action-settings/header.hbs'
|
||||||
|
},
|
||||||
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
|
base: {
|
||||||
|
id: 'base',
|
||||||
|
template: 'systems/daggerheart/templates/sheets-settings/action-settings/base.hbs'
|
||||||
|
},
|
||||||
|
configuration: {
|
||||||
|
id: 'configuration',
|
||||||
|
template: 'systems/daggerheart/templates/sheets-settings/action-settings/configuration.hbs'
|
||||||
|
},
|
||||||
|
effect: {
|
||||||
|
id: 'effect',
|
||||||
|
template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static TABS = {
|
||||||
|
base: {
|
||||||
|
active: true,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'base',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.GENERAL.Tabs.base'
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'config',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.GENERAL.Tabs.configuration'
|
||||||
|
},
|
||||||
|
effect: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'effect',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.GENERAL.Tabs.effects'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects'];
|
||||||
|
|
||||||
|
_getTabs(tabs) {
|
||||||
|
for (const v of Object.values(tabs)) {
|
||||||
|
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
|
||||||
|
v.cssClass = v.active ? 'active' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options, 'action');
|
||||||
|
context.source = this.action.toObject(false);
|
||||||
|
context.openSection = this.openSection;
|
||||||
|
context.tabs = this._getTabs(this.constructor.TABS);
|
||||||
|
context.config = CONFIG.DH;
|
||||||
|
if (this.action.damage?.hasOwnProperty('includeBase') && this.action.type === 'attack')
|
||||||
|
context.hasBaseDamage = !!this.action.parent.attack;
|
||||||
|
context.costOptions = this.getCostOptions();
|
||||||
|
context.getRollTypeOptions = this.getRollTypeOptions();
|
||||||
|
context.disableOption = this.disableOption.bind(this);
|
||||||
|
context.isNPC = this.action.actor?.isNPC;
|
||||||
|
context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty;
|
||||||
|
context.baseAttackBonus = this.action.actor?.system.attack?.roll.bonus;
|
||||||
|
context.hasRoll = this.action.hasRoll;
|
||||||
|
|
||||||
|
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
|
||||||
|
context.tierOptions = [
|
||||||
|
{ key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') },
|
||||||
|
...Object.values(settingsTiers).map(x => ({ key: x.tier, label: x.name }))
|
||||||
|
];
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static toggleSection(_, button) {
|
||||||
|
this.openSection = button.dataset.section === this.openSection ? null : button.dataset.section;
|
||||||
|
this.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCostOptions() {
|
||||||
|
const options = foundry.utils.deepClone(CONFIG.DH.GENERAL.abilityCosts);
|
||||||
|
const resource = this.action.parent.resource;
|
||||||
|
if (resource) {
|
||||||
|
options.resource = {
|
||||||
|
label: 'DAGGERHEART.GENERAL.itemResource',
|
||||||
|
group: 'Global'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.action.parent.metadata?.isQuantifiable) {
|
||||||
|
options.quantity = {
|
||||||
|
label: 'DAGGERHEART.GENERAL.itemQuantity',
|
||||||
|
group: 'Global'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRollTypeOptions() {
|
||||||
|
const types = foundry.utils.deepClone(CONFIG.DH.GENERAL.rollTypes);
|
||||||
|
if (!this.action.actor) return types;
|
||||||
|
Object.values(types).forEach(t => {
|
||||||
|
if (this.action.actor.type !== 'character' && t.playerOnly) delete types[t.id];
|
||||||
|
});
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
_prepareSubmitData(_event, formData) {
|
||||||
|
const submitData = foundry.utils.expandObject(formData.object);
|
||||||
|
|
||||||
|
const itemAbilityCostKeys = Object.keys(CONFIG.DH.GENERAL.itemAbilityCosts);
|
||||||
|
for (const keyPath of this.constructor.CLEAN_ARRAYS) {
|
||||||
|
const data = foundry.utils.getProperty(submitData, keyPath);
|
||||||
|
const dataValues = data ? Object.values(data) : [];
|
||||||
|
if (keyPath === 'cost') {
|
||||||
|
for (var value of dataValues) {
|
||||||
|
value.itemId = itemAbilityCostKeys.includes(value.key) ? this.action.parent.parent.id : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) foundry.utils.setProperty(submitData, keyPath, dataValues);
|
||||||
|
}
|
||||||
|
return submitData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateForm(event, _, formData) {
|
||||||
|
const submitData = this._prepareSubmitData(event, formData),
|
||||||
|
data = foundry.utils.mergeObject(this.action.toObject(), submitData);
|
||||||
|
this.action = await this.action.update(data);
|
||||||
|
|
||||||
|
this.sheetUpdate?.(this.action);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static addElement(event) {
|
||||||
|
const data = this.action.toObject(),
|
||||||
|
key = event.target.closest('[data-key]').dataset.key;
|
||||||
|
if (!this.action[key]) return;
|
||||||
|
|
||||||
|
data[key].push(this.action.defaultValues[key] ?? {});
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeElement(event, button) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const data = this.action.toObject(),
|
||||||
|
key = event.target.closest('[data-key]').dataset.key,
|
||||||
|
index = button.dataset.index;
|
||||||
|
data[key].splice(index, 1);
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static addDamage(_event) {
|
||||||
|
if (!this.action.damage.parts) return;
|
||||||
|
const data = this.action.toObject(),
|
||||||
|
part = {};
|
||||||
|
if (this.action.actor?.isNPC) part.value = { multiplier: 'flat' };
|
||||||
|
data.damage.parts.push(part);
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeDamage(_event, button) {
|
||||||
|
if (!this.action.damage.parts) return;
|
||||||
|
const data = this.action.toObject(),
|
||||||
|
index = button.dataset.index;
|
||||||
|
data.damage.parts.splice(index, 1);
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Specific implementation in extending classes **/
|
||||||
|
static async addEffect(_event) {}
|
||||||
|
static removeEffect(_event, _button) {}
|
||||||
|
static editEffect(_event) {}
|
||||||
|
|
||||||
|
async close(options) {
|
||||||
|
this.tabGroups.primary = 'base';
|
||||||
|
await super.close(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,241 +1,32 @@
|
||||||
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
import DHActionBaseConfig from './action-base-config.mjs';
|
||||||
|
|
||||||
const { ApplicationV2 } = foundry.applications.api;
|
|
||||||
export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|
||||||
constructor(action, sheetUpdate) {
|
|
||||||
super({});
|
|
||||||
|
|
||||||
this.action = action;
|
|
||||||
this.sheetUpdate = sheetUpdate;
|
|
||||||
this.openSection = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get title() {
|
|
||||||
return `${game.i18n.localize('DAGGERHEART.GENERAL.Tabs.settings')}: ${this.action.name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export default class DHActionConfig extends DHActionBaseConfig {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
...DHActionBaseConfig.DEFAULT_OPTIONS,
|
||||||
classes: ['daggerheart', 'dh-style', 'dialog', 'max-800'],
|
|
||||||
window: {
|
|
||||||
icon: 'fa-solid fa-wrench',
|
|
||||||
resizable: false
|
|
||||||
},
|
|
||||||
position: { width: 600, height: 'auto' },
|
|
||||||
actions: {
|
actions: {
|
||||||
toggleSection: this.toggleSection,
|
...DHActionBaseConfig.DEFAULT_OPTIONS.actions,
|
||||||
addEffect: this.addEffect,
|
addEffect: this.addEffect,
|
||||||
removeEffect: this.removeEffect,
|
removeEffect: this.removeEffect,
|
||||||
addElement: this.addElement,
|
editEffect: this.editEffect
|
||||||
removeElement: this.removeElement,
|
|
||||||
editEffect: this.editEffect,
|
|
||||||
addDamage: this.addDamage,
|
|
||||||
removeDamage: this.removeDamage
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
async _prepareContext(options) {
|
||||||
header: {
|
const context = await super._prepareContext(options);
|
||||||
id: 'header',
|
|
||||||
template: 'systems/daggerheart/templates/sheets-settings/action-settings/header.hbs'
|
|
||||||
},
|
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
|
||||||
base: {
|
|
||||||
id: 'base',
|
|
||||||
template: 'systems/daggerheart/templates/sheets-settings/action-settings/base.hbs'
|
|
||||||
},
|
|
||||||
configuration: {
|
|
||||||
id: 'configuration',
|
|
||||||
template: 'systems/daggerheart/templates/sheets-settings/action-settings/configuration.hbs'
|
|
||||||
},
|
|
||||||
effect: {
|
|
||||||
id: 'effect',
|
|
||||||
template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static TABS = {
|
|
||||||
base: {
|
|
||||||
active: true,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'base',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.GENERAL.Tabs.base'
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'config',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.GENERAL.Tabs.configuration'
|
|
||||||
},
|
|
||||||
effect: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'effect',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.GENERAL.Tabs.effects'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects'];
|
|
||||||
|
|
||||||
_getTabs(tabs) {
|
|
||||||
for (const v of Object.values(tabs)) {
|
|
||||||
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
|
|
||||||
v.cssClass = v.active ? 'active' : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return tabs;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
|
||||||
const context = await super._prepareContext(_options, 'action');
|
|
||||||
context.source = this.action.toObject(false);
|
|
||||||
context.openSection = this.openSection;
|
|
||||||
context.tabs = this._getTabs(this.constructor.TABS);
|
|
||||||
context.config = CONFIG.DH;
|
|
||||||
if (!!this.action.effects) context.effects = this.action.effects.map(e => this.action.item.effects.get(e._id));
|
if (!!this.action.effects) context.effects = this.action.effects.map(e => this.action.item.effects.get(e._id));
|
||||||
if (this.action.damage?.hasOwnProperty('includeBase') && this.action.type === 'attack')
|
|
||||||
context.hasBaseDamage = !!this.action.parent.attack;
|
|
||||||
context.getEffectDetails = this.getEffectDetails.bind(this);
|
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||||
context.costOptions = this.getCostOptions();
|
|
||||||
context.getRollTypeOptions = this.getRollTypeOptions();
|
|
||||||
context.disableOption = this.disableOption.bind(this);
|
|
||||||
context.isNPC = this.action.actor?.isNPC;
|
|
||||||
context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty;
|
|
||||||
context.baseAttackBonus = this.action.actor?.system.attack?.roll.bonus;
|
|
||||||
context.hasRoll = this.action.hasRoll;
|
|
||||||
|
|
||||||
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
|
|
||||||
context.tierOptions = [
|
|
||||||
{ key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') },
|
|
||||||
...Object.values(settingsTiers).map(x => ({ key: x.tier, label: x.name }))
|
|
||||||
];
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toggleSection(_, button) {
|
static async addEffect(_event) {
|
||||||
this.openSection = button.dataset.section === this.openSection ? null : button.dataset.section;
|
|
||||||
this.render(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCostOptions() {
|
|
||||||
const options = foundry.utils.deepClone(CONFIG.DH.GENERAL.abilityCosts);
|
|
||||||
const resource = this.action.parent.resource;
|
|
||||||
if (resource) {
|
|
||||||
options.resource = {
|
|
||||||
label: 'DAGGERHEART.GENERAL.itemResource',
|
|
||||||
group: 'Global'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.action.parent.metadata?.isQuantifiable) {
|
|
||||||
options.quantity = {
|
|
||||||
label: 'DAGGERHEART.GENERAL.itemQuantity',
|
|
||||||
group: 'Global'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRollTypeOptions() {
|
|
||||||
const types = foundry.utils.deepClone(CONFIG.DH.GENERAL.rollTypes);
|
|
||||||
if (!this.action.actor) return types;
|
|
||||||
Object.values(types).forEach(t => {
|
|
||||||
if (this.action.actor.type !== 'character' && t.playerOnly) delete types[t.id];
|
|
||||||
});
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
getEffectDetails(id) {
|
|
||||||
return this.action.item.effects.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
_prepareSubmitData(_event, formData) {
|
|
||||||
const submitData = foundry.utils.expandObject(formData.object);
|
|
||||||
|
|
||||||
const itemAbilityCostKeys = Object.keys(CONFIG.DH.GENERAL.itemAbilityCosts);
|
|
||||||
for (const keyPath of this.constructor.CLEAN_ARRAYS) {
|
|
||||||
const data = foundry.utils.getProperty(submitData, keyPath);
|
|
||||||
const dataValues = data ? Object.values(data) : [];
|
|
||||||
if (keyPath === 'cost') {
|
|
||||||
for (var value of dataValues) {
|
|
||||||
value.itemId = itemAbilityCostKeys.includes(value.key) ? this.action.parent.parent.id : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data) foundry.utils.setProperty(submitData, keyPath, dataValues);
|
|
||||||
}
|
|
||||||
return submitData;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
|
||||||
const submitData = this._prepareSubmitData(event, formData),
|
|
||||||
data = foundry.utils.mergeObject(this.action.toObject(), submitData);
|
|
||||||
this.action = await this.action.update(data);
|
|
||||||
|
|
||||||
this.sheetUpdate?.(this.action);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
static addElement(event) {
|
|
||||||
const data = this.action.toObject(),
|
|
||||||
key = event.target.closest('[data-key]').dataset.key;
|
|
||||||
if (!this.action[key]) return;
|
|
||||||
|
|
||||||
data[key].push(this.action.defaultValues[key] ?? {});
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
static removeElement(event, button) {
|
|
||||||
event.stopPropagation();
|
|
||||||
const data = this.action.toObject(),
|
|
||||||
key = event.target.closest('[data-key]').dataset.key,
|
|
||||||
index = button.dataset.index;
|
|
||||||
data[key].splice(index, 1);
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
static addDamage(event) {
|
|
||||||
if (!this.action.damage.parts) return;
|
|
||||||
const data = this.action.toObject(),
|
|
||||||
part = {};
|
|
||||||
if (this.action.actor?.isNPC) part.value = { multiplier: 'flat' };
|
|
||||||
data.damage.parts.push(part);
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
static removeDamage(event, button) {
|
|
||||||
if (!this.action.damage.parts) return;
|
|
||||||
const data = this.action.toObject(),
|
|
||||||
index = button.dataset.index;
|
|
||||||
data.damage.parts.splice(index, 1);
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addEffect(event) {
|
|
||||||
if (!this.action.effects) return;
|
if (!this.action.effects) return;
|
||||||
const effectData = this._addEffectData.bind(this)(),
|
const effectData = this._addEffectData.bind(this)();
|
||||||
[created] = await this.action.item.createEmbeddedDocuments('ActiveEffect', [effectData], { render: false }),
|
const data = this.action.toObject();
|
||||||
data = this.action.toObject();
|
|
||||||
|
const [created] = await this.action.item.createEmbeddedDocuments('ActiveEffect', [effectData], {
|
||||||
|
render: false
|
||||||
|
});
|
||||||
data.effects.push({ _id: created._id });
|
data.effects.push({ _id: created._id });
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
this.action.item.effects.get(created._id).sheet.render(true);
|
this.action.item.effects.get(created._id).sheet.render(true);
|
||||||
|
|
@ -255,6 +46,10 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEffectDetails(id) {
|
||||||
|
return this.action.item.effects.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
static removeEffect(event, button) {
|
static removeEffect(event, button) {
|
||||||
if (!this.action.effects) return;
|
if (!this.action.effects) return;
|
||||||
const index = button.dataset.index,
|
const index = button.dataset.index,
|
||||||
|
|
@ -267,9 +62,4 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
const id = event.target.closest('[data-effect-id]')?.dataset?.effectId;
|
const id = event.target.closest('[data-effect-id]')?.dataset?.effectId;
|
||||||
this.action.item.effects.get(id).sheet.render(true);
|
this.action.item.effects.get(id).sheet.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async close(options) {
|
|
||||||
this.tabGroups.primary = 'base';
|
|
||||||
await super.close(options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
import DHActionBaseConfig from './action-base-config.mjs';
|
||||||
|
|
||||||
|
export default class DHActionSettingsConfig extends DHActionBaseConfig {
|
||||||
|
constructor(action, effects, sheetUpdate) {
|
||||||
|
super(action);
|
||||||
|
|
||||||
|
this.effects = effects;
|
||||||
|
this.sheetUpdate = sheetUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...DHActionBaseConfig.DEFAULT_OPTIONS,
|
||||||
|
actions: {
|
||||||
|
...DHActionBaseConfig.DEFAULT_OPTIONS.actions,
|
||||||
|
addEffect: this.addEffect,
|
||||||
|
removeEffect: this.removeEffect,
|
||||||
|
editEffect: this.editEffect
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareContext(options) {
|
||||||
|
const context = await super._prepareContext(options);
|
||||||
|
context.effects = this.effects;
|
||||||
|
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEffectDetails(id) {
|
||||||
|
return this.effects.find(x => x.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async addEffect(_event) {
|
||||||
|
if (!this.action.effects) return;
|
||||||
|
const effectData = game.system.api.data.activeEffects.BaseEffect.getDefaultObject();
|
||||||
|
const data = this.action.toObject();
|
||||||
|
|
||||||
|
this.sheetUpdate(data, effectData);
|
||||||
|
this.effects = [...this.effects, effectData];
|
||||||
|
data.effects.push({ _id: effectData.id });
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeEffect(event, button) {
|
||||||
|
if (!this.action.effects) return;
|
||||||
|
const index = button.dataset.index,
|
||||||
|
effectId = this.action.effects[index]._id;
|
||||||
|
this.constructor.removeElement.bind(this)(event, button);
|
||||||
|
this.sheetUpdate(
|
||||||
|
this.action.toObject(),
|
||||||
|
this.effects.find(x => x.id === effectId),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async editEffect(event) {
|
||||||
|
const id = event.target.closest('[data-effect-id]')?.dataset?.effectId;
|
||||||
|
const updatedEffect = await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(
|
||||||
|
this.getEffectDetails(id)
|
||||||
|
);
|
||||||
|
if (!updatedEffect) return;
|
||||||
|
|
||||||
|
this.effects = await this.sheetUpdate(this.action.toObject(), { ...updatedEffect, id });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config'],
|
classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config', 'standard-form'],
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
position: {
|
position: {
|
||||||
width: 560
|
width: 560
|
||||||
|
|
@ -131,6 +131,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
||||||
if (partId in context.tabs) context.tab = context.tabs[partId];
|
if (partId in context.tabs) context.tab = context.tabs[partId];
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
case 'details':
|
case 'details':
|
||||||
|
context.statuses = CONFIG.statusEffects.map(s => ({ value: s.id, label: game.i18n.localize(s.name) }));
|
||||||
context.isActorEffect = false;
|
context.isActorEffect = false;
|
||||||
context.isItemEffect = true;
|
context.isItemEffect = true;
|
||||||
const useGeneric = game.settings.get(
|
const useGeneric = game.settings.get(
|
||||||
|
|
@ -138,10 +139,13 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
||||||
CONFIG.DH.SETTINGS.gameSettings.appearance
|
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||||
).showGenericStatusEffects;
|
).showGenericStatusEffects;
|
||||||
if (!useGeneric) {
|
if (!useGeneric) {
|
||||||
context.statuses = Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({
|
context.statuses = [
|
||||||
value: status.id,
|
...context.statuses,
|
||||||
label: game.i18n.localize(status.name)
|
Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({
|
||||||
}));
|
value: status.id,
|
||||||
|
label: game.i18n.localize(status.name)
|
||||||
|
}))
|
||||||
|
];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'changes':
|
case 'changes':
|
||||||
|
|
@ -157,7 +161,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onSubmit(event, form, formData) {
|
static async #onSubmit(_event, _form, formData) {
|
||||||
this.data = foundry.utils.expandObject(formData.object);
|
this.data = foundry.utils.expandObject(formData.object);
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
@ -193,11 +197,11 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #addChange() {
|
static async #addChange() {
|
||||||
const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object);
|
const { changes, ...rest } = foundry.utils.expandObject(new FormDataExtended(this.form).object);
|
||||||
const changes = Object.values(submitData.changes ?? {});
|
const updatedChanges = Object.values(changes ?? {});
|
||||||
changes.push({});
|
updatedChanges.push({});
|
||||||
|
|
||||||
this.effect.changes = changes;
|
this.effect = { ...rest, changes: updatedChanges };
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,12 +212,12 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
||||||
*/
|
*/
|
||||||
static async #deleteChange(event) {
|
static async #deleteChange(event) {
|
||||||
const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object);
|
const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object);
|
||||||
const changes = Object.values(submitData.changes);
|
const updatedChanges = Object.values(submitData.changes);
|
||||||
const row = event.target.closest('li');
|
const row = event.target.closest('li');
|
||||||
const index = Number(row.dataset.index) || 0;
|
const index = Number(row.dataset.index) || 0;
|
||||||
changes.splice(index, 1);
|
updatedChanges.splice(index, 1);
|
||||||
|
|
||||||
this.effect.changes = changes;
|
this.effect = { ...submitData, changes: updatedChanges };
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { actionsTypes } from '../../data/action/_module.mjs';
|
import { actionsTypes } from '../../data/action/_module.mjs';
|
||||||
import DHActionConfig from './action-config.mjs';
|
import ActionSettingsConfig from './action-settings-config.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
|
@ -102,6 +102,8 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
return (
|
return (
|
||||||
(await foundry.applications.api.DialogV2.input({
|
(await foundry.applications.api.DialogV2.input({
|
||||||
window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') },
|
window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') },
|
||||||
|
position: { width: 300 },
|
||||||
|
classes: ['daggerheart', 'dh-style'],
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/actionTypes/actionType.hbs',
|
'systems/daggerheart/templates/actionTypes/actionType.hbs',
|
||||||
{ types: CONFIG.DH.ACTIONS.actionTypes }
|
{ types: CONFIG.DH.ACTIONS.actionTypes }
|
||||||
|
|
@ -158,16 +160,55 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
this.render();
|
this.render();
|
||||||
} else {
|
} else {
|
||||||
const action = this.move.actions.get(id);
|
const action = this.move.actions.get(id);
|
||||||
await new DHActionConfig(action, async updatedMove => {
|
await new ActionSettingsConfig(action, this.move.effects, async (updatedMove, effectData, deleteEffect) => {
|
||||||
|
let updatedEffects = null;
|
||||||
|
if (effectData) {
|
||||||
|
const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`);
|
||||||
|
const existingEffectIndex = currentEffects.findIndex(x => x.id === effectData.id);
|
||||||
|
|
||||||
|
updatedEffects = deleteEffect
|
||||||
|
? currentEffects.filter(x => x.id !== effectData.id)
|
||||||
|
: existingEffectIndex === -1
|
||||||
|
? [...currentEffects, effectData]
|
||||||
|
: currentEffects.with(existingEffectIndex, effectData);
|
||||||
|
await this.settings.updateSource({
|
||||||
|
[`${this.movePath}.effects`]: updatedEffects
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await this.settings.updateSource({ [`${this.actionsPath}.${id}`]: updatedMove });
|
await this.settings.updateSource({ [`${this.actionsPath}.${id}`]: updatedMove });
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||||
this.render();
|
this.render();
|
||||||
|
return updatedEffects;
|
||||||
}).render(true);
|
}).render(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async removeItem(_, target) {
|
static async removeItem(_, target) {
|
||||||
await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
|
const { type, id } = target.dataset;
|
||||||
|
if (type === 'effect') {
|
||||||
|
const move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||||
|
for (const action of move.actions) {
|
||||||
|
const remainingEffects = action.effects.filter(x => x._id !== id);
|
||||||
|
if (action.effects.length !== remainingEffects.length) {
|
||||||
|
await action.update({
|
||||||
|
effects: remainingEffects.map(x => {
|
||||||
|
const { _id, ...rest } = x;
|
||||||
|
return { ...rest, _id: _id };
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.settings.updateSource({
|
||||||
|
[this.movePath]: {
|
||||||
|
effects: move.effects.filter(x => x.id !== id),
|
||||||
|
actions: move.actions
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
|
||||||
|
}
|
||||||
|
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -459,7 +459,16 @@ export const allArmorFeatures = () => {
|
||||||
...armorFeatures,
|
...armorFeatures,
|
||||||
...Object.keys(homebrewFeatures).reduce((acc, key) => {
|
...Object.keys(homebrewFeatures).reduce((acc, key) => {
|
||||||
const feature = homebrewFeatures[key];
|
const feature = homebrewFeatures[key];
|
||||||
acc[key] = { ...feature, label: feature.name };
|
const actions = feature.actions.map(action => ({
|
||||||
|
...action,
|
||||||
|
effects: action.effects.map(effect => feature.effects.find(x => x.id === effect._id)),
|
||||||
|
type: action.type
|
||||||
|
}));
|
||||||
|
const actionEffects = actions.flatMap(a => a.effects);
|
||||||
|
|
||||||
|
const effects = feature.effects.filter(effect => !actionEffects.some(x => x.id === effect.id));
|
||||||
|
|
||||||
|
acc[key] = { ...feature, label: feature.name, effects, actions };
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
};
|
};
|
||||||
|
|
@ -1414,11 +1423,21 @@ export const weaponFeatures = {
|
||||||
export const allWeaponFeatures = () => {
|
export const allWeaponFeatures = () => {
|
||||||
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
||||||
.weaponFeatures;
|
.weaponFeatures;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...weaponFeatures,
|
...weaponFeatures,
|
||||||
...Object.keys(homebrewFeatures).reduce((acc, key) => {
|
...Object.keys(homebrewFeatures).reduce((acc, key) => {
|
||||||
const feature = homebrewFeatures[key];
|
const feature = homebrewFeatures[key];
|
||||||
acc[key] = { ...feature, label: feature.name };
|
|
||||||
|
const actions = feature.actions.map(action => ({
|
||||||
|
...action,
|
||||||
|
effects: action.effects.map(effect => feature.effects.find(x => x.id === effect._id)),
|
||||||
|
type: action.type
|
||||||
|
}));
|
||||||
|
const actionEffects = actions.flatMap(a => a.effects);
|
||||||
|
const effects = feature.effects.filter(effect => !actionEffects.some(x => x.id === effect.id));
|
||||||
|
|
||||||
|
acc[key] = { ...feature, label: feature.name, effects, actions };
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import BaseDataActor from './base.mjs';
|
import BaseDataActor from './base.mjs';
|
||||||
import DhLevelData from '../levelData.mjs';
|
import DhLevelData from '../levelData.mjs';
|
||||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||||
import { ActionField, ActionsField } from '../fields/actionField.mjs';
|
import { ActionField } from '../fields/actionField.mjs';
|
||||||
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
|
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
|
||||||
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
|
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
|
||||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
<div class="settings-items">
|
<div class="settings-items">
|
||||||
{{#each move.effects}}
|
{{#each move.effects}}
|
||||||
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" id=this.id type="effect" }}
|
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" actionId=../move.id id=this.id type="effect" }}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@
|
||||||
{{formGroup fields.disabled value=source.disabled rootId=rootId}}
|
{{formGroup fields.disabled value=source.disabled rootId=rootId}}
|
||||||
|
|
||||||
{{#if isActorEffect}}
|
{{#if isActorEffect}}
|
||||||
{{formGroup fields.origin value=source.origin rootId=rootId disabled=true}}
|
{{formGroup fields.origin value=source.origin rootId=rootId disabled=true}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isItemEffect}}
|
{{#if isItemEffect}}
|
||||||
{{formGroup fields.transfer value=source.transfer rootId=rootId label=legacyTransfer.label hint=(localize "DAGGERHEART.EFFECTS.Attachments.transferHint")}}
|
{{formGroup fields.transfer value=source.transfer rootId=rootId label=legacyTransfer.label hint=(localize "DAGGERHEART.EFFECTS.Attachments.transferHint")}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{formGroup fields.statuses value=source.statuses options=statuses rootId=rootId classes="statuses"}}
|
{{formGroup fields.statuses value=source.statuses options=statuses rootId=rootId classes="statuses"}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue