Added ownership to countdowns

This commit is contained in:
WBHarry 2025-06-21 21:37:01 +02:00
parent 81cda222da
commit 08c136d740
13 changed files with 431 additions and 55 deletions

View file

@ -4,7 +4,7 @@ import * as models from './module/data/_module.mjs';
import * as documents from './module/documents/_module.mjs';
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.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 DhpChatLog from './module/ui/chatLog.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 { abilities } from './module/config/actorConfig.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 { DualityRollColor } from './module/data/settings/Appearance.mjs';
import { DhMeasuredTemplate } from './module/placeables/_module.mjs';
import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs';
import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs';
import { registerCountdownHooks } from './module/data/countdowns.mjs';
globalThis.SYSTEM = SYSTEM;
@ -135,34 +136,12 @@ Hooks.on('ready', () => {
);
registerCountdownHooks();
registerSocketHooks();
registerCountdownApplicationHooks();
});
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) => {
element
.querySelectorAll('.duality-roll-button')

View file

@ -1056,12 +1056,20 @@
"AddCountdown": "Add Countdown",
"RemoveCountdownTitle": "Remove Countdown",
"RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?",
"OpenOwnership": "Edit Player Ownership",
"Title": "{type} Countdowns",
"Types": {
"narrative": "Narrative",
"encounter": "Encounter"
},
"Notifications": {
"LimitedOwnershipMaximise": "You don't have permission to enter edit view"
}
},
"OwnershipSelection": {
"Title": "Ownership Selection - {name}",
"Default": "Default Ownership"
},
"Sheets": {
"PC": {
"Name": "Name",

View file

@ -1,4 +1,6 @@
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;
@ -28,7 +30,9 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
actions: {
addCountdown: this.addCountdown,
removeCountdown: this.removeCountdown,
editImage: this.onEditImage
editImage: this.onEditImage,
openOwnership: this.openOwnership,
openCountdownOwnership: this.openCountdownOwnership
},
form: { handler: this.updateData, submitOnChange: true }
};
@ -60,8 +64,23 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
const context = await super._prepareContext(_options);
const countdownData = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
context.isGM = game.user.isGM;
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.countdownFields = context.systemFields.countdowns.element.fields;
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(),
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() {
@ -88,6 +119,12 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
async maximize() {
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('.minimized-view').classList.toggle('hidden');
}
@ -95,6 +132,31 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
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) {
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
const current = setting.countdowns[target.dataset.countdown].img;
@ -114,13 +176,51 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
[`${this.basePath}.countdowns.${countdown}.img`]: path
});
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, setting);
this.render();
await this.updateSetting(setting);
}
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) {
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
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;
if (increase && currentValue === countdown.progress.max) return;
@ -132,20 +232,25 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
: currentValue - 1
});
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, countdownSetting.toObject());
this.render();
await this.updateSetting(countdownSetting.toObject());
}
static async addCountdown() {
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
await countdownSetting.updateSource({
[`${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());
this.render();
await this.updateSetting(countdownSetting.toObject());
}
static async removeCountdown(_, target) {
@ -162,8 +267,7 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
await countdownSetting.updateSource({ [`${this.basePath}.countdowns.-=${target.dataset.countdown}`]: null });
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, countdownSetting.toObject());
this.render();
await this.updateSetting(countdownSetting.toObject());
}
async open() {
@ -197,7 +301,7 @@ export class EncounterCountdowns extends Countdowns {
};
}
export const registerCountdownHooks = () => {
export const registerCountdownApplicationHooks = () => {
const updateCountdowns = async shouldIncrease => {
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).countdowns) {
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);

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/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();
}
}

View file

@ -1,4 +1,5 @@
import { countdownTypes } from '../config/generalConfig.mjs';
import { RefreshType, socketEvent } from '../helpers/socket.mjs';
export default class DhCountdowns extends foundry.abstract.DataModel {
static defineSchema() {
@ -19,9 +20,40 @@ class DhCountdownData extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
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 {
@ -37,6 +69,22 @@ class DhCountdown extends foundry.abstract.DataModel {
base64: false,
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({
current: new fields.NumberField({
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;
}
});
};

View file

@ -5,7 +5,7 @@ import BaseDataItem from './base.mjs';
const featureSchema = () => {
return new foundry.data.fields.SchemaField({
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())
});
};

View file

@ -1,20 +1,68 @@
export function handleSocketEvent({ action = null, data = {} } = {}) {
switch (action) {
case socketEvent.GMUpdate:
Hooks.callAll(socketEvent.GMUpdate, data.action, data.uuid, data.update);
Hooks.callAll(socketEvent.GMUpdate, data);
break;
case socketEvent.DhpFearUpdate:
Hooks.callAll(socketEvent.DhpFearUpdate);
break;
case socketEvent.Refresh:
Hooks.call(socketEvent.Refresh, data);
break;
}
}
export const socketEvent = {
GMUpdate: 'DhpGMUpdate',
DhpFearUpdate: 'DhpFearUpdate'
GMUpdate: 'DhGMUpdate',
Refresh: 'DhRefresh',
DhpFearUpdate: 'DhFearUpdate'
};
export const GMUpdateEvent = {
UpdateDocument: 'DhpGMUpdateDocument',
UpdateFear: 'DhpUpdateFear'
UpdateDocument: 'DhGMUpdateDocument',
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);
}
}
});
};

View file

@ -90,6 +90,14 @@
.countdown-fieldset {
width: 340px;
height: min-content;
position: relative;
.ownership-button {
position: absolute;
top: 8px;
right: 8px;
font-size: 18px;
}
.countdown-container {
display: flex;
@ -100,6 +108,10 @@
width: 150px;
height: 150px;
cursor: pointer;
&.disabled {
cursor: initial;
}
}
.countdown-inner-container {

View file

@ -3077,6 +3077,24 @@ div.daggerheart.views.multiclass {
.daggerheart.levelup .levelup-footer {
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 {
--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;
@ -3268,6 +3286,13 @@ div.daggerheart.views.multiclass {
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset {
width: 340px;
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 {
display: flex;
@ -3279,6 +3304,9 @@ div.daggerheart.views.multiclass {
height: 150px;
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 {
display: flex;
flex-direction: column;

View file

@ -10,6 +10,7 @@
@import './dialog.less';
@import './characterCreation.less';
@import './levelup.less';
@import './ownershipSelection.less';
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
@import './resources.less';
@import './countdown.less';

View 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;
}
}
}
}

View file

@ -1,21 +1,29 @@
<div>
<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">
{{#each source.countdowns}}
<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">
<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">
{{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">
{{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.max name=(concat @root.base ".countdowns." @key ".progress.max") value=this.progress.max 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 disabled=(not this.canEdit)}}
</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>
</fieldset>
@ -24,7 +32,7 @@
</div>
<div class="minimized-view {{#if (not minimized)}}hidden{{/if}}">
{{#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}}" />
<div class="mini-countdown-name">{{this.name}}</div>
<div class="mini-countdown-value">{{this.progress.current}}/{{this.progress.max}}</div>

View 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>