Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom condition effects to support the basic statuses the system has #68

Merged
merged 14 commits into from
May 21, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- feature: Support the system's new concentration rolls with sources and messages with `flags.adv-reminder.message.ability.concentration` (system already handles advantage/disadvantage)
- feature: Support `flags.midi-qol.grants.critical.range` for conditions like Paralyzed that turn hits into crits if the attacker is adjacent. Does not currently work with Ready Set Roll, will just be ignored.
- feature: Apply conditions directly bases on the `statuses` instead of adding Midi's flags to status effects

# 3.4.1

Expand Down
4 changes: 2 additions & 2 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"Norm": "Normal from {sources}"
},
"adv-reminder.UpdateStatusEffects": {
"Name": "Update Status Effects",
"Hint": "Update the status effects that the system adds to include the appropriate advantage/disadvantage flags. Not recommended if you are using another module to modify or replace them."
"Name": "Enable Condition Effects",
"Hint": "Apply adv/dis/crit with the system's conditions (e.g. apply disadvantage to attacks when Poisoned). Not recommended if you are using another module to modify or replace the status effects."
}
}
16 changes: 15 additions & 1 deletion src/fails.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ class BaseFail {
return ["fail.all"];
}

get failCondition() {
return undefined;
}

/**
* Check for auto-fail flags to see if this roll should fail.
* @param {object} options the roll options
Expand All @@ -33,7 +37,9 @@ class BaseFail {
debug("failKeys", failKeys);

const actorFlags = this._getFlags(this.actor);
const shouldFail = failKeys.reduce((accum, curr) => actorFlags[curr] || accum, false);
const shouldFail =
failKeys.reduce((accum, curr) => actorFlags[curr] || accum, false) ||
this.actor.hasConditionEffect(this.failCondition);
if (shouldFail) {
const messageData = this.createMessageData(options);
this.toMessage(messageData);
Expand Down Expand Up @@ -78,6 +84,14 @@ export class AbilitySaveFail extends BaseFail {
]);
}

/** @override */
get failCondition() {
switch (this.abilityId) {
case "dex": return "advReminderFailDexSave";
case "str": return "advReminderFailDexSave";
}
}

createMessageData(options = {}) {
// build title, probably used as chat message flavor
const label = CONFIG.DND5E.abilities[this.abilityId];
Expand Down
296 changes: 22 additions & 274 deletions src/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,282 +40,30 @@ function applyMidiCustom(actor, change) {
}

Hooks.once("setup", () => {
if (game.settings.get("adv-reminder", "updateStatusEffects")) {
updateStatusEffects();
Hooks.on("preCreateActiveEffect", addExhaustionEffects);
Hooks.on("preUpdateActiveEffect", addExhaustionEffects);
}
if (game.settings.get("adv-reminder", "updateStatusEffects")) updateConditionEffects();
});

function updateStatusEffects() {
debug("updateStatusEffects");

const effectChanges = {
blinded: {
changes: [
{
key: "flags.midi-qol.disadvantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.advantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
dodging: {
flags: {
dae: {
specialDuration: ["turnStart"],
},
},
changes: [
{
key: "flags.midi-qol.grants.disadvantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.advantage.ability.save.dex",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
frightened: {
changes: [
{
key: "flags.midi-qol.disadvantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.disadvantage.ability.check.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
hidden: {
changes: [
{
key: "flags.midi-qol.advantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.disadvantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
invisible: {
changes: [
{
key: "flags.midi-qol.advantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.disadvantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
paralyzed: {
changes: [
{
key: "flags.midi-qol.fail.ability.save.dex",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.fail.ability.save.str",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.advantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.critical.range",
mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE,
value: "5",
},
],
},
petrified: {
changes: [
{
key: "flags.midi-qol.grants.advantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.fail.ability.save.dex",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.fail.ability.save.str",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
poisoned: {
changes: [
{
key: "flags.midi-qol.disadvantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.disadvantage.ability.check.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
prone: {
changes: [
{
key: "flags.midi-qol.grants.advantage.attack.mwak",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.advantage.attack.msak",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.disadvantage.attack.rwak",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.disadvantage.attack.rsak",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.disadvantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
restrained: {
changes: [
{
key: "flags.midi-qol.disadvantage.ability.save.dex",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.disadvantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.advantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
stunned: {
changes: [
{
key: "flags.midi-qol.fail.ability.save.dex",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.fail.ability.save.str",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.advantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
],
},
unconscious: {
changes: [
{
key: "flags.midi-qol.fail.ability.save.dex",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.fail.ability.save.str",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.advantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.grants.critical.range",
mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE,
value: "5",
},
],
},
};

Object.entries(effectChanges).forEach(([id, data]) => {
const effect = CONFIG.statusEffects.find((e) => e.id === id);
if (effect) foundry.utils.mergeObject(effect, data);
});
}

function addExhaustionEffects(effect, updates) {
debug("addExhaustionEffects");

if (effect.id !== dnd5e.documents.ActiveEffect5e.ID.EXHAUSTION) return;
const level = foundry.utils.getProperty(updates, "flags.dnd5e.exhaustionLevel");
if (!level) return;
// build the changes based on exhaustion level
const changes = [
{
key: "flags.midi-qol.disadvantage.ability.check.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.dnd5e.initiativeDisadv",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
];
if (level >= 3)
changes.push(
{
key: "flags.midi-qol.disadvantage.attack.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
},
{
key: "flags.midi-qol.disadvantage.ability.save.all",
mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM,
value: "1",
}
);
// add changes to the active effect
effect.updateSource({ changes });
/**
* Add advantage-like condition effects for all status effects.
* details: when adding to CONFIG.DND5E.conditionEffects, make sure to include an "advReminder"
* prefix to serve as a namespace to avoid conflicts
*/
function updateConditionEffects() {
const ce = CONFIG.DND5E.conditionEffects;
ce.advReminderAdvantageAttack = new Set(["hiding", "invisible"]);
ce.advReminderAdvantageDexSave = new Set(["dodging"]);
ce.advReminderDisadvantageAttack = new Set(["blinded", "frightened", "poisoned", "prone", "restrained"]);
ce.advReminderDisadvantageAbility = new Set(["exhaustion-1", "frightened", "poisoned"]);
ce.advReminderDisadvantageSave = new Set(["exhaustion-3"]);
ce.advReminderDisadvantageDexSave = new Set(["restrained"]);
ce.advReminderDisadvantagePhysicalRolls = new Set(["heavilyEncumbered"]);
ce.advReminderFailDexSave = new Set(["paralyzed", "petrified", "stunned", "unconscious"]);
ce.advReminderFailStrSave = new Set(["paralyzed", "petrified", "stunned", "unconscious"]);
ce.advReminderGrantAdvantageAttack = new Set(["blinded", "paralyzed", "petrified", "restrained", "stunned", "unconscious"]);
ce.advReminderGrantAdjacentCritical = new Set(["paralyzed", "unconscious"]);
ce.advReminderGrantDisadvantageAttack = new Set(["dodging", "exhaustion-3", "hidden", "invisible"]);
// if adjacent, grant advantage on the attack, else grant disadvantage
ce.advReminderGrantAdjacentAttack = new Set(["prone"]);
}

// Add message flags to DAE so it shows them in the AE editor
Expand Down
Loading
Loading