mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 11:41:08 +01:00
Added ownership to countdowns
This commit is contained in:
parent
81cda222da
commit
08c136d740
13 changed files with 431 additions and 55 deletions
|
|
@ -4,7 +4,7 @@ import * as models from './module/data/_module.mjs';
|
||||||
import * as documents from './module/documents/_module.mjs';
|
import * as documents from './module/documents/_module.mjs';
|
||||||
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
|
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
|
||||||
import DhCombatTracker from './module/ui/combatTracker.mjs';
|
import DhCombatTracker from './module/ui/combatTracker.mjs';
|
||||||
import { GMUpdateEvent, handleSocketEvent, socketEvent } from './module/helpers/socket.mjs';
|
import { handleSocketEvent, registerSocketHooks } from './module/helpers/socket.mjs';
|
||||||
import { registerDHSettings } from './module/applications/settings.mjs';
|
import { registerDHSettings } from './module/applications/settings.mjs';
|
||||||
import DhpChatLog from './module/ui/chatLog.mjs';
|
import DhpChatLog from './module/ui/chatLog.mjs';
|
||||||
import DhpRuler from './module/ui/ruler.mjs';
|
import DhpRuler from './module/ui/ruler.mjs';
|
||||||
|
|
@ -13,12 +13,13 @@ import { DhDualityRollEnricher, DhTemplateEnricher } from './module/enrichers/_m
|
||||||
import { getCommandTarget, rollCommandToJSON, setDiceSoNiceForDualityRoll } from './module/helpers/utils.mjs';
|
import { getCommandTarget, rollCommandToJSON, setDiceSoNiceForDualityRoll } from './module/helpers/utils.mjs';
|
||||||
import { abilities } from './module/config/actorConfig.mjs';
|
import { abilities } from './module/config/actorConfig.mjs';
|
||||||
import Resources from './module/applications/resources.mjs';
|
import Resources from './module/applications/resources.mjs';
|
||||||
import { NarrativeCountdowns, registerCountdownHooks } from './module/applications/countdowns.mjs';
|
import { NarrativeCountdowns, registerCountdownApplicationHooks } from './module/applications/countdowns.mjs';
|
||||||
import DHDualityRoll from './module/data/chat-message/dualityRoll.mjs';
|
import DHDualityRoll from './module/data/chat-message/dualityRoll.mjs';
|
||||||
import { DualityRollColor } from './module/data/settings/Appearance.mjs';
|
import { DualityRollColor } from './module/data/settings/Appearance.mjs';
|
||||||
import { DhMeasuredTemplate } from './module/placeables/_module.mjs';
|
import { DhMeasuredTemplate } from './module/placeables/_module.mjs';
|
||||||
import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs';
|
import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs';
|
||||||
import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs';
|
import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs';
|
||||||
|
import { registerCountdownHooks } from './module/data/countdowns.mjs';
|
||||||
|
|
||||||
globalThis.SYSTEM = SYSTEM;
|
globalThis.SYSTEM = SYSTEM;
|
||||||
|
|
||||||
|
|
@ -135,34 +136,12 @@ Hooks.on('ready', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
registerCountdownHooks();
|
registerCountdownHooks();
|
||||||
|
registerSocketHooks();
|
||||||
|
registerCountdownApplicationHooks();
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.once('dicesoniceready', () => {});
|
Hooks.once('dicesoniceready', () => {});
|
||||||
|
|
||||||
Hooks.on(socketEvent.GMUpdate, async (action, uuid, update) => {
|
|
||||||
if (game.user.isGM) {
|
|
||||||
const document = uuid ? await fromUuid(uuid) : null;
|
|
||||||
switch (action) {
|
|
||||||
case GMUpdateEvent.UpdateDocument:
|
|
||||||
if (document && update) {
|
|
||||||
await document.update(update);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GMUpdateEvent.UpdateFear:
|
|
||||||
if (game.user.isGM) {
|
|
||||||
await game.settings.set(
|
|
||||||
SYSTEM.id,
|
|
||||||
SYSTEM.SETTINGS.gameSettings.Resources.Fear,
|
|
||||||
Math.max(Math.min(update, 6), 0)
|
|
||||||
);
|
|
||||||
Hooks.callAll(socketEvent.DhpFearUpdate);
|
|
||||||
await game.socket.emit(`system.${SYSTEM.id}`, { action: socketEvent.DhpFearUpdate });
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Hooks.on('renderChatMessageHTML', (_, element) => {
|
Hooks.on('renderChatMessageHTML', (_, element) => {
|
||||||
element
|
element
|
||||||
.querySelectorAll('.duality-roll-button')
|
.querySelectorAll('.duality-roll-button')
|
||||||
|
|
|
||||||
|
|
@ -1056,12 +1056,20 @@
|
||||||
"AddCountdown": "Add Countdown",
|
"AddCountdown": "Add Countdown",
|
||||||
"RemoveCountdownTitle": "Remove Countdown",
|
"RemoveCountdownTitle": "Remove Countdown",
|
||||||
"RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?",
|
"RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?",
|
||||||
|
"OpenOwnership": "Edit Player Ownership",
|
||||||
"Title": "{type} Countdowns",
|
"Title": "{type} Countdowns",
|
||||||
"Types": {
|
"Types": {
|
||||||
"narrative": "Narrative",
|
"narrative": "Narrative",
|
||||||
"encounter": "Encounter"
|
"encounter": "Encounter"
|
||||||
|
},
|
||||||
|
"Notifications": {
|
||||||
|
"LimitedOwnershipMaximise": "You don't have permission to enter edit view"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"OwnershipSelection": {
|
||||||
|
"Title": "Ownership Selection - {name}",
|
||||||
|
"Default": "Default Ownership"
|
||||||
|
},
|
||||||
"Sheets": {
|
"Sheets": {
|
||||||
"PC": {
|
"PC": {
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import { countdownTypes } from '../config/generalConfig.mjs';
|
import { countdownTypes } from '../config/generalConfig.mjs';
|
||||||
|
import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs';
|
||||||
|
import OwnershipSelection from './ownershipSelection.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
|
@ -28,7 +30,9 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
actions: {
|
actions: {
|
||||||
addCountdown: this.addCountdown,
|
addCountdown: this.addCountdown,
|
||||||
removeCountdown: this.removeCountdown,
|
removeCountdown: this.removeCountdown,
|
||||||
editImage: this.onEditImage
|
editImage: this.onEditImage,
|
||||||
|
openOwnership: this.openOwnership,
|
||||||
|
openCountdownOwnership: this.openCountdownOwnership
|
||||||
},
|
},
|
||||||
form: { handler: this.updateData, submitOnChange: true }
|
form: { handler: this.updateData, submitOnChange: true }
|
||||||
};
|
};
|
||||||
|
|
@ -60,8 +64,23 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
const countdownData = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
const countdownData = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||||
|
|
||||||
|
context.isGM = game.user.isGM;
|
||||||
context.base = this.basePath;
|
context.base = this.basePath;
|
||||||
context.source = countdownData.toObject();
|
|
||||||
|
context.canCreate = countdownData.playerOwnership[game.user.id].value === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
||||||
|
context.source = {
|
||||||
|
...countdownData,
|
||||||
|
countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => {
|
||||||
|
const countdown = countdownData.countdowns[key];
|
||||||
|
|
||||||
|
const ownershipValue = countdown.playerOwnership[game.user.id].value;
|
||||||
|
if (ownershipValue > CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
||||||
|
acc[key] = { ...countdown, canEdit: ownershipValue === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER };
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
};
|
||||||
context.systemFields = countdownData.schema.fields;
|
context.systemFields = countdownData.schema.fields;
|
||||||
context.countdownFields = context.systemFields.countdowns.element.fields;
|
context.countdownFields = context.systemFields.countdowns.element.fields;
|
||||||
context.minimized = this.minimized || _options.isFirstRender;
|
context.minimized = this.minimized || _options.isFirstRender;
|
||||||
|
|
@ -75,8 +94,20 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns).toObject(),
|
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns).toObject(),
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, newSetting);
|
|
||||||
this.render();
|
if (game.user.isGM) {
|
||||||
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, newSetting);
|
||||||
|
this.render();
|
||||||
|
} else {
|
||||||
|
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||||
|
action: socketEvent.GMUpdate,
|
||||||
|
data: {
|
||||||
|
action: GMUpdateEvent.UpdateSetting,
|
||||||
|
uuid: SYSTEM.SETTINGS.gameSettings.Countdowns,
|
||||||
|
update: newSetting
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async minimize() {
|
async minimize() {
|
||||||
|
|
@ -88,6 +119,12 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
async maximize() {
|
async maximize() {
|
||||||
if (this.minimized) {
|
if (this.minimized) {
|
||||||
|
const settings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||||
|
if (settings.playerOwnership[game.user.id].value <= CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED) {
|
||||||
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.Countdown.Notifications.LimitedOwnership'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
||||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
||||||
}
|
}
|
||||||
|
|
@ -95,6 +132,31 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
await super.maximize();
|
await super.maximize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateSetting(update) {
|
||||||
|
if (game.user.isGM) {
|
||||||
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update);
|
||||||
|
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||||
|
action: socketEvent.Refresh,
|
||||||
|
data: {
|
||||||
|
refreshType: RefreshType.Countdown,
|
||||||
|
application: `${this.basePath}-countdowns`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
} else {
|
||||||
|
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||||
|
action: socketEvent.GMUpdate,
|
||||||
|
data: {
|
||||||
|
action: GMUpdateEvent.UpdateSetting,
|
||||||
|
uuid: SYSTEM.SETTINGS.gameSettings.Countdowns,
|
||||||
|
update: update,
|
||||||
|
refresh: { refreshType: RefreshType.Countdown, application: `${this.basePath}-countdowns` }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static onEditImage(_, target) {
|
static onEditImage(_, target) {
|
||||||
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||||
const current = setting.countdowns[target.dataset.countdown].img;
|
const current = setting.countdowns[target.dataset.countdown].img;
|
||||||
|
|
@ -114,13 +176,51 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
[`${this.basePath}.countdowns.${countdown}.img`]: path
|
[`${this.basePath}.countdowns.${countdown}.img`]: path
|
||||||
});
|
});
|
||||||
|
|
||||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, setting);
|
await this.updateSetting(setting);
|
||||||
this.render();
|
}
|
||||||
|
|
||||||
|
static openOwnership(_, target) {
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||||
|
const ownership = { default: setting.ownership.default, players: setting.playerOwnership };
|
||||||
|
new OwnershipSelection(resolve, reject, this.title, ownership).render(true);
|
||||||
|
}).then(async ownership => {
|
||||||
|
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||||
|
await setting.updateSource({
|
||||||
|
[`${this.basePath}.ownership`]: ownership
|
||||||
|
});
|
||||||
|
|
||||||
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, setting.toObject());
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static openCountdownOwnership(_, target) {
|
||||||
|
const countdownId = target.dataset.countdown;
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const countdown = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath]
|
||||||
|
.countdowns[countdownId];
|
||||||
|
const ownership = { default: countdown.ownership.default, players: countdown.playerOwnership };
|
||||||
|
new OwnershipSelection(resolve, reject, countdown.name, ownership).render(true);
|
||||||
|
}).then(async ownership => {
|
||||||
|
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||||
|
await setting.updateSource({
|
||||||
|
[`${this.basePath}.countdowns.${countdownId}.ownership`]: ownership
|
||||||
|
});
|
||||||
|
|
||||||
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, setting);
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateCountdownValue(event, increase) {
|
async updateCountdownValue(event, increase) {
|
||||||
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||||
const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown];
|
const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown];
|
||||||
|
|
||||||
|
if (countdown.playerOwnership[game.user.id] < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const currentValue = countdown.progress.current;
|
const currentValue = countdown.progress.current;
|
||||||
|
|
||||||
if (increase && currentValue === countdown.progress.max) return;
|
if (increase && currentValue === countdown.progress.max) return;
|
||||||
|
|
@ -132,20 +232,25 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
: currentValue - 1
|
: currentValue - 1
|
||||||
});
|
});
|
||||||
|
|
||||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, countdownSetting.toObject());
|
await this.updateSetting(countdownSetting.toObject());
|
||||||
this.render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addCountdown() {
|
static async addCountdown() {
|
||||||
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||||
await countdownSetting.updateSource({
|
await countdownSetting.updateSource({
|
||||||
[`${this.basePath}.countdowns.${foundry.utils.randomID()}`]: {
|
[`${this.basePath}.countdowns.${foundry.utils.randomID()}`]: {
|
||||||
name: game.i18n.localize('DAGGERHEART.Countdown.NewCountdown')
|
name: game.i18n.localize('DAGGERHEART.Countdown.NewCountdown'),
|
||||||
|
ownership: game.user.isGM
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
players: {
|
||||||
|
[game.user.id]: { type: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, countdownSetting.toObject());
|
await this.updateSetting(countdownSetting.toObject());
|
||||||
this.render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async removeCountdown(_, target) {
|
static async removeCountdown(_, target) {
|
||||||
|
|
@ -162,8 +267,7 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
await countdownSetting.updateSource({ [`${this.basePath}.countdowns.-=${target.dataset.countdown}`]: null });
|
await countdownSetting.updateSource({ [`${this.basePath}.countdowns.-=${target.dataset.countdown}`]: null });
|
||||||
|
|
||||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, countdownSetting.toObject());
|
await this.updateSetting(countdownSetting.toObject());
|
||||||
this.render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async open() {
|
async open() {
|
||||||
|
|
@ -197,7 +301,7 @@ export class EncounterCountdowns extends Countdowns {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const registerCountdownHooks = () => {
|
export const registerCountdownApplicationHooks = () => {
|
||||||
const updateCountdowns = async shouldIncrease => {
|
const updateCountdowns = async shouldIncrease => {
|
||||||
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).countdowns) {
|
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).countdowns) {
|
||||||
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||||
|
|
|
||||||
72
module/applications/ownershipSelection.mjs
Normal file
72
module/applications/ownershipSelection.mjs
Normal 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/views/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,
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { countdownTypes } from '../config/generalConfig.mjs';
|
import { countdownTypes } from '../config/generalConfig.mjs';
|
||||||
|
import { RefreshType, socketEvent } from '../helpers/socket.mjs';
|
||||||
|
|
||||||
export default class DhCountdowns extends foundry.abstract.DataModel {
|
export default class DhCountdowns extends foundry.abstract.DataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
|
|
@ -19,9 +20,40 @@ class DhCountdownData extends foundry.abstract.DataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
countdowns: new fields.TypedObjectField(new fields.EmbeddedDataField(DhCountdown))
|
countdowns: new fields.TypedObjectField(new fields.EmbeddedDataField(DhCountdown)),
|
||||||
|
ownership: new fields.SchemaField({
|
||||||
|
default: new fields.NumberField({
|
||||||
|
required: true,
|
||||||
|
choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
|
||||||
|
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE
|
||||||
|
}),
|
||||||
|
players: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
type: new fields.NumberField({
|
||||||
|
required: true,
|
||||||
|
choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
|
||||||
|
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DhCountdown extends foundry.abstract.DataModel {
|
class DhCountdown extends foundry.abstract.DataModel {
|
||||||
|
|
@ -37,6 +69,22 @@ class DhCountdown extends foundry.abstract.DataModel {
|
||||||
base64: false,
|
base64: false,
|
||||||
initial: 'icons/magic/time/hourglass-yellow-green.webp'
|
initial: 'icons/magic/time/hourglass-yellow-green.webp'
|
||||||
}),
|
}),
|
||||||
|
ownership: new fields.SchemaField({
|
||||||
|
default: new fields.NumberField({
|
||||||
|
required: true,
|
||||||
|
choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
|
||||||
|
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE
|
||||||
|
}),
|
||||||
|
players: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
type: new fields.NumberField({
|
||||||
|
required: true,
|
||||||
|
choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
|
||||||
|
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
progress: new fields.SchemaField({
|
progress: new fields.SchemaField({
|
||||||
current: new fields.NumberField({
|
current: new fields.NumberField({
|
||||||
required: true,
|
required: true,
|
||||||
|
|
@ -64,4 +112,28 @@ class DhCountdown extends foundry.abstract.DataModel {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const registerCountdownHooks = () => {
|
||||||
|
Hooks.on(socketEvent.Refresh, ({ refreshType, application }) => {
|
||||||
|
if (refreshType === RefreshType.Countdown) {
|
||||||
|
foundry.applications.instances.get(application)?.render();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import BaseDataItem from './base.mjs';
|
||||||
const featureSchema = () => {
|
const featureSchema = () => {
|
||||||
return new foundry.data.fields.SchemaField({
|
return new foundry.data.fields.SchemaField({
|
||||||
name: new foundry.data.fields.StringField({ required: true }),
|
name: new foundry.data.fields.StringField({ required: true }),
|
||||||
effects: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
effects: new ForeignDocumentUUIDArrayField({ type: 'ActiveEffect', required: false }),
|
||||||
actions: new foundry.data.fields.ArrayField(new ActionField())
|
actions: new foundry.data.fields.ArrayField(new ActionField())
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,68 @@
|
||||||
export function handleSocketEvent({ action = null, data = {} } = {}) {
|
export function handleSocketEvent({ action = null, data = {} } = {}) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case socketEvent.GMUpdate:
|
case socketEvent.GMUpdate:
|
||||||
Hooks.callAll(socketEvent.GMUpdate, data.action, data.uuid, data.update);
|
Hooks.callAll(socketEvent.GMUpdate, data);
|
||||||
break;
|
break;
|
||||||
case socketEvent.DhpFearUpdate:
|
case socketEvent.DhpFearUpdate:
|
||||||
Hooks.callAll(socketEvent.DhpFearUpdate);
|
Hooks.callAll(socketEvent.DhpFearUpdate);
|
||||||
break;
|
break;
|
||||||
|
case socketEvent.Refresh:
|
||||||
|
Hooks.call(socketEvent.Refresh, data);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const socketEvent = {
|
export const socketEvent = {
|
||||||
GMUpdate: 'DhpGMUpdate',
|
GMUpdate: 'DhGMUpdate',
|
||||||
DhpFearUpdate: 'DhpFearUpdate'
|
Refresh: 'DhRefresh',
|
||||||
|
DhpFearUpdate: 'DhFearUpdate'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GMUpdateEvent = {
|
export const GMUpdateEvent = {
|
||||||
UpdateDocument: 'DhpGMUpdateDocument',
|
UpdateDocument: 'DhGMUpdateDocument',
|
||||||
UpdateFear: 'DhpUpdateFear'
|
UpdateSetting: 'DhGMUpdateSetting',
|
||||||
|
UpdateFear: 'DhGMUpdateFear'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RefreshType = {
|
||||||
|
Countdown: 'DhCoundownRefresh'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerSocketHooks = () => {
|
||||||
|
Hooks.on(socketEvent.GMUpdate, async data => {
|
||||||
|
if (game.user.isGM) {
|
||||||
|
const document = data.uuid ? await fromUuid(data.uuid) : null;
|
||||||
|
switch (data.action) {
|
||||||
|
case GMUpdateEvent.UpdateDocument:
|
||||||
|
if (document && data.update) {
|
||||||
|
await document.update(data.update);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GMUpdateEvent.UpdateSetting:
|
||||||
|
if (game.user.isGM) {
|
||||||
|
await game.settings.set(SYSTEM.id, data.uuid, data.update);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GMUpdateEvent.UpdateFear:
|
||||||
|
if (game.user.isGM) {
|
||||||
|
await game.settings.set(
|
||||||
|
SYSTEM.id,
|
||||||
|
SYSTEM.SETTINGS.gameSettings.Resources.Fear,
|
||||||
|
Math.max(Math.min(data.update, 6), 0)
|
||||||
|
);
|
||||||
|
Hooks.callAll(socketEvent.DhpFearUpdate);
|
||||||
|
await game.socket.emit(`system.${SYSTEM.id}`, { action: socketEvent.DhpFearUpdate });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.refresh) {
|
||||||
|
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||||
|
action: socketEvent.Refresh,
|
||||||
|
data: data.refresh
|
||||||
|
});
|
||||||
|
Hooks.call(socketEvent.Refresh, data.refresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,14 @@
|
||||||
.countdown-fieldset {
|
.countdown-fieldset {
|
||||||
width: 340px;
|
width: 340px;
|
||||||
height: min-content;
|
height: min-content;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.ownership-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.countdown-container {
|
.countdown-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -100,6 +108,10 @@
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: initial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-inner-container {
|
.countdown-inner-container {
|
||||||
|
|
|
||||||
|
|
@ -3077,6 +3077,24 @@ div.daggerheart.views.multiclass {
|
||||||
.daggerheart.levelup .levelup-footer {
|
.daggerheart.levelup .levelup-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
.daggerheart.views.ownership-selection .ownership-outer-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container {
|
||||||
|
display: flex;
|
||||||
|
border: 2px solid light-dark(#18162e, #f3c267);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 4px 0 0;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container img {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
border-radius: 6px 0 0 6px;
|
||||||
|
}
|
||||||
:root {
|
:root {
|
||||||
--shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
--shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
||||||
--fear-animation: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease;
|
--fear-animation: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease;
|
||||||
|
|
@ -3268,6 +3286,13 @@ div.daggerheart.views.multiclass {
|
||||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset {
|
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset {
|
||||||
width: 340px;
|
width: 340px;
|
||||||
height: min-content;
|
height: min-content;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .ownership-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container {
|
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -3279,6 +3304,9 @@ div.daggerheart.views.multiclass {
|
||||||
height: 150px;
|
height: 150px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container img.disabled {
|
||||||
|
cursor: initial;
|
||||||
|
}
|
||||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container .countdown-inner-container {
|
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container .countdown-inner-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
@import './dialog.less';
|
@import './dialog.less';
|
||||||
@import './characterCreation.less';
|
@import './characterCreation.less';
|
||||||
@import './levelup.less';
|
@import './levelup.less';
|
||||||
|
@import './ownershipSelection.less';
|
||||||
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
||||||
@import './resources.less';
|
@import './resources.less';
|
||||||
@import './countdown.less';
|
@import './countdown.less';
|
||||||
|
|
|
||||||
22
styles/ownershipSelection.less
Normal file
22
styles/ownershipSelection.less
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
.daggerheart.views.ownership-selection {
|
||||||
|
.ownership-outer-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.ownership-container {
|
||||||
|
display: flex;
|
||||||
|
border: 2px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 4px 0 0;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
border-radius: 6px 0 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,21 +1,29 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="expanded-view {{#if minimized}}hidden{{/if}}">
|
<div class="expanded-view {{#if minimized}}hidden{{/if}}">
|
||||||
<div class="flexrow"><button data-action="addCountdown">{{localize "DAGGERHEART.Countdown.AddCountdown"}}</button></div>
|
<div class="flexrow">
|
||||||
|
{{#if canCreate}}<button data-action="addCountdown">{{localize "DAGGERHEART.Countdown.AddCountdown"}}</button>{{/if}}
|
||||||
|
{{#if isGM}}<button class="flex0" data-action="openOwnership" data-tooltip="{{localize "DAGGERHEART.Countdown.OpenOwnership"}}"><i class="fa-solid fa-users"></i></button>{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="countdowns-container">
|
<div class="countdowns-container">
|
||||||
{{#each source.countdowns}}
|
{{#each source.countdowns}}
|
||||||
<fieldset class="countdown-fieldset">
|
<fieldset class="countdown-fieldset">
|
||||||
<legend>{{this.name}} <a><i class="fa-solid fa-trash icon-button" data-action="removeCountdown" data-countdown="{{@key}}"></i></a></legend>
|
<legend>
|
||||||
|
{{this.name}}
|
||||||
|
{{#if this.canEdit}}<a><i class="fa-solid fa-trash icon-button" data-action="removeCountdown" data-countdown="{{@key}}"></i></a>{{/if}}
|
||||||
|
{{#if @root.isGM}}<a><i class="fa-solid fa-users icon-button" data-action="openCountdownOwnership" data-countdown="{{@key}}" data-tooltip="{{localize "DAGGERHEART.Countdown.OpenOwnership"}}"></i></a>{{/if}}
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
|
||||||
<div class="countdown-container">
|
<div class="countdown-container">
|
||||||
<img src="{{this.img}}" data-action='editImage' data-countdown="{{@key}}" />
|
<img src="{{this.img}}" {{#if this.canEdit}}data-action='editImage'{{else}}class="disabled"{{/if}} data-countdown="{{@key}}" />
|
||||||
<div class="countdown-inner-container">
|
<div class="countdown-inner-container">
|
||||||
{{formGroup @root.countdownFields.name name=(concat @root.base ".countdowns." @key ".name") value=this.name localize=true}}
|
{{formGroup @root.countdownFields.name name=(concat @root.base ".countdowns." @key ".name") value=this.name localize=true disabled=(not this.canEdit)}}
|
||||||
<div class="countdown-value-container">
|
<div class="countdown-value-container">
|
||||||
{{formGroup @root.countdownFields.progress.fields.current name=(concat @root.base ".countdowns." @key ".progress.current") value=this.progress.current localize=true}}
|
{{formGroup @root.countdownFields.progress.fields.current name=(concat @root.base ".countdowns." @key ".progress.current") value=this.progress.current localize=true disabled=(not this.canEdit)}}
|
||||||
{{formGroup @root.countdownFields.progress.fields.max name=(concat @root.base ".countdowns." @key ".progress.max") value=this.progress.max localize=true}}
|
{{formGroup @root.countdownFields.progress.fields.max name=(concat @root.base ".countdowns." @key ".progress.max") value=this.progress.max localize=true disabled=(not this.canEdit)}}
|
||||||
</div>
|
</div>
|
||||||
{{formGroup @root.countdownFields.progress.fields.type.fields.value name=(concat @root.base ".countdowns." @key ".progress.type.value") value=this.progress.type.value localize=true localize=true}}
|
{{formGroup @root.countdownFields.progress.fields.type.fields.value name=(concat @root.base ".countdowns." @key ".progress.type.value") value=this.progress.type.value localize=true localize=true disabled=(not this.canEdit)}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
@ -24,7 +32,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="minimized-view {{#if (not minimized)}}hidden{{/if}}">
|
<div class="minimized-view {{#if (not minimized)}}hidden{{/if}}">
|
||||||
{{#each source.countdowns}}
|
{{#each source.countdowns}}
|
||||||
<a class="mini-countdown-container" data-countdown="{{@key}}">
|
<a class="mini-countdown-container {{#if (not this.canEdit)}}disabled{{/if}}" data-countdown="{{@key}}">
|
||||||
<img src="{{this.img}}" />
|
<img src="{{this.img}}" />
|
||||||
<div class="mini-countdown-name">{{this.name}}</div>
|
<div class="mini-countdown-name">{{this.name}}</div>
|
||||||
<div class="mini-countdown-value">{{this.progress.current}}/{{this.progress.max}}</div>
|
<div class="mini-countdown-value">{{this.progress.current}}/{{this.progress.max}}</div>
|
||||||
|
|
|
||||||
22
templates/views/ownershipSelection.hbs
Normal file
22
templates/views/ownershipSelection.hbs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<div class="ownership-outer-container">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "DAGGERHEART.OwnershipSelection.Default"}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<select name="ownership.default" data-dtype="Number">
|
||||||
|
{{selectOptions @root.ownershipOptions selected=ownership.default labelAttr="label" valueAttr="value" }}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#each ownership.players as |player id|}}
|
||||||
|
<div class="ownership-container">
|
||||||
|
{{#if player.img}}<img src="{{player.img}}" />{{/if}}
|
||||||
|
<div>{{player.name}}</div>
|
||||||
|
<select name="{{concat "ownership.players." id ".type"}}" data-dtype="Number">
|
||||||
|
{{selectOptions @root.ownershipOptions selected=player.ownership labelAttr="label" valueAttr="value" }}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
<footer class="flexrow">
|
||||||
|
<button type="submit">{{localize "Save"}}</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue