File Structure Rework (#262)

* Restructured all the files

* Moved build/daggerheart.js to ./daggerheart.js. Changed rollup to use the css file instead of the less

* Restored build/ folder

* Mvoed config out form under application

* Moved roll.mjs to module/dice and renamed to dhRolls.mjs

* Update module/canvas/placeables/_module.mjs

Co-authored-by: joaquinpereyra98 <24190917+joaquinpereyra98@users.noreply.github.com>

* Le massive export update

* Removed unncessary import

---------

Co-authored-by: joaquinpereyra98 <24190917+joaquinpereyra98@users.noreply.github.com>
This commit is contained in:
WBHarry 2025-07-05 00:26:33 +02:00 committed by GitHub
parent 099a4576da
commit 9d76405221
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
203 changed files with 1556 additions and 2565 deletions

View file

@ -0,0 +1,9 @@
export { default as BeastformDialog } from './beastformDialog.mjs';
export { default as costSelectionDialog } from './costSelectionDialog.mjs';
export { default as d20RollDialog } from './d20RollDialog.mjs';
export { default as DamageDialog } from './damageDialog.mjs';
export { default as DamageReductionDialog } from './damageReductionDialog.mjs';
export { default as DamageSelectionDialog } from './damageSelectionDialog.mjs';
export { default as DeathMove } from './deathMove.mjs';
export { default as Downtime } from './downtime.mjs';
export { default as OwnershipSelection } from './ownershipSelection.mjs';

View file

@ -0,0 +1,84 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class BeastformDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(configData) {
super();
this.configData = configData;
this.selected = null;
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'dh-style', 'beastform-selection'],
position: {
width: 600,
height: 'auto'
},
actions: {
selectBeastform: this.selectBeastform,
submitBeastform: this.submitBeastform
},
form: {
handler: this.updateBeastform,
submitOnChange: true,
submitOnClose: false
}
};
get title() {
return game.i18n.localize('DAGGERHEART.Sheets.Beastform.dialogTitle');
}
/** @override */
static PARTS = {
beastform: {
template: 'systems/daggerheart/templates/dialogs/beastformDialog.hbs'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.beastformTiers = game.items.reduce((acc, x) => {
const tier = CONFIG.DH.GENERAL.tiers[x.system.tier];
if (x.type !== 'beastform' || tier.value > this.configData.tierLimit) return acc;
if (!acc[tier.value]) acc[tier.value] = { label: game.i18n.localize(tier.label), values: {} };
acc[tier.value].values[x.uuid] = { selected: this.selected == x.uuid, value: x };
return acc;
}, {}); // Also get from compendium when added
context.canSubmit = this.selected;
return context;
}
static updateBeastform(event, _, formData) {
this.selected = foundry.utils.mergeObject(this.selected, formData.object);
this.render();
}
static selectBeastform(_, target) {
this.selected = this.selected === target.dataset.uuid ? null : target.dataset.uuid;
this.render();
}
static async submitBeastform() {
await this.close({ submitted: true });
}
/** @override */
_onClose(options = {}) {
if (!options.submitted) this.config = false;
}
static async configure(configData) {
return new Promise(resolve => {
const app = new this(configData);
app.addEventListener('close', () => resolve(app.selected), { once: true });
app.render({ force: true });
});
}
}

View file

@ -0,0 +1,66 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class CostSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(costs, uses, action, resolve) {
super({});
this.costs = costs;
this.uses = uses;
this.action = action;
this.resolve = resolve;
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'damage-selection'],
position: {
width: 400,
height: 'auto'
},
actions: {
sendCost: this.sendCost
},
form: {
handler: this.updateForm,
submitOnChange: true,
closeOnSubmit: false
}
};
/** @override */
static PARTS = {
costSelection: {
id: 'costSelection',
template: 'systems/daggerheart/templates/dialogs/costSelection.hbs'
}
};
/* -------------------------------------------- */
/** @inheritDoc */
get title() {
return `Cost Options`;
}
async _prepareContext(_options) {
const updatedCosts = this.action.calcCosts(this.costs),
updatedUses = this.action.calcUses(this.uses);
return {
costs: updatedCosts,
uses: updatedUses,
canUse: this.action.hasCost(updatedCosts) && this.action.hasUses(updatedUses)
};
}
static async updateForm(event, _, formData) {
const data = foundry.utils.expandObject(formData.object);
this.costs = foundry.utils.mergeObject(this.costs, data.costs);
this.uses = foundry.utils.mergeObject(this.uses, data.uses);
this.render(true);
}
static sendCost(event) {
event.preventDefault();
this.resolve({ costs: this.action.getRealCosts(this.costs), uses: this.uses });
this.close();
}
}

View file

@ -0,0 +1,132 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(roll, config = {}, options = {}) {
super(options);
this.roll = roll;
this.config = config;
this.config.experiences = [];
if (config.source?.action) {
this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent;
this.action =
config.data.attack?._id == config.source.action
? config.data.attack
: this.item.system.actions.find(a => a._id === config.source.action);
}
}
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'roll-selection',
classes: ['daggerheart', 'views', 'roll-selection'],
position: {
width: 400,
height: 'auto'
},
actions: {
updateIsAdvantage: this.updateIsAdvantage,
selectExperience: this.selectExperience,
submitRoll: this.submitRoll
},
form: {
handler: this.updateRollConfiguration,
submitOnChange: true,
submitOnClose: false
}
};
/** @override */
static PARTS = {
costSelection: {
id: 'costSelection',
template: 'systems/daggerheart/templates/dialogs/costSelection.hbs'
},
rollSelection: {
id: 'rollSelection',
template: 'systems/daggerheart/templates/dialogs/rollSelection.hbs'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.hasRoll = !!this.config.roll;
context.roll = this.roll;
context.rollType = this.roll?.constructor.name;
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
id,
...this.config.data.experiences[id]
}));
context.selectedExperiences = this.config.experiences;
context.advantage = this.config.roll?.advantage;
context.diceOptions = CONFIG.DH.GENERAL.diceTypes;
context.canRoll = true;
context.isLite = this.config.roll?.lite;
if (this.config.costs?.length) {
const updatedCosts = this.action.calcCosts(this.config.costs);
context.costs = updatedCosts;
context.canRoll = this.action.hasCost(updatedCosts);
this.config.data.scale = this.config.costs[0].total;
}
if (this.config.uses?.max) {
context.uses = this.action.calcUses(this.config.uses);
context.canRoll = context.canRoll && this.action.hasUses(context.uses);
}
context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config);
return context;
}
static updateRollConfiguration(event, _, formData) {
const { ...rest } = foundry.utils.expandObject(formData.object);
if (this.config.costs) {
this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs);
}
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
if (rest.roll?.dice) {
Object.entries(rest.roll.dice).forEach(([key, value]) => {
this.roll[key] = value;
});
}
this.config.extraFormula = rest.extraFormula;
this.render();
}
static updateIsAdvantage(_, button) {
const advantage = Number(button.dataset.advantage);
this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
this.render();
}
static selectExperience(_, button) {
/* if (this.config.experiences.find(x => x === button.dataset.key)) {
this.config.experiences = this.config.experiences.filter(x => x !== button.dataset.key);
} else {
this.config.experiences = [...this.config.experiences, button.dataset.key];
} */
this.config.experiences =
this.config.experiences.indexOf(button.dataset.key) > -1
? this.config.experiences.filter(x => x !== button.dataset.key)
: [...this.config.experiences, button.dataset.key];
this.render();
}
static async submitRoll() {
await this.close({ submitted: true });
}
/** @override */
_onClose(options = {}) {
if (!options.submitted) this.config = false;
}
static async configure(roll, config = {}, options = {}) {
return new Promise(resolve => {
const app = new this(roll, config, options);
app.addEventListener('close', () => resolve(app.config), { once: true });
app.render({ force: true });
});
}
}

View file

@ -0,0 +1,67 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(roll, config = {}, options = {}) {
super(options);
this.roll = roll;
this.config = config;
}
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'roll-selection',
classes: ['daggerheart', 'views', 'damage-selection'],
position: {
width: 400,
height: 'auto'
},
actions: {
submitRoll: this.submitRoll
},
form: {
handler: this.updateRollConfiguration,
submitOnChange: true,
submitOnClose: false
}
};
/** @override */
static PARTS = {
damageSelection: {
id: 'damageSelection',
template: 'systems/daggerheart/templates/dialogs/damageSelection.hbs'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.title = this.config.title;
context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config);
return context;
}
static updateRollConfiguration(event, _, formData) {
const { ...rest } = foundry.utils.expandObject(formData.object);
this.config.extraFormula = rest.extraFormula;
this.render();
}
static async submitRoll() {
await this.close({ submitted: true });
}
/** @override */
_onClose(options = {}) {
if (!options.submitted) this.config = false;
}
static async configure(roll, config = {}) {
return new Promise(resolve => {
const app = new this(roll, config);
app.addEventListener('close', () => resolve(app.config), { once: true });
app.render({ force: true });
});
}
}

View file

@ -0,0 +1,220 @@
import { damageKeyToNumber, getDamageLabel } from '../../helpers/utils.mjs';
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageReductionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(resolve, reject, actor, damage) {
super({});
this.resolve = resolve;
this.reject = reject;
this.actor = actor;
this.damage = damage;
const maxArmorMarks = Math.min(
actor.system.armorScore - actor.system.armor.system.marks.value,
actor.system.rules.maxArmorMarked.total
);
const armor = [...Array(maxArmorMarks).keys()].reduce((acc, _) => {
acc[foundry.utils.randomID()] = { selected: false };
return acc;
}, {});
const stress = [...Array(actor.system.rules.maxArmorMarked.stressExtra ?? 0).keys()].reduce((acc, _) => {
acc[foundry.utils.randomID()] = { selected: false };
return acc;
}, {});
this.marks = { armor, stress };
this.availableStressReductions = Object.keys(actor.system.rules.stressDamageReduction).reduce((acc, key) => {
const dr = actor.system.rules.stressDamageReduction[key];
if (dr.enabled) {
if (acc === null) acc = {};
const damage = damageKeyToNumber(key);
acc[damage] = {
cost: dr.cost,
selected: false,
from: getDamageLabel(damage),
to: getDamageLabel(damage - 1)
};
}
return acc;
}, null);
}
get title() {
return game.i18n.localize('DAGGERHEART.DamageReduction.Title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'damage-reduction'],
position: {
width: 240,
height: 'auto'
},
actions: {
setMarks: this.setMarks,
useStressReduction: this.useStressReduction,
takeDamage: this.takeDamage
},
form: {
handler: this.updateData,
submitOnChange: true,
closeOnSubmit: false
}
};
/** @override */
static PARTS = {
damageSelection: {
id: 'damageReduction',
template: 'systems/daggerheart/templates/dialogs/damageReduction.hbs'
}
};
/* -------------------------------------------- */
/** @inheritDoc */
get title() {
return game.i18n.localize('DAGGERHEART.DamageReduction.Title');
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
const { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage } =
this.getDamageInfo();
context.armorScore = this.actor.system.armorScore;
context.armorMarks = currentMarks;
context.basicMarksUsed = selectedArmorMarks.length === this.actor.system.rules.maxArmorMarked.total;
const stressReductionStress = this.availableStressReductions
? stressReductions.reduce((acc, red) => acc + red.cost, 0)
: 0;
context.stress =
selectedStressMarks.length > 0 || this.availableStressReductions
? {
value:
this.actor.system.resources.stress.value + selectedStressMarks.length + stressReductionStress,
maxTotal: this.actor.system.resources.stress.maxTotal
}
: null;
context.marks = this.marks;
context.availableStressReductions = this.availableStressReductions;
context.damage = getDamageLabel(this.damage);
context.reducedDamage = currentDamage !== this.damage ? getDamageLabel(currentDamage) : null;
context.currentDamage = context.reducedDamage ?? context.damage;
return context;
}
static updateData(event, _, formData) {
const form = foundry.utils.expandObject(formData.object);
this.render(true);
}
getDamageInfo = () => {
const selectedArmorMarks = Object.values(this.marks.armor).filter(x => x.selected);
const selectedStressMarks = Object.values(this.marks.stress).filter(x => x.selected);
const stressReductions = Object.values(this.availableStressReductions).filter(red => red.selected);
const currentMarks =
this.actor.system.armor.system.marks.value + selectedArmorMarks.length + selectedStressMarks.length;
const currentDamage =
this.damage - selectedArmorMarks.length - selectedStressMarks.length - stressReductions.length;
return { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage };
};
static setMarks(_, target) {
const currentMark = this.marks[target.dataset.type][target.dataset.key];
const { selectedStressMarks, stressReductions, currentMarks, currentDamage } = this.getDamageInfo();
if (!currentMark.selected && currentDamage === 0) {
ui.notifications.info(game.i18n.localize('DAGGERHEART.DamageReduction.Notifications.DamageAlreadyNone'));
return;
}
if (!currentMark.selected && currentMarks === this.actor.system.armorScore) {
ui.notifications.info(
game.i18n.localize('DAGGERHEART.DamageReduction.Notifications.NoAvailableArmorMarks')
);
return;
}
if (currentMark.selected) {
const currentDamageLabel = getDamageLabel(currentDamage);
for (let reduction of stressReductions) {
if (reduction.selected && reduction.to === currentDamageLabel) {
reduction.selected = false;
}
}
if (target.dataset.type === 'armor' && selectedStressMarks.length > 0) {
selectedStressMarks.forEach(mark => (mark.selected = false));
}
}
currentMark.selected = !currentMark.selected;
this.render();
}
static useStressReduction(_, target) {
const damageValue = Number(target.dataset.reduction);
const stressReduction = this.availableStressReductions[damageValue];
const { currentDamage, selectedStressMarks, stressReductions } = this.getDamageInfo();
if (stressReduction.selected) {
stressReduction.selected = false;
const currentDamageLabel = getDamageLabel(currentDamage);
for (let reduction of stressReductions) {
if (reduction.selected && reduction.to === currentDamageLabel) {
reduction.selected = false;
}
}
this.render();
} else {
const stressReductionStress = this.availableStressReductions
? stressReductions.reduce((acc, red) => acc + red.cost, 0)
: 0;
const currentStress =
this.actor.system.resources.stress.value + selectedStressMarks.length + stressReductionStress;
if (currentStress + stressReduction.cost > this.actor.system.resources.stress.maxTotal) {
ui.notifications.info(game.i18n.localize('DAGGERHEART.DamageReduction.Notifications.NotEnoughStress'));
return;
}
const reducedDamage = currentDamage !== this.damage ? getDamageLabel(currentDamage) : null;
const currentDamageLabel = reducedDamage ?? getDamageLabel(this.damage);
if (stressReduction.from !== currentDamageLabel) return;
stressReduction.selected = true;
this.render();
}
}
static async takeDamage() {
const { selectedArmorMarks, selectedStressMarks, stressReductions, currentDamage } = this.getDamageInfo();
const armorSpent = selectedArmorMarks.length + selectedStressMarks.length;
const stressSpent = selectedStressMarks.length + stressReductions.reduce((acc, red) => acc + red.cost, 0);
this.resolve({ modifiedDamage: currentDamage, armorSpent, stressSpent });
await this.close(true);
}
async close(fromSave) {
if (!fromSave) {
this.reject();
}
await super.close({});
}
}

View file

@ -0,0 +1,126 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(rollString, bonusDamage, resolve, hope = 0) {
super({});
this.data = {
rollString,
bonusDamage: bonusDamage.reduce((acc, x) => {
if (x.appliesOn === CONFIG.DH.EFFECTS.applyLocations.damageRoll.id) {
acc.push({
...x,
hopeUses: 0
});
}
return acc;
}, []),
hope
};
this.resolve = resolve;
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'damage-selection'],
position: {
width: 400,
height: 'auto'
},
actions: {
decreaseHopeUse: this.decreaseHopeUse,
increaseHopeUse: this.increaseHopeUse,
rollDamage: this.rollDamage
},
form: {
handler: this.updateSelection,
submitOnChange: true,
closeOnSubmit: false
}
};
/** @override */
static PARTS = {
damageSelection: {
id: 'damageSelection',
template: 'systems/daggerheart/templates/dialogs/damageSelection.hbs'
}
};
/* -------------------------------------------- */
/** @inheritDoc */
get title() {
return `Damage Options`;
}
async _prepareContext(_options) {
return {
rollString: this.getRollString(),
bonusDamage: this.data.bonusDamage,
hope: this.data.hope + 1,
hopeUsed: this.getHopeUsed()
};
}
static updateSelection(event, _, formData) {
const { bonusDamage, ...rest } = foundry.utils.expandObject(formData.object);
for (var index in bonusDamage) {
this.data.bonusDamage[index].initiallySelected = bonusDamage[index].initiallySelected;
if (bonusDamage[index].hopeUses) {
const value = Number.parseInt(bonusDamage[index].hopeUses);
if (!Number.isNaN(value)) this.data.bonusDamage[index].hopeUses = value;
}
}
this.data = foundry.utils.mergeObject(this.data, rest);
this.render(true);
}
getRollString() {
return this.data.rollString.concat(
this.data.bonusDamage.reduce((acc, x) => {
if (x.initiallySelected) {
const nr = 1 + x.hopeUses;
const baseDamage = x.value;
return acc.concat(` + ${nr}${baseDamage}`);
}
return acc;
}, '')
);
}
getHopeUsed() {
return this.data.bonusDamage.reduce((acc, x) => acc + x.hopeUses, 0);
}
static decreaseHopeUse(_, button) {
const index = Number.parseInt(button.dataset.index);
if (this.data.bonusDamage[index].hopeUses - 1 >= 0) {
this.data.bonusDamage[index].hopeUses -= 1;
this.render(true);
}
}
static increaseHopeUse(_, button) {
const index = Number.parseInt(button.dataset.index);
if (this.data.bonusDamage[index].hopeUses <= this.data.hope + 1) {
this.data.bonusDamage[index].hopeUses += 1;
this.render(true);
}
}
static rollDamage(event) {
event.preventDefault();
this.resolve({
rollString: this.getRollString(),
bonusDamage: this.data.bonusDamage,
hopeUsed: this.getHopeUsed()
});
this.close();
}
}

View file

@ -0,0 +1,65 @@
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DhpDeathMove extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(actor) {
super({});
this.actor = actor;
this.selectedMove = null;
}
get title() {
return game.i18n.format('DAGGERHEART.Application.DeathMove.Title', { actor: this.actor.name });
}
static DEFAULT_OPTIONS = {
classes: ['daggerheart', 'views', 'death-move'],
position: { width: 800, height: 'auto' },
actions: {
selectMove: this.selectMove,
takeMove: this.takeMove
}
};
static PARTS = {
application: {
id: 'death-move',
template: 'systems/daggerheart/templates/dialogs/deathMove.hbs'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.selectedMove = this.selectedMove;
context.options = CONFIG.DH.GENERAL.deathMoves;
return context;
}
static selectMove(_, button) {
const move = button.dataset.move;
this.selectedMove = CONFIG.DH.GENERAL.deathMoves[move];
this.render();
}
static async takeMove() {
const cls = getDocumentClass('ChatMessage');
const msg = new cls({
user: game.user.id,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/chat/deathMove.hbs',
{
player: this.actor.name,
title: game.i18n.localize(this.selectedMove.name),
img: this.selectedMove.img,
description: game.i18n.localize(this.selectedMove.description)
}
)
});
cls.create(msg.toObject());
this.close();
}
}

View file

@ -0,0 +1,106 @@
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(actor, shortrest) {
super({});
this.actor = actor;
this.shortrest = shortrest;
const options = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves;
this.moveData = shortrest ? options.shortRest : options.longRest;
}
get title() {
return '';
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'downtime'],
position: { width: 680, height: 'auto' },
actions: {
selectMove: this.selectMove,
takeDowntime: this.takeDowntime
},
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
};
static PARTS = {
application: {
id: 'downtime',
template: 'systems/daggerheart/templates/dialogs/downtime.hbs'
}
};
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
htmlElement
.querySelectorAll('.activity-image')
.forEach(element => element.addEventListener('contextmenu', this.deselectMove.bind(this)));
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.selectedActivity = this.selectedActivity;
context.moveData = this.moveData;
context.nrCurrentChoices = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
context.disabledDowntime = context.nrCurrentChoices < context.moveData.nrChoices;
return context;
}
static selectMove(_, button) {
const nrSelected = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
if (nrSelected === this.moveData.nrChoices) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Downtime.Notifications.NoMoreMoves'));
return;
}
const move = button.dataset.move;
this.moveData.moves[move].selected = this.moveData.moves[move].selected
? this.moveData.moves[move].selected + 1
: 1;
this.render();
}
deselectMove(event) {
const move = event.currentTarget.dataset.move;
this.moveData.moves[move].selected = this.moveData.moves[move].selected
? this.moveData.moves[move].selected - 1
: 0;
this.render();
}
static async takeDowntime() {
const moves = Object.values(this.moveData.moves).filter(x => x.selected);
const cls = getDocumentClass('ChatMessage');
const msg = new cls({
user: game.user.id,
system: {
moves: moves,
actor: this.actor.uuid
},
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/chat/downtime.hbs',
{
title: `${this.actor.name} - ${game.i18n.localize(`DAGGERHEART.Downtime.${this.shortRest ? 'ShortRest' : 'LongRest'}.title`)}`,
moves: moves
}
)
});
cls.create(msg.toObject());
this.close();
}
static async updateData(event, element, formData) {
this.customActivity = foundry.utils.mergeObject(this.customActivity, formData.object);
this.render();
}
}

View file

@ -0,0 +1,72 @@
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class OwnershipSelection extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(resolve, reject, name, ownership) {
super({});
this.resolve = resolve;
this.reject = reject;
this.name = name;
this.ownership = ownership;
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'ownership-selection'],
position: {
width: 600,
height: 'auto'
},
form: { handler: this.updateData }
};
static PARTS = {
selection: {
template: 'systems/daggerheart/templates/dialogs/ownershipSelection.hbs'
}
};
get title() {
return game.i18n.format('DAGGERHEART.OwnershipSelection.Title', { name: this.name });
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.ownershipOptions = Object.keys(CONST.DOCUMENT_OWNERSHIP_LEVELS).map(level => ({
value: CONST.DOCUMENT_OWNERSHIP_LEVELS[level],
label: game.i18n.localize(`OWNERSHIP.${level}`)
}));
context.ownership = {
default: this.ownership.default,
players: Object.keys(this.ownership.players).reduce((acc, x) => {
const user = game.users.get(x);
if (!user.isGM) {
acc[x] = {
img: user.character?.img ?? 'icons/svg/cowled.svg',
name: user.name,
ownership: this.ownership.players[x].value
};
}
return acc;
}, {})
};
return context;
}
static async updateData(event, _, formData) {
const { ownership } = foundry.utils.expandObject(formData.object);
this.resolve(ownership);
this.close(true);
}
async close(fromSave) {
if (!fromSave) {
this.reject();
}
await super.close();
}
}