From ffaec9be93a3611909f2202c47a6fa7f3db1fada Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 1 Mar 2020 15:07:09 -0800 Subject: [PATCH] Add support for rolling death saving throws --- lang/en.json | 2 +- module/actor/entity.js | 50 +++++++++++++++++++++++++++ module/actor/sheets/character.js | 15 ++++++++ templates/actors/character-sheet.html | 2 +- 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/lang/en.json b/lang/en.json index 0e4778f256..5674d319c8 100644 --- a/lang/en.json +++ b/lang/en.json @@ -282,7 +282,7 @@ "DND5E.DamImm": "Damage Immunities", "DND5E.DamRes": "Damage Resistances", "DND5E.DamVuln": "Damage Vulnerabilities", - "DND5E.DeathSave": "Death Saving Throws", + "DND5E.DeathSave": "Death Saves", "DND5E.Description": "Description", "DND5E.Details": "Details", "DND5E.Equipped": "Equipped", diff --git a/module/actor/entity.js b/module/actor/entity.js index 9572eae42f..28e5a67be1 100644 --- a/module/actor/entity.js +++ b/module/actor/entity.js @@ -392,6 +392,56 @@ export class Actor5e extends Actor { /* -------------------------------------------- */ + /** + * Perform a death saving throw, rolling a d20 plus any global save bonuses + * @param {Object} options Additional options which modify the roll + * @return {Promise} A Promise which resolves to the Roll instance + */ + async rollDeathSave(options={}) { + const bonus = getProperty(this.data.data.bonuses, "abilities.save"); + const parts = !!bonus ? ["@saveBonus"] : []; + const speaker = ChatMessage.getSpeaker({actor: this}); + const roll = await Dice5e.d20Roll({ + event: options.event, + parts: parts, + data: {saveBonus: parseInt(bonus)}, + title: `Death Saving Throw`, + speaker: speaker + }); + + // Take action depending on the result + const success = roll.total >= 10; + const death = this.data.data.attributes.death; + + // Save success + if ( success ) { + let successes = (death.success || 0) + (roll.total === 20 ? 2 : 1); + if ( successes === 3 ) { // Survival + await this.update({ + "data.attributes.death.success": 0, + "data.attributes.death.failure": 0, + "data.attributes.hp.value": 1 + }); + await ChatMessage.create({content: `${this.name} has survived with 3 death save successes!`, speaker}); + } + else await this.update({"data.attributes.death.success": Math.clamped(successes, 0, 3)}); + } + + // Save failure + else { + let failures = (death.failure || 0) + (roll.total === 1 ? 2 : 1); + await this.update({"data.attributes.death.failure": Math.clamped(failures, 0, 3)}); + if ( failures === 3 ) { // Death + await ChatMessage.create({content: `${this.name} has died with 3 death save failures!`, speaker}); + } + } + + // Return the rolled result + return roll; + } + + /* -------------------------------------------- */ + /** * Roll a hit die of the appropriate type, gaining hit points equal to the die roll plus your CON modifier * @param {string} formula The hit die type to roll. Example "d8" diff --git a/module/actor/sheets/character.js b/module/actor/sheets/character.js index 477dbc6a6e..1bb78c6424 100644 --- a/module/actor/sheets/character.js +++ b/module/actor/sheets/character.js @@ -230,6 +230,21 @@ export class ActorSheet5eCharacter extends ActorSheet5e { // Short and Long Rest html.find('.short-rest').click(this._onShortRest.bind(this)); html.find('.long-rest').click(this._onLongRest.bind(this)); + + // Death saving throws + html.find('.death-save').click(this._onDeathSave.bind(this)); + } + + /* -------------------------------------------- */ + + /** + * Handle rolling a death saving throw for the Character + * @param {MouseEvent} event The originating click event + * @private + */ + _onDeathSave(event) { + event.preventDefault(); + return this.actor.rollDeathSave({event: event}); } /* -------------------------------------------- */ diff --git a/templates/actors/character-sheet.html b/templates/actors/character-sheet.html index 117801cabd..c8de6c2d8e 100644 --- a/templates/actors/character-sheet.html +++ b/templates/actors/character-sheet.html @@ -183,7 +183,7 @@

{{ localize "DND5E.Initiative" }}

{{!-- Counters --}}
-

{{ localize "DND5E.DeathSave" }}

+

{{ localize "DND5E.DeathSave" }}