This commit is contained in:
WBHarry 2026-03-08 13:59:08 +01:00
parent f1f5102af1
commit c6bf482b07
12 changed files with 280 additions and 94 deletions

View file

@ -97,6 +97,30 @@ Hooks.once('init', () => {
fields
};
CONFIG.DH.ACTOR.characterResources.corruption = {
id: 'corruption',
initial: 0,
max: 4,
reverse: false,
label: 'Corruption'
};
CONFIG.DH.ACTOR.characterResources.hunger = {
id: 'hunger',
initial: 0,
max: 6,
reverse: false,
label: 'Hunger'
};
CONFIG.DH.ACTOR.characterResources.glitched = {
id: 'glitched',
initial: 0,
max: 6,
reverse: false,
label: 'Glitched'
};
game.system.registeredTriggers = new game.system.api.data.RegisteredTriggers();
const { DocumentSheetConfig } = foundry.applications.apps;

View file

@ -33,6 +33,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
handleResourceDice: CharacterSheet.#handleResourceDice,
advanceResourceDie: CharacterSheet.#advanceResourceDie,
cancelBeastform: CharacterSheet.#cancelBeastform,
toggleResourceManagement: CharacterSheet.#toggleResourceManagement,
useDowntime: this.useDowntime,
viewParty: CharacterSheet.#viewParty
},
@ -942,6 +943,71 @@ export default class CharacterSheet extends DHBaseActorSheet {
});
}
static async #toggleResourceManagement(_event, button) {
const existingTooltip = document.body.querySelector('.locked-tooltip .resource-management-container');
if (existingTooltip) {
game.tooltip.dismissLockedTooltips();
return;
}
const extraResources = Object.values(CONFIG.DH.ACTOR.characterResources).reduce((acc, resource) => {
if (CONFIG.DH.ACTOR.characterBaseResources[resource.id]) return acc;
const resourceData = this.document.system.resources[resource.id];
acc[resource.id] = {
id: resource.id,
label: game.i18n.localize(resource.label),
value: resourceData.value,
max: resourceData.max
};
return acc;
}, {});
const html = document.createElement('div');
html.innerHTML = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/resourceManagement.hbs`,
{
resources: extraResources
}
);
const target = button.closest('.resource-section');
game.tooltip.dismissLockedTooltips();
game.tooltip.activate(target, {
html,
locked: true,
cssClass: 'bordered-tooltip',
direction: 'DOWN'
});
for (const element of html.querySelectorAll('.resource-value'))
element.addEventListener('click', CharacterSheet.resourceUpdate.bind(this));
}
static async resourceUpdate(event) {
const target = event.target.closest('.resource-value');
const { resource, value: textValue } = target.dataset;
const inputValue = Number.parseInt(textValue);
const decreasing = inputValue <= this.document.system.resources[resource].value;
const value = decreasing ? inputValue - 1 : inputValue;
await this.document.update({ [`system.resources.${resource}.value`]: value });
/* Update resource symbols */
const section = target.closest('.resource-section');
for (const element of section.querySelectorAll('.resource-value')) {
if (Number.parseInt(element.dataset.value) <= value) {
element.querySelector('.fa-diamond').classList.remove('hidden');
element.querySelector('.fa-circle').classList.add('hidden');
} else {
element.querySelector('.fa-diamond').classList.add('hidden');
element.querySelector('.fa-circle').classList.remove('hidden');
}
}
}
/**
* Open the downtime application.
* @type {ApplicationClickAction}

View file

@ -55,24 +55,47 @@ export const abilities = {
}
};
export const scrollingTextResource = {
const baseResources = {
hitPoints: {
id: 'hitPoints',
initial: 0,
max: 0,
reverse: true,
label: 'DAGGERHEART.GENERAL.HitPoints.plural',
reversed: true
maxLabel: 'DAGGERHEART.ACTORS.Character.maxHPBonus'
},
stress: {
label: 'DAGGERHEART.GENERAL.stress',
reversed: true
id: 'stress',
initial: 0,
max: 6,
reverse: true,
label: 'DAGGERHEART.GENERAL.stress'
},
hope: {
id: 'hope',
initial: 2,
min: 0,
reverse: false,
label: 'DAGGERHEART.GENERAL.hope'
},
armor: {
label: 'DAGGERHEART.GENERAL.armor',
reversed: true
}
};
export const characterBaseResources = {
...baseResources
};
export const characterResources = {
...characterBaseResources
};
export const getScrollingTextResources = actorType => ({
armor: {
label: 'DAGGERHEART.GENERAL.armor',
reverse: true
},
...(actorType === 'character' ? characterResources : {})
});
export const featureProperties = {
agility: {
name: 'DAGGERHEART.CONFIG.Traits.agility.name',
@ -518,7 +541,7 @@ export const adversaryScalingData = {
severeThreshold: 10,
hp: 1,
stress: 2,
attack: 2,
attack: 2
},
3: {
difficulty: 2,
@ -526,7 +549,7 @@ export const adversaryScalingData = {
severeThreshold: 15,
hp: 1,
stress: 0,
attack: 2,
attack: 2
},
4: {
difficulty: 2,
@ -534,7 +557,7 @@ export const adversaryScalingData = {
severeThreshold: 25,
hp: 1,
stress: 0,
attack: 2,
attack: 2
}
},
horde: {
@ -544,7 +567,7 @@ export const adversaryScalingData = {
severeThreshold: 8,
hp: 2,
stress: 0,
attack: 0,
attack: 0
},
3: {
difficulty: 2,
@ -552,7 +575,7 @@ export const adversaryScalingData = {
severeThreshold: 12,
hp: 0,
stress: 1,
attack: 1,
attack: 1
},
4: {
difficulty: 2,
@ -560,7 +583,7 @@ export const adversaryScalingData = {
severeThreshold: 15,
hp: 2,
stress: 0,
attack: 0,
attack: 0
}
},
leader: {
@ -570,7 +593,7 @@ export const adversaryScalingData = {
severeThreshold: 10,
hp: 0,
stress: 0,
attack: 1,
attack: 1
},
3: {
difficulty: 2,
@ -578,7 +601,7 @@ export const adversaryScalingData = {
severeThreshold: 15,
hp: 1,
stress: 0,
attack: 2,
attack: 2
},
4: {
difficulty: 2,
@ -586,7 +609,7 @@ export const adversaryScalingData = {
severeThreshold: 25,
hp: 1,
stress: 1,
attack: 3,
attack: 3
}
},
minion: {
@ -596,7 +619,7 @@ export const adversaryScalingData = {
severeThreshold: 0,
hp: 0,
stress: 0,
attack: 1,
attack: 1
},
3: {
difficulty: 2,
@ -604,7 +627,7 @@ export const adversaryScalingData = {
severeThreshold: 0,
hp: 0,
stress: 1,
attack: 1,
attack: 1
},
4: {
difficulty: 2,
@ -612,7 +635,7 @@ export const adversaryScalingData = {
severeThreshold: 0,
hp: 0,
stress: 0,
attack: 1,
attack: 1
}
},
ranged: {
@ -622,7 +645,7 @@ export const adversaryScalingData = {
severeThreshold: 6,
hp: 1,
stress: 0,
attack: 1,
attack: 1
},
3: {
difficulty: 2,
@ -630,7 +653,7 @@ export const adversaryScalingData = {
severeThreshold: 14,
hp: 1,
stress: 1,
attack: 2,
attack: 2
},
4: {
difficulty: 2,
@ -638,7 +661,7 @@ export const adversaryScalingData = {
severeThreshold: 10,
hp: 1,
stress: 1,
attack: 1,
attack: 1
}
},
skulk: {
@ -648,7 +671,7 @@ export const adversaryScalingData = {
severeThreshold: 8,
hp: 1,
stress: 1,
attack: 1,
attack: 1
},
3: {
difficulty: 2,
@ -656,7 +679,7 @@ export const adversaryScalingData = {
severeThreshold: 12,
hp: 1,
stress: 1,
attack: 1,
attack: 1
},
4: {
difficulty: 2,
@ -664,7 +687,7 @@ export const adversaryScalingData = {
severeThreshold: 10,
hp: 1,
stress: 1,
attack: 1,
attack: 1
}
},
solo: {
@ -674,7 +697,7 @@ export const adversaryScalingData = {
severeThreshold: 10,
hp: 0,
stress: 1,
attack: 2,
attack: 2
},
3: {
difficulty: 2,
@ -682,7 +705,7 @@ export const adversaryScalingData = {
severeThreshold: 15,
hp: 2,
stress: 1,
attack: 2,
attack: 2
},
4: {
difficulty: 2,
@ -690,7 +713,7 @@ export const adversaryScalingData = {
severeThreshold: 25,
hp: 0,
stress: 1,
attack: 3,
attack: 3
}
},
standard: {
@ -700,7 +723,7 @@ export const adversaryScalingData = {
severeThreshold: 8,
hp: 0,
stress: 0,
attack: 1,
attack: 1
},
3: {
difficulty: 2,
@ -708,7 +731,7 @@ export const adversaryScalingData = {
severeThreshold: 15,
hp: 1,
stress: 1,
attack: 1,
attack: 1
},
4: {
difficulty: 2,
@ -716,7 +739,7 @@ export const adversaryScalingData = {
severeThreshold: 15,
hp: 0,
stress: 1,
attack: 1,
attack: 1
}
},
support: {
@ -726,7 +749,7 @@ export const adversaryScalingData = {
severeThreshold: 8,
hp: 1,
stress: 1,
attack: 1,
attack: 1
},
3: {
difficulty: 2,
@ -734,7 +757,7 @@ export const adversaryScalingData = {
severeThreshold: 12,
hp: 0,
stress: 0,
attack: 1,
attack: 1
},
4: {
difficulty: 2,
@ -742,7 +765,7 @@ export const adversaryScalingData = {
severeThreshold: 10,
hp: 1,
stress: 1,
attack: 1,
attack: 1
}
}
};

View file

@ -213,7 +213,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
const textData = Object.keys(changes.system.resources).reduce((acc, key) => {
const resource = changes.system.resources[key];
if (resource.value !== undefined && resource.value !== this.resources[key].value) {
acc.push(getScrollTextData(this.resources, resource, key));
acc.push(getScrollTextData(this.resources, resource, key, this.parent.type));
}
return acc;

View file

@ -28,26 +28,32 @@ export default class DhCharacter extends DhCreature {
return {
...super.defineSchema(),
resources: new fields.SchemaField({
hitPoints: resourceField(
0,
0,
'DAGGERHEART.GENERAL.HitPoints.plural',
true,
'DAGGERHEART.ACTORS.Character.maxHPBonus'
),
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
hope: new fields.SchemaField(
...Object.values(CONFIG.DH.ACTOR.characterResources).reduce((acc, resource) => {
if (resource.max !== undefined) {
acc[resource.id] = resourceField(
resource.max,
resource.initial,
resource.label,
resource.reverse,
resource.maxLabel
);
} else {
acc[resource.id] = new fields.SchemaField(
{
value: new fields.NumberField({
initial: 2,
min: 0,
initial: resource.initial,
min: resource.min,
integer: true,
label: 'DAGGERHEART.GENERAL.hope'
label: resource.label
}),
isReversed: new fields.BooleanField({ initial: false })
isReversed: new fields.BooleanField({ initial: resource.reverse })
},
{ label: 'DAGGERHEART.GENERAL.hope' }
)
{ label: resource.label }
);
}
return acc;
}, {})
}),
traits: new fields.SchemaField({
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),

View file

@ -224,7 +224,12 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
const armorChanged =
changed.system?.marks?.value !== undefined && changed.system.marks.value !== this.marks.value;
if (armorChanged && autoSettings.resourceScrollTexts && this.parent.parent?.type === 'character') {
const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor');
const armorData = getScrollTextData(
this.parent.parent.system.resources,
changed.system.marks,
'armor',
this.parent.parent.type
);
options.scrollingTextData = [armorData];
}

View file

@ -378,17 +378,17 @@ export const arraysEqual = (a, b) =>
export const setsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));
export function getScrollTextData(resources, resource, key) {
const { reversed, label } = CONFIG.DH.ACTOR.scrollingTextResource[key];
export function getScrollTextData(resources, resource, key, actorType) {
const { reverse, label } = CONFIG.DH.ACTOR.getScrollingTextResources(actorType)[key];
const { BOTTOM, TOP } = CONST.TEXT_ANCHOR_POINTS;
const increased = resources[key].value < resource.value;
const value = -1 * (resources[key].value - resource.value);
const text = `${game.i18n.localize(label)} ${value.signedString()}`;
const stroke = increased ? (reversed ? 0xffffff : 0x000000) : reversed ? 0x000000 : 0xffffff;
const fill = increased ? (reversed ? 0x0032b1 : 0xffe760) : reversed ? 0xffe760 : 0x0032b1;
const direction = increased ? (reversed ? BOTTOM : TOP) : reversed ? TOP : BOTTOM;
const stroke = increased ? (reverse ? 0xffffff : 0x000000) : reverse ? 0x000000 : 0xffffff;
const fill = increased ? (reverse ? 0x0032b1 : 0xffe760) : reverse ? 0xffe760 : 0x0032b1;
const direction = increased ? (reverse ? BOTTOM : TOP) : reverse ? TOP : BOTTOM;
return { text, stroke, fill, direction };
}

View file

@ -133,8 +133,15 @@
padding: 0;
margin-bottom: 15px;
.hope-section {
.resource-section {
display: flex;
align-items: center;
gap: 4px;
margin-right: 20px;
.resource-manager {
color: light-dark(@dark-blue, @golden);
}
}
.downtime-section {

View file

@ -4,3 +4,5 @@
@import './tooltip/domain-cards.less';
@import './autocomplete/autocomplete.less';
@import './tooltip/resource-management.less';

View file

@ -0,0 +1,37 @@
.bordered-tooltip.locked-tooltip .daggerheart.resource-management-container {
display: flex;
flex-direction: column;
gap: 16px;
.resource-section {
position: relative;
display: flex;
gap: 10px;
background-color: light-dark(transparent, @dark-blue);
color: light-dark(@dark-blue, @golden);
padding: 5px 10px;
border: 1px solid light-dark(@dark-blue, @golden);
border-radius: 6px;
align-items: center;
width: fit-content;
height: 30px;
h4 {
font-family: var(--dh-font-body, 'Montserrat'), sans-serif;
font-size: var(--font-size-14);
font-weight: bold;
text-transform: uppercase;
color: light-dark(@dark-blue, @golden);
margin: 0;
}
.resource-value {
display: flex;
cursor: pointer;
.hidden {
display: none;
}
}
}
}

View file

@ -65,6 +65,7 @@
</div>
<div class="character-row">
<div class="resource-section">
<div class="hope-section">
<h4>{{localize "DAGGERHEART.GENERAL.hope"}}</h4>
{{#times document.system.resources.hope.max}}
@ -82,6 +83,8 @@
</span>
{{/times}}
</div>
<a class="resource-manager" data-action="toggleResourceManagement"><i class="fa-solid fa-gear"></i></a>
</div>
{{#if document.system.class.value}}
<div class="domains-section">
{{#each document.system.domainData as |data|}}

View file

@ -0,0 +1,13 @@
<div class="daggerheart resource-management-container">
{{#each resources as |resource|}}
<div class="resource-section">
<h4>{{resource.label}}</h4>
{{#times resource.max}}
<span class='resource-value' data-action='toggleResource' data-value="{{add this 1}}" data-resource="{{resource.id}}">
<i class='fa-solid fa-diamond {{#unless (gte ../value (add this 1))}}hidden{{/unless}}'></i>
<i class='fa-regular fa-circle {{#if (gte ../value (add this 1))}}hidden{{/if}}'></i>
</span>
{{/times}}
</div>
{{/each}}
</div>