').addClass('character-name').html(this.tokenname))
.append($('
').addClass('token-stats flexrow').attr('title', this.tokentooltip).html((this.tokentemp ? `
${this.tokentemp}
` : '') + (this.tokenstat ? `
${this.tokenstat}
` : '')));
@@ -108,77 +105,67 @@ export class AlwaysHP extends Application {
break;
}
}
- let entities = canvas.tokens.controlled.flatMap((t) => {
+
+ let actors = canvas.tokens.controlled.flatMap((t) => {
if (t.actor?.type == "group") {
return Array.from(t.actor?.system.members);
} else
- return t;
+ return t.actor;
});
- for (let e of entities) {
- let a = e instanceof Token ? e.actor : e;
- let t = e instanceof Actor ? e.token : e;
-
- if (!a)
+ for (let a of actors) {
+ if (!a || !(a instanceof Actor))
continue;
- let tValue = duplicate(value);
+ let tValue = foundry.utils.duplicate(value);
- let dataname = (isV10() ? "system." : "data.");
let resourcename = (setting("resourcename") || (game.system?.primaryTokenAttribute ?? game.data?.primaryTokenAttribute) || 'attributes.hp');
- let resource = getProperty(isV10() ? a : a.data, dataname + resourcename);
+ let resource = foundry.utils.getProperty(a, `system.${resourcename}`);
if (tValue.value == 'zero')
tValue.value = this.getResValue(resource, "value", resource) + this.getResValue(resource, "temp");
if (value.value == 'full')
tValue.value = (resource instanceof Object ? resource.value - resource.max : resource);
- let defeatedStatus = (isV10() ? CONFIG.specialStatusEffects.DEFEATED : CONFIG.Combat.defeatedStatusId);
+ let defeatedStatus = CONFIG.specialStatusEffects.DEFEATED;
if (active != undefined && setting("add-defeated")) {
let status = CONFIG.statusEffects.find(e => e.id === defeatedStatus);
let effect = game.system.id == "pf2e" ? game.settings.get("pf2e", "deathIcon") : a && status ? status : CONFIG.controlIcons.defeated;
- if (t) {
- let overlay = (isV10() ? t.document.overlayEffect : t.data.overlayEffect);
- const exists = (effect.icon == undefined ? overlay == effect : a.statuses.has(effect.id));
+ const exists = a.statuses.has(effect.id);
- if (exists != active)
- await t.toggleEffect(effect, { overlay: true, active: (active == 'toggle' ? !exists : active) });
- }
+ if (exists != active)
+ await a.toggleStatusEffect(effect.id, { active: (active == 'toggle' ? !exists : active) });
}
if (active === false && setting("clear-savingthrows")) {
- a.update(isV10()
- ? { "system.attributes.death.failure": 0, "system.attributes.death.success": 0 }
- : { "data.attributes.death.failure": 0, "data.attributes.death.success": 0 });
+ a.update({
+ "system.attributes.death.failure": 0,
+ "system.attributes.death.success": 0
+ });
}
log('applying damage', a, tValue);
if (tValue.value != 0) {
- //if (game.system.id == "dnd5e" && setting("resourcename") == 'attributes.hp') {
- // await a.applyDamage(value);
- //} else {
- await this.applyDamage(a, t, tValue);
- //}
+ await this.applyDamage(a, tValue);
}
};
this.refreshSelected();
}
- async applyDamage(actor, token, amount, multiplier = 1) {
+ async applyDamage(actor, amount, multiplier = 1) {
let { value, target } = amount;
let updates = {};
- let dataname = (isV10() ? "system." : "data.");
let resourcename = (setting("resourcename") || (game.system?.primaryTokenAttribute ?? game.data?.primaryTokenAttribute) || 'attributes.hp');
- let resource = getProperty((isV10() ? actor : actor.data), dataname + resourcename);
+ let resource = foundry.utils.getProperty(actor, `system.${resourcename}`);
if (resource instanceof Object) {
value = Math.floor(parseInt(value) * multiplier);
// Deduct damage from temp HP first
if (resource.hasOwnProperty("tempmax") && target == "max") {
const dm = (resource.tempmax ?? 0) - value;
- updates[dataname + resourcename + ".tempmax"] = dm;
+ updates[`system.${resourcename}.tempmax`] = dm;
} else {
let dt = 0;
let tmpMax = 0;
@@ -189,43 +176,19 @@ export class AlwaysHP extends Application {
tmpMax = parseInt(resource.tempmax) || 0;
- updates[dataname + resourcename + ".temp"] = tmp - dt;
+ updates[`system.${resourcename}.temp`] = tmp - dt;
}
// Update the Actor
if (target != 'temp' && target != 'max' && dt >= 0) {
let change = (value - dt);
- const dh = Math.clamped(resource.value - change, (game.system.id == 'D35E' || game.system.id == 'pf1' ? -2000 : 0), resource.max + tmpMax);
- updates[dataname + resourcename + ".value"] = dh;
-
- if (token) {
- if (isV10()) {
- let display = change + dt;
- canvas.interface.createScrollingText(token.center, (-display).signedString(), {
- anchor: CONST.TEXT_ANCHOR_POINTS.CENTER,
- direction: display > 0 ? CONST.TEXT_ANCHOR_POINTS.BOTTOM : CONST.TEXT_ANCHOR_POINTS.TOP,
- distance: token.h,
- fontSize: 28,
- stroke: 0x000000,
- strokeThickness: 4,
- jitter: 0.25
- });
- } else {
- token.hud.createScrollingText((-change).signedString(), {
- anchor: CONST.TEXT_ANCHOR_POINTS.TOP,
- fontSize: 32,
- fill: (change > 0 ? 16711680 : 65280),
- stroke: 0x000000,
- strokeThickness: 4,
- jitter: 0.25
- });
- }
- }
+ const dh = Math.clamp(resource.value - change, (game.system.id == 'D35E' || game.system.id == 'pf1' ? -2000 : 0), resource.max + tmpMax);
+ updates[`system.${resourcename}.value`] = dh;
}
}
} else {
let val = Math.floor(parseInt(resource));
- updates[dataname + resourcename] = (val - value);
+ updates[`system.${resourcename}`] = (val - value);
}
return await actor.update(updates);
@@ -250,7 +213,6 @@ export class AlwaysHP extends Application {
this.tokenstat = "";
this.tokentemp = "";
this.tokentooltip = "";
- let dataname = (isV10() ? "system." : "data.");
$('.character-name', this.element).removeClass("single");
if (canvas.tokens?.controlled.length == 0)
this.tokenname = "";
@@ -261,7 +223,7 @@ export class AlwaysHP extends Application {
else {
$('.character-name', this.element).addClass("single");
let resourcename = setting("resourcename");
- let resource = getProperty((isV10() ? a : a.data), dataname + resourcename);
+ let resource = foundry.utils.getProperty(a, `system.${resourcename}`);
let value = this.getResValue(resource, "value", resource);
let max = this.getResValue(resource, "max");
@@ -274,8 +236,8 @@ export class AlwaysHP extends Application {
let displayMax = max + (tempmax > 0 ? tempmax : 0);
// Allocate percentages of the total
- const tempPct = Math.clamped(temp, 0, displayMax) / displayMax;
- const valuePct = Math.clamped(value, 0, effectiveMax) / displayMax;
+ const tempPct = Math.clamp(temp, 0, displayMax) / displayMax;
+ const valuePct = Math.clamp(value, 0, effectiveMax) / displayMax;
this.valuePct = valuePct;
this.tempPct = tempPct;
@@ -301,12 +263,11 @@ export class AlwaysHP extends Application {
if (!a)
return;
- let prop = (isV10() ? a.system.attributes.death : a.data.data.attributes.death);
+ let prop = a.system.attributes.death;
prop[save ? 'success' : 'failure'] = Math.max(0, Math.min(3, prop[save ? 'success' : 'failure'] + value));
- let dataname = (isV10() ? "system." : "data.");
let updates = {};
- updates[dataname + "attributes.death." + (save ? 'success' : 'failure')] = prop[save ? 'success' : 'failure'];
+ updates["system.attributes.death." + (save ? 'success' : 'failure')] = prop[save ? 'success' : 'failure'];
canvas.tokens.controlled[0].actor.update(updates);
this.changeToken();
@@ -318,7 +279,7 @@ export class AlwaysHP extends Application {
$('.token-stats', this.element).attr('title', this.tokentooltip).html((this.tokentemp ? `
${this.tokentemp}
` : '') + (this.tokenstat ? `
${this.tokenstat}
` : ''));
let actor = (canvas.tokens.controlled.length == 1 ? canvas.tokens.controlled[0].actor : null);
- let data = (isV10() ? actor?.system : actor?.data?.data);
+ let data = actor?.system;
let showST = (actor != undefined && game.system.id == "dnd5e" && data?.attributes?.hp?.value == 0 && actor?.hasPlayerOwner && setting("show-savingthrows"));
$('.death-savingthrow', this.element).css({ display: (showST ? 'inline-block' : 'none') });
if (showST && data.attributes.death) {
@@ -368,9 +329,8 @@ export class AlwaysHP extends Application {
if (!actor)
return;
- let dataname = (isV10() ? "system." : "data.");
let resourcename = (setting("resourcename") || (game.system.primaryTokenAttribute ?? game.system.data.primaryTokenAttribute) || 'attributes.hp');
- let resource = getProperty(actor, dataname + resourcename);
+ let resource = foundry.utils.getProperty(actor, `system.${resourcename}`);
if (resource.hasOwnProperty("max")) {
let max = this.getResValue(resource, "max");
@@ -537,6 +497,19 @@ Hooks.on('init', () => {
},
});
+ game.keybindings.register('always-hp', 'focus-key', {
+ name: 'ALWAYSHP.focus-key.name',
+ hint: 'ALWAYSHP.focus-key.hint',
+ editable: [],
+ onDown: () => {
+ if (!game.AlwaysHP.app)
+ game.AlwaysHP.app = new AlwaysHP().render(true);
+ else
+ game.AlwaysHP.app.bringToTop();
+ $('#alwayshp-hp', game.AlwaysHP.app.element).focus();
+ },
+ });
+
game.AlwaysHP = {
app: null,
toggleApp: (show = 'toggle') => {
@@ -555,6 +528,12 @@ Hooks.on('init', () => {
});
Hooks.on('ready', () => {
+ let r = document.querySelector(':root');
+ r.style.setProperty('--ahp-heal-dark', setting("heal-dark"));
+ r.style.setProperty('--ahp-heal-light', setting("heal-light"));
+ r.style.setProperty('--ahp-hurt-dark', setting("hurt-dark"));
+ r.style.setProperty('--ahp-hurt-light', setting("hurt-light"));
+
if ((setting("show-option") == 'on' || (setting("show-option") == 'toggle' && setting("show-dialog"))) && (setting("load-option") == 'everyone' || (setting("load-option") == 'gm' == game.user.isGM)))
game.AlwaysHP.toggleApp(true);
@@ -591,10 +570,9 @@ Hooks.on('controlToken', () => {
Hooks.on('updateActor', (actor, data) => {
//log('Updating actor', actor, data);
- let dataname = (isV10() ? "system." : "data.");
if (canvas.tokens.controlled.length == 1
&& canvas.tokens.controlled[0]?.actor?.id == actor.id
- && (getProperty(data, dataname + "attributes.death") != undefined || getProperty(data, dataname + setting("resourcename")))) {
+ && (foundry.utils.getProperty(data, "system.attributes.death") != undefined || foundry.utils.getProperty(data, `system.${setting("resourcename") }`))) {
game.AlwaysHP.refresh();
}
});
@@ -631,3 +609,15 @@ Hooks.on("getSceneControlButtons", (controls) => {
});
}
});
+
+Hooks.on("renderSettingsConfig", (app, html, user) => {
+ $("input[name='always-hp.heal-dark']", html).replaceWith(`
+
+
+ `);
+ $("input[name='always-hp.hurt-dark']", html).replaceWith(`
+
+
+ `);
+});
+
diff --git a/css/alwayshp.css b/css/alwayshp.css
index 0719027..751d05d 100644
--- a/css/alwayshp.css
+++ b/css/alwayshp.css
@@ -1,3 +1,10 @@
+:root {
+ --ahp-heal-light: #15838d;
+ --ahp-heal-dark: #4dd0e1;
+ --ahp-hurt-light: #ff6400;
+ --ahp-hurt-dark: #ff0000;
+}
+
#alwayshp-container {
height: auto;
width: 300px;
@@ -75,13 +82,27 @@
}
.death-savingthrow > div.active {
- border: 1px solid red;
- background-color: #ff6400;
+ border: 1px solid var(--ahp-hurt-dark);
+ background-color: var(--ahp-hurt-light);
+}
+
+.death-savingthrow > div.active:after {
+ content: "";
+ display: block;
+ width: 6px;
+ height: 6px;
+ margin: 2px;
+ border-radius: 100%;
+ background-color: var(--ahp-hurt-dark);
}
.death-savingthrow.save > div.active {
- border: 1px solid #15838d;
- background-color: rgb(77, 208, 225);
+ border: 1px solid var(--ahp-heal-light);
+ background-color: var(--ahp-heal-dark);
+}
+
+.death-savingthrow.save > div.active:after {
+ background-color: var(--ahp-heal-light);
}
#alwayshp-btn-fullheal {
@@ -92,24 +113,22 @@
padding-top:6px;
}
-.bad-ones{
-
- border: 1px solid red;
- border-bottom: 1px solid #ff6400;
- box-shadow: 0 0 10px #ff6400;
+.bad-ones {
+ border: 1px solid var(--ahp-hurt-dark);
+ box-shadow: 0 0 10px var(--ahp-hurt-light);
}
.bad-ones i {
- color: red;
+ color: var(--ahp-hurt-dark);
}
.good-ones {
- border: 1px solid rgb(77,208,225);
- box-shadow: 0 0 10px rgb(77, 208, 225);
+ border: 1px solid var(--ahp-heal-dark);
+ box-shadow: 0 0 10px var(--ahp-heal-dark);
}
.good-ones i {
- color: rgb(77, 208, 225);
+ color: var(--ahp-heal-dark);
}
#always-hp .window-content {
diff --git a/lang/en.json b/lang/en.json
index aa15701..ece1203 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -43,6 +43,12 @@
"ALWAYSHP.show-savingthrows.hint": "Show death saving throws on the always HP display",
"ALWAYSHP.toggle-key.name": "Toggle Key",
"ALWAYSHP.toggle-key.hint": "Toggle Always HP on and off with a key press",
+ "ALWAYSHP.heal-color.name": "Heal Colors",
+ "ALWAYSHP.heal-color.hint": "Color of the heal buttons, foreground and background",
+ "ALWAYSHP.hurt-color.name": "Hurt Colors",
+ "ALWAYSHP.hurt-color.hint": "Color of the hurt buttons, foreground and background",
+ "ALWAYSHP.focus-key.name": "Focus Key",
+ "ALWAYSHP.focus-key.hint": "Focus on the Always HP input field",
"ALWAYSHP.DeathSavingThrowFail": "Death Savingthrow, Fail",
"ALWAYSHP.DeathSavingThrowPass": "Death Savingthrow, Pass"
diff --git a/module.json b/module.json
index a8ab975..f64f379 100644
--- a/module.json
+++ b/module.json
@@ -1,7 +1,7 @@
{
"title": "Always HP",
"description": "We're always updating HP, add a window to make it easier to adjust that",
- "version": "11.07",
+ "version": "12.01",
"authors": [
{
"name": "IronMonk",
@@ -47,16 +47,16 @@
"css/alwayshp.css"
],
"url": "https://github.com/ironmonk88/always-hp",
- "download": "https://github.com/ironmonk88/always-hp/archive/11.07.zip",
+ "download": "https://github.com/ironmonk88/always-hp/archive/12.01.zip",
"manifest": "https://github.com/ironmonk88/always-hp/releases/latest/download/module.json",
"bugs": "https://github.com/ironmonk88/always-hp/issues",
"allowBugReporter": true,
"id": "always-hp",
"compatibility": {
- "minimum": "11",
- "verified": "11"
+ "minimum": "12",
+ "verified": "12"
},
"name": "always-hp",
- "minimumCoreVersion": "11",
- "compatibleCoreVersion": "11"
+ "minimumCoreVersion": "12",
+ "compatibleCoreVersion": "12"
}
\ No newline at end of file
diff --git a/modules/settings.js b/settings.js
similarity index 73%
rename from modules/settings.js
rename to settings.js
index de77e46..7f407db 100644
--- a/modules/settings.js
+++ b/settings.js
@@ -1,4 +1,4 @@
-import { i18n, setting } from "../alwayshp.js";
+import { i18n, setting } from "./alwayshp.js";
export const registerSettings = function () {
let modulename = "always-hp";
@@ -70,7 +70,7 @@ export const registerSettings = function () {
default: false,
type: Boolean,
config: true
- });
+ });
game.settings.register(modulename, "add-defeated", {
name: i18n("ALWAYSHP.add-defeated.name"),
@@ -126,6 +126,54 @@ export const registerSettings = function () {
config: true
});
+ game.settings.register(modulename, "heal-dark", {
+ name: i18n("ALWAYSHP.heal-color.name"),
+ hint: i18n("ALWAYSHP.heal-color.hint"),
+ scope: "client",
+ default: "",
+ type: String,
+ config: true,
+ onChange: (value) => {
+ let r = document.querySelector(':root');
+ r.style.setProperty('--ahp-heal-dark', value || '#4dd0e1');
+ },
+ });
+
+ game.settings.register(modulename, "heal-light", {
+ scope: "client",
+ default: "",
+ type: String,
+ config: false,
+ onChange: (value) => {
+ let r = document.querySelector(':root');
+ r.style.setProperty('--ahp-heal-light', value || '#15838d');
+ },
+ });
+
+ game.settings.register(modulename, "hurt-dark", {
+ name: i18n("ALWAYSHP.hurt-color.name"),
+ hint: i18n("ALWAYSHP.hurt-color.hint"),
+ scope: "client",
+ default: "",
+ type: String,
+ config: true,
+ onChange: (value) => {
+ let r = document.querySelector(':root');
+ r.style.setProperty('--ahp-hurt-dark', value || '#ff0000');
+ },
+ });
+
+ game.settings.register(modulename, "hurt-light", {
+ scope: "client",
+ default: "",
+ type: String,
+ config: false,
+ onChange: (value) => {
+ let r = document.querySelector(':root');
+ r.style.setProperty('--ahp-hurt-light', value || '#ff6400');
+ },
+ });
+
/*
game.settings.register(modulename, "gm-only", {
name: i18n("ALWAYSHP.gm-only.name"),