Downtime can now display both ShortRest and LongRest options depending on character rules

This commit is contained in:
WBHarry 2025-07-17 15:35:27 +02:00
parent ab56f2c23e
commit 475a63f120
8 changed files with 161 additions and 55 deletions

View file

@ -251,6 +251,8 @@
"Downtime": { "Downtime": {
"downtimeHeader": "Downtime Moves ({current}/{max})", "downtimeHeader": "Downtime Moves ({current}/{max})",
"longRest": { "longRest": {
"title": "Long Rest",
"moves": "Long Rest Moves ({current}/{max})",
"clearStress": { "clearStress": {
"description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress.", "description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress.",
"name": "Clear Stress" "name": "Clear Stress"
@ -267,7 +269,6 @@
"description": "Describe how you patch yourself up and remove all marked Hit Points. You may also do this on an ally instead.", "description": "Describe how you patch yourself up and remove all marked Hit Points. You may also do this on an ally instead.",
"name": "Tend to Wounds" "name": "Tend to Wounds"
}, },
"title": "Long Rest",
"workOnAProject": { "workOnAProject": {
"description": "Establish or continue work on a project.", "description": "Establish or continue work on a project.",
"name": "Work on a Project" "name": "Work on a Project"
@ -275,6 +276,7 @@
}, },
"shortRest": { "shortRest": {
"title": "Short Rest", "title": "Short Rest",
"moves": "Short Rest Moves ({current}/{max})",
"tendToWounds": { "tendToWounds": {
"name": "Tend to Wounds", "name": "Tend to Wounds",
"description": "Describe how you hastily patch yourself up, then clear a number of Hit Points equal to 1d4 + your tier. You can do this to an ally instead." "description": "Describe how you hastily patch yourself up, then clear a number of Hit Points equal to 1d4 + your tier. You can do this to an ally instead."
@ -291,7 +293,8 @@
"name": "Prepare", "name": "Prepare",
"description": "Describe how you prepare yourself for the path ahead, then gain a Hope. If you choose to Prepare with one or more members of your party, you each gain 2 Hope." "description": "Describe how you prepare yourself for the path ahead, then gain a Hope. If you choose to Prepare with one or more members of your party, you each gain 2 Hope."
} }
} },
"takeDowntime": "Take Downtime"
}, },
"HUD": { "HUD": {
"tokenHUD": { "tokenHUD": {
@ -1161,6 +1164,28 @@
"hint": "The cost in stress you can pay to reduce minor damage to none." "hint": "The cost in stress you can pay to reduce minor damage to none."
} }
} }
},
"rest": {
"shortRest": {
"shortRestMoves": {
"label": "Short Rest: Short Rest Moves",
"hint": "The number of Short Rest Moves the character can take during a Short Rest."
},
"longRestMoves": {
"label": "Short Rest: Long Rest Moves",
"hint": "The number of Long Rest Moves the character can take during a Short Rest."
}
},
"longRest": {
"shortRestMoves": {
"label": "Long Rest: Short Rest Moves",
"hint": "The number of Short Rest Moves the character can take during a Long Rest."
},
"longRestMoves": {
"label": "Long Rest: Long Rest Moves",
"hint": "The number of Long Rest Moves the character can take during a Long Rest."
}
}
} }
}, },
"Tabs": { "Tabs": {

View file

@ -7,8 +7,16 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
this.actor = actor; this.actor = actor;
this.shortrest = shortrest; this.shortrest = shortrest;
const options = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves; this.moveData = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves;
this.moveData = shortrest ? options.shortRest : options.longRest; this.nrChoices = {
shortRest: {
max: actor.system.rules.rest[`${shortrest ? 'short' : 'long'}Rest`].shortMoves
},
longRest: {
max: actor.system.rules.rest[`${shortrest ? 'short' : 'long'}Rest`].longMoves
}
};
this.nrChoices.total = { max: this.nrChoices.shortRest.max + this.nrChoices.longRest.max };
} }
get title() { get title() {
@ -17,7 +25,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
tag: 'form', tag: 'form',
classes: ['daggerheart', 'views', 'downtime'], classes: ['daggerheart', 'views', 'dh-style', 'downtime'],
position: { width: 680, height: 'auto' }, position: { width: 680, height: 'auto' },
actions: { actions: {
selectMove: this.selectMove, selectMove: this.selectMove,
@ -29,7 +37,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
static PARTS = { static PARTS = {
application: { application: {
id: 'downtime', id: 'downtime',
template: 'systems/daggerheart/templates/dialogs/downtime.hbs' template: 'systems/daggerheart/templates/dialogs/downtime/downtime.hbs'
} }
}; };
@ -45,38 +53,70 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.selectedActivity = this.selectedActivity; context.selectedActivity = this.selectedActivity;
context.moveData = this.moveData; context.moveData = this.moveData;
context.nrCurrentChoices = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0); context.nrCurrentChoices = Object.values(this.moveData).reduce((acc, category) => {
context.disabledDowntime = context.nrCurrentChoices < context.moveData.nrChoices; acc += Object.values(category.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
return acc;
}, 0);
context.nrChoices = {
...this.nrChoices,
shortRest: {
...this.nrChoices.shortRest,
current: Object.values(this.moveData.shortRest.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0)
},
longRest: {
...this.nrChoices.longRest,
current: Object.values(this.moveData.longRest.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0)
}
};
context.nrChoices.total = {
...this.nrChoices.total,
current: context.nrChoices.shortRest.current + context.nrChoices.longRest.current
};
context.shortRestMoves = this.nrChoices.shortRest.max > 0 ? this.moveData.shortRest : null;
context.longRestMoves = this.nrChoices.longRest.max > 0 ? this.moveData.longRest : null;
context.disabledDowntime = context.nrChoices.total.current < context.nrChoices.total.max;
return context; return context;
} }
static selectMove(_, button) { static selectMove(_, target) {
const nrSelected = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0); const nrSelected = Object.values(this.moveData[target.dataset.category].moves).reduce(
if (nrSelected === this.moveData.nrChoices) { (acc, x) => acc + (x.selected ?? 0),
0
);
if (nrSelected === this.nrChoices[target.dataset.category].max) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noMoreMoves')); ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noMoreMoves'));
return; return;
} }
const move = button.dataset.move; const move = target.dataset.move;
this.moveData.moves[move].selected = this.moveData.moves[move].selected this.moveData[target.dataset.category].moves[move].selected = this.moveData[target.dataset.category].moves[move]
? this.moveData.moves[move].selected + 1 .selected
? this.moveData[target.dataset.category].moves[move].selected + 1
: 1; : 1;
this.render(); this.render();
} }
deselectMove(event) { deselectMove(event) {
const move = event.currentTarget.dataset.move; const button = event.target.closest('.activity-image');
this.moveData.moves[move].selected = this.moveData.moves[move].selected const move = button.dataset.move;
? this.moveData.moves[move].selected - 1 this.moveData[button.dataset.category].moves[move].selected = this.moveData[button.dataset.category].moves[move]
.selected
? this.moveData[button.dataset.category].moves[move].selected - 1
: 0; : 0;
this.render(); this.render();
} }
static async takeDowntime() { static async takeDowntime() {
const moves = Object.values(this.moveData.moves).filter(x => x.selected); const moves = Object.values(this.moveData).flatMap(category => {
return Object.values(category.moves).filter(x => x.selected);
});
const cls = getDocumentClass('ChatMessage'); const cls = getDocumentClass('ChatMessage');
const msg = new cls({ const msg = new cls({

View file

@ -122,12 +122,9 @@ export default class DhCharacter extends BaseDataActor {
label: 'DAGGERHEART.GENERAL.Range.other' label: 'DAGGERHEART.GENERAL.Range.other'
}) })
}), }),
rally: new fields.ArrayField( rally: new fields.ArrayField(new fields.StringField(), {
new fields.StringField(),
{
label: 'DAGGERHEART.CLASS.Feature.rallyDice' label: 'DAGGERHEART.CLASS.Feature.rallyDice'
} })
)
}), }),
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }), companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
rules: new fields.SchemaField({ rules: new fields.SchemaField({
@ -174,6 +171,44 @@ export default class DhCharacter extends BaseDataActor {
*/ */
flipMinDiceValue: new fields.BooleanField({ intial: false }) flipMinDiceValue: new fields.BooleanField({ intial: false })
}), }),
rest: new fields.SchemaField({
shortRest: new fields.SchemaField({
shortMoves: new fields.NumberField({
required: true,
integer: true,
min: 1,
initial: 2,
label: 'DAGGERHEART.GENERAL.Rules.rest.shortRest.shortRestMoves.label',
hint: 'DAGGERHEART.GENERAL.Rules.rest.shortRest.shortRestMoves.hint'
}),
longMoves: new fields.NumberField({
required: true,
integer: true,
min: 0,
initial: 0,
label: 'DAGGERHEART.GENERAL.Rules.rest.shortRest.longRestMoves.label',
hint: 'DAGGERHEART.GENERAL.Rules.rest.shortRest.longRestMoves.hint'
})
}),
longRest: new fields.SchemaField({
shortMoves: new fields.NumberField({
required: true,
integer: true,
min: 0,
initial: 0,
label: 'DAGGERHEART.GENERAL.Rules.rest.longRest.shortRestMoves.label',
hint: 'DAGGERHEART.GENERAL.Rules.rest.longRest.shortRestMoves.hint'
}),
longMoves: new fields.NumberField({
required: true,
integer: true,
min: 1,
initial: 2,
label: 'DAGGERHEART.GENERAL.Rules.rest.longRest.longRestMoves.label',
hint: 'DAGGERHEART.GENERAL.Rules.rest.longRest.longRestMoves.hint'
})
})
}),
runeWard: new fields.BooleanField({ initial: false }) runeWard: new fields.BooleanField({ initial: false })
}) })
}; };

View file

@ -25,6 +25,7 @@ export const preloadHandlebarsTemplates = async function () {
'systems/daggerheart/templates/settings/components/settings-item-line.hbs', 'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
'systems/daggerheart/templates/ui/chat/parts/damage-chat.hbs', 'systems/daggerheart/templates/ui/chat/parts/damage-chat.hbs',
'systems/daggerheart/templates/ui/chat/parts/target-chat.hbs', 'systems/daggerheart/templates/ui/chat/parts/target-chat.hbs',
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs' 'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
'systems/daggerheart/templates/dialogs/downtime/activities.hbs'
]); ]);
}; };

View file

@ -3,12 +3,6 @@
.daggerheart.views { .daggerheart.views {
.downtime-container { .downtime-container {
.downtime-header {
margin: 0;
color: light-dark(@dark-blue, @golden);
text-align: center;
}
.activity-container { .activity-container {
display: flex; display: flex;
align-items: center; align-items: center;
@ -73,6 +67,12 @@
} }
} }
footer {
margin-top: 8px;
display: flex;
gap: 8px;
}
&.downtime { &.downtime {
.activity-text-area { .activity-text-area {
resize: none; resize: none;

View file

@ -1,24 +0,0 @@
<div>
<div class="downtime-container">
<h2 class="downtime-header">{{localize "DAGGERHEART.APPLICATIONS.Downtime.downtimeHeader" current=nrCurrentChoices max=moveData.nrChoices}}</h2>
{{#each moveData.moves as |move key|}}
<div class="activity-container">
<div class="activity-title">
<div class="activity-image {{#if this.selected}}selected{{/if}}" data-action="selectMove" data-move="{{key}}">
{{#if this.selected}}<div class="activity-select-label">{{move.selected}}</div>{{/if}}
<img src="{{move.img}}" />
</div>
<span class="activity-title-text">{{localize this.name}}</span>
</div>
<div class="activity-body">
{{localize this.description}}
</div>
</div>
{{/each}}
</div>
<footer class="flexrow">
<button type="button" data-action="close">{{localize "DAGGERHEART.Application.Cancel"}}</button>
<button type="button" data-action="takeDowntime" {{#if this.disabledDowntime}}disabled{{/if}}>{{localize "DAGGERHEART.Application.Downtime.TakeDowntime"}}</button>
</footer>
</div>

View file

@ -0,0 +1,19 @@
<fieldset class="one-column">
<legend>{{localize (concat "DAGGERHEART.APPLICATIONS.Downtime." category ".moves") max=nrChoices.max current=nrChoices.current}}</legend>
{{#each moves as |move key|}}
<div class="activity-container">
<div class="activity-title">
<div class="activity-image {{#if move.selected}}selected{{/if}}" data-action="selectMove" data-category="{{../category}}" data-move="{{key}}">
{{#if move.selected}}<div class="activity-select-label">{{move.selected}}</div>{{/if}}
<img src="{{move.img}}" />
</div>
<span class="activity-title-text">{{localize move.name}}</span>
</div>
<div class="activity-body">
{{localize move.description}}
</div>
</div>
{{/each}}
</fieldset>

View file

@ -0,0 +1,10 @@
<div>
<div class="downtime-container">
{{#if shortRestMoves.moves}}{{> "systems/daggerheart/templates/dialogs/downtime/activities.hbs" moves=shortRestMoves.moves category='shortRest' nrChoices=nrChoices.shortRest}}{{/if}}
{{#if longRestMoves.moves}}{{> "systems/daggerheart/templates/dialogs/downtime/activities.hbs" moves=longRestMoves.moves category='longRest' nrChoices=nrChoices.longRest}}{{/if}}
</div>
<footer class="flexrow">
<button type="button" data-action="close">{{localize "Cancel"}}</button>
<button type="button" data-action="takeDowntime" {{#if disabledDowntime}}disabled{{/if}}>{{localize "DAGGERHEART.APPLICATIONS.Downtime.takeDowntime"}}</button>
</footer>
</div>