229 - Narrative Countdown Window Update (#237)

* Improved

* Fixed the mode not sticking

* Removed console log
This commit is contained in:
WBHarry 2025-07-02 23:37:23 +02:00 committed by GitHub
parent ac7fb93635
commit a79b7189b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 258 additions and 187 deletions

View file

@ -1138,6 +1138,7 @@
"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", "OpenOwnership": "Edit Player Ownership",
"Title": "{type} Countdowns", "Title": "{type} Countdowns",
"ToggleSimple": "Toggle Simple View",
"Types": { "Types": {
"narrative": "Narrative", "narrative": "Narrative",
"encounter": "Encounter" "encounter": "Encounter"

View file

@ -1,5 +1,6 @@
import { countdownTypes } from '../config/generalConfig.mjs'; import { countdownTypes } from '../config/generalConfig.mjs';
import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs'; import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs';
import constructHTMLButton from '../helpers/utils.mjs';
import OwnershipSelection from './ownershipSelection.mjs'; import OwnershipSelection from './ownershipSelection.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -25,14 +26,15 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
frame: true, frame: true,
title: 'Countdowns', title: 'Countdowns',
resizable: true, resizable: true,
minimizable: true minimizable: false
}, },
actions: { actions: {
addCountdown: this.addCountdown, addCountdown: this.addCountdown,
removeCountdown: this.removeCountdown, removeCountdown: this.removeCountdown,
editImage: this.onEditImage, editImage: this.onEditImage,
openOwnership: this.openOwnership, openOwnership: this.openOwnership,
openCountdownOwnership: this.openCountdownOwnership openCountdownOwnership: this.openCountdownOwnership,
toggleSimpleView: this.toggleSimpleView
}, },
form: { handler: this.updateData, submitOnChange: true } form: { handler: this.updateData, submitOnChange: true }
}; };
@ -53,11 +55,47 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
}); });
} }
async _onFirstRender(context, options) { async _preFirstRender(context, options) {
super._onFirstRender(context, options); options.position =
game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].position) ??
Countdowns.DEFAULT_OPTIONS.position;
this.element.querySelector('.expanded-view').classList.toggle('hidden'); const viewSetting =
this.element.querySelector('.minimized-view').classList.toggle('hidden'); game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].simple) ?? !game.user.isGM;
this.simpleView =
game.user.isGM || !this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER) ? viewSetting : true;
context.simple = this.simpleView;
}
_onPosition(position) {
game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].position, position);
}
async _renderFrame(options) {
const frame = await super._renderFrame(options);
if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER)) {
const button = constructHTMLButton({
label: '',
classes: ['header-control', 'icon', 'fa-solid', 'fa-wrench'],
dataset: { action: 'toggleSimpleView', tooltip: 'DAGGERHEART.Countdown.ToggleSimple' }
});
this.window.controls.after(button);
}
return frame;
}
testUserPermission(level, exact, altSettings) {
if (game.user.isGM) return true;
const settings =
altSettings ?? game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
const defaultAllowed = exact ? settings.ownership.default === level : settings.ownership.default >= level;
const userAllowed = exact
? settings.playerOwnership[game.user.id]?.value === level
: settings.playerOwnership[game.user.id]?.value >= level;
return defaultAllowed || userAllowed;
} }
async _prepareContext(_options) { async _prepareContext(_options) {
@ -67,15 +105,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
context.isGM = game.user.isGM; context.isGM = game.user.isGM;
context.base = this.basePath; context.base = this.basePath;
context.canCreate = countdownData.playerOwnership[game.user.id].value === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER; context.canCreate = this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true);
context.source = { context.source = {
...countdownData, ...countdownData,
countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => { countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => {
const countdown = countdownData.countdowns[key]; const countdown = countdownData.countdowns[key];
const ownershipValue = countdown.playerOwnership[game.user.id].value; if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED, false, countdown)) {
if (ownershipValue > CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { acc[key] = {
acc[key] = { ...countdown, canEdit: ownershipValue === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER }; ...countdown,
canEdit: this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true, countdown)
};
} }
return acc; return acc;
@ -83,7 +123,7 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
}; };
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.simple = this.simpleView;
return context; return context;
} }
@ -110,28 +150,6 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
} }
} }
async minimize() {
await super.minimize();
this.element.querySelector('.expanded-view').classList.toggle('hidden');
this.element.querySelector('.minimized-view').classList.toggle('hidden');
}
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');
}
await super.maximize();
}
async updateSetting(update) { async updateSetting(update) {
if (game.user.isGM) { if (game.user.isGM) {
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update); await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update);
@ -213,11 +231,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
}); });
} }
static async toggleSimpleView() {
this.simpleView = !this.simpleView;
await game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].simple, this.simpleView);
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) { if (!this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)) {
return; return;
} }

View file

@ -56,7 +56,6 @@ export class DHRoll extends Roll {
} }
static async buildPost(roll, config, message) { static async buildPost(roll, config, message) {
console.log(config)
for (const hook of config.hooks) { for (const hook of config.hooks) {
if (Hooks.call(`${SYSTEM.id}.postRoll${hook.capitalize()}`, config, message) === false) return null; if (Hooks.call(`${SYSTEM.id}.postRoll${hook.capitalize()}`, config, message) === false) return null;
} }
@ -441,9 +440,9 @@ export class DamageRoll extends DHRoll {
static async postEvaluate(roll, config = {}) { static async postEvaluate(roll, config = {}) {
super.postEvaluate(roll, config); super.postEvaluate(roll, config);
config.roll.type = config.type; config.roll.type = config.type;
if(config.source?.message) { if (config.source?.message) {
const chatMessage = ui.chat.collection.get(config.source.message); const chatMessage = ui.chat.collection.get(config.source.message);
chatMessage.update({'system.damage': config}); chatMessage.update({ 'system.damage': config });
} }
} }
} }

View file

@ -1 +1,9 @@
export const displayDomainCardsAsList = 'displayDomainCardsAsList'; export const displayDomainCardsAsList = 'displayDomainCardsAsList';
export const narrativeCountdown = {
simple: 'countdown-narrative-simple',
position: 'countdown-narrative-position'
};
export const encounterCountdown = {
simple: 'countdown-encounter-simple',
position: 'countdown-encounter-position'
};

View file

@ -403,11 +403,18 @@ export class DHBaseAction extends foundry.abstract.DataModel {
hasCost(costs) { hasCost(costs) {
const realCosts = this.getRealCosts(costs), const realCosts = this.getRealCosts(costs),
hasFearCost = realCosts.findIndex(c => c.type === 'fear'); hasFearCost = realCosts.findIndex(c => c.type === 'fear');
if(hasFearCost > -1) { if (hasFearCost > -1) {
const fearCost = realCosts.splice(hasFearCost, 1); const fearCost = realCosts.splice(hasFearCost, 1);
if(!game.user.isGM || fearCost[0].total > game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear)) return false; if (
!game.user.isGM ||
fearCost[0].total > game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear)
)
return false;
} }
return realCosts.reduce((a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), true); return realCosts.reduce(
(a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value),
true
);
} }
/* COST */ /* COST */
@ -499,19 +506,25 @@ export class DHBaseAction extends foundry.abstract.DataModel {
/* SAVE */ /* SAVE */
async rollSave(target, event, message) { async rollSave(target, event, message) {
if(!target?.actor) return; if (!target?.actor) return;
return target.actor.diceRoll({ return target.actor
event, .diceRoll({
title: 'Roll Save', event,
roll: { title: 'Roll Save',
trait: this.save.trait, roll: {
difficulty: this.save.difficulty, trait: this.save.trait,
type: "reaction" difficulty: this.save.difficulty,
}, type: 'reaction'
data: target.actor.getRollData() },
}).then(async (result) => { data: target.actor.getRollData()
if(result) this.updateChatMessage(message, target.id, {result: result.roll.total, success: result.roll.success}); })
}) .then(async result => {
if (result)
this.updateChatMessage(message, target.id, {
result: result.roll.total,
success: result.roll.success
});
});
} }
async updateChatMessage(message, targetId, changes, chain = true) { async updateChatMessage(message, targetId, changes, chain = true) {

View file

@ -1,4 +1,4 @@
import { DHBaseAction } from "../action/action.mjs"; import { DHBaseAction } from '../action/action.mjs';
const fields = foundry.data.fields; const fields = foundry.data.fields;
@ -42,6 +42,9 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
prepareDerivedData() { prepareDerivedData() {
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
this.currentTargets = this.targetSelection !== true ? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t)) : this.targets; this.currentTargets =
this.targetSelection !== true
? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t))
: this.targets;
} }
} }

View file

@ -3,7 +3,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
messageType: new fields.StringField({initial: 'damage'}), messageType: new fields.StringField({ initial: 'damage' }),
title: new fields.StringField(), title: new fields.StringField(),
roll: new fields.DataField({}), roll: new fields.DataField({}),
targets: new fields.ArrayField( targets: new fields.ArrayField(
@ -28,7 +28,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
action: new fields.StringField(), action: new fields.StringField(),
message: new fields.StringField() message: new fields.StringField()
}), }),
directDamage: new fields.BooleanField({initial: true}) directDamage: new fields.BooleanField({ initial: true })
}; };
} }
@ -38,6 +38,9 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
prepareDerivedData() { prepareDerivedData() {
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
this.currentTargets = this.targetSelection !== true ? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t)) : this.targets; this.currentTargets =
this.targetSelection !== true
? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t))
: this.targets;
} }
} }

View file

@ -1,4 +1,4 @@
import DHAdversaryRoll from "./adversaryRoll.mjs"; import DHAdversaryRoll from './adversaryRoll.mjs';
export default class DHDualityRoll extends DHAdversaryRoll { export default class DHDualityRoll extends DHAdversaryRoll {
get messageTemplate() { get messageTemplate() {

View file

@ -36,7 +36,8 @@ class DhCountdownData extends foundry.abstract.DataModel {
}) })
}) })
) )
}) }),
window: new fields.SchemaField({})
}; };
} }

View file

@ -485,7 +485,9 @@ export default class DhpActor extends Actor {
resources.forEach(r => { resources.forEach(r => {
switch (r.type) { switch (r.type) {
case 'fear': case 'fear':
ui.resources.updateFear(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear) + r.value); ui.resources.updateFear(
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear) + r.value
);
break; break;
case 'armorStack': case 'armorStack':
updates.armor.resources['system.marks.value'] = Math.max( updates.armor.resources['system.marks.value'] = Math.max(

View file

@ -259,6 +259,28 @@ export const damageKeyToNumber = key => {
} }
}; };
export default function constructHTMLButton({
label,
dataset = {},
classes = [],
icon = '',
type = 'button',
disabled = false
}) {
const button = document.createElement('button');
button.type = type;
for (const [key, value] of Object.entries(dataset)) {
button.dataset[key] = value;
}
button.classList.add(...classes);
if (icon) icon = `<i class="${icon}"></i> `;
if (disabled) button.disabled = true;
button.innerHTML = `${icon}${label}`;
return button;
}
export const adjustDice = (dice, decrease) => { export const adjustDice = (dice, decrease) => {
const diceKeys = Object.keys(diceTypes); const diceKeys = Object.keys(diceTypes);
const index = diceKeys.indexOf(dice); const index = diceKeys.indexOf(dice);

View file

@ -37,7 +37,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
element.addEventListener('click', this.clickTarget); element.addEventListener('click', this.clickTarget);
}); });
html.querySelectorAll('.button-target-selection').forEach(element => { html.querySelectorAll('.button-target-selection').forEach(element => {
element.addEventListener('click', event => this.onTargetSelection(event, data.message)) element.addEventListener('click', event => this.onTargetSelection(event, data.message));
}); });
html.querySelectorAll('.damage-button').forEach(element => html.querySelectorAll('.damage-button').forEach(element =>
element.addEventListener('click', event => this.onDamage(event, data.message)) element.addEventListener('click', event => this.onDamage(event, data.message))
@ -122,11 +122,13 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
onRollAllSave = async (event, message) => { onRollAllSave = async (event, message) => {
event.stopPropagation(); event.stopPropagation();
const targets = event.target.parentElement.querySelectorAll('.target-section > [data-token] .target-save-container'); const targets = event.target.parentElement.querySelectorAll(
targets.forEach((el) => { '.target-section > [data-token] .target-save-container'
el.dispatchEvent(new PointerEvent("click", { shiftKey: true})) );
}) targets.forEach(el => {
} el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
});
};
onApplyEffect = async (event, message) => { onApplyEffect = async (event, message) => {
event.stopPropagation(); event.stopPropagation();
@ -146,18 +148,26 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
event.stopPropagation(); event.stopPropagation();
const targetSelection = Boolean(event.target.dataset.targetHit), const targetSelection = Boolean(event.target.dataset.targetHit),
msg = ui.chat.collection.get(message._id); msg = ui.chat.collection.get(message._id);
if(msg.system.targetSelection === targetSelection) return; if (msg.system.targetSelection === targetSelection) return;
if(targetSelection !== true && !Array.from(game.user.targets).length) return ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected')); if (targetSelection !== true && !Array.from(game.user.targets).length)
return ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
msg.system.targetSelection = targetSelection; msg.system.targetSelection = targetSelection;
msg.system.prepareDerivedData(); msg.system.prepareDerivedData();
ui.chat.updateMessage(msg); ui.chat.updateMessage(msg);
} };
getTargetList = (event, message) => { getTargetList = (event, message) => {
const targetSelection = event.target.closest('.message-content').querySelector('.button-target-selection.target-selected'), const targetSelection = event.target
.closest('.message-content')
.querySelector('.button-target-selection.target-selected'),
isHit = Boolean(targetSelection.dataset.targetHit); isHit = Boolean(targetSelection.dataset.targetHit);
return {isHit, targets: isHit ? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id)) : Array.from(game.user.targets)}; return {
} isHit,
targets: isHit
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id))
: Array.from(game.user.targets)
};
};
hoverTarget = event => { hoverTarget = event => {
event.stopPropagation(); event.stopPropagation();
@ -185,9 +195,11 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
event.stopPropagation(); event.stopPropagation();
const { isHit, targets } = this.getTargetList(event, message); const { isHit, targets } = this.getTargetList(event, message);
if(message.system.onSave && isHit) { if (message.system.onSave && isHit) {
const pendingingSaves = message.system.targets.filter(target => target.hit && target.saved.success === null); const pendingingSaves = message.system.targets.filter(
if(pendingingSaves.length) { target => target.hit && target.saved.success === null
);
if (pendingingSaves.length) {
const confirm = await foundry.applications.api.DialogV2.confirm({ const confirm = await foundry.applications.api.DialogV2.confirm({
window: { title: 'Pending Reaction Rolls found' }, window: { title: 'Pending Reaction Rolls found' },
content: `<p>Some Tokens still need to roll their Reaction Roll.</p><p>Are you sure you want to continue ?</p><p><i>Undone reaction rolls will be considered as failed</i></p>` content: `<p>Some Tokens still need to roll their Reaction Roll.</p><p>Are you sure you want to continue ?</p><p><i>Undone reaction rolls will be considered as failed</i></p>`

View file

@ -15,13 +15,15 @@ fieldset.daggerheart.chat {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
&:before, &:after { &:before,
&:after {
content: '\f0d8'; content: '\f0d8';
font-family: "Font Awesome 6 Pro"; font-family: 'Font Awesome 6 Pro';
} }
} }
&.expanded { &.expanded {
legend:before, legend:after { legend:before,
legend:after {
content: '\f0d7'; content: '\f0d7';
} }
} }
@ -229,20 +231,20 @@ fieldset.daggerheart.chat {
.target-selection { .target-selection {
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
input[type="radio"] { input[type='radio'] {
display: none; display: none;
&:checked + label { &:checked + label {
text-shadow: 0px 0px 4px #CE5937; text-shadow: 0px 0px 4px #ce5937;
} }
&:not(:checked) + label { &:not(:checked) + label {
opacity: .75; opacity: 0.75;
} }
} }
label { label {
cursor: pointer; cursor: pointer;
opacity: .75; opacity: 0.75;
&.target-selected { &.target-selected {
text-shadow: 0px 0px 4px #CE5937; text-shadow: 0px 0px 4px #ce5937;
opacity: 1; opacity: 1;
} }
} }
@ -273,7 +275,8 @@ fieldset.daggerheart.chat {
background: @miss; background: @miss;
} }
img, .target-save-container { img,
.target-save-container {
width: 22px; width: 22px;
height: 22px; height: 22px;
align-self: center; align-self: center;
@ -401,7 +404,7 @@ fieldset.daggerheart.chat {
display: none; display: none;
} }
&::after { &::after {
content: "??"; content: '??';
} }
} }
} }
@ -414,7 +417,8 @@ fieldset.daggerheart.chat {
border-top-width: 0; border-top-width: 0;
display: contents; display: contents;
legend { legend {
&:before, &:after { &:before,
&:after {
display: none; display: none;
} }
} }

View file

@ -1,6 +1,6 @@
.theme-light { .theme-light {
.daggerheart.dh-style.countdown { .daggerheart.dh-style.countdown {
&.minimized .minimized-view .mini-countdown-container { .minimized-view .mini-countdown-container {
background-image: url('../assets/parchments/dh-parchment-dark.png'); background-image: url('../assets/parchments/dh-parchment-dark.png');
} }
} }
@ -26,51 +26,38 @@
} }
} }
&.minimized { .minimized-view {
height: auto !important; display: flex;
max-height: unset !important; gap: 8px;
max-width: 740px !important; flex-wrap: wrap;
width: auto !important;
.window-content { .mini-countdown-container {
display: flex; width: fit-content;
padding: 4px 8px;
justify-content: center;
}
.minimized-view {
display: flex; display: flex;
align-items: center;
gap: 8px; gap: 8px;
flex-wrap: wrap; border: 2px solid light-dark(@dark-blue, @golden);
border-radius: 6px;
padding: 0 4px 0 0;
background-image: url('../assets/parchments/dh-parchment-light.png');
color: light-dark(@beige, @dark);
cursor: pointer;
.mini-countdown-container { &.disabled {
width: fit-content; cursor: initial;
display: flex; }
align-items: center;
gap: 8px;
border: 2px solid light-dark(@dark-blue, @golden);
border-radius: 6px;
padding: 0 4px 0 0;
background-image: url('../assets/parchments/dh-parchment-light.png');
color: light-dark(@beige, @dark);
cursor: pointer;
&.disabled { img {
cursor: initial; width: 30px;
} height: 30px;
border-radius: 6px 0 0 6px;
}
img { .mini-countdown-name {
width: 30px; white-space: nowrap;
height: 30px; }
border-radius: 6px 0 0 6px;
}
.mini-countdown-name { .mini-countdown-value {
white-space: nowrap;
}
.mini-countdown-value {
}
} }
} }
} }

View file

@ -1414,7 +1414,7 @@ fieldset.daggerheart.chat legend {
fieldset.daggerheart.chat legend:before, fieldset.daggerheart.chat legend:before,
fieldset.daggerheart.chat legend:after { fieldset.daggerheart.chat legend:after {
content: '\f0d8'; content: '\f0d8';
font-family: "Font Awesome 6 Pro"; font-family: 'Font Awesome 6 Pro';
} }
fieldset.daggerheart.chat.expanded legend:before, fieldset.daggerheart.chat.expanded legend:before,
fieldset.daggerheart.chat.expanded legend:after { fieldset.daggerheart.chat.expanded legend:after {
@ -1559,13 +1559,13 @@ fieldset.daggerheart.chat .daggerheart.chat {
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
} }
.daggerheart.chat.roll .target-selection input[type="radio"] { .daggerheart.chat.roll .target-selection input[type='radio'] {
display: none; display: none;
} }
.daggerheart.chat.roll .target-selection input[type="radio"]:checked + label { .daggerheart.chat.roll .target-selection input[type='radio']:checked + label {
text-shadow: 0px 0px 4px #CE5937; text-shadow: 0px 0px 4px #ce5937;
} }
.daggerheart.chat.roll .target-selection input[type="radio"]:not(:checked) + label { .daggerheart.chat.roll .target-selection input[type='radio']:not(:checked) + label {
opacity: 0.75; opacity: 0.75;
} }
.daggerheart.chat.roll .target-selection label { .daggerheart.chat.roll .target-selection label {
@ -1573,7 +1573,7 @@ fieldset.daggerheart.chat .daggerheart.chat {
opacity: 0.75; opacity: 0.75;
} }
.daggerheart.chat.roll .target-selection label.target-selected { .daggerheart.chat.roll .target-selection label.target-selected {
text-shadow: 0px 0px 4px #CE5937; text-shadow: 0px 0px 4px #ce5937;
opacity: 1; opacity: 1;
} }
.daggerheart.chat.roll .target-section { .daggerheart.chat.roll .target-section {
@ -1700,7 +1700,7 @@ fieldset.daggerheart.chat .daggerheart.chat {
display: none; display: none;
} }
.daggerheart.chat [data-view-perm='false']::after { .daggerheart.chat [data-view-perm='false']::after {
content: "??"; content: '??';
} }
.theme-colorful .chat-message.duality { .theme-colorful .chat-message.duality {
border-color: black; border-color: black;
@ -3474,7 +3474,7 @@ div.daggerheart.views.multiclass {
#resources:has(.fear-bar) { #resources:has(.fear-bar) {
min-width: 200px; min-width: 200px;
} }
.theme-light .daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container { .theme-light .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container {
background-image: url('../assets/parchments/dh-parchment-dark.png'); background-image: url('../assets/parchments/dh-parchment-dark.png');
} }
.daggerheart.dh-style.countdown { .daggerheart.dh-style.countdown {
@ -3494,23 +3494,12 @@ div.daggerheart.views.multiclass {
.daggerheart.dh-style.countdown fieldset legend a { .daggerheart.dh-style.countdown fieldset legend a {
text-shadow: none; text-shadow: none;
} }
.daggerheart.dh-style.countdown.minimized { .daggerheart.dh-style.countdown .minimized-view {
height: auto !important;
max-height: unset !important;
max-width: 740px !important;
width: auto !important;
}
.daggerheart.dh-style.countdown.minimized .window-content {
display: flex;
padding: 4px 8px;
justify-content: center;
}
.daggerheart.dh-style.countdown.minimized .minimized-view {
display: flex; display: flex;
gap: 8px; gap: 8px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container { .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container {
width: fit-content; width: fit-content;
display: flex; display: flex;
align-items: center; align-items: center;
@ -3522,15 +3511,15 @@ div.daggerheart.views.multiclass {
color: light-dark(#efe6d8, #222); color: light-dark(#efe6d8, #222);
cursor: pointer; cursor: pointer;
} }
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container.disabled { .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container.disabled {
cursor: initial; cursor: initial;
} }
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container img { .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container img {
width: 30px; width: 30px;
height: 30px; height: 30px;
border-radius: 6px 0 0 6px; border-radius: 6px 0 0 6px;
} }
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container .mini-countdown-name { .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container .mini-countdown-name {
white-space: nowrap; white-space: nowrap;
} }
.daggerheart.dh-style.countdown .hidden { .daggerheart.dh-style.countdown .hidden {

View file

@ -1,42 +1,45 @@
<div> <div>
<div class="expanded-view {{#if minimized}}hidden{{/if}}"> {{#if simple}}
<div class="countdowns-menu"> <div class="minimized-view">
{{#if canCreate}}<button class="flex" data-action="addCountdown">{{localize "DAGGERHEART.Countdown.AddCountdown"}}</button>{{/if}}
{{#if isGM}}<button 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}} {{#each source.countdowns}}
<fieldset class="countdown-fieldset"> <a class="mini-countdown-container {{#if (not this.canEdit)}}disabled{{/if}}" data-countdown="{{@key}}">
<legend> <img src="{{this.img}}" />
{{this.name}} <div class="mini-countdown-name">{{this.name}}</div>
{{#if this.canEdit}}<a><i class="fa-solid fa-trash icon-button" data-action="removeCountdown" data-countdown="{{@key}}"></i></a>{{/if}} <div class="mini-countdown-value">{{this.progress.current}}/{{this.progress.max}}</div>
{{#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}} </a>
</legend>
<div class="countdown-container">
<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 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 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 disabled=(not this.canEdit)}}
</div>
</div>
</fieldset>
{{/each}} {{/each}}
</div> </div>
</div> {{else}}
<div class="minimized-view {{#if (not minimized)}}hidden{{/if}}"> <div class="expanded-view">
{{#each source.countdowns}} <div class="countdowns-menu">
<a class="mini-countdown-container {{#if (not this.canEdit)}}disabled{{/if}}" data-countdown="{{@key}}"> {{#if canCreate}}<button class="flex" data-action="addCountdown">{{localize "DAGGERHEART.Countdown.AddCountdown"}}</button>{{/if}}
<img src="{{this.img}}" /> {{#if isGM}}<button data-action="openOwnership" data-tooltip="{{localize "DAGGERHEART.Countdown.OpenOwnership"}}"><i class="fa-solid fa-users"></i></button>{{/if}}
<div class="mini-countdown-name">{{this.name}}</div> </div>
<div class="mini-countdown-value">{{this.progress.current}}/{{this.progress.max}}</div>
</a> <div class="countdowns-container">
{{/each}} {{#each source.countdowns}}
</div> <fieldset class="countdown-fieldset">
<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}}" {{#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 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 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 disabled=(not this.canEdit)}}
</div>
</div>
</fieldset>
{{/each}}
</div>
</div>
{{/if}}
</div> </div>