Skip to content

Commit

Permalink
feat: Add DnD 5e Cr2.0 Simple XP Calculation System (#230)
Browse files Browse the repository at this point in the history
Co-authored-by: Eichler Lukas <eichler.lukas@bcgplatinion.com>
  • Loading branch information
eicnix and Eichler Lukas authored Oct 9, 2023
1 parent 811c7cd commit f369cad
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
159 changes: 159 additions & 0 deletions src/utils/rpg-system/dnd5e-cr2-simple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { RpgSystem } from "./rpgSystem";
import { crToString, getFromCreatureOrBestiary } from "..";
import type InitiativeTracker from "src/main";
import type { DifficultyLevel, GenericCreature, DifficultyThreshold } from ".";

const PLAYER_POWER_BY_LEVEL: Record<number, number> = {
1: 11,
2: 14,
3: 18,
4: 23,
5: 32,
6: 35,
7: 41,
8: 44,
9: 49,
10: 53,
11: 62,
12: 68,
13: 71,
14: 74,
15: 82,
16: 84,
17: 103,
18: 119,
19: 131,
20: 141
}

const POWER_BY_CR: Record<string, number> = {
"0": 1,
"1/8": 5,
"0.25": 5,
"1/4": 10,
"0.5": 10,
"1/2": 16,
"1": 22,
"2": 28,
"3": 37,
"4": 48,
"5": 60,
"6": 65,
"7": 70,
"8": 85,
"9": 85,
"10": 95,
"11": 106,
"12": 115,
"13": 120,
"14": 125,
"15": 130,
"16": 140,
"17": 150,
"18": 160,
"19": 165,
"20": 180,
"21": 200,
"22": 225,
"23": 250,
"24": 275,
"25": 300,
"26": 325,
"27": 350,
"28": 375,
"29": 400,
"30": 425
};

const DIFFICULTY_TO_CSS: Record<string, string> = {
"Mild": "easy",
"Bruising": "medium",
"Bloddy": "medium",
"Brutal": "hard",
"Oppressive": "deadly"
}

export class Dnd5eCr2SimpleRpgSystem extends RpgSystem {
plugin: InitiativeTracker;

override valueUnit = "Power";

override systemDifficulties: [string, string, ...string[]] = [
"Mild",
"Bruising",
"Bloody",
"Brutal",
"Oppressive",
]

constructor(plugin: InitiativeTracker) {
super();
this.plugin = plugin;
this.displayName = "DnD 5e CR2.0 Simple";
}

getCreatureDifficulty(creature: GenericCreature, _?: number[]): number {
const cr = getFromCreatureOrBestiary(this.plugin, creature, c => c?.cr ?? "0");
return POWER_BY_CR[cr] ?? 0;
}

getAdditionalCreatureDifficultyStats(
creature: GenericCreature,
_?: number[]
): string[] {
const cr = getFromCreatureOrBestiary(
this.plugin, creature, c => c?.cr ?? 0);
return [`${crToString(cr)} CR`];
}

getEncounterDifficulty(
creatures: Map<GenericCreature, number>,
playerLevels: number[]
) : DifficultyLevel {
const creaturePower = [...creatures].reduce((acc, [creature, count]) => acc + this.getCreatureDifficulty(creature) * count, 0)

const thresholds = this.getDifficultyThresholds(playerLevels);
const displayName = thresholds
.reverse() // Should now be in descending order
.find(threshold => creaturePower >= threshold.minValue)?.displayName
?? "Mild";

const thresholdSummary = thresholds
.map(threshold => `${threshold.displayName}: ${threshold.minValue}`)
.join("\n");

const summary = `Encounter is ${displayName}
Total Power: ${creaturePower}
Threshold
${thresholdSummary}`;

return {
displayName,
summary,
cssClass: DIFFICULTY_TO_CSS[displayName],
value: creaturePower,
title: "Total Creature Power",
intermediateValues: [],
};
}

getDifficultyThresholds(playerLevels: number[]): DifficultyThreshold[] {
const budget: Record<string, number> = {
mild: 0.4,
bruising: 0.6,
bloddy: 0.75,
brutal: 0.9,
Oppressive: 1
};

const partyPower = playerLevels.map(x => PLAYER_POWER_BY_LEVEL[x]).reduce((a, b) => a+b)
return Object.entries(budget)
.map(([name, value]) => ({
displayName: (name.charAt(0).toUpperCase() + name.slice(1)),
minValue: Math.round(value * partyPower)
}))
.sort((a, b) => a.minValue - b.minValue);
}
}
3 changes: 3 additions & 0 deletions src/utils/rpg-system/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { SRDMonster } from "../../../index";
import type InitiativeTracker from "../../main";
import { Dnd5eRpgSystem } from "./dnd5e";
import { Dnd5eLazyGmRpgSystem } from "./dnd5e-lazygm";
import { Dnd5eCr2SimpleRpgSystem } from "./dnd5e-cr2-simple";
import { Pathfinder2eRpgSystem } from "./pf2e"
import { RpgSystem } from "./rpgSystem";
import { DEFAULT_UNDEFINED } from "../constants";
Expand Down Expand Up @@ -54,6 +55,7 @@ export type IntermediateValues = { label: string, value: number }[]
export enum RpgSystemSetting {
Dnd5e = "dnd5e",
Dnd5eLazyGm = "dnd5e-lazygm",
Dnd5eCR2Simple = "dnd5e-cr2-simple",
Pathfinder2e = "pathfinder2e"
}

Expand All @@ -70,6 +72,7 @@ export function getRpgSystem(plugin: InitiativeTracker, settingId?: string): Rpg
switch (settingId ? settingId : plugin.data.rpgSystem) {
case RpgSystemSetting.Dnd5e: return new Dnd5eRpgSystem(plugin);
case RpgSystemSetting.Dnd5eLazyGm: return new Dnd5eLazyGmRpgSystem(plugin);
case RpgSystemSetting.Dnd5eCR2Simple: return new Dnd5eCr2SimpleRpgSystem(plugin);
case RpgSystemSetting.Pathfinder2e: return new Pathfinder2eRpgSystem(plugin);
}
return new UndefinedRpgSystem();
Expand Down

0 comments on commit f369cad

Please sign in to comment.