mrkwnzl / cyphersystem-foundryvtt

The Cypher System for Foundry VTT
Other
21 stars 14 forks source link

Auto-assign status effect on player's token when Impaired or Debilitated #317

Closed farling42 closed 1 year ago

farling42 commented 1 year ago

When an Actor becomes Impaired or Debilitated, then the corresponding status effect should automatically get applied to the actor's linked tokens on the current scene.

If the Impaired/Debilitated status of the Actor changes, then the corresponding status effects should have their active state set/cleared accordingly.

Having two predefined system-specific status would be good for this.

It also leads to the Automated Animations module being able to be configured to have a specific animation appear on the token while it is has the Impaired or Debilitated status.

It would also be a useful reminder to other players that the affected character isn't looking in a particularly good health.


New Status Effects can be added by:

CONFIG.statusEffects.push({icon: "icons/svg/d20.svg", id: "impaired", name: "Impaired"})

Code to toggle a status is something like:

const actor = game.actors.getName('Martin');
const status = CONFIG.statusEffects.find(e => e.id === 'impaired');
for (const token of actor .getActiveTokens()) 
   token.toggleEffect(status);

You can see if a token has an effect with

const has_effect = token.hasStatusEffect('impaired');
mrkwnzl commented 1 year ago

Good idea. I have implemented that, but only one way form the character sheet to the tokens. I’m having trouble with the other way around, though. Adding the status effect on the token triggers the actor update, which then applies the status effect again, resulting in a conflict.

Would a one-way sync work? You can still change the damage track with macros or the Cypher Combat module as well. I‘d just not register the status effects so that they don’t appear in the list on the token.

mrkwnzl commented 1 year ago

Nevermind, got it. Works both ways now.

Edit: Performance isn’t great, though.

farling42 commented 1 year ago

Is performance affected by some looping? Like maybe hooks on both token and actor are generating updates?

mrkwnzl commented 1 year ago

Might be related to this: https://github.com/foundryvtt/foundryvtt/issues/9651

There’s something not quite working, and setting status effects in my tests might have thrown something out of whack. After a few reloads, the performance is good again.

mrkwnzl commented 1 year ago

As promised over on Discord, here’s the code. You could make that a little more elegant, I’m sure.

I used https://game-icons.net/1x1/delapouite/heart-beats.html and https://game-icons.net/1x1/delapouite/arm-sling.html for impaired and debilitated, respectively.

Custom Status effects in Hooks.on("init"), path needs changing:

 CONFIG.statusEffects.push({id: "impaired", label: "Impaired", icon: "path/to/impaired.svg"});
 CONFIG.statusEffects.push({id: "debilitated", label: "Debilitated", icon: "path/to/debilitated.svg"});
Hooks.on("updateActor", async function (actor, data) {
  if (actor.type == "pc") {
    const impaired = CONFIG.statusEffects.find(element => element.id === 'impaired');
    const debilitated = CONFIG.statusEffects.find(element => element.id === 'debilitated');

    for (let token of actor.getActiveTokens()) {
      if (token.actor._id == actor._id) {
        if (actor.system.combat.damageTrack.state == "Hale") {
          if (token.actor.effects.find(el => el.name == "Impaired")) {
            await token.toggleEffect(impaired);
          }
          if (token.actor.effects.find(el => el.name == "Debilitated")) {
            await token.toggleEffect(debilitated);
          }
        } else if (actor.system.combat.damageTrack.state == "Impaired") {
          if (!token.actor.effects.find(el => el.name == "Impaired")) {
            await token.toggleEffect(impaired);
          }
          if (token.actor.effects.find(el => el.name == "Debilitated")) {
            await token.toggleEffect(debilitated);
          }
        } else if (actor.system.combat.damageTrack.state == "Debilitated") {
          if (token.actor.effects.find(el => el.name == "Impaired")) {
            await token.toggleEffect(impaired);
          }
          if (!token.actor.effects.find(el => el.name == "Debilitated")) {
            await token.toggleEffect(debilitated);
          }
        }
      }
    }
  }
});
Hooks.on('createActiveEffect', async function (effect) {
  if (effect.parent.type == "pc") {
    if (effect.name == "Impaired") {
      await effect.parent.update({"system.combat.damageTrack.state": "Impaired"});
    } else if (effect.name == "Debilitated") {
      await effect.parent.update({"system.combat.damageTrack.state": "Debilitated"});
    }
  }
});

Hooks.on('deleteActiveEffect', async function (effect) {
  if (effect.parent.type == "pc") {
    if ((effect.name == "Impaired" || effect.name == "Debilitated") && (!effect.parent.effects.find(el => el.name == "Impaired") && !effect.parent.effects.find(el => el.name == "Debilitated"))) {
      await effect.parent.update({"system.combat.damageTrack.state": "Hale"});
    }
  }
});
farling42 commented 1 year ago

The solution to prevent recursion between the two hooks is to pass in an extra flag in the options so that the create/deleteActiveEffect can ignore the hook when triggered by the updateActor routine doing its work. (It is in version 1.5.2 of the "Cypher System Active Effects" module)

mrkwnzl commented 1 year ago

Closed as it’s being moved over to the Active Effects module.