mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
Merge from main, with conflict fixes
This commit is contained in:
commit
a11a2d4e30
1252 changed files with 2537 additions and 22178 deletions
|
|
@ -123,7 +123,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
}
|
||||
|
||||
const tagTeamSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||
if (this.actor?.id && tagTeamSetting.members[this.actor.id] && !this.config.skips?.createMessage) {
|
||||
if (this.actor && tagTeamSetting.members[this.actor.id] && !this.config.skips?.createMessage) {
|
||||
context.activeTagTeamRoll = true;
|
||||
context.tagTeamSelected = this.config.tagTeamSelected;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { refreshIsAllowed } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
|
|
@ -94,11 +96,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
const actionItems = this.actor.items.reduce((acc, x) => {
|
||||
if (x.system.actions) {
|
||||
const recoverable = x.system.actions.reduce((acc, action) => {
|
||||
if (
|
||||
action.uses.recovery &&
|
||||
((action.uses.recovery === 'longRest' && !this.shortrest) ||
|
||||
action.uses.recovery === 'shortRest')
|
||||
) {
|
||||
if (refreshIsAllowed([this.shortrest ? 'shortRest' : 'longRest'], action.uses.recovery)) {
|
||||
acc.push({
|
||||
title: x.name,
|
||||
name: action.name,
|
||||
|
|
@ -120,8 +118,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
if (
|
||||
x.system.resource &&
|
||||
x.system.resource.type &&
|
||||
((x.system.resource.recovery === 'longRest') === !this.shortrest ||
|
||||
x.system.resource.recovery === 'shortRest')
|
||||
refreshIsAllowed([this.shortrest ? 'shortRest' : 'longRest'], x.system.resource.recovery)
|
||||
) {
|
||||
acc.push({
|
||||
title: game.i18n.localize(`TYPES.Item.${x.type}`),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { getCritDamageBonus } from '../../helpers/utils.mjs';
|
||||
import { GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
|
@ -76,28 +77,37 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
cost: this.data.initiator.cost
|
||||
};
|
||||
|
||||
context.selectedData = Object.values(context.members).reduce(
|
||||
(acc, member) => {
|
||||
if (!member.roll) return acc;
|
||||
if (member.selected) {
|
||||
acc.result = `${member.roll.system.roll.total} ${member.roll.system.roll.result.label}`;
|
||||
}
|
||||
const selectedMember = Object.values(context.members).find(x => x.selected);
|
||||
const selectedIsCritical = selectedMember?.roll?.system?.isCritical;
|
||||
context.selectedData = {
|
||||
result: selectedMember
|
||||
? `${selectedMember.roll.system.roll.total} ${selectedMember.roll.system.roll.result.label}`
|
||||
: null,
|
||||
damageValues: null
|
||||
};
|
||||
|
||||
if (context.usesDamage) {
|
||||
if (!acc.damageValues) acc.damageValues = {};
|
||||
for (let damage of member.damageValues) {
|
||||
if (acc.damageValues[damage.key]) {
|
||||
acc.damageValues[damage.key].total += damage.total;
|
||||
} else {
|
||||
acc.damageValues[damage.key] = foundry.utils.deepClone(damage);
|
||||
}
|
||||
for (const member of Object.values(context.members)) {
|
||||
if (!member.roll) continue;
|
||||
if (context.usesDamage) {
|
||||
if (!context.selectedData.damageValues) context.selectedData.damageValues = {};
|
||||
for (let damage of member.damageValues) {
|
||||
const damageTotal = member.roll.system.isCritical
|
||||
? damage.total
|
||||
: selectedIsCritical
|
||||
? damage.total + (await getCritDamageBonus(member.roll.system.damage[damage.key].formula))
|
||||
: damage.total;
|
||||
if (context.selectedData.damageValues[damage.key]) {
|
||||
context.selectedData.damageValues[damage.key].total += damageTotal;
|
||||
} else {
|
||||
context.selectedData.damageValues[damage.key] = {
|
||||
...foundry.utils.deepClone(damage),
|
||||
total: damageTotal
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ result: null, damageValues: null }
|
||||
);
|
||||
context.showResult = Object.values(context.members).reduce((enabled, member) => {
|
||||
if (!member.roll) return enabled;
|
||||
if (context.usesDamage) {
|
||||
|
|
@ -201,21 +211,41 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
.map(key => game.messages.get(this.data.members[key].messageId));
|
||||
|
||||
const systemData = foundry.utils.deepClone(mainRoll).system.toObject();
|
||||
const criticalRoll = systemData.roll.isCritical;
|
||||
for (let roll of secondaryRolls) {
|
||||
if (roll.system.hasDamage) {
|
||||
for (let key in roll.system.damage) {
|
||||
var damage = roll.system.damage[key];
|
||||
const damageTotal =
|
||||
!roll.system.isCritical && criticalRoll
|
||||
? (await getCritDamageBonus(damage.formula)) + damage.total
|
||||
: damage.total;
|
||||
if (systemData.damage[key]) {
|
||||
systemData.damage[key].total += damage.total;
|
||||
systemData.damage[key].parts = [...systemData.damage[key].parts, ...damage.parts];
|
||||
const updatedDamageParts = damage.parts;
|
||||
if (!roll.system.isCritical && criticalRoll) {
|
||||
for (let part of updatedDamageParts) {
|
||||
const criticalDamage = await getCritDamageBonus(part.formula);
|
||||
if (criticalDamage) {
|
||||
damage.formula = `${damage.formula} + ${criticalDamage}`;
|
||||
part.formula = `${part.formula} + ${criticalDamage}`;
|
||||
part.modifierTotal = part.modifierTotal + criticalDamage;
|
||||
part.total += criticalDamage;
|
||||
part.roll = new Roll(part.formula);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
systemData.damage[key].formula = `${systemData.damage[key].formula} + ${damage.formula}`;
|
||||
systemData.damage[key].total += damageTotal;
|
||||
systemData.damage[key].parts = [...systemData.damage[key].parts, ...updatedDamageParts];
|
||||
} else {
|
||||
systemData.damage[key] = damage;
|
||||
systemData.damage[key] = { ...damage, total: damageTotal, parts: updatedDamageParts };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
systemData.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.chatMessageRollTitle');
|
||||
|
||||
systemData.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.chatMessageRollTitle');
|
||||
const cls = getDocumentClass('ChatMessage'),
|
||||
msgData = {
|
||||
type: 'dualityRoll',
|
||||
|
|
@ -233,14 +263,16 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
const fearUpdate = { key: 'fear', value: null, total: null, enabled: true };
|
||||
for (let memberId of Object.keys(this.data.members)) {
|
||||
const resourceUpdates = [];
|
||||
if (systemData.roll.isCritical || systemData.roll.result.duality === 1) {
|
||||
const value =
|
||||
memberId !== this.data.initiator.id
|
||||
? 1
|
||||
: this.data.initiator.cost
|
||||
? 1 - this.data.initiator.cost
|
||||
: 1;
|
||||
const rollGivesHope = systemData.roll.isCritical || systemData.roll.result.duality === 1;
|
||||
if (memberId === this.data.initiator.id) {
|
||||
const value = this.data.initiator.cost
|
||||
? rollGivesHope
|
||||
? 1 - this.data.initiator.cost
|
||||
: -this.data.initiator.cost
|
||||
: 1;
|
||||
resourceUpdates.push({ key: 'hope', value: value, total: -value, enabled: true });
|
||||
} else if (rollGivesHope) {
|
||||
resourceUpdates.push({ key: 'hope', value: 1, total: -1, enabled: true });
|
||||
}
|
||||
if (systemData.roll.isCritical) resourceUpdates.push({ key: 'stress', value: -1, total: 1, enabled: true });
|
||||
if (systemData.roll.result.duality === -1) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
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 AdversarySettings } from './adversary-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';
|
||||
|
||||
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}`;
|
||||
}
|
||||
import DHActionBaseConfig from './action-base-config.mjs';
|
||||
|
||||
export default class DHActionConfig extends DHActionBaseConfig {
|
||||
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' },
|
||||
...DHActionBaseConfig.DEFAULT_OPTIONS,
|
||||
actions: {
|
||||
toggleSection: this.toggleSection,
|
||||
...DHActionBaseConfig.DEFAULT_OPTIONS.actions,
|
||||
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
|
||||
editEffect: this.editEffect
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
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.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;
|
||||
}
|
||||
|
||||
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) {
|
||||
static async addEffect(_event) {
|
||||
if (!this.action.effects) return;
|
||||
const effectData = this._addEffectData.bind(this)(),
|
||||
[created] = await this.action.item.createEmbeddedDocuments('ActiveEffect', [effectData], { render: false }),
|
||||
data = this.action.toObject();
|
||||
const effectData = this._addEffectData.bind(this)();
|
||||
const data = this.action.toObject();
|
||||
|
||||
const [created] = await this.action.item.createEmbeddedDocuments('ActiveEffect', [effectData], {
|
||||
render: false
|
||||
});
|
||||
data.effects.push({ _id: created._id });
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
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) {
|
||||
if (!this.action.effects) return;
|
||||
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;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -109,9 +109,9 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
|
|||
|
||||
async _onDrop(event) {
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
if (data.fromInternal) return;
|
||||
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (data.fromInternal && item?.parent?.uuid === this.actor.uuid) return;
|
||||
|
||||
if (item.type === 'adversary' && event.target.closest('.category-container')) {
|
||||
const target = event.target.closest('.category-container');
|
||||
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
|||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config'],
|
||||
classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config', 'standard-form'],
|
||||
tag: 'form',
|
||||
position: {
|
||||
width: 560
|
||||
|
|
@ -131,6 +131,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
|||
if (partId in context.tabs) context.tab = context.tabs[partId];
|
||||
switch (partId) {
|
||||
case 'details':
|
||||
context.statuses = CONFIG.statusEffects.map(s => ({ value: s.id, label: game.i18n.localize(s.name) }));
|
||||
context.isActorEffect = false;
|
||||
context.isItemEffect = true;
|
||||
const useGeneric = game.settings.get(
|
||||
|
|
@ -138,10 +139,13 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
|||
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||
).showGenericStatusEffects;
|
||||
if (!useGeneric) {
|
||||
context.statuses = Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({
|
||||
value: status.id,
|
||||
label: game.i18n.localize(status.name)
|
||||
}));
|
||||
context.statuses = [
|
||||
...context.statuses,
|
||||
Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({
|
||||
value: status.id,
|
||||
label: game.i18n.localize(status.name)
|
||||
}))
|
||||
];
|
||||
}
|
||||
break;
|
||||
case 'changes':
|
||||
|
|
@ -157,7 +161,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
|||
return context;
|
||||
}
|
||||
|
||||
static async #onSubmit(event, form, formData) {
|
||||
static async #onSubmit(_event, _form, formData) {
|
||||
this.data = foundry.utils.expandObject(formData.object);
|
||||
this.close();
|
||||
}
|
||||
|
|
@ -193,11 +197,11 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
|||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #addChange() {
|
||||
const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object);
|
||||
const changes = Object.values(submitData.changes ?? {});
|
||||
changes.push({});
|
||||
const { changes, ...rest } = foundry.utils.expandObject(new FormDataExtended(this.form).object);
|
||||
const updatedChanges = Object.values(changes ?? {});
|
||||
updatedChanges.push({});
|
||||
|
||||
this.effect.changes = changes;
|
||||
this.effect = { ...rest, changes: updatedChanges };
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
@ -208,12 +212,12 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
|||
*/
|
||||
static async #deleteChange(event) {
|
||||
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 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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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;
|
||||
|
||||
|
|
@ -102,6 +102,8 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
|||
return (
|
||||
(await foundry.applications.api.DialogV2.input({
|
||||
window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') },
|
||||
position: { width: 300 },
|
||||
classes: ['daggerheart', 'dh-style'],
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/actionTypes/actionType.hbs',
|
||||
{ types: CONFIG.DH.ACTIONS.actionTypes }
|
||||
|
|
@ -158,16 +160,55 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
|||
this.render();
|
||||
} else {
|
||||
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 });
|
||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||
this.render();
|
||||
return updatedEffects;
|
||||
}).render(true);
|
||||
}
|
||||
}
|
||||
|
||||
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.render();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
action: 'editAttribution'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
dragDrop: [{ dragSelector: '[data-item-id][draggable="true"]', dropSelector: null }]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
|
|
|
|||
|
|
@ -139,10 +139,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
element.addEventListener('change', this.updateItemResource.bind(this));
|
||||
element.addEventListener('click', e => e.stopPropagation());
|
||||
});
|
||||
htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => {
|
||||
element.addEventListener('change', this.updateItemQuantity.bind(this));
|
||||
element.addEventListener('click', e => e.stopPropagation());
|
||||
});
|
||||
|
||||
// Add listener for armor marks input
|
||||
htmlElement.querySelectorAll('.armor-marks-input').forEach(element => {
|
||||
|
|
@ -214,34 +210,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
context.resources.stress.emptyPips =
|
||||
context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0;
|
||||
|
||||
context.inventory = { currencies: {} };
|
||||
const { title, ...currencies } = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Homebrew
|
||||
).currency;
|
||||
for (let key in currencies) {
|
||||
context.inventory.currencies[key] = {
|
||||
...currencies[key],
|
||||
field: context.systemFields.gold.fields[key],
|
||||
value: context.source.system.gold[key]
|
||||
};
|
||||
}
|
||||
// context.inventory = {
|
||||
// currency: {
|
||||
// title: game.i18n.localize('DAGGERHEART.CONFIG.Gold.title'),
|
||||
// coins: game.i18n.localize('DAGGERHEART.CONFIG.Gold.coins'),
|
||||
// handfuls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfuls'),
|
||||
// bags: game.i18n.localize('DAGGERHEART.CONFIG.Gold.bags'),
|
||||
// chests: game.i18n.localize('DAGGERHEART.CONFIG.Gold.chests')
|
||||
// }
|
||||
// };
|
||||
|
||||
context.beastformActive = this.document.effects.find(x => x.type === 'beastform');
|
||||
|
||||
// if (context.inventory.length === 0) {
|
||||
// context.inventory = Array(1).fill(Array(5).fill([]));
|
||||
// }
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -619,14 +589,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
this.render();
|
||||
}
|
||||
|
||||
async updateItemQuantity(event) {
|
||||
const item = await getDocFromElement(event.currentTarget);
|
||||
if (!item) return;
|
||||
|
||||
await item.update({ 'system.quantity': event.currentTarget.value });
|
||||
this.render();
|
||||
}
|
||||
|
||||
async updateArmorMarks(event) {
|
||||
const armor = this.document.system.armor;
|
||||
if (!armor) return;
|
||||
|
|
@ -903,47 +865,9 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
});
|
||||
}
|
||||
|
||||
async _onDragStart(event) {
|
||||
const item = await getDocFromElement(event.target);
|
||||
|
||||
const dragData = {
|
||||
originActor: this.document.uuid,
|
||||
originId: item.id,
|
||||
type: item.documentName,
|
||||
uuid: item.uuid
|
||||
};
|
||||
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
|
||||
|
||||
super._onDragStart(event);
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
// Prevent event bubbling to avoid duplicate handling
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
|
||||
const { cancel } = await super._onDrop(event);
|
||||
if (cancel) return;
|
||||
|
||||
this._onDropItem(event, data);
|
||||
}
|
||||
|
||||
async _onDropItem(event, data) {
|
||||
const item = await Item.implementation.fromDropData(data);
|
||||
const itemData = item.toObject();
|
||||
|
||||
if (item.type === 'domainCard' && !this.document.system.loadoutSlot.available) {
|
||||
itemData.system.inVault = true;
|
||||
}
|
||||
|
||||
const typesThatReplace = ['ancestry', 'community'];
|
||||
if (typesThatReplace.includes(item.type)) {
|
||||
await this.document.deleteEmbeddedDocuments(
|
||||
'Item',
|
||||
this.document.items.filter(x => x.type === item.type).map(x => x.id)
|
||||
);
|
||||
async _onDropItem(event, item) {
|
||||
if (this.document.uuid === item.parent?.uuid) {
|
||||
return super._onDropItem(event, item);
|
||||
}
|
||||
|
||||
if (item.type === 'beastform') {
|
||||
|
|
@ -953,20 +877,27 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
);
|
||||
}
|
||||
|
||||
const itemData = item.toObject();
|
||||
const data = await game.system.api.data.items.DHBeastform.getWildcardImage(this.document, itemData);
|
||||
if (data) {
|
||||
if (!data.selectedImage) return;
|
||||
else {
|
||||
if (data.usesDynamicToken) itemData.system.tokenRingImg = data.selectedImage;
|
||||
else itemData.system.tokenImg = data.selectedImage;
|
||||
}
|
||||
if (!data?.selectedImage) {
|
||||
return;
|
||||
} else if (data) {
|
||||
if (data.usesDynamicToken) itemData.system.tokenRingImg = data.selectedImage;
|
||||
else itemData.system.tokenImg = data.selectedImage;
|
||||
return await this._onDropItemCreate(itemData);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.document.uuid === item.parent?.uuid) return this._onSortItem(event, itemData);
|
||||
const createdItem = await this._onDropItemCreate(itemData);
|
||||
// If this is a type that gets deleted, delete it first (but still defer to super)
|
||||
const typesThatReplace = ['ancestry', 'community'];
|
||||
if (typesThatReplace.includes(item.type)) {
|
||||
await this.document.deleteEmbeddedDocuments(
|
||||
'Item',
|
||||
this.document.items.filter(x => x.type === item.type).map(x => x.id)
|
||||
);
|
||||
}
|
||||
|
||||
return createdItem;
|
||||
return super._onDropItem(event, item);
|
||||
}
|
||||
|
||||
async _onDropItemCreate(itemData, event) {
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
|||
header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
|
||||
features: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/environment/features.hbs',
|
||||
scrollable: ['feature-section']
|
||||
scrollable: ['.feature-section']
|
||||
},
|
||||
potentialAdversaries: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/environment/potentialAdversaries.hbs',
|
||||
scrollable: ['items-sections']
|
||||
scrollable: ['.items-section']
|
||||
},
|
||||
notes: { template: 'systems/daggerheart/templates/sheets/actors/environment/notes.hbs' }
|
||||
};
|
||||
|
|
@ -130,12 +130,13 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
|||
/* -------------------------------------------- */
|
||||
|
||||
async _onDragStart(event) {
|
||||
const item = event.currentTarget.closest('.inventory-item');
|
||||
|
||||
const item = event.currentTarget.closest('.inventory-item[data-type=adversary]');
|
||||
if (item) {
|
||||
const adversaryData = { type: 'Actor', uuid: item.dataset.itemUuid };
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData));
|
||||
event.dataTransfer.setDragImage(item, 60, 0);
|
||||
} else {
|
||||
return super._onDragStart(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,25 +93,6 @@ export default class Party extends DHBaseActorSheet {
|
|||
/* Prepare Context */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
|
||||
context.inventory = { currencies: {} };
|
||||
const { title, ...currencies } = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Homebrew
|
||||
).currency;
|
||||
for (let key in currencies) {
|
||||
context.inventory.currencies[key] = {
|
||||
...currencies[key],
|
||||
field: context.systemFields.gold.fields[key],
|
||||
value: context.source.system.gold[key]
|
||||
};
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
async _preparePartContext(partId, context, options) {
|
||||
context = await super._preparePartContext(partId, context, options);
|
||||
switch (partId) {
|
||||
|
|
@ -438,30 +419,9 @@ export default class Party extends DHBaseActorSheet {
|
|||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async _onDragStart(event) {
|
||||
const item = await getDocFromElement(event.target);
|
||||
const dragData = {
|
||||
originActor: this.document.uuid,
|
||||
originId: item.id,
|
||||
type: item.documentName,
|
||||
uuid: item.uuid
|
||||
};
|
||||
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
|
||||
super._onDragStart(event);
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
// Prevent event bubbling to avoid duplicate handling
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
async _onDropActor(event, document) {
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
|
||||
const { cancel } = await super._onDrop(event);
|
||||
if (cancel) return;
|
||||
|
||||
const document = await foundry.utils.fromUuid(data.uuid);
|
||||
|
||||
if (document instanceof DhpActor && Party.ALLOWED_ACTOR_TYPES.includes(document.type)) {
|
||||
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
|
||||
if (currentMembers.includes(data.uuid)) {
|
||||
|
|
@ -469,11 +429,11 @@ export default class Party extends DHBaseActorSheet {
|
|||
}
|
||||
|
||||
await this.document.update({ 'system.partyMembers': [...currentMembers, document.uuid] });
|
||||
} else if (document instanceof DHItem) {
|
||||
this.document.createEmbeddedDocuments('Item', [document.toObject()]);
|
||||
} else {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.onlyCharactersInPartySheet'));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static async #deletePartyMember(event, target) {
|
||||
|
|
|
|||
|
|
@ -322,10 +322,11 @@ export default function DHApplicationMixin(Base) {
|
|||
_onDrop(event) {
|
||||
event.stopPropagation();
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
if (data.fromInternal === this.document.uuid) return;
|
||||
|
||||
if (data.type === 'ActiveEffect') {
|
||||
if (data.type === 'ActiveEffect' && data.fromInternal !== this.document.uuid) {
|
||||
this.document.createEmbeddedDocuments('ActiveEffect', [data.data]);
|
||||
} else {
|
||||
// Fallback to super, but note that item sheets do not have this function
|
||||
return super._onDrop?.(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -662,6 +663,9 @@ export default function DHApplicationMixin(Base) {
|
|||
};
|
||||
if (inVault) data['system.inVault'] = true;
|
||||
if (disabled) data.disabled = true;
|
||||
if (type === "domainCard" && parent?.system.domains?.length) {
|
||||
data.system.domain = parent.system.domains[0];
|
||||
}
|
||||
|
||||
const doc = await cls.create(data, { parent, renderSheet: !event.shiftKey });
|
||||
if (parentIsItem && type === 'feature') {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { itemIsIdentical } from '../../../helpers/utils.mjs';
|
||||
import { getDocFromElement, itemIsIdentical } from '../../../helpers/utils.mjs';
|
||||
import DHBaseActorSettings from './actor-setting.mjs';
|
||||
import DHApplicationMixin from './application-mixin.mjs';
|
||||
|
||||
|
|
@ -69,6 +69,29 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
context.showAttribution = !game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
|
||||
.hideAttribution;
|
||||
|
||||
// Prepare inventory data
|
||||
if (['party', 'character'].includes(this.document.type)) {
|
||||
context.inventory = {
|
||||
currencies: {},
|
||||
weapons: this.document.itemTypes.weapon.sort((a, b) => a.sort - b.sort),
|
||||
armor: this.document.itemTypes.armor.sort((a, b) => a.sort - b.sort),
|
||||
consumables: this.document.itemTypes.consumable.sort((a, b) => a.sort - b.sort),
|
||||
loot: this.document.itemTypes.loot.sort((a, b) => a.sort - b.sort)
|
||||
};
|
||||
const { title, ...currencies } = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Homebrew
|
||||
).currency;
|
||||
for (const key in currencies) {
|
||||
context.inventory.currencies[key] = {
|
||||
...currencies[key],
|
||||
field: context.systemFields.gold.fields[key],
|
||||
value: context.source.system.gold[key]
|
||||
};
|
||||
}
|
||||
context.inventory.hasCurrency = Object.values(context.inventory.currencies).some((c) => c.enabled);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +135,10 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => {
|
||||
element.addEventListener('change', this.updateItemQuantity.bind(this));
|
||||
element.addEventListener('click', e => e.stopPropagation());
|
||||
});
|
||||
htmlElement.querySelectorAll('.item-button .action-uses-button').forEach(element => {
|
||||
element.addEventListener('contextmenu', DHBaseActorSheet.#modifyActionUses);
|
||||
});
|
||||
|
|
@ -150,6 +177,15 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Application Listener Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async updateItemQuantity(event) {
|
||||
const item = await getDocFromElement(event.currentTarget);
|
||||
await item?.update({ 'system.quantity': event.currentTarget.value });
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Application Clicks Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
|
@ -218,68 +254,59 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
/* Application Drag/Drop */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async _onDrop(event) {
|
||||
async _onDropItem(event, item) {
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
if (data.originActor === this.document.uuid) return { cancel: true };
|
||||
|
||||
/* Handling transfer of inventoryItems */
|
||||
let cancel = false;
|
||||
const physicalActorTypes = ['character', 'party'];
|
||||
if (physicalActorTypes.includes(this.document.type)) {
|
||||
const originActor = data.originActor ? await foundry.utils.fromUuid(data.originActor) : null;
|
||||
if (data.originId && originActor && physicalActorTypes.includes(originActor.type)) {
|
||||
const dropDocument = await foundry.utils.fromUuid(data.uuid);
|
||||
|
||||
if (dropDocument.system.metadata.isInventoryItem) {
|
||||
cancel = true;
|
||||
if (dropDocument.system.metadata.isQuantifiable) {
|
||||
const actorItem = originActor.items.get(data.originId);
|
||||
const quantityTransfered =
|
||||
actorItem.system.quantity === 1
|
||||
? 1
|
||||
: await game.system.api.applications.dialogs.ItemTransferDialog.configure(dropDocument);
|
||||
|
||||
if (quantityTransfered) {
|
||||
if (quantityTransfered === actorItem.system.quantity) {
|
||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
||||
} else {
|
||||
cancel = true;
|
||||
await actorItem.update({
|
||||
'system.quantity': actorItem.system.quantity - quantityTransfered
|
||||
});
|
||||
}
|
||||
|
||||
const existingItem = this.document.items.find(x => itemIsIdentical(x, dropDocument));
|
||||
if (existingItem) {
|
||||
cancel = true;
|
||||
await existingItem.update({
|
||||
'system.quantity': existingItem.system.quantity + quantityTransfered
|
||||
});
|
||||
} else {
|
||||
const createData = dropDocument.toObject();
|
||||
await this.document.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...createData,
|
||||
system: {
|
||||
...createData.system,
|
||||
quantity: quantityTransfered
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
cancel = true;
|
||||
}
|
||||
} else {
|
||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
||||
const createData = dropDocument.toObject();
|
||||
await this.document.createEmbeddedDocuments('Item', [createData]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const originActor = item.actor;
|
||||
if (
|
||||
item.actor?.uuid === this.document.uuid ||
|
||||
!originActor ||
|
||||
!physicalActorTypes.includes(this.document.type)
|
||||
) {
|
||||
return super._onDropItem(event, item);
|
||||
}
|
||||
|
||||
return { cancel };
|
||||
/* Handling transfer of inventoryItems */
|
||||
if (item.system.metadata.isInventoryItem) {
|
||||
if (item.system.metadata.isQuantifiable) {
|
||||
const actorItem = originActor.items.get(data.originId);
|
||||
const quantityTransfered =
|
||||
actorItem.system.quantity === 1
|
||||
? 1
|
||||
: await game.system.api.applications.dialogs.ItemTransferDialog.configure(item);
|
||||
|
||||
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));
|
||||
if (existingItem) {
|
||||
await existingItem.update({
|
||||
'system.quantity': existingItem.system.quantity + quantityTransfered
|
||||
});
|
||||
} else {
|
||||
const createData = item.toObject();
|
||||
await this.document.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...createData,
|
||||
system: {
|
||||
...createData.system,
|
||||
quantity: quantityTransfered
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
||||
await this.document.createEmbeddedDocuments('Item', [item.toObject()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -288,7 +315,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
*/
|
||||
async _onDragStart(event) {
|
||||
const attackItem = event.currentTarget.closest('.inventory-item[data-type="attack"]');
|
||||
|
||||
if (attackItem) {
|
||||
const attackData = {
|
||||
type: 'Attack',
|
||||
|
|
@ -298,8 +324,20 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
};
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
|
||||
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
|
||||
} else if (this.document.type !== 'environment') {
|
||||
super._onDragStart(event);
|
||||
return;
|
||||
}
|
||||
|
||||
const item = await getDocFromElement(event.target);
|
||||
if (item) {
|
||||
const dragData = {
|
||||
originActor: this.document.uuid,
|
||||
originId: item.id,
|
||||
type: item.documentName,
|
||||
uuid: item.uuid
|
||||
};
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
|
||||
}
|
||||
|
||||
super._onDragStart(event);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { refreshIsAllowed } from '../../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const { AbstractSidebarTab } = foundry.applications.sidebar;
|
||||
/**
|
||||
|
|
@ -58,7 +60,7 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
|||
if (['character', 'adversary'].includes(actor.type) && actor.prototypeToken.actorLink) {
|
||||
const updates = {};
|
||||
for (let item of actor.items) {
|
||||
if (item.system.metadata?.hasResource && types.includes(item.system.resource?.recovery)) {
|
||||
if (item.system.metadata?.hasResource && refreshIsAllowed(types, item.system.resource?.recovery)) {
|
||||
if (!refreshedActors[actor.id])
|
||||
refreshedActors[actor.id] = { name: actor.name, img: actor.img, refreshed: new Set() };
|
||||
refreshedActors[actor.id].refreshed.add(
|
||||
|
|
@ -79,7 +81,7 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
|||
if (item.system.metadata?.hasActions) {
|
||||
const refreshTypes = new Set();
|
||||
const actions = item.system.actions.filter(action => {
|
||||
if (types.includes(action.uses.recovery)) {
|
||||
if (refreshIsAllowed(types, action.uses.recovery)) {
|
||||
refreshTypes.add(action.uses.recovery);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
|
||||
|
||||
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||
static DEFAULT_OPTIONS = {
|
||||
actions: {
|
||||
|
|
@ -20,11 +22,33 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
}
|
||||
};
|
||||
|
||||
/** @inheritDoc */
|
||||
async _preparePartContext(_partId, context, _options) {
|
||||
return context;
|
||||
}
|
||||
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
|
||||
await this._prepareTrackerContext(context, options);
|
||||
await this._prepareCombatContext(context, options);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
async _prepareCombatContext(context, options) {
|
||||
await super._prepareCombatContext(context, options);
|
||||
|
||||
const modifierBP =
|
||||
this.combats
|
||||
.find(x => x.active)
|
||||
?.system?.extendedBattleToggles?.reduce((acc, toggle) => acc + toggle.category, 0) ?? 0;
|
||||
const maxBP = CONFIG.DH.ENCOUNTER.BaseBPPerEncounter(context.characters.length) + modifierBP;
|
||||
const currentBP = AdversaryBPPerEncounter(context.adversaries, context.characters);
|
||||
|
||||
Object.assign(context, {
|
||||
fear: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
|
||||
fear: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
|
||||
battlepoints: { max: maxBP, current: currentBP, hasModifierBP: Boolean(modifierBP) }
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -99,6 +123,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
resource,
|
||||
active: index === combat.turn,
|
||||
canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'),
|
||||
type: combatant.actor.system.type,
|
||||
img: await this._getCombatantThumbnail(combatant)
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue