[Feature] Downtime Improvements (#411)

* Initial

* Fixed dialog again

* Fix healing targeted resource in setting

* Removed unused templates

---------

Co-authored-by: Dapoolp <elcatnet@gmail.com>
This commit is contained in:
WBHarry 2025-07-25 21:31:05 +02:00 committed by GitHub
parent 3f95740b7a
commit 62b9a8fbee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 538 additions and 457 deletions

View file

@ -1,5 +1,5 @@
import DHActionConfig from "../../applications/sheets-configs/action-config.mjs";
import MappingField from "./mappingField.mjs";
import DHActionConfig from '../../applications/sheets-configs/action-config.mjs';
import MappingField from './mappingField.mjs';
/**
* Specialized collection type for stored actions.
@ -7,52 +7,52 @@ import MappingField from "./mappingField.mjs";
* @param {Action[]} entries The actions to store.
*/
export class ActionCollection extends Collection {
constructor(model, entries) {
super();
this.#model = model;
for ( const entry of entries ) {
if ( !(entry instanceof game.system.api.models.actions.actionsTypes.base) ) continue;
this.set(entry._id, entry);
constructor(model, entries) {
super();
this.#model = model;
for (const entry of entries) {
if (!(entry instanceof game.system.api.models.actions.actionsTypes.base)) continue;
this.set(entry._id, entry);
}
}
}
/* -------------------------------------------- */
/* Properties */
/* -------------------------------------------- */
/* -------------------------------------------- */
/* Properties */
/* -------------------------------------------- */
/**
* The parent DataModel to which this ActionCollection belongs.
* @type {DataModel}
*/
#model;
/**
* The parent DataModel to which this ActionCollection belongs.
* @type {DataModel}
*/
#model;
/* -------------------------------------------- */
/* -------------------------------------------- */
/* -------------------------------------------- */
/* Methods */
/* -------------------------------------------- */
/* -------------------------------------------- */
/* Methods */
/* -------------------------------------------- */
/* -------------------------------------------- */
/* -------------------------------------------- */
/**
* Test the given predicate against every entry in the Collection.
* @param {function(*, number, ActionCollection): boolean} predicate The predicate.
* @returns {boolean}
*/
every(predicate) {
return this.reduce((pass, v, i) => pass && predicate(v, i, this), true);
}
/**
* Test the given predicate against every entry in the Collection.
* @param {function(*, number, ActionCollection): boolean} predicate The predicate.
* @returns {boolean}
*/
every(predicate) {
return this.reduce((pass, v, i) => pass && predicate(v, i, this), true);
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/**
* Convert the ActionCollection to an array of simple objects.
* @param {boolean} [source=true] Draw data for contained Documents from the underlying data source?
* @returns {object[]} The extracted array of primitive objects.
*/
toObject(source=true) {
return this.map(doc => doc.toObject(source));
}
/**
* Convert the ActionCollection to an array of simple objects.
* @param {boolean} [source=true] Draw data for contained Documents from the underlying data source?
* @returns {object[]} The extracted array of primitive objects.
*/
toObject(source = true) {
return this.map(doc => doc.toObject(source));
}
}
/* -------------------------------------------- */
@ -61,17 +61,17 @@ export class ActionCollection extends Collection {
* Field that stores actions.
*/
export class ActionsField extends MappingField {
constructor(options) {
super(new ActionField(), options);
}
constructor(options) {
super(new ActionField(), options);
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/** @inheritDoc */
initialize(value, model, options) {
const actions = Object.values(super.initialize(value, model, options));
return new ActionCollection(model, actions);
}
/** @inheritDoc */
initialize(value, model, options) {
const actions = Object.values(super.initialize(value, model, options));
return new ActionCollection(model, actions);
}
}
/* -------------------------------------------- */
@ -81,7 +81,10 @@ export class ActionsField extends MappingField {
*/
export class ActionField extends foundry.data.fields.ObjectField {
getModel(value) {
return game.system.api.models.actions.actionsTypes[value.type] ?? game.system.api.models.actions.actionsTypes.attack;
return (
game.system.api.models.actions.actionsTypes[value.type] ??
game.system.api.models.actions.actionsTypes.attack
);
}
/* -------------------------------------------- */
@ -122,138 +125,146 @@ export class ActionField extends foundry.data.fields.ObjectField {
export function ActionMixin(Base) {
class Action extends Base {
static metadata = Object.freeze({
name: "Action",
label: "DAGGERHEART.GENERAL.Action.single",
sheetClass: DHActionConfig
name: 'Action',
label: 'DAGGERHEART.GENERAL.Action.single',
sheetClass: DHActionConfig
});
static _sheets = new Map();
static get documentName() {
return this.metadata.name;
return this.metadata.name;
}
get documentName() {
return this.constructor.documentName;
return this.constructor.documentName;
}
static defaultName() {
return this.documentName;
return this.documentName;
}
get relativeUUID() {
return `.Item.${this.item.id}.Action.${this.id}`;
return `.Item.${this.item.id}.Action.${this.id}`;
}
get uuid() {
return `${this.item.uuid}.${this.documentName}.${this.id}`;
return `${this.item.uuid}.${this.documentName}.${this.id}`;
}
get sheet() {
if(!this.constructor._sheets.has(this.uuid)) {
const sheet = new this.constructor.metadata.sheetClass(this);
this.constructor._sheets.set(this.uuid, sheet);
}
return this.constructor._sheets.get(this.uuid);
if (!this.constructor._sheets.has(this.uuid)) {
const sheet = new this.constructor.metadata.sheetClass(this);
this.constructor._sheets.set(this.uuid, sheet);
}
return this.constructor._sheets.get(this.uuid);
}
get inCollection() {
return foundry.utils.getProperty(this.parent, this.systemPath) instanceof Collection;
return foundry.utils.getProperty(this.parent, this.systemPath) instanceof Collection;
}
static async create(data, operation={}) {
const { parent, renderSheet } = operation;
let { type } = data;
if(!type || !game.system.api.models.actions.actionsTypes[type]) {
({ type } =
(await foundry.applications.api.DialogV2.input({
window: { title: 'Select Action Type' },
position: { width: 300 },
classes: ['daggerheart', 'dh-style'],
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/actionTypes/actionType.hbs',
{
types: CONFIG.DH.ACTIONS.actionTypes,
itemName: parent.parent?.name
static async create(data, operation = {}) {
const { parent, renderSheet } = operation;
let { type } = data;
if (!type || !game.system.api.models.actions.actionsTypes[type]) {
({ type } =
(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 }
),
ok: {
label: game.i18n.format('DOCUMENT.Create', {
type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single')
})
}
),
ok: {
label: game.i18n.format('DOCUMENT.Create', {
type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single')
})
}
})) ?? {});
}
if (!type) return;
const cls = game.system.api.models.actions.actionsTypes[type];
const action = new cls(
{
type,
...cls.getSourceConfig(parent)
},
{
parent
}
);
const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() });
const newAction = parent.actions.get(action.id);
if(!newAction) return null;
if( renderSheet ) newAction.sheet.render({ force: true });
return newAction;
})) ?? {});
}
if (!type) return;
const cls = game.system.api.models.actions.actionsTypes[type];
const action = new cls(
{
type,
...cls.getSourceConfig(parent)
},
{
parent
}
);
const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() });
const newAction = parent.actions.get(action.id);
if (!newAction) return null;
if (renderSheet) newAction.sheet.render({ force: true });
return newAction;
}
async update(updates, options={}) {
const path = this.inCollection ? `system.${this.systemPath}.${this.id}` : `system.${this.systemPath}`,
result = await this.item.update({[path]: updates}, options);
return this.inCollection ? foundry.utils.getProperty(result, `system.${this.systemPath}`).get(this.id) : foundry.utils.getProperty(result, `system.${this.systemPath}`);
async update(updates, options = {}) {
const isSetting = !this.parent.parent;
const basePath = isSetting ? this.systemPath : `system.${this.systemPath}`;
const path = this.inCollection ? `${basePath}.${this.id}` : basePath;
let result = null;
if (isSetting) {
await this.parent.updateSource({ [path]: updates }, options);
result = this.parent;
} else {
result = await this.item.update({ [path]: updates }, options);
}
return this.inCollection
? foundry.utils.getProperty(result, basePath).get(this.id)
: foundry.utils.getProperty(result, basePath);
}
delete() {
if(!this.inCollection) return this.item;
const action = foundry.utils.getProperty(this.item, `system.${this.systemPath}`)?.get(this.id);
if ( !action ) return this.item;
this.item.update({ [`system.${this.systemPath}.-=${this.id}`]: null });
this.constructor._sheets.get(this.uuid)?.close();
if (!this.inCollection) return this.item;
const action = foundry.utils.getProperty(this.item, `system.${this.systemPath}`)?.get(this.id);
if (!action) return this.item;
this.item.update({ [`system.${this.systemPath}.-=${this.id}`]: null });
this.constructor._sheets.get(this.uuid)?.close();
}
async deleteDialog() {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`),
name: this.name
})
},
content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', {
name: this.name
})
});
if (!confirmed) return;
return this.delete();
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`),
name: this.name
})
},
content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', {
name: this.name
})
});
if (!confirmed) return;
return this.delete();
}
async toChat(origin) {
const cls = getDocumentClass('ChatMessage');
const systemData = {
title: game.i18n.localize('DAGGERHEART.CONFIG.ActionType.action'),
origin: origin,
img: this.img,
name: this.name,
description: this.description,
actions: []
};
const msg = {
type: 'abilityUse',
user: game.user.id,
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/chat/ability-use.hbs',
systemData
)
};
const cls = getDocumentClass('ChatMessage');
const systemData = {
title: game.i18n.localize('DAGGERHEART.CONFIG.ActionType.action'),
origin: origin,
img: this.img,
name: this.name,
description: this.description,
actions: []
};
const msg = {
type: 'abilityUse',
user: game.user.id,
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/chat/ability-use.hbs',
systemData
)
};
cls.create(msg);
cls.create(msg);
}
}

View file

@ -13,7 +13,8 @@ export default class DhAppearance extends foundry.abstract.DataModel {
dualityColorScheme: new fields.StringField({
required: true,
choices: DualityRollColor,
initial: DualityRollColor.normal.value
initial: DualityRollColor.normal.value,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.dualityColorScheme.label'
}),
diceSoNice: new fields.SchemaField({
hope: new fields.SchemaField({

View file

@ -1,4 +1,5 @@
import { defaultRestOptions } from '../../config/generalConfig.mjs';
import { ActionsField } from '../fields/actionField.mjs';
export default class DhHomebrew extends foundry.abstract.DataModel {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Homebrew']; // Doesn't work for some reason
@ -61,7 +62,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
base64: false
}),
description: new fields.HTMLField(),
actions: new fields.ArrayField(new fields.ObjectField())
actions: new ActionsField()
}),
{ initial: defaultRestOptions.longRest() }
)
@ -78,7 +79,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
base64: false
}),
description: new fields.HTMLField(),
actions: new fields.ArrayField(new fields.ObjectField())
actions: new ActionsField()
}),
{ initial: defaultRestOptions.shortRest() }
)