Skip to content

Commit

Permalink
v1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Eligarf committed Apr 16, 2024
1 parent ae1b9cd commit 606b075
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 71 deletions.
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# v1.1.0
* Put detection results in a table for better viewing

# v1.0.1
* Restore the tooltips on adjusted stealth initiative chat messages
* More robust tooltip generation
Expand Down
23 changes: 15 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@

A module for [FoundryVTT](https://foundryvtt.com) that shows results of initiative stealth check vs combatant perception DCs on the initiative messages.

* Appends results of checking the initiative stealth value against the non-allied combatants to the initiative roll message, grouped by detection status: `Unnoticed`, `Undetected`, and `Observed`
* An *Unnoticed* setting controls whether or not to use `Unnoticed` rather than `Undetected` for stealth checks that beat both the target's perception DC and initiative. `Undetected` is always used if the stealth initiative roll doesn't beat the target's initiative roll.
* If *PF2e Perception* is active, *PF2e Avoid Notice* will set the appropriate visibility flags on the combatant tokens to reflect the detection status determined by stealth initiative checks.
* An *Override* setting allows *PF2e Avoid Notice* to override existing *PF2e Perception* visibility flags on tokens. Disabling this is useful if one wishes to setup any complicated situation beforehand and not get it stomped by the initiative rolls. Even if *Override* is disabled, *Pf2e Avoid Notice* can still change an `Observed` status to something else
* If *PF2e Perception* cover flags are found, standard or greater cover bonuses will apply as appropriate against perception DCs.

# PF2e Perception
The 'Pathfinder on Foundry VTT Community and Volunteer Development Server' discord server leads to the excellent unlisted module [PF2e Perception](https://github.com/reonZ/pf2e-perception). It isn't required for the overall operation of this module.
## Initiative messages
The module appends results of checking the initiative stealth value against the non-allied combatants to the initiative roll message, grouped by detection status: `Unnoticed`, `Undetected`, and `Observed`. The numbers by each listed token in the groups shows the difference between the stealth initiative roll and the perception DC of that token. `Unnoticed` and `Undetected` status means that the token rolling initiative was not observed by the listed tokens, so the number listed will be zero or positive. Tokens listed in the `Observed` group are able to observe the stealth-using token.

![image](https://github.com/Eligarf/avoid-notice/assets/16523503/194d98aa-5a60-4564-9971-e368fa5b83f9)

In the above, Amiri's stealth roll of 16 was successful against *monster* and *creature*, who had perception DC's of 10 and 13. It was not successful against *beast* or *grumpkin*, who in turn had perception DC's of 17 and 20.

An *Unnoticed* setting controls whether or not to use `Unnoticed` rather than `Undetected` for stealth checks that beat both the target's perception DC and initiative. `Undetected` is always used if the stealth initiative roll beats the target's perception DC but doesn't beat the target's initiative roll.

## PF2e Perception
The 'Pathfinder on Foundry VTT Community and Volunteer Development Server' discord server leads to the excellent unlisted module [PF2e Perception](https://github.com/reonZ/pf2e-perception). It isn't required for the overall operation of this module, but additional capabilities become available:

* *PF2e Avoid Notice* will set the appropriate visibility flags on the combatant tokens to reflect the detection status determined by stealth initiative checks.
* An *Override* setting allows *PF2e Avoid Notice* to override existing *PF2e Perception* visibility flags on tokens. Disabling this is useful if one wishes to setup any complicated situation beforehand and not get it stomped by the initiative rolls.
* If *PF2e Perception* cover flags are found on a token using stealth for initiative, standard or greater cover bonuses will apply as appropriate against perception DCs.
6 changes: 3 additions & 3 deletions languages/en.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"pf2e-avoid-notice": {
"detectionTitle": {
"observed": "is Observed by",
"undetected": "is Undetected by",
"unnoticed": "is Unnoticed by"
"observed": "Observed",
"undetected": "Undetected",
"unnoticed": "Unnoticed"
},
"negativeDelta": "",
"positiveDelta": "",
Expand Down
109 changes: 53 additions & 56 deletions scripts/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,74 +3,73 @@ const HIDDEN = ['action:hide', 'action:create-a-diversion', 'action:sneak'];
const MODULE_ID = 'pf2e-avoid-notice';
const PERCEPTION_ID = 'pf2e-perception';

class AvoidNotice {

static colorizeOutput(format, ...args) {
return [
`%c${MODULE_ID} %c|`,
...CONSOLE_COLORS,
format,
...args,
];
}
function colorizeOutput(format, ...args) {
return [
`%c${MODULE_ID} %c|`,
...CONSOLE_COLORS,
format,
...args,
];
}

static log(format, ...args) {
const level = game.settings.get(MODULE_ID, 'logLevel');
if (level !== 'none') {
function log(format, ...args) {
const level = game.settings.get(MODULE_ID, 'logLevel');
if (level !== 'none') {

if (level === 'debug')
console.debug(...AvoidNotice.colorizeOutput(format, ...args));
else if (level === 'log')
console.log(...AvoidNotice.colorizeOutput(format, ...args));
}
if (level === 'debug')
console.debug(...colorizeOutput(format, ...args));
else if (level === 'log')
console.log(...colorizeOutput(format, ...args));
}
}

function renderDice(roll, rollClass) {
function renderInitiativeDice(roll) {
let content = `
<div class="dice-roll ${rollClass}" data-tooltip-class="pf2e">
<div class="dice-result">
<div class="dice-formula">${roll.formula}</div>
<div class="dice-tooltip">
<section class="tooltip-part">`;
<div class="dice-roll initiative" data-tooltip-class="pf2e">
<div class="dice-result">
<div class="dice-formula">${roll.formula}</div>
<div class="dice-tooltip">
<section class="tooltip-part">`;
for (const die of roll.dice) {
content += `
<div class="dice">
<header class="part-header flexrow">
<span class="part-formula">${die.formula}</span>
<span class="part-total">${die.total}</span>
</header>
<ol class="dice-rolls">`;
<div class="dice">
<header class="part-header flexrow">
<span class="part-formula">${die.formula}</span>
<span class="part-total">${die.total}</span>
</header>
<ol class="dice-rolls">`;
for (const r of die.results) {
content += `<li class="roll die d${die.faces}">${r.result}</li>`;
content += `
<li class="roll die d${die.faces}">${r.result}</li>`;
}

content += `</ol></div>`;
content += `
</ol>
</div>`;
}

content += `
</section>
</div>
<h4 class="dice-total">${roll.total}</h4>
</div>
</div><br>`;
</section>
</div>
<h4 class="dice-total">${roll.total}</h4>
</div>
</div><br>`;
return content;
}

Hooks.once('init', () => {
// Hooks.on('createChatMessage', async (message, options, id) => {
// AvoidNotice.log('createChatMessage', message);
// log('createChatMessage', message);
// const pf2eContext = message.flags.pf2e.context;
// switch (pf2eContext?.type) {
// case 'perception-check':
// if (pf2eContext?.options.includes('action:seek')) {
// AvoidNotice.log('perception-check', message);
// log('perception-check', message);
// }
// break;
// case 'skill-check':
// const tags = pf2eContext?.options.filter((t) => HIDDEN.includes(t));
// if (tags.length > 0) {
// AvoidNotice.log('skill-check', tags);
// log('skill-check', tags);
// }
// break;
// }
Expand All @@ -84,7 +83,7 @@ Hooks.once('init', () => {
const stealthies = encounter.combatants.contents.filter((c) => c.flags.pf2e.initiativeStatistic === 'stealth');
let perceptionChanges = {};
for (const combatant of stealthies) {
// AvoidNotice.log('combatant', combatant);
// log('combatant', combatant);

// Only check against non-allies
const disposition = combatant.token.disposition;
Expand Down Expand Up @@ -119,7 +118,7 @@ Hooks.once('init', () => {
const delta = combatant.initiative - target.dc;
if (delta < 0) {
target.result = 'observed';
target.delta = `by ${delta}`;
target.delta = `${delta}`;

// Remove any existing perception flag as we are observed
if (override && perceptionData && other.token.id in perceptionData)
Expand All @@ -129,7 +128,7 @@ Hooks.once('init', () => {
// combatant beat the other token at the stealth battle
else {
let visibility = 'undetected';
target.delta = `by +${delta}`;
target.delta = `+${delta}`;
if (useUnnoticed && combatant.initiative > other.initiative) {
visibility = 'unnoticed';
}
Expand Down Expand Up @@ -160,17 +159,14 @@ Hooks.once('init', () => {
m.speaker.token === combatant.tokenId && m.flags?.core?.initiativeRoll
);
if (!messages.length) {
AvoidNotice.log(`Couldn't find initiative card for ${combatant.token.name}`);
log(`Couldn't find initiative card for ${combatant.token.name}`);
continue;
}

// Push the new detection statuses into that message
const lastMessage = await game.messages.get(messages.pop()._id);
AvoidNotice.log(`messageData updates for ${combatantDoc.name}`, messageData );
const roll = lastMessage.rolls[0];
const die = roll.dice[0];

let content = renderDice(roll, 'initiatve');
log(`messageData updates for ${combatantDoc.name}`, messageData);
let content = renderInitiativeDice(lastMessage.rolls[0]);

for (const t of ['unnoticed', 'undetected', 'observed']) {
const status = messageData[t];
Expand All @@ -182,17 +178,18 @@ Hooks.once('init', () => {
await lastMessage.update({ content });
}

// If PF2e-perception is around, move its changes into an update array and batch update
// all the tokens at once
// If PF2e-perception is around, move any non-empty changes into an update array
if (perceptionActive) {
let updates = [];
for (const id in perceptionChanges) {
const update = perceptionChanges[id];
if (!Object.keys(update).length) continue;
updates.push({ _id: id, ...update });
if (Object.keys(update).length)
updates.push({ _id: id, ...update });
}

// Update all the tokens at once, skipping an empty update
log('token updates', updates);
if (updates.length > 0) {
AvoidNotice.log('token updates', updates);
canvas.scene.updateEmbeddedDocuments("Token", updates);
}
}
Expand Down Expand Up @@ -235,5 +232,5 @@ Hooks.once('setup', () => {
default: 'none'
});

AvoidNotice.log(`Setup ${moduleVersion}`);
log(`Setup ${moduleVersion}`);
});
14 changes: 10 additions & 4 deletions templates/combat-start.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
<div data-visibility="gm">
{{title}}
<hr>
<span>{{title}}</span>
<table>
<tbody>
{{#each targets as |target|}}
<b>{{target.name}}</b> {{target.delta}}.<br>
<tr>
<th>{{target.name}}</th>
<th>{{target.delta}}</th>
</tr>
{{/each}}
</div>
</tbody>
</table>
</div>

0 comments on commit 606b075

Please sign in to comment.