mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-07-21 18:09:54 +02:00
224 lines
8.8 KiB
JavaScript
224 lines
8.8 KiB
JavaScript
import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
|
import FormulaField from './fields/formulaField.mjs';
|
|
|
|
export default class DhCountdowns extends foundry.abstract.DataModel {
|
|
static defineSchema() {
|
|
const fields = foundry.data.fields;
|
|
|
|
return {
|
|
countdowns: new fields.TypedObjectField(new fields.EmbeddedDataField(DhCountdown)),
|
|
defaultOwnership: new fields.NumberField({
|
|
required: true,
|
|
choices: CONFIG.DH.GENERAL.basicOwnershiplevels,
|
|
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER
|
|
})
|
|
};
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
_initialize(options) {
|
|
super._initialize(options);
|
|
for (const [id, countdown] of Object.entries(this.countdowns)) {
|
|
countdown.id = id;
|
|
}
|
|
}
|
|
|
|
async handleChange() {
|
|
const previousCountdowns = foundry.ui.countdowns.previousCountdownData;
|
|
const changedCountdowns = Object.entries(this.countdowns).reduce((acc, [key, countdown]) => {
|
|
const previous = previousCountdowns[key];
|
|
const currentChanged = !previous || (previous.progress.current !== countdown.progress.current);
|
|
if (currentChanged && previous?.progress.start === countdown.progress.start) {
|
|
acc.push(key);
|
|
}
|
|
return acc;
|
|
}, []);
|
|
|
|
// Re-render countdowns applications. When the change is due to an actual update, resync the editor
|
|
if (!foundry.utils.equals(previousCountdowns, this.countdowns)) {
|
|
await foundry.ui.countdowns.render({ animate: changedCountdowns });
|
|
for (const instance of game.system.api.applications.ui.CountdownEdit.instances()) {
|
|
instance.data = this;
|
|
await instance.render();
|
|
}
|
|
}
|
|
|
|
// Inform modules of updates
|
|
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown });
|
|
}
|
|
|
|
static migrateData(source) {
|
|
const migrateOldCountdowns = (data, type) => {
|
|
for (const key of Object.keys(data.countdowns)) {
|
|
const countdown = data.countdowns[key];
|
|
source.countdowns[key] = {
|
|
...countdown,
|
|
type: type,
|
|
ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => {
|
|
acc[key] =
|
|
countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type;
|
|
return acc;
|
|
}, {}),
|
|
progress: {
|
|
...countdown.progress,
|
|
type: countdown.progress.type.value
|
|
}
|
|
};
|
|
}
|
|
|
|
source[type] = null;
|
|
};
|
|
|
|
if (source.narrative) {
|
|
migrateOldCountdowns(source.narrative, 'narrative');
|
|
}
|
|
|
|
if (source.encounter) {
|
|
migrateOldCountdowns(source.encounter, 'encounter');
|
|
}
|
|
|
|
return super.migrateData(source);
|
|
}
|
|
}
|
|
|
|
export class DhCountdown extends foundry.abstract.DataModel {
|
|
static defineSchema() {
|
|
const fields = foundry.data.fields;
|
|
return {
|
|
type: new fields.StringField({
|
|
required: true,
|
|
choices: CONFIG.DH.GENERAL.countdownTypes,
|
|
initial: CONFIG.DH.GENERAL.countdownTypes.encounter.id,
|
|
label: 'DAGGERHEART.GENERAL.type'
|
|
}),
|
|
name: new fields.StringField({
|
|
required: true,
|
|
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.name.label'
|
|
}),
|
|
img: new fields.FilePathField({
|
|
categories: ['IMAGE'],
|
|
base64: false,
|
|
initial: 'icons/magic/time/hourglass-yellow-green.webp'
|
|
}),
|
|
ownership: new fields.TypedObjectField(
|
|
new fields.NumberField({
|
|
required: true,
|
|
choices: CONFIG.DH.GENERAL.simpleOwnershiplevels,
|
|
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
|
|
})
|
|
),
|
|
progress: new fields.SchemaField({
|
|
current: new fields.NumberField({
|
|
required: true,
|
|
integer: true,
|
|
initial: 1,
|
|
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.current.label'
|
|
}),
|
|
start: new fields.NumberField({
|
|
required: true,
|
|
integer: true,
|
|
initial: 1,
|
|
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.start.label',
|
|
deterministic: false
|
|
}),
|
|
startFormula: new FormulaField({
|
|
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.startFormula.label',
|
|
deterministic: false
|
|
}),
|
|
looping: new fields.StringField({
|
|
required: true,
|
|
choices: CONFIG.DH.GENERAL.countdownLoopingTypes,
|
|
initial: CONFIG.DH.GENERAL.countdownLoopingTypes.noLooping.id,
|
|
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.looping.label'
|
|
}),
|
|
type: new fields.StringField({
|
|
required: true,
|
|
choices: CONFIG.DH.GENERAL.countdownProgressionTypes,
|
|
initial: CONFIG.DH.GENERAL.countdownProgressionTypes.custom.id,
|
|
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.type.label'
|
|
})
|
|
})
|
|
};
|
|
}
|
|
|
|
static defaultCountdown(type, playerHidden) {
|
|
const ownership = playerHidden
|
|
? game.users.reduce((acc, user) => {
|
|
if (!user.isGM) {
|
|
acc[user.id] = CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE;
|
|
}
|
|
return acc;
|
|
}, {})
|
|
: undefined;
|
|
|
|
return {
|
|
type: type ?? CONFIG.DH.GENERAL.countdownTypes.encounter.id,
|
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Countdown.newCountdown'),
|
|
img: 'icons/magic/time/hourglass-yellow-green.webp',
|
|
ownership: ownership,
|
|
progress: {
|
|
current: 1,
|
|
start: 1
|
|
}
|
|
};
|
|
}
|
|
|
|
get playerOwnership() {
|
|
return Array.from(game.users).reduce((acc, user) => {
|
|
acc[user.id] = {
|
|
value: user.isGM
|
|
? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER
|
|
: this.ownership.players[user.id] && this.ownership.players[user.id].type !== -1
|
|
? this.ownership.players[user.id].type
|
|
: this.ownership.default,
|
|
isGM: user.isGM
|
|
};
|
|
|
|
return acc;
|
|
}, {});
|
|
}
|
|
|
|
/**
|
|
* A boolean indicator for whether the current game User has ownership rights for this countdown
|
|
* @returns {boolean}
|
|
*/
|
|
get isOwner() {
|
|
return this.getUserLevel(game.user) === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
static migrateData(source) {
|
|
if (source.progress.max) {
|
|
source.progress.start = Number(source.progress.max);
|
|
source.progress.max = null;
|
|
source.progress.startFormula = null;
|
|
}
|
|
|
|
return super.migrateData(source);
|
|
}
|
|
|
|
/**
|
|
* Get the explicit permission level that a User has over this Document, a value in CONST.DOCUMENT_OWNERSHIP_LEVELS.
|
|
* Compendium content ignores the ownership field in favor of User role-based ownership. Otherwise, Documents use
|
|
* granular per-User ownership definitions and Embedded Documents defer to their parent ownership.
|
|
*
|
|
* @param {BaseUser} [user=game.user] The User being tested
|
|
* @returns {DocumentOwnershipNumber} A numeric permission level from {@link CONST.DOCUMENT_OWNERSHIP_LEVELS}
|
|
*/
|
|
getUserLevel(user) {
|
|
if (user.isGM) return CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
|
|
|
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
|
const playerOwnership = this.ownership[user.id];
|
|
return playerOwnership === undefined || playerOwnership === CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
|
|
? setting.defaultOwnership
|
|
: playerOwnership;
|
|
}
|
|
|
|
async delete() {
|
|
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
|
const data = foundry.utils.deepClone(setting._source);
|
|
delete data.countdowns[this.id];
|
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data);
|
|
}
|
|
}
|