mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-07-21 18:09:54 +02:00
Merge branch 'main' into feature/combo-dice-modifier
This commit is contained in:
commit
0301d6f134
8 changed files with 119 additions and 37 deletions
13
daggerheart.d.ts
vendored
13
daggerheart.d.ts
vendored
|
|
@ -11,6 +11,9 @@ import * as documents from './module/documents/_module.mjs';
|
||||||
import { macros } from './module/_module.mjs';
|
import { macros } from './module/_module.mjs';
|
||||||
import * as dice from './module/dice/_module.mjs';
|
import * as dice from './module/dice/_module.mjs';
|
||||||
import * as fields from './module/data/fields/_module.mjs';
|
import * as fields from './module/data/fields/_module.mjs';
|
||||||
|
import { gameSettings } from './module/config/settingsConfig.mjs';
|
||||||
|
import DhCountdowns from './module/data/countdowns.mjs';
|
||||||
|
import DhAutomation from './module/data/settings/Automation.mjs';
|
||||||
|
|
||||||
|
|
||||||
// Foundry's use of `Object.assign(globalThis) means many globally available objects are not read as such
|
// Foundry's use of `Object.assign(globalThis) means many globally available objects are not read as such
|
||||||
|
|
@ -103,3 +106,13 @@ declare module '@client/packages/system.mjs' {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@client/helpers/client-settings.mjs' {
|
||||||
|
// Add explicit typed overrides for auto complete. These require /** @type {"string"} on the vars themselves to work */
|
||||||
|
export default interface ClientSettings {
|
||||||
|
get(namespace: 'daggerheart', key: typeof gameSettings.Automation): DhAutomation;
|
||||||
|
get(namespace: 'daggerheart', key: typeof gameSettings.Homebrew): DhHomebrew;
|
||||||
|
get(namespace: 'daggerheart', key: typeof gameSettings.Countdowns): DhCountdowns;
|
||||||
|
get(namespace: 'daggerheart', key: string): unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -153,7 +153,6 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
|
||||||
action: socketEvent.Refresh,
|
action: socketEvent.Refresh,
|
||||||
data: { refreshType: RefreshType.Countdown }
|
data: { refreshType: RefreshType.Countdown }
|
||||||
});
|
});
|
||||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static #addCountdown() {
|
static #addCountdown() {
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,11 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
export default class DhCountdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class DhCountdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
previousCountdownData = null;
|
previousCountdownData = null;
|
||||||
changedCountdownsForAnimation = new Set();
|
|
||||||
|
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
this.previousCountdownData =
|
this.previousCountdownData =
|
||||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).countdowns;
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).countdowns;
|
||||||
this.setupHooks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
@ -80,7 +77,15 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
async _onFirstRender(context, options) {
|
||||||
|
await super._onFirstRender(context, options);
|
||||||
|
this._createContextMenu(this._getCountdownContextOptions, '.countdown-container[data-countdown]', {
|
||||||
|
parentClassHooks: false, fixed: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
async _onRender(context, options) {
|
async _onRender(context, options) {
|
||||||
await super._onRender(context, options);
|
await super._onRender(context, options);
|
||||||
|
|
||||||
|
|
@ -95,7 +100,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
|
|
||||||
/* Handle animations to draw attention to countdown values changing */
|
/* Handle animations to draw attention to countdown values changing */
|
||||||
const typesToAnimate = new Set();
|
const typesToAnimate = new Set();
|
||||||
for (const countdownKey of this.changedCountdownsForAnimation) {
|
for (const countdownKey of options.animate ?? []) {
|
||||||
const shimmerAnimation = [
|
const shimmerAnimation = [
|
||||||
{ backgroundPositionX: '98%' },
|
{ backgroundPositionX: '98%' },
|
||||||
{ backgroundPositionX: '0%' }
|
{ backgroundPositionX: '0%' }
|
||||||
|
|
@ -127,8 +132,6 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
const element = this.element.querySelector(`.header-type-toggles .header-type[data-type="${type}"]`);
|
const element = this.element.querySelector(`.header-type-toggles .header-type[data-type="${type}"]`);
|
||||||
element?.animate(pulseAnimation, pulseTiming);
|
element?.animate(pulseAnimation, pulseTiming);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changedCountdownsForAnimation.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns countdown data filtered by ownership */
|
/** Returns countdown data filtered by ownership */
|
||||||
|
|
@ -137,17 +140,15 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
const values = Object.entries(setting.countdowns).map(([key, countdown]) => ({
|
const values = Object.entries(setting.countdowns).map(([key, countdown]) => ({
|
||||||
key,
|
key,
|
||||||
countdown,
|
countdown,
|
||||||
ownership: DhCountdowns.#getPlayerOwnership(game.user, setting, countdown)
|
ownership: countdown.getUserLevel(game.user)
|
||||||
}));
|
}));
|
||||||
return values.filter(v => v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE);
|
return values.filter(v => v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getCountdownData() {
|
_getCountdownData() {
|
||||||
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
|
||||||
|
|
||||||
return this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => {
|
return this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => {
|
||||||
const playersWithAccess = game.users.reduce((acc, user) => {
|
const playersWithAccess = game.users.reduce((acc, user) => {
|
||||||
const ownership = DhCountdowns.#getPlayerOwnership(user, setting, countdown);
|
const ownership = countdown.getUserLevel(user);
|
||||||
if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
||||||
acc.push(user);
|
acc.push(user);
|
||||||
}
|
}
|
||||||
|
|
@ -213,19 +214,6 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #getPlayerOwnership(user, setting, countdown) {
|
|
||||||
if (user.isGM) return CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
|
||||||
|
|
||||||
const playerOwnership = countdown.ownership[user.id];
|
|
||||||
return playerOwnership === undefined || playerOwnership === CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
|
|
||||||
? setting.defaultOwnership
|
|
||||||
: playerOwnership;
|
|
||||||
}
|
|
||||||
|
|
||||||
cooldownRefresh = ({ refreshType }) => {
|
|
||||||
if (refreshType === RefreshType.Countdown) this.render();
|
|
||||||
};
|
|
||||||
|
|
||||||
static canPerformEdit() {
|
static canPerformEdit() {
|
||||||
if (game.user.isGM) return true;
|
if (game.user.isGM) return true;
|
||||||
|
|
||||||
|
|
@ -323,18 +311,12 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
action: socketEvent.Refresh,
|
action: socketEvent.Refresh,
|
||||||
data: { refreshType: RefreshType.Countdown }
|
data: { refreshType: RefreshType.Countdown }
|
||||||
});
|
});
|
||||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown });
|
|
||||||
}
|
|
||||||
|
|
||||||
setupHooks() {
|
|
||||||
Hooks.on(socketEvent.Refresh, this.cooldownRefresh.bind());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async close(options) {
|
async close(options) {
|
||||||
/* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */
|
/* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */
|
||||||
if (options.closeKey) return;
|
if (options.closeKey) return;
|
||||||
|
|
||||||
Hooks.off(socketEvent.Refresh, this.cooldownRefresh);
|
|
||||||
return super.close(options);
|
return super.close(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,4 +359,30 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
refreshType: RefreshType.Countdown
|
refreshType: RefreshType.Countdown
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]}
|
||||||
|
*/
|
||||||
|
_getCountdownContextOptions() {
|
||||||
|
/** @param {HTMLElement} element */
|
||||||
|
const getCountdownFromElement = element => {
|
||||||
|
const id = element.closest('[data-countdown]').dataset.countdown;
|
||||||
|
if (!id) return null;
|
||||||
|
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
||||||
|
return setting.countdowns[id ?? ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'CONTROLS.CommonDelete',
|
||||||
|
icon: 'fa-solid fa-trash',
|
||||||
|
visible: element => {
|
||||||
|
return getCountdownFromElement(element)?.isOwner;
|
||||||
|
},
|
||||||
|
onClick: (_, target) => {
|
||||||
|
getCountdownFromElement(target)?.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,10 @@ export const menu = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const gameSettings = {
|
export const gameSettings = {
|
||||||
|
/** @type {'Automation'} */
|
||||||
Automation: 'Automation',
|
Automation: 'Automation',
|
||||||
Metagaming: 'Metagaming',
|
Metagaming: 'Metagaming',
|
||||||
|
/** @type {'Homebrew'} */
|
||||||
Homebrew: 'Homebrew',
|
Homebrew: 'Homebrew',
|
||||||
appearance: 'Appearance',
|
appearance: 'Appearance',
|
||||||
GlobalOverrides: 'GlobalOverrides',
|
GlobalOverrides: 'GlobalOverrides',
|
||||||
|
|
@ -37,6 +39,7 @@ export const gameSettings = {
|
||||||
Fear: 'ResourcesFear'
|
Fear: 'ResourcesFear'
|
||||||
},
|
},
|
||||||
LevelTiers: 'LevelTiers',
|
LevelTiers: 'LevelTiers',
|
||||||
|
/** @type {'Countdowns'} */
|
||||||
Countdowns: 'Countdowns',
|
Countdowns: 'Countdowns',
|
||||||
LastMigrationVersion: 'LastMigrationVersion',
|
LastMigrationVersion: 'LastMigrationVersion',
|
||||||
SpotlightRequestQueue: 'SpotlightRequestQueue',
|
SpotlightRequestQueue: 'SpotlightRequestQueue',
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import * as HOOKS from './hooksConfig.mjs';
|
||||||
import * as TRIGGER from './triggerConfig.mjs';
|
import * as TRIGGER from './triggerConfig.mjs';
|
||||||
import * as ITEMBROWSER from './itemBrowserConfig.mjs';
|
import * as ITEMBROWSER from './itemBrowserConfig.mjs';
|
||||||
|
|
||||||
|
/** @type {"daggerheart"} */
|
||||||
export const SYSTEM_ID = 'daggerheart';
|
export const SYSTEM_ID = 'daggerheart';
|
||||||
|
|
||||||
export const SYSTEM = {
|
export const SYSTEM = {
|
||||||
|
|
|
||||||
7
module/data/_types.d.ts
vendored
Normal file
7
module/data/_types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { DhCountdown } from './countdowns.mjs'
|
||||||
|
|
||||||
|
declare module './countdowns.mjs' {
|
||||||
|
export default interface DhCountdowns {
|
||||||
|
countdowns: Record<string, DhCountdown>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
||||||
import FormulaField from './fields/formulaField.mjs';
|
import FormulaField from './fields/formulaField.mjs';
|
||||||
|
|
||||||
export default class DhCountdowns extends foundry.abstract.DataModel {
|
export default class DhCountdowns extends foundry.abstract.DataModel {
|
||||||
|
|
@ -14,19 +15,36 @@ export default class DhCountdowns extends foundry.abstract.DataModel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange() {
|
/** @inheritdoc */
|
||||||
const previousCountdowns = foundry.ui.countdowns.previousCountdownData;
|
_initialize(options) {
|
||||||
const changedCountdowns = Object.entries(this.countdowns).reduce((acc, [key, countdown]) => {
|
super._initialize(options);
|
||||||
const previousCountdown = previousCountdowns[key];
|
for (const [id, countdown] of Object.entries(this.countdowns)) {
|
||||||
if (!previousCountdown || (previousCountdown.progress.current !== countdown.progress.current)) {
|
countdown.id = id;
|
||||||
acc.push(key);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
for (const countdownKey of changedCountdowns)
|
// Re-render countdowns applications. When the change is due to an actual update, resync the editor
|
||||||
foundry.ui.countdowns.changedCountdownsForAnimation.add(countdownKey);
|
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) {
|
static migrateData(source) {
|
||||||
|
|
@ -160,6 +178,14 @@ export class DhCountdown extends foundry.abstract.DataModel {
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 */
|
/** @inheritDoc */
|
||||||
static migrateData(source) {
|
static migrateData(source) {
|
||||||
if (source.progress.max) {
|
if (source.progress.max) {
|
||||||
|
|
@ -170,4 +196,29 @@ export class DhCountdown extends foundry.abstract.DataModel {
|
||||||
|
|
||||||
return super.migrateData(source);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"version": "2.5.4",
|
"version": "2.5.4",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "14.364",
|
"minimum": "14.364",
|
||||||
"verified": "14.364",
|
"verified": "14.365",
|
||||||
"maximum": "14"
|
"maximum": "14"
|
||||||
},
|
},
|
||||||
"url": "https://github.com/Foundryborne/daggerheart",
|
"url": "https://github.com/Foundryborne/daggerheart",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue