Skip to content

Commit

Permalink
Merge pull request #238 from roth-michael/1.11.3
Browse files Browse the repository at this point in the history
fix npc resting
  • Loading branch information
roth-michael authored Jun 3, 2024
2 parents f92dcaf + c150bf7 commit f80045f
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 13 deletions.
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Rest Recovery Changelog

## Version 1.11.3
- Fixed NPC rests when using Tidy (only from their sheet - still don't support adding them in a prompted rest)
- Due to improvements made to NPC actor data in dnd5e v3.2.0, NPCs should now mostly be able to benefit from the full Rest Recovery workflow; in dnd5e v3.1.x, however, NPC rests will simply use the core rest methods

## Version 1.11.2
- Actors with tokens on the viewed scene will now appear at the top of the rest prompt list dropdown
- Selected actors for a rest prompt are now remembered across browsers
Expand Down
2 changes: 1 addition & 1 deletion module.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"id": "rest-recovery",
"title": "Rest Recovery for 5E",
"description": "Adds 5e long and short rest mechanics and variant rules, such as feats, class features, and hit-dice healing.",
"version": "1.11.2",
"version": "1.11.3",
"authors": [
{
"name": "Wasp",
Expand Down
2 changes: 1 addition & 1 deletion scripts/formapplications/long-rest/long-rest-shell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
maxSpendHitDice = Math.floor(res?.total ?? 0);
});
} else {
maxSpendHitDice = Math.floor(actor.system.details.level * maxHitDiceSpendMultiplier);
maxSpendHitDice = Math.floor((actor.type === "npc" ? actor.system.attributes.hd.max : actor.system.details.level) * maxHitDiceSpendMultiplier);
}
// const maxSpendHitDice = typeof maxHitDiceSpendMultiplier === "string"
// ? Math.floor(lib.evaluateFormula(maxHitDiceSpendMultiplier, actor.getRollData())?.total ?? 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
maxSpendHitDice = Math.floor(res?.total ?? 0);
});
} else {
maxSpendHitDice = Math.floor(actor.system.details.level * maxHitDiceSpendMultiplier);
maxSpendHitDice = Math.floor((actor.type === 'npc' ? actor.system.attributes.hd.max : actor.system.details.level) * maxHitDiceSpendMultiplier);
}
maxSpendHitDice = Math.max(minSpendHitDice, maxSpendHitDice);
Expand Down
37 changes: 27 additions & 10 deletions scripts/rest-workflow.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default class RestWorkflow {
}

get totalHitDice() {
return this.actor.system.attributes.hd;
return this.actor.type === 'npc' ? this.actor.system.attributes.hd.value : this.actor.system.attributes.hd;
}

get recoveredSlots() {
Expand Down Expand Up @@ -174,7 +174,7 @@ export default class RestWorkflow {

const denomination = cachedDenomination;

const hitDice = updates.class["system.hitDiceUsed"] - 1;
const hitDice = updates.class?.["system.hitDiceUsed"] - 1;

const clsItem = actor.items.find(i => {
return i.system.hitDice === denomination && i.system.hitDiceUsed === hitDice;
Expand Down Expand Up @@ -210,6 +210,8 @@ export default class RestWorkflow {
}

if (!config.dialog) return true;

if (foundry.utils.isNewerVersion('3.2.0', game.system.version) && actor.type === "npc") return true;

RestWorkflow.make(actor, false, config).then(() => {

Expand All @@ -220,7 +222,7 @@ export default class RestWorkflow {

config.newDay = newDay;

const dhd = actor.system.attributes.hd - hd0;
const dhd = actor.type === 'npc' ? (actor.system.attributes.hd.value - hd0.value) : (actor.system.attributes.hd - hd0);
const dhp = actor.system.attributes.hp.value - hp0;

return actor._rest(config, {dhd, dhp});
Expand Down Expand Up @@ -249,6 +251,8 @@ export default class RestWorkflow {

if (!config.dialog) return true;

if (foundry.utils.isNewerVersion('3.2.0', game.system.version) && actor.type === "npc") return true;

RestWorkflow.make(actor, true, config).then((workflow) => {
LongRestDialog.show({ ...config, actor }).then(async (newDay) => {

Expand Down Expand Up @@ -298,6 +302,7 @@ export default class RestWorkflow {

static ready() {
Hooks.on("dnd5e.preRestCompleted", (actor, results, config) => {
if (foundry.utils.isNewerVersion('3.2.0', game.system.version) && actor.type === "npc") return true;
const workflow = RestWorkflow.get(actor);
if (workflow) {
workflow.patchRestResults(results).then(async () => {
Expand Down Expand Up @@ -372,7 +377,7 @@ export default class RestWorkflow {
const actorHasNonLightArmor = !!this.actor.items.find(item => item.type === "equipment" && ["heavy", "medium"].indexOf(item.system?.type?.value) > -1 && item.system.equipped)

this.healthData = {
level: this.actor.system.details.level,
level: this.actor.type === "npc" ? this.actor.system.attributes.hd.max : this.actor.system.details.level,
startingHitDice: this.actor.system.attributes.hd,
startingHealth: this.actor.system.attributes.hp.value,
hitDiceSpent: 0,
Expand Down Expand Up @@ -411,6 +416,12 @@ export default class RestWorkflow {
}

getHitDice() {
if (this.actor.type === "npc") {
let denomination = `d${this.actor.system.attributes.hd.denomination}`;
return {
[denomination]: this.actor.system.attributes.hd.value
}
}
return this.actor.items.reduce((hd, item) => {
if (item.type === "class") {
const d = item.system;
Expand Down Expand Up @@ -601,13 +612,13 @@ export default class RestWorkflow {
const maxHitDiceSpendMultiplier = lib.determineMultiplier(CONSTANTS.SETTINGS.LONG_MAX_HIT_DICE_SPEND);
maxSpendHitDice = typeof maxHitDiceSpendMultiplier === "string"
? Math.floor((await lib.evaluateFormula(maxHitDiceSpendMultiplier, this.actor.getRollData()))?.total ?? 0)
: Math.floor(this.actor.system.details.level * maxHitDiceSpendMultiplier);
: Math.floor((this.actor.type === "npc" ? this.actor.system.attributes.hd.max : this.actor.system.details.level) * maxHitDiceSpendMultiplier);
} else {
minSpendHitDice = getSetting(CONSTANTS.SETTINGS.MIN_HIT_DIE_SPEND) || 0;
const maxHitDiceSpendMultiplier = lib.determineMultiplier(CONSTANTS.SETTINGS.MAX_HIT_DICE_SPEND);
maxSpendHitDice = typeof maxHitDiceSpendMultiplier === "string"
? Math.floor((await lib.evaluateFormula(maxHitDiceSpendMultiplier, this.actor.getRollData()))?.total ?? 0)
: Math.floor(this.actor.system.details.level * maxHitDiceSpendMultiplier);
: Math.floor((this.actor.type === "npc" ? this.actor.system.attributes.hd.max : this.actor.system.details.level) * maxHitDiceSpendMultiplier);
maxSpendHitDice = Math.max(minSpendHitDice, maxSpendHitDice);
}

Expand Down Expand Up @@ -771,7 +782,7 @@ export default class RestWorkflow {

this.preRestRegainHitDice = true;
const maxHitDice = await this._getMaxHitDiceRecovery();
let { updates, hitDiceRecovered } = this.actor._getRestHitDiceRecovery({ maxHitDice });
let { updates=[], actorUpdates, hitDiceRecovered } = this.actor._getRestHitDiceRecovery({ maxHitDice });
this.preRestRegainHitDice = false;

let hitDiceLeftToRecover = Math.max(0, maxHitDice - hitDiceRecovered);
Expand All @@ -792,6 +803,9 @@ export default class RestWorkflow {
}

await this.actor.updateEmbeddedDocuments("Item", updates);
if (actorUpdates) {
await this.actor.update(actorUpdates);
}

this.healthData.availableHitDice = this.getHitDice();
this.healthData.totalHitDice = this.totalHitDice;
Expand Down Expand Up @@ -945,7 +959,7 @@ export default class RestWorkflow {
}

const maxHitDice = await this._getMaxHitDiceRecovery();
let { updates, hitDiceRecovered } = this.actor._getRestHitDiceRecovery({ maxHitDice });
let { updates=[], actorUpdates, hitDiceRecovered } = this.actor._getRestHitDiceRecovery({ maxHitDice });

updates.forEach(update => lib.addToUpdates(results.updateItems, update));

Expand All @@ -967,6 +981,9 @@ export default class RestWorkflow {
}
}
}
if (actorUpdates) {
await this.actor.update(actorUpdates);
}
results.dhd = hitDiceRecovered;
}

Expand Down Expand Up @@ -1229,7 +1246,7 @@ export default class RestWorkflow {
duration = this.config.duration;
units = duration > 1 ? 'Minutes' : 'Minute';
}
flavor = game.i18n.format(`REST-RECOVERY.Chat.Flavor.${this.config.longRest ? 'Long' : 'Short'}RestNormal`, {duration: duration, units: units});
flavor = game.i18n.format(`REST-RECOVERY.Chat.Flavor.${this.longRest ? 'Long' : 'Short'}RestNormal`, {duration: duration, units: units});
}

let extra = this.spellSlotsRegainedMessage
Expand Down Expand Up @@ -1295,7 +1312,7 @@ export default class RestWorkflow {

let multiplier = lib.determineMultiplier(CONSTANTS.SETTINGS.HD_MULTIPLIER);
let roundingMethod = lib.determineRoundingMethod(CONSTANTS.SETTINGS.HD_ROUNDING);
const actorLevel = this.actor.system.details.level;
const actorLevel = this.actor.type === "npc" ? this.actor.system.attributes.hd.max : this.actor.system.details.level;

if (lib.getSetting(CONSTANTS.SETTINGS.LONG_REST_ARMOR_AUTOMATION) && lib.getSetting(CONSTANTS.SETTINGS.LONG_REST_ARMOR_HIT_DICE)) {
const armor = this.actor.items.find(item => item.type === "equipment" && ["heavy", "medium"].indexOf(item.system?.type?.value) > -1 && item.system.equipped);
Expand Down

0 comments on commit f80045f

Please sign in to comment.