Skip to content

Commit

Permalink
Opt-out setting to clear Stealthy flags on combatants at combat end
Browse files Browse the repository at this point in the history
  • Loading branch information
Eligarf committed Oct 30, 2024
1 parent 29ec50e commit 318166c
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 2 deletions.
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# v4.10
* Add an opt-out setting to clear Stealthy's stealth/perception flags from combatants at combat end.

# v4.9.1
* Apply alias to Hidden, not Spot (whoops!!)
* When testing for hidden/spot effects, first check for Stealthy's flags before resorting to names.
Expand Down
4 changes: 4 additions & 0 deletions languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
"name": "Global Illumination dim light threshold",
"hint": "On scenes with GI enabled, bright light exposure is assumed unless the darkness level exceeds the scene's GI threshold multiplied by this factor, yielding dim light. Use 1.0 to transition directly from bright to dark."
},
"clearAfterCombat": {
"name": "Clear stealth effects after combat",
"hint": "Removes Stealthy's spot/hidden effects from only the combatants"
},
"stealthToActor": {
"name": "Bank stealth rolls to actor",
"hint": "Bank stealth rolls in an actor effect/item rather than the token."
Expand Down
4 changes: 4 additions & 0 deletions languages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
"name": "Global Illumination dim light threshold",
"hint": "On scenes with GI enabled, bright light exposure is assumed unless the darkness level exceeds the scene's GI threshold multiplied by this factor, yielding dim light. Use 1.0 to transition directly from bright to dark."
},
"clearAfterCombat": {
"name": "Clear stealth effects after combat",
"hint": "Removes Stealthy's spot/hidden effects from only the combatants"
},
"stealthToActor": {
"name": "Bank stealth rolls to actor",
"hint": "Bank stealth rolls in an actor effect/item rather than the token."
Expand Down
6 changes: 5 additions & 1 deletion languages/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
"name": "Limite de Iluminação Global de Penumbra",
"hint": "Em cenas com Iluminação Global ativada, se assume a exposição à luz plena, a menos que o nível de escuridão exceda o limite de Iluminação Global da cena, multiplicado por este fator, produzindo penumbra. Use 1.0 para fazer a transição direta do claro para escuro."
},
"clearAfterCombat": {
"name": "Clear stealth effects after combat",
"hint": "Removes Stealthy's spot/hidden effects from only the combatants"
},
"stealthToActor": {
"name": "Acumular Furtividade no Ator",
"hint": "Acumula as rolagens de Furtividade no efeito/item de um ator, ao invés do próprio ator."
Expand Down Expand Up @@ -150,4 +154,4 @@
"dependency": "Stealthy requer as ações Hide and Seek das macros de ação básicas no PF2e Workbench"
}
}
}
}
4 changes: 4 additions & 0 deletions languages/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
"name": "Порог яркости глобального освещения (GI)",
"hint": "В сценах с включенным GI предполагается яркое освещение, пока уровень темноты не превысит порог GI сцены, умноженный на этот коэффициент, что приведет к тусклому освещению. Используйте 1,0 для прямого перехода от яркого света к темноте."
},
"clearAfterCombat": {
"name": "Clear stealth effects after combat",
"hint": "Removes Stealthy's spot/hidden effects from only the combatants"
},
"stealthToActor": {
"name": "Сохранять Стелс бросок в актере",
"hint": "Сохранять бросок в актере вместо токена."
Expand Down
80 changes: 79 additions & 1 deletion scripts/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ function versionAtLeast(version, target) {
return true;
}

function interpolateString(str, interpolations) {
return str.replace(
/\{([A-Za-z0-9_]+)\}/g,
(match, key) => interpolations.hasOwnProperty(key) ? interpolations[key] : match
);
}

export default class Engine {

constructor() {
Expand Down Expand Up @@ -92,6 +99,7 @@ export default class Engine {
game.settings.register(Stealthy.MODULE_ID, 'gIDimThreshold', settings.gIDimThreshold);
game.settings.register(Stealthy.MODULE_ID, 'spotSecretDoors', settings.spotSecretDoors);
game.settings.register(Stealthy.MODULE_ID, 'hiddenAliases', settings.hiddenAliases);
game.settings.register(Stealthy.MODULE_ID, 'clearAfterCombat', settings.clearAfterCombat);

game.settings.register(Stealthy.MODULE_ID, 'stealthToActor', settings.stealthToActor);
game.settings.register(Stealthy.MODULE_ID, 'perceptionToActor', settings.perceptionToActor);
Expand Down Expand Up @@ -304,6 +312,15 @@ export default class Engine {
type: String,
default: 'Hidden;Caché;Escondido;Стелс'
},
clearAfterCombat: {
name: "stealthy.clearAfterCombat.name",
hint: "stealthy.clearAfterCombat.hint",
scope: 'world',
config: true,
type: Boolean,
default: true,
},

hiddenSource: {
name: "stealthy.hidden.source",
hint: "stealthy.source.hint",
Expand Down Expand Up @@ -554,6 +571,67 @@ export default class Engine {
await this.updateOrCreatePerceptionEffect(token.actor, { perception: value });
}

async createClearMessage(token, skill, value) {
const text = interpolateString(
game.i18n.localize(`${Stealthy.MODULE_ID}.clearAfterCombat.${skill}`),
{ name: token.name }
);
const tooltip = game.i18n.localize(`${Stealthy.MODULE_ID}.clearAfterCombat.restoreBank`);
const content = `
<div>
<span>${text}</span>
<button id="rebank-${skill}-${token.id}" data-button='${JSON.stringify(value)}' title="${tooltip}">
<i class="fas fa-undo"></i>
</button>
</div>`;
ChatMessage.create({
user: game.user._id,
speaker: ChatMessage.getSpeaker({ token }),
content,
type: CONST.CHAT_MESSAGE_TYPES.OTHER
});
}

async clearStealth(token) {
let value = undefined;
const tokenDoc = (token instanceof Token) ? token.document : token;
if (stealthy.stealthToActor) {
const stealth = this.findStealthEffect(token.actor);
if (!stealth) return;
value = stealth?.flags?.stealthy?.stealth;
if (value === undefined) return;
await token.actor.deleteEmbeddedDocuments('ActiveEffect', [stealth._id]);
} else {
value = tokenDoc.flags?.stealthy?.stealth;
if (value === undefined) return;
let update = { _id: token.id, };
update['flags.stealthy.-=stealth'] = true;
await canvas.scene.updateEmbeddedDocuments("Token", [update]);
}

// await this.createClearMessage(token, 'stealth', value);
}

async clearPerception(token) {
let value = undefined;
const tokenDoc = (token instanceof Token) ? token.document : token;
if (stealthy.perceptionToActor) {
const perception = this.findPerceptionEffect(token.actor);
if (!perception) return;
value = perception?.flags?.stealthy?.perception;
if (value === undefined) return;
await token.actor.deleteEmbeddedDocuments('ActiveEffect', [perception._id]);
} else {
value = tokenDoc.flags?.stealthy?.perception;
if (value === undefined) return;
let update = { _id: token.id, };
update['flags.stealthy.-=perception'] = true;
await canvas.scene.updateEmbeddedDocuments("Token", [update]);
}

// await this.createClearMessage(token, 'perception', value);
}

rollStealth() {
stealthy.socket.executeForEveryone('RefreshPerception');
}
Expand Down Expand Up @@ -665,7 +743,7 @@ export default class Engine {
let effect = actor.effects.find((e) => name === (beforeV11 ? e.label : e.name));

if (!effect) {
effect = await this.createSourcedEffect({ name, actor,source, makeEffect });
effect = await this.createSourcedEffect({ name, actor, source, makeEffect });

// If we haven't found an ouside source, create the default one
if (!effect) {
Expand Down
11 changes: 11 additions & 0 deletions scripts/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,14 @@ Hooks.once('ready', async () => {
ui.notifications.error("Stealthy requires the 'libWrapper' module. Please install and activate it.");
}
});

Hooks.on('deleteCombat', async (combat, ...args) => {
const clearAfterCombat = game.settings.get(Stealthy.MODULE_ID, 'clearAfterCombat');
if (!clearAfterCombat) return;
for (const combatant of combat.combatants.contents) {
const token = combatant?.token;
if (!token) continue;
await stealthy.clearPerception(token);
await stealthy.clearStealth(token);
}
});
10 changes: 10 additions & 0 deletions scripts/stealthy.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ export class Stealthy {
await this.engine.bankStealth(token, value);
}

async clearPerception(token) {
Stealthy.log(`stealthy.clearPerception`, { token });
await this.engine.clearPerception(token);
}

async clearStealth(token) {
Stealthy.log(`stealthy.clearStealth`, { token });
await this.engine.clearStealth(token);
}

async togglePerceptionBanking(toggled) {
Stealthy.log(`ToggletPerceptionBanking <= ${toggled}`);
stealthy.bankingPerception = toggled;
Expand Down

0 comments on commit 318166c

Please sign in to comment.