forked from GitHub-Mirrors/foundry-sw5e
Flesh out New Dice structure - Not complete
add structure for Hull, Shield, and Power Dice to allow for recharge, refitting, and regeneration. Not complete.
This commit is contained in:
parent
0607152f51
commit
cacc43740e
8 changed files with 546 additions and 88 deletions
|
@ -317,13 +317,13 @@ export default class Actor5e extends Actor {
|
|||
const data = actorData.data;
|
||||
|
||||
// Proficiency
|
||||
data.attributes.prof = Math.floor((Math.max(data.details.tier, 1) + 7) / 4);
|
||||
data.attributes.prof = Math.floor((Math.max(data.details.tier, 1) + 7) / 4);
|
||||
|
||||
// Link hull to hp and shields to temp hp
|
||||
data.attributes.hull.value = data.attributes.hp.value;
|
||||
data.attributes.hull.max = data.attributes.hp.max;
|
||||
data.attributes.shld.value = data.attributes.hp.temp;
|
||||
data.attributes.shld.max = data.attributes.hp.tempmax;
|
||||
//data.attributes.hull.value = data.attributes.hp.value;
|
||||
//data.attributes.hull.max = data.attributes.hp.max;
|
||||
//data.attributes.shld.value = data.attributes.hp.temp;
|
||||
//data.attributes.shld.max = data.attributes.hp.tempmax;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -1147,6 +1147,196 @@ export default class Actor5e extends Actor {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Roll a hull die of the appropriate type, gaining hull points equal to the die roll plus your CON modifier
|
||||
* @param {string} [denomination] The hit denomination of hull die to roll. Example "d8".
|
||||
* If no denomination is provided, the first available HD will be used
|
||||
* @param {string} [numDice] How many damage dice to roll?
|
||||
* @param {string} [keep] Which dice to keep? Example "kh1".
|
||||
* @param {boolean} [dialog] Show a dialog prompt for configuring the hull die roll?
|
||||
* @return {Promise<Roll|null>} The created Roll instance, or null if no hull die was rolled
|
||||
*/
|
||||
async rollHullDie(denomination, numDice="1", keep="",{dialog=true}={}) {
|
||||
|
||||
// If no denomination was provided, choose the first available
|
||||
let sship = null;
|
||||
if ( !denomination ) {
|
||||
sship = this.itemTypes.class.find(s => s.data.data.hullDiceUsed < (s.data.data.tier + s.data.data.hullDiceStart));
|
||||
if ( !sship ) return null;
|
||||
denomination = sship.data.data.hullDice;
|
||||
}
|
||||
|
||||
// Otherwise locate a starship (if any) which has an available hit die of the requested denomination
|
||||
else {
|
||||
sship = this.items.find(i => {
|
||||
const d = i.data.data;
|
||||
return (d.hullDice === denomination) && ((d.hitDiceUsed || 0) < ((d.tier || 0) + d.hullDiceStart));
|
||||
});
|
||||
}
|
||||
|
||||
// If no class is available, display an error notification
|
||||
if ( !sship ) {
|
||||
ui.notifications.error(game.i18n.format("SW5E.HullDiceWarn", {name: this.name, formula: denomination}));
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prepare roll data
|
||||
const parts = [`${numDice}${denomination}${keep}`, "@abilities.con.mod"];
|
||||
const title = game.i18n.localize("SW5E.HullDiceRoll");
|
||||
const rollData = duplicate(this.data.data);
|
||||
|
||||
// Call the roll helper utility
|
||||
const roll = await damageRoll({
|
||||
event: new Event("hitDie"),
|
||||
parts: parts,
|
||||
data: rollData,
|
||||
title: title,
|
||||
speaker: ChatMessage.getSpeaker({actor: this}),
|
||||
allowcritical: false,
|
||||
fastForward: !dialog,
|
||||
dialogOptions: {width: 350},
|
||||
messageData: {"flags.sw5e.roll": {type: "hullDie"}}
|
||||
});
|
||||
if ( !roll ) return null;
|
||||
|
||||
// Adjust actor data
|
||||
await sship.update({"data.hullDiceUsed": sship.data.data.hullDiceUsed + 1});
|
||||
const hp = this.data.data.attributes.hp;
|
||||
const dhp = Math.min(hp.max - hp.value, roll.total);
|
||||
await this.update({"data.attributes.hp.value": hp.value + dhp});
|
||||
return roll;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Roll a hull die of the appropriate type, gaining hull points equal to the die roll plus your CON modifier
|
||||
* @return {Promise<Roll|null>} The created Roll instance, or null if no hull die was rolled
|
||||
*/
|
||||
async rollHullDieCheck() {
|
||||
|
||||
// If no denomination was provided, choose the first available
|
||||
let sship = null;
|
||||
if ( !denomination ) {
|
||||
sship = this.itemTypes.class.find(s => s.data.data.hullDiceUsed < (s.data.data.tier + s.data.data.hullDiceStart));
|
||||
if ( !sship ) return null;
|
||||
denomination = sship.data.data.hullDice;
|
||||
}
|
||||
|
||||
// Otherwise locate a starship (if any) which has an available hit die of the requested denomination
|
||||
else {
|
||||
sship = this.items.find(i => {
|
||||
const d = i.data.data;
|
||||
return (d.hullDice === denomination) && ((d.hitDiceUsed || 0) < ((d.tier || 0) + d.hullDiceStart));
|
||||
});
|
||||
}
|
||||
|
||||
// If no class is available, display an error notification
|
||||
if ( !sship ) {
|
||||
ui.notifications.error(game.i18n.format("SW5E.HullDiceWarn", {name: this.name, formula: denomination}));
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prepare roll data
|
||||
const parts = [`${numDice}${denomination}${keep}`, "@abilities.con.mod"];
|
||||
const title = game.i18n.localize("SW5E.HullDiceRoll");
|
||||
const rollData = duplicate(this.data.data);
|
||||
|
||||
// Call the roll helper utility
|
||||
const roll = await damageRoll({
|
||||
event: new Event("hitDie"),
|
||||
parts: parts,
|
||||
data: rollData,
|
||||
title: title,
|
||||
speaker: ChatMessage.getSpeaker({actor: this}),
|
||||
allowcritical: false,
|
||||
fastForward: !dialog,
|
||||
dialogOptions: {width: 350},
|
||||
messageData: {"flags.sw5e.roll": {type: "hullDie"}}
|
||||
});
|
||||
if ( !roll ) return null;
|
||||
|
||||
// Adjust actor data
|
||||
await sship.update({"data.hullDiceUsed": sship.data.data.hullDiceUsed + 1});
|
||||
const hp = this.data.data.attributes.hp;
|
||||
const dhp = Math.min(hp.max - hp.value, roll.total);
|
||||
await this.update({"data.attributes.hp.value": hp.value + dhp});
|
||||
return roll;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Roll a shield die of the appropriate type, gaining shield points equal to the die roll
|
||||
* multiplied by the shield regeneration coefficient
|
||||
* @param {string} [denomination] The denomination of shield die to roll. Example "d8".
|
||||
* If no denomination is provided, the first available SD will be used
|
||||
* @param {boolean} [natural] Natural ship shield regeneration (true) or user action (false)?
|
||||
* @param {string} [numDice] How many damage dice to roll?
|
||||
* @param {string} [keep] Which dice to keep? Example "kh1".
|
||||
* @param {boolean} [dialog] Show a dialog prompt for configuring the shield die roll?
|
||||
* @return {Promise<Roll|null>} The created Roll instance, or null if no shield die was rolled
|
||||
*/
|
||||
async rollShieldDie(denomination, natural=false, numDice="1", keep="", {dialog=true}={}) {
|
||||
|
||||
// If no denomination was provided, choose the first available
|
||||
let sship = null;
|
||||
if ( !denomination ) {
|
||||
sship = this.itemTypes.class.find(s => s.data.data.shldDiceUsed < (s.data.data.tier + s.data.data.shldDiceStart));
|
||||
if ( !sship ) return null;
|
||||
denomination = sship.data.data.shldDice;
|
||||
}
|
||||
|
||||
// Otherwise locate a starship (if any) which has an available hit die of the requested denomination
|
||||
else {
|
||||
sship = this.items.find(i => {
|
||||
const d = i.data.data;
|
||||
return (d.shldDice === denomination) && ((d.shldDiceUsed || 0) < ((d.tier || 0) + d.shldDiceStart));
|
||||
});
|
||||
}
|
||||
|
||||
// If no starship is available, display an error notification
|
||||
if ( !sship ) {
|
||||
ui.notifications.error(game.i18n.format("SW5E.ShldDiceWarn", {name: this.name, formula: denomination}));
|
||||
return null;
|
||||
}
|
||||
|
||||
// if natural regeneration roll max
|
||||
if (natural) {
|
||||
numdice = denomination.substring(1);
|
||||
denomination = "";
|
||||
keep = "";
|
||||
}
|
||||
|
||||
// Prepare roll data
|
||||
const parts = [`${numDice}${denomination}${keep} * @attributes.regenRate`];
|
||||
const title = game.i18n.localize("SW5E.ShieldDiceRoll");
|
||||
const rollData = duplicate(this.data.data);
|
||||
|
||||
// Call the roll helper utility
|
||||
roll = await damageRoll({
|
||||
event: new Event("shldDie"),
|
||||
parts: parts,
|
||||
data: rollData,
|
||||
title: title,
|
||||
speaker: ChatMessage.getSpeaker({actor: this}),
|
||||
allowcritical: false,
|
||||
fastForward: !dialog,
|
||||
dialogOptions: {width: 350},
|
||||
messageData: {"flags.sw5e.roll": {type: "shldDie"}}
|
||||
});
|
||||
if ( !roll ) return null;
|
||||
|
||||
// Adjust actor data
|
||||
await sship.update({"data.shldDiceUsed": sship.data.data.shldDiceUsed + 1});
|
||||
const hp = this.data.data.attributes.hp;
|
||||
const dhp = Math.min(hp.tempmax - hp.temp, roll.total);
|
||||
await this.update({"data.attributes.hp.temp": hp.temp + dhp});
|
||||
return roll;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Cause this Actor to take a Short Rest and regain all Tech Points
|
||||
* During a Short Rest resources and limited item uses may be recovered
|
||||
|
|
117
module/apps/recharge-rest.js
Normal file
117
module/apps/recharge-rest.js
Normal file
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* A helper Dialog subclass for rolling Hit Dice on a recharge rest
|
||||
* @extends {Dialog}
|
||||
*/
|
||||
export default class RechargeRestDialog extends Dialog {
|
||||
constructor(actor, dialogData={}, options={}) {
|
||||
super(dialogData, options);
|
||||
|
||||
/**
|
||||
* Store a reference to the Actor entity which is resting
|
||||
* @type {Actor}
|
||||
*/
|
||||
this.actor = actor;
|
||||
|
||||
/**
|
||||
* Track the most recently used HD denomination for re-rendering the form
|
||||
* @type {string}
|
||||
*/
|
||||
this._denom = null;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return mergeObject(super.defaultOptions, {
|
||||
template: "systems/sw5e/templates/apps/recharge-rest.html",
|
||||
classes: ["sw5e", "dialog"]
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
getData() {
|
||||
const data = super.getData();
|
||||
|
||||
// Determine Hull Dice
|
||||
data.availableHD = this.actor.data.items.reduce((hd, item) => {
|
||||
if ( item.type === "starship" ) {
|
||||
const d = item.data;
|
||||
const denom = d.hullDice || "d6";
|
||||
const available = parseInt(d.hullDiceStart || 1) + parseInt(d.tier || 0) - parseInt(d.hullDiceUsed || 0);
|
||||
hd[denom] = denom in hd ? hd[denom] + available : available;
|
||||
}
|
||||
return hd;
|
||||
}, {});
|
||||
data.canRoll = this.actor.data.data.attributes.hull.dice > 0;
|
||||
data.denomination = this._denom;
|
||||
|
||||
// Determine rest type
|
||||
const variant = game.settings.get("sw5e", "restVariant");
|
||||
data.promptNewDay = variant !== "epic"; // It's never a new day when only resting 1 minute
|
||||
data.newDay = false; // It may be a new day, but not by default
|
||||
return data;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
||||
/** @override */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
let btn = html.find("#roll-hulld");
|
||||
btn.click(this._onRollHullDie.bind(this));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Handle rolling a Hull Die as part of a Recharge Rest action
|
||||
* @param {Event} event The triggering click event
|
||||
* @private
|
||||
*/
|
||||
async _onRollHullDie(event) {
|
||||
event.preventDefault();
|
||||
const btn = event.currentTarget;
|
||||
this._denom = btn.form.hulld.value;
|
||||
await this.actor.rollHullDie(this._denom);
|
||||
this.render();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* A helper constructor function which displays the Short Rest dialog and returns a Promise once it's workflow has
|
||||
* been resolved.
|
||||
* @param {Actor5e} actor
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async rechargeRestDialog({actor}={}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const dlg = new this(actor, {
|
||||
title: "Recharge Rest",
|
||||
buttons: {
|
||||
rest: {
|
||||
icon: '<i class="fas fa-bed"></i>',
|
||||
label: "Rest",
|
||||
callback: html => {
|
||||
let newDay = false;
|
||||
if (game.settings.get("sw5e", "restVariant") === "gritty")
|
||||
newDay = html.find('input[name="newDay"]')[0].checked;
|
||||
resolve(newDay);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
label: "Cancel",
|
||||
callback: reject
|
||||
}
|
||||
},
|
||||
close: reject
|
||||
});
|
||||
dlg.render(true);
|
||||
});
|
||||
}
|
||||
}
|
69
module/apps/refitting-rest.js
Normal file
69
module/apps/refitting-rest.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* A helper Dialog subclass for completing a refitting rest
|
||||
* @extends {Dialog}
|
||||
*/
|
||||
export default class RefittingRestDialog extends Dialog {
|
||||
constructor(actor, dialogData = {}, options = {}) {
|
||||
super(dialogData, options);
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return mergeObject(super.defaultOptions, {
|
||||
template: "systems/sw5e/templates/apps/refitting-rest.html",
|
||||
classes: ["sw5e", "dialog"]
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
getData() {
|
||||
const data = super.getData();
|
||||
const variant = game.settings.get("sw5e", "restVariant");
|
||||
data.promptNewDay = variant !== "gritty"; // It's always a new day when resting 1 week
|
||||
data.newDay = variant === "normal"; // It's probably a new day when resting normally (8 hours)
|
||||
return data;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* A helper constructor function which displays the Refitting Rest confirmation dialog and returns a Promise once it's
|
||||
* workflow has been resolved.
|
||||
* @param {Actor5e} actor
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async refittingRestDialog({ actor } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const dlg = new this(actor, {
|
||||
title: "Refitting Rest",
|
||||
buttons: {
|
||||
rest: {
|
||||
icon: '<i class="fas fa-bed"></i>',
|
||||
label: "Rest",
|
||||
callback: html => {
|
||||
let newDay = false;
|
||||
if (game.settings.get("sw5e", "restVariant") === "normal")
|
||||
newDay = html.find('input[name="newDay"]')[0].checked;
|
||||
else if(game.settings.get("sw5e", "restVariant") === "gritty")
|
||||
newDay = true;
|
||||
resolve(newDay);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
label: "Cancel",
|
||||
callback: reject
|
||||
}
|
||||
},
|
||||
default: 'rest',
|
||||
close: reject
|
||||
});
|
||||
dlg.render(true);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue