Skip to content

Instantly share code, notes, and snippets.

@sean-clayton
Created October 1, 2021 17:12
Show Gist options
  • Save sean-clayton/bdb9a930b16b07ae32bf8351f9c063c7 to your computer and use it in GitHub Desktop.
Save sean-clayton/bdb9a930b16b07ae32bf8351f9c063c7 to your computer and use it in GitHub Desktop.
Calculate Encounter Severity For Selected Tokens
(function main() {
let errors = [];
const playerTokens = canvas.tokens.controlled.filter((t) => {
return ["character"].includes(t.actor.data.type);
});
const enemyTokens = canvas.tokens.controlled.filter((t) => {
return ["npc"].includes(t.actor.data.type) && t.data.disposition === -1;
});
if (playerTokens.length === 0) {
errors.push("You must select at least one player token.");
}
if (enemyTokens.length === 0) {
errors.push("You must select at least one hostile npc token.");
}
const partyLevels = playerTokens.map(
(t) => t.actor.data.data.details.level.value
);
const partyLevel = Math.min(...partyLevels);
const numPlayers = playerTokens.length;
const encounterThreats = {
TRIVIAL: "Trivial",
LOW: "Low",
MODERATE: "Moderate",
SEVERE: "Severe",
EXTREME: "Extreme",
};
const threatLevelAdjustments = {
[encounterThreats.TRIVIAL]: 10,
[encounterThreats.LOW]: 15,
[encounterThreats.MODERATE]: 20,
[encounterThreats.SEVERE]: 30,
[encounterThreats.EXTREME]: 40,
};
function getThreatMaxBudgetMap(numPlayers) {
const difference = numPlayers - 4;
return {
[encounterThreats.TRIVIAL]:
40 + threatLevelAdjustments[encounterThreats.TRIVIAL] * difference,
[encounterThreats.LOW]:
60 + threatLevelAdjustments[encounterThreats.LOW] * difference,
[encounterThreats.MODERATE]:
80 + threatLevelAdjustments[encounterThreats.MODERATE] * difference,
[encounterThreats.SEVERE]:
120 + threatLevelAdjustments[encounterThreats.SEVERE] * difference,
[encounterThreats.EXTREME]:
160 + threatLevelAdjustments[encounterThreats.EXTREME] * difference,
};
}
const creatureLevelToXp = {
[-4]: 10,
[-3]: 15,
[-2]: 20,
[-1]: 30,
0: 40,
1: 60,
2: 80,
3: 120,
4: 160,
};
function calculateEnemyLevelDifference(partyLevel, enemyLevel) {
return (partyLevel - enemyLevel) * -1;
}
const enemyXps = enemyTokens.map((t) => {
const level = t.actor.data.data.details.level.value;
const difference = calculateEnemyLevelDifference(partyLevel, level);
if (difference > 4) {
errors.push(
"Cannot calculate severity because a creature was too high of level"
);
}
if (difference < -4) {
errors.push(
"Cannot calculate severity because a creature was too low of level"
);
}
return creatureLevelToXp[difference];
});
if (errors.length > 0) {
return errors.forEach((error) => ui.notifications.error(error));
}
function getThreat(enemyXp) {
const threatArr = Object.entries(getThreatMaxBudgetMap(numPlayers)).sort(
([_keyA, a], [_keyB, b]) => a - b
);
return threatArr.find(([_threat, maxExp]) => maxExp >= enemyXp)[0];
}
const enemyExpTotal = enemyXps.reduce((acc, curr) => acc + curr, 0);
const threat = getThreat(enemyExpTotal);
ui.notifications.info(
`Encounter threat for a party size of ${numPlayers} at level ${partyLevel}: ${threat}.`
);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment