diff --git a/CHANGELOG.md b/CHANGELOG.md index 21d5b577..e3709b7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Version 4.0.6 (So far...) [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt) - Migrations are no longer supported from versions before 3.0.76. Migrate through any version prior to 4.0.6 if you find your world in this situation. +- Initial support for Only In Heroic ID. There is a checkbox on the OTHER tab. Powers/skills with OIHID will be disabled when not in heroic ID. (#232)(https://github.com/dmdorman/hero6e-foundryvtt/issues/232) ## Version 4.0.5 diff --git a/module/actor/actor-sheet.mjs b/module/actor/actor-sheet.mjs index ea36e66f..9b0a8406 100644 --- a/module/actor/actor-sheet.mjs +++ b/module/actor/actor-sheet.mjs @@ -36,6 +36,7 @@ export class HeroSystemActorSheet extends ActorSheet { /** @override */ async getData() { const data = super.getData(); + data.system = data.actor.system; try { // Show an unsupported actor warning when the sheet opens. An actor can be unsupported if: @@ -267,14 +268,6 @@ export class HeroSystemActorSheet extends ActorSheet { ); await pdAttack._postUpload(); - let { - defenseValue: _defenseValuePD, - resistantValue: _resistantValuePD /*impenetrableValue*/, - //damageReductionValue: _damageReductionValuePD, - //damageNegationValue: _damageNegationValuePD /*knockbackResistance*/, - // defenseTags: defenseTagsP, - } = determineDefense(this.actor, pdAttack, { suppressDeprecationWarn: true }); - // New PD const { defenseValue: defenseValuePD, @@ -283,12 +276,6 @@ export class HeroSystemActorSheet extends ActorSheet { damageNegationValue: damageNegationValuePD, defenseTags: defenseTagsPD, } = getActorDefensesVsAttack(this.actor, pdAttack); - if (_defenseValuePD != defenseValuePD) { - console.warn("PD Defense mismatch", _defenseValuePD, defenseValuePD); - } - if (_resistantValuePD != resistantValuePD) { - console.warn("rPD Defense mismatch", _resistantValuePD, resistantValuePD); - } defense.PD = defenseValuePD; for (const tag of defenseTagsPD.filter((o) => o.operation === "add" && !o.options?.resistant)) { defense.PDtags = `${defense.PDtags || ""}${tag.value.signedString()} ${tag.name} ${tag.shortDesc}\n`; @@ -317,14 +304,6 @@ export class HeroSystemActorSheet extends ActorSheet { ); await edAttack._postUpload(); - let { - defenseValue: _defenseValueED, - resistantValue: _resistantValueED /* impenetrableValueE */, - //damageReductionValue: _damageReductionValueED, - //damageNegationValue: _damageNegationValueED /* knockbackResistanceE */, - // defenseTags: _defenseTagsE, - } = determineDefense(this.actor, edAttack, { suppressDeprecationWarn: true }); - // New ED const { defenseValue: defenseValueED, @@ -333,12 +312,6 @@ export class HeroSystemActorSheet extends ActorSheet { damageNegationValue: damageNegationValueED, defenseTags: defenseTagsED, } = getActorDefensesVsAttack(this.actor, edAttack); - if (_defenseValueED != defenseValueED) { - console.warn("ED Defense mismatch", _defenseValueED, defenseValueED); - } - if (_resistantValueED != resistantValueED) { - console.warn("rED Defense mismatch", _defenseValueED, defenseValueED); - } defense.ED = defenseValueED; for (const tag of defenseTagsED.filter((o) => o.operation === "add" && !o.options?.resistant)) { defense.EDtags = `${defense.EDtags || ""}${tag.value.signedString()} ${tag.name} ${tag.shortDesc}\n`; @@ -368,14 +341,6 @@ export class HeroSystemActorSheet extends ActorSheet { ); await mdAttack._postUpload(); - let { - defenseValue: _defenseValueMD, - //resistantValue: _resistantValueMD /*impenetrableValueM*/, - //damageReductionValue: _damageReductionValueMD, - //damageNegationValue: _damageNegationValueMD /*knockbackResistanceM*/, - defenseTags: _defenseTagsMD, - } = determineDefense(this.actor, mdAttack, { suppressDeprecationWarn: true }); - // New MD const { defenseValue: defenseValueMD, @@ -384,9 +349,6 @@ export class HeroSystemActorSheet extends ActorSheet { damageNegationValue: damageNegationValueMD, defenseTags: defenseTagsMD, } = getActorDefensesVsAttack(this.actor, mdAttack); - if (_defenseValueMD != defenseValueMD) { - console.warn("MD Defense mismatch", _defenseValueMD, defenseValueMD, _defenseTagsMD); - } defense.MD = defenseValueMD; for (const tag of defenseTagsMD.filter((o) => o.operation === "add" && !o.options?.resistant)) { defense.MDtags = `${defense.MDtags || ""}${tag.value.signedString()} ${tag.name} ${tag.shortDesc}\n`; @@ -461,7 +423,7 @@ export class HeroSystemActorSheet extends ActorSheet { (o) => (o.system.subType || o.type) === "defense" && !o.effects.size, ); for (let d of defensePowers) { - d.disabled = !d.system.active; + d.disabled = !d.isActive; switch (getPowerInfo({ xmlid: d.system.XMLID, actor: this.actor })?.duration) { case "instant": // Might Vary @@ -849,9 +811,12 @@ export class HeroSystemActorSheet extends ActorSheet { } /** @override */ - async _updateObject(_event, formData) { + async _updateObject(event, formData) { + event.preventDefault(); + let expandedData = foundry.utils.expandObject(formData); + // Left Sidebar of actor sheet has Xsystem characteristics const characteristics = getCharacteristicInfoArrayForActor(this.actor).filter((o) => ["BODY", "STUN", "END"].includes(o.key), ); @@ -867,7 +832,7 @@ export class HeroSystemActorSheet extends ActorSheet { } } - // EndReserve + // Left Sidebar may have EndReserve if (expandedData.endReserve) { const endReserveId = Object.keys(expandedData.endReserve)?.[0]; const endReserve = this.actor.items.find((o) => o.id === endReserveId); @@ -875,20 +840,28 @@ export class HeroSystemActorSheet extends ActorSheet { await endReserve.update({ "system.value": parseInt(expandedData.endReserve[endReserveId].value || 0) }); } } - console.log(formData); this.options.itemFilters.power = expandedData.itemFilters.power; this.options.itemFilters.skill = expandedData.itemFilters.skill; this.options.itemFilters.equipment = expandedData.itemFilters.equipment; - await this.actor.update(expandedData); + // If core characteristics changed the re-calculate costs + let recalculateCosts = false; + for (const char of Object.keys(expandedData.system.characteristics)) { + if (this.actor.system.characteristics[char].core !== expandedData.system.characteristics[char].core) { + recalculateCosts = true; + } + } + + // Do all the standard things like updating item properties that match the name of input boxes + await super._updateObject(event, formData); - if (expandedData.system.characteristics) { + if (recalculateCosts) { await this.actor.calcCharacteristicsCost(); await this.actor.CalcActorRealAndActivePoints(); } - this.render(); + await this.render(); } async _onItemRoll(event) { diff --git a/module/actor/actor.mjs b/module/actor/actor.mjs index ef2124ca..62e7a2e9 100644 --- a/module/actor/actor.mjs +++ b/module/actor/actor.mjs @@ -276,6 +276,42 @@ export class HeroSystem6eActor extends Actor { await this.update(changes); } + // Heroic ID + if (data.system?.heroicIdentity !== undefined) { + // Loop thru all the active effects, checking if source has OIHID + const allEffects = await this.allApplicableEffects(); + for (const ae of allEffects) { + const item = ae.parent; + if (!item) continue; + if (item instanceof HeroSystem6eItem === false) continue; + if (item.findModsByXmlid("OIHID")) { + await ae.update({ disabled: !data.system.heroicIdentity }); + + // Modify characteristics as appropriate + for (const change of ae.changes) { + if (change.key.match(/max$/) && change.mode === 2 && change.value > 0) { + const valueKey = change.key.replace(/.max$/, ".value"); + let max = this; + valueKey.split(".").forEach((subPath) => { + max = max[subPath] || null; + }); + if (parseInt(max || 0) > 0) { + if (ae.disabled) { + await this.update({ + [valueKey]: max - parseInt(change.value), + }); + } else { + await this.update({ + [valueKey]: max + parseInt(change.value), + }); + } + } + } + } + } + } + } + // Display changes from _preUpdate for (let d of options.displayScrollingChanges) { this._displayScrollingChange(d.value, d.options); @@ -804,7 +840,7 @@ export class HeroSystem6eActor extends Actor { (o) => o.system.XMLID === "PENALTY_SKILL_LEVELS" && o.system.penalty === "encumbrance" && - (o.type === "skill" || o.system.active), + (o.type === "skill" || o.isActive), )) { dcvDex = Math.min(0, dcvDex + parseInt(pslEncumbrance.system.LEVELS)); } @@ -922,7 +958,7 @@ export class HeroSystem6eActor extends Actor { // 0 on movement and DCV occur 5 points of STR // sooner. const massMultiplier = this.items - .filter((o) => o.system.XMLID === "DENSITYINCREASE" && o.system.active) + .filter((o) => o.system.XMLID === "DENSITYINCREASE" && o.isActive) .reduce((p, a) => p + parseInt(a.system.LEVELS), 0); const minStr = massMultiplier * 5; @@ -1233,7 +1269,7 @@ export class HeroSystem6eActor extends Actor { getActiveConstantItems() { let results = []; - for (let item of this.items.filter((o) => o.system.active)) { + for (let item of this.items.filter((o) => o.isActive)) { let duration = getPowerInfo({ xmlid: item.system.XMLID, actor: this, @@ -1999,6 +2035,13 @@ export class HeroSystem6eActor extends Actor { await this.update({ [`system.is5e`]: this.system.is5e }); } + // ONLY IN ALTERNATE IDENTITY (OIAID) + // Assume we are in our super/heroic identity + if (this.system.heroicIdentity === undefined) { + this.system.heroicIdentity = false; + changes[`system.heroicIdentity`] = true; + } + // isHeroic // Need to be a careful as there are custom templates ('Nekhbet Vulture Child Goddess') // that we are unlikely able to decode heroic status. @@ -2295,7 +2338,7 @@ export class HeroSystem6eActor extends Actor { // Hero Designer appears to store WEIGHT as LBS instead of KG. const equipment = this.items.filter( - (o) => o.type === "equipment" && (o.parentItem ? o.parentItem.system.active : o.system.active), + (o) => o.type === "equipment" && (o.parentItem ? o.parentItem.isActive : o.isActive), ); const weightLbs = equipment.reduce((a, b) => a + parseFloat(b.system?.WEIGHT || 0), 0); const weightKg = (weightLbs / 2.2046226218) * equipmentWeightPercentage; @@ -2304,7 +2347,7 @@ export class HeroSystem6eActor extends Actor { } get netWorth() { - const equipment = this.items.filter((o) => o.type === "equipment" && o.system.active); + const equipment = this.items.filter((o) => o.type === "equipment" && o.isActive); const price = equipment.reduce((a, b) => a + parseFloat(b.system.PRICE), 0); return price.toFixed(2); } diff --git a/module/combat.mjs b/module/combat.mjs index cf31c31d..074a9b0f 100644 --- a/module/combat.mjs +++ b/module/combat.mjs @@ -489,7 +489,7 @@ export class HeroSystem6eCombat extends Combat { for (const powerUsingResourcesToContinue of combatant.actor.items.filter( (item) => - item.system.active === true && // Is the power active? + item.isActive === true && // Is the power active? item.baseInfo.duration !== "instant" && // Is the power non instant ((parseInt(item.system.end || 0) > 0 && // Does the power use END? !item.system.MODIFIER?.find((o) => o.XMLID === "COSTSEND" && o.OPTION === "ACTIVATE")) || // Does the power use END continuously? @@ -541,7 +541,11 @@ export class HeroSystem6eCombat extends Combat { if (content !== "" && (spentResources.totalEnd > 0 || spentResources.charges > 0)) { const segment = this.combatant.flags.segment; - content = `Spent ${spentResources.end} END, ${spentResources.reserveEnd} reserve END, and ${spentResources.charges} charge${spentResources.charges > 1 ? "s" : ""} on turn ${this.round} segment ${segment}: