Skip to content
This repository has been archived by the owner on Oct 11, 2024. It is now read-only.

Commit

Permalink
612 roll streaks chat cards spoil blind gm rolls (#615)
Browse files Browse the repository at this point in the history
* DND5e dice streak message when user is rolling in public view

* PF2e dice streak message when user is rolling in public view

* Allow setting of threshold for dice tracking

* Force download backup of data on delete

* Group players who tie for stats

* ignore no-self-assign
  • Loading branch information
johnnolan authored Oct 2, 2023
1 parent a23ba4f commit 4374d0d
Show file tree
Hide file tree
Showing 16 changed files with 186 additions and 40 deletions.
2 changes: 2 additions & 0 deletions languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"EncounterStats.opt_enable_dice_streak_hint": "Track the number of times a player rolls the same result on a D20.",
"EncounterStats.opt_enable_dice_streak_to_chat_name": "Track Dice Streaks Chat Message",
"EncounterStats.opt_enable_dice_streak_to_chat_hint": "Show a message in Chat when a player rolls the same result on a D20.",
"EncounterStats.opt_enable_dice_streak_threshold_name": "Dice Streak threshold to track",
"EncounterStats.opt_enable_dice_streak_threshold_hint": "Set the number pf consecutive dice rolls to alert for a streak.",
"EncounterStats.template.most_damage_overall": "Most Damage Overall",
"EncounterStats.template.most_damage_per_turn": "Most Damage in 1 turn",
"EncounterStats.template.highest_average_damage": "Highest Average Damage",
Expand Down
19 changes: 11 additions & 8 deletions scripts/CampaignRenderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,18 @@ describe("CampaignRenderer", () => {

expect(result.nat1).toStrictEqual([
{ name: "Fighter", value: 7 },
{ name: "Orc", value: 1 },
{ name: "Lorena Aldabra", value: 1 },
{ name: "Orc & Lorena Aldabra", value: 1 },
]);

expect(result.nat20).toStrictEqual([
{ name: "Fighter", value: 3 },
{ name: "Orc", value: 1 },
{ name: "Lorena Aldabra", value: 1 },
{ name: "Orc & Lorena Aldabra", value: 1 },
]);

expect(result.heals).toStrictEqual([{ name: "Graa", value: 1 }]);
expect(result.heals).toStrictEqual([{ name: "Graa", value: 2 }]);

expect(result.kills).toStrictEqual([
{ name: "Graa", value: 1 },
{ name: "Lorena Aldabra", value: 1 },
{ name: "Fighter", value: 1 },
{ name: "Graa & Lorena Aldabra & Fighter", value: 1 },
]);

expect(result.criticalHistory).toStrictEqual([
Expand Down Expand Up @@ -146,6 +142,13 @@ describe("CampaignRenderer", () => {
date: "30 September 2022 20:03",
total: 15,
},
{
actorName: "Graa",
flavor:
"@UUID[Actor.2ybHnw0DeYqwDPyV.Item.yAmmCAv5PSEM8X5f]{Potion of Healing (Greater)}",
date: "30 September 2022 20:03",
total: 15,
},
],
},
]);
Expand Down
7 changes: 6 additions & 1 deletion scripts/CampaignRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,12 @@ class CampaignRenderer {
});

result.forEach((entry) => {
data.push({ name: entry.name, value: entry.value });
const existingValue = data.find(({ value }) => value === entry.value);
if (existingValue) {
existingValue.name = existingValue.name + " & " + entry.name;
} else {
data.push({ name: entry.name, value: entry.value });
}
});

return data;
Expand Down
43 changes: 22 additions & 21 deletions scripts/CampaignStat.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import CampaignRenderer from "./CampaignRenderer";
import CampaignStat from "./CampaignStat";
import EncounterJournal from "./EncounterJournal";
import { RoleType } from "./enums";
import { ChatRollMode, RoleType } from "./enums";
import Gamemaster from "./Helpers/Gamemaster";
import "../__mocks__/chat-message";

Expand Down Expand Up @@ -45,7 +45,11 @@ describe("CampaignStat", () => {
format: jest.fn().mockReturnValue("TestKeyValue"),
},
settings: {
get: jest.fn().mockReturnValue(true),
get: jest.fn()
.mockReturnValueOnce(true).mockReturnValueOnce("2")
.mockReturnValueOnce(true).mockReturnValueOnce("2").mockReturnValueOnce(true)
.mockReturnValueOnce(true).mockReturnValueOnce("2").mockReturnValueOnce(true)
.mockReturnValueOnce(true).mockReturnValueOnce("2").mockReturnValueOnce(true),
},
};
mockGamemasterGetStats.mockReturnValue({
Expand All @@ -59,10 +63,10 @@ describe("CampaignStat", () => {
});

test("it adds the Roll Streak correctly", async () => {
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV");
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV");
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV");
await CampaignStat.AddRollStreak(1, "Graa", "2ybHnw0DeYqwDPyV");
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV", ChatRollMode.publicroll);
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV", ChatRollMode.publicroll);
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV", ChatRollMode.publicroll);
await CampaignStat.AddRollStreak(1, "Graa", "2ybHnw0DeYqwDPyV", ChatRollMode.publicroll);
expect(mockEncounterJournalUpdateJournalData).toBeCalled();
expect(mock_sendRollStreakChatMessage).toBeCalled();
expect(mock_sendRollStreakChatMessage).toBeCalledTimes(2);
Expand Down Expand Up @@ -99,7 +103,11 @@ describe("CampaignStat", () => {
format: jest.fn().mockReturnValue("TestKeyValue"),
},
settings: {
get: jest.fn().mockReturnValue(true),
get: jest.fn()
.mockReturnValueOnce(true).mockReturnValueOnce("2")
.mockReturnValueOnce(true).mockReturnValueOnce("2").mockReturnValueOnce(true)
.mockReturnValueOnce(true).mockReturnValueOnce("2").mockReturnValueOnce(true)
.mockReturnValueOnce(true).mockReturnValueOnce("2").mockReturnValueOnce(true),
},
};
mockGamemasterGetStats.mockReturnValue({
Expand Down Expand Up @@ -128,14 +136,14 @@ describe("CampaignStat", () => {
});

test("it adds the Roll Streak correctly", async () => {
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV");
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV");
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV");
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV");
await CampaignStat.AddRollStreak(1, "Graa", "2ybHnw0DeYqwDPyV");
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV", ChatRollMode.publicroll);
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV", ChatRollMode.publicroll);
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV", ChatRollMode.blindroll);
await CampaignStat.AddRollStreak(9, "Graa", "2ybHnw0DeYqwDPyV", ChatRollMode.blindroll);
await CampaignStat.AddRollStreak(1, "Graa", "2ybHnw0DeYqwDPyV", ChatRollMode.blindroll);
expect(mockEncounterJournalUpdateJournalData).toBeCalled();
expect(mock_sendRollStreakChatMessage).toBeCalled();
expect(mock_sendRollStreakChatMessage).toBeCalledTimes(6);
expect(mock_sendRollStreakChatMessage).toBeCalledTimes(3);
expect(mockGamemasterSetStats).toBeCalled();
expect(mockGamemasterSetStats).toBeCalledWith({
kills: [],
Expand All @@ -150,19 +158,12 @@ describe("CampaignStat", () => {
"dateDisplay": "01 November 2022",
"roll": 4,
"total": 3
},
{
"actorId": "2ybHnw0DeYqwDPyV",
"actorName": "Graa",
"dateDisplay": "01 January 2020",
"roll": 9,
"total": 5
}
],
rollstreaklog: [
{
actorId: "2ybHnw0DeYqwDPyV",
results: [1],
results: [9,9,9,9,9],
},
],
});
Expand Down
18 changes: 15 additions & 3 deletions scripts/CampaignStat.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import CampaignRenderer from "./CampaignRenderer";
import Chat from "./Chat";
import EncounterJournal from "./EncounterJournal";
import { RoleType } from "./enums";
import { ChatRollMode, RoleType } from "./enums";
import Dates from "./Helpers/Dates";
import Gamemaster from "./Helpers/Gamemaster";
import Trans from "./Helpers/Trans";
import {
MODULE_ID,
OPT_SETTINGS_DICE_STREAK_ENABLE,
OPT_SETTINGS_DICE_STREAK_THRESHOLD,
OPT_SETTINGS_DICE_STREAK_TO_CHAT_ENABLE,
} from "./Settings";

Expand Down Expand Up @@ -108,13 +109,22 @@ export default class CampaignStat {
result: number,
actorName: string,
actorId: string,
chatRollMode: ChatRollMode,
) {
if (
!game.settings.get(`${MODULE_ID}`, `${OPT_SETTINGS_DICE_STREAK_ENABLE}`)
) {
return;
}

const diceStreakThreshold: number = parseInt(
<string>(
game.settings.get(
`${MODULE_ID}`,
`${OPT_SETTINGS_DICE_STREAK_THRESHOLD}`,
)
),
);
const campaignStats = await this.Get();
const date = Dates.now;

Expand Down Expand Up @@ -144,12 +154,14 @@ export default class CampaignStat {
game.settings.get(
`${MODULE_ID}`,
`${OPT_SETTINGS_DICE_STREAK_TO_CHAT_ENABLE}`,
)
) &&
chatRollMode === ChatRollMode.publicroll.valueOf() &&
actorStreakLog.length >= diceStreakThreshold
) {
this._sendRollStreakChatMessage(actorName, actorStreakLog);
}
} else {
if (actorStreakLog.length > 1) {
if (actorStreakLog.length >= diceStreakThreshold) {
// Save to streak length and result
campaignStats.rollstreak.push(<RollStreakTrack>{
actorId: actorId,
Expand Down
6 changes: 3 additions & 3 deletions scripts/Handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Stat from "./stats/Stat";
import MidiQolStat from "./stats/MidiQolStat";
import DND5eStat from "./stats/DND5eStat";
import CampaignStat from "./CampaignStat";
import { ChatType, RoleType } from "./enums";
import { ChatRollMode, ChatType, RoleType } from "./enums";
import Logger from "./Helpers/Logger";
import { EncounterWorkflow } from "EncounterWorkflow";
import PF2eStat from "./stats/PF2eStat";
Expand All @@ -18,10 +18,10 @@ export async function OnTrackRollStreak(
result: number,
actorName: string,
actorId: string,
chatRollMode: ChatRollMode,
): Promise<void> {
if (!result) return;

CampaignStat.AddRollStreak(result, actorName, actorId);
CampaignStat.AddRollStreak(result, actorName, actorId, chatRollMode);
}

export async function OnTrackDiceRoll(
Expand Down
14 changes: 14 additions & 0 deletions scripts/ModuleSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
OPT_ENABLE,
OPT_ENABLE_EXPORT_JSON,
OPT_SETTINGS_DICE_STREAK_ENABLE,
OPT_SETTINGS_DICE_STREAK_THRESHOLD,
OPT_SETTINGS_DICE_STREAK_TO_CHAT_ENABLE,
} from "./Settings";
import Trans from "./Helpers/Trans";
Expand Down Expand Up @@ -76,6 +77,19 @@ class ModuleSettings {
type: Boolean,
},
);

game.settings.register(
`${MODULE_ID}`,
`${OPT_SETTINGS_DICE_STREAK_THRESHOLD}`,
{
name: Trans.Get("opt_enable_dice_streak_threshold_name"),
hint: Trans.Get("opt_enable_dice_streak_threshold_hint"),
scope: "world",
config: false,
default: "2",
type: String,
},
);
}
}

Expand Down
1 change: 1 addition & 0 deletions scripts/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export const OPT_ENABLE_EXPORT_JSON = "enableExportJson";

export const OPT_SETTINGS_DICE_STREAK_ENABLE = "enableDiceStreak";
export const OPT_SETTINGS_DICE_STREAK_TO_CHAT_ENABLE = "enableDiceStreakToChat";
export const OPT_SETTINGS_DICE_STREAK_THRESHOLD = "diceStreakThreshold";
15 changes: 14 additions & 1 deletion scripts/SetupHooksDND5e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import StatManager from "./StatManager";
import DND5e from "./parsers/DND5e";
import MidiQol from "./parsers/MidiQol";
import { CombatDetailType, ChatType } from "./enums";
import { CombatDetailType, ChatType, ChatRollMode } from "./enums";
import Stat from "./stats/Stat";
import ReadySetRoll from "./parsers/ReadySetRoll";
import { EncounterWorkflow } from "EncounterWorkflow";
Expand Down Expand Up @@ -72,6 +72,7 @@ export default class SetupHooksDND5e {
midiWorkflow.diceTotal,
rollCheck.tokenName ?? rollCheck.name,
midiWorkflow.actor.id,
ChatRollMode[game.settings.get("core", "rollMode")],
);
}
},
Expand Down Expand Up @@ -124,6 +125,8 @@ export default class SetupHooksDND5e {
data: {
workflow: MidiQol.ParseWorkflow(workflow),
rollCheck: MidiQol.RollCheck(workflow),
chatRollMode:
ChatRollMode[game.settings.get("core", "rollMode")],
},
});
},
Expand Down Expand Up @@ -182,6 +185,8 @@ export default class SetupHooksDND5e {
roll,
),
ChatType: ChatType.DND5e,
chatRollMode:
ChatRollMode[game.settings.get("core", "rollMode")],
},
});
}
Expand All @@ -205,6 +210,8 @@ export default class SetupHooksDND5e {
roll,
),
ChatType: ChatType.DND5e,
chatRollMode:
ChatRollMode[game.settings.get("core", "rollMode")],
},
});
}
Expand All @@ -224,6 +231,7 @@ export default class SetupHooksDND5e {
actorId: actor.id,
flavor: roll.options.flavor,
tokenName: actor.prototypeToken.name,
chatRollMode: ChatRollMode[game.settings.get("core", "rollMode")],
},
});
},
Expand All @@ -242,6 +250,7 @@ export default class SetupHooksDND5e {
actorId: actor.id,
flavor: roll.options.flavor,
tokenName: actor.prototypeToken.name,
chatRollMode: ChatRollMode[game.settings.get("core", "rollMode")],
},
});
},
Expand All @@ -260,6 +269,7 @@ export default class SetupHooksDND5e {
actorId: actor.id,
flavor: roll.options.flavor,
tokenName: actor.prototypeToken.name,
chatRollMode: ChatRollMode[game.settings.get("core", "rollMode")],
},
});
},
Expand Down Expand Up @@ -302,6 +312,7 @@ export default class SetupHooksDND5e {
payload.data.workflow.diceTotal,
payload.data.rollCheck.tokenName ?? payload.data.rollCheck.name,
payload.data.workflow.actor.id,
payload.data.chatRollMode,
);
break;
case "dnd5e.rollAttack":
Expand All @@ -310,6 +321,7 @@ export default class SetupHooksDND5e {
encounterData.diceTotal,
encounterData.tokenName ?? encounterData.actor.actorName,
encounterData.actor.id,
payload.data.chatRollMode,
);
break;
case "dnd5e.useItem":
Expand All @@ -328,6 +340,7 @@ export default class SetupHooksDND5e {
payload.data.result,
payload.data.tokenName ?? payload.data.alias,
payload.data.actorId,
payload.data.chatRollMode,
);
break;
}
Expand Down
7 changes: 6 additions & 1 deletion scripts/SetupHooksPF2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
OnTrackRollStreak,
} from "./Handlers";
import StatManager from "./StatManager";
import { CombatDetailType, ChatType } from "./enums";
import { CombatDetailType, ChatType, ChatRollMode } from "./enums";
import Stat from "./stats/Stat";
import PF2e from "./parsers/PF2e";

Expand All @@ -23,6 +23,9 @@ export default class SetupHooksPF2e {
window.Hooks.on(
"createChatMessage",
async function (chatMessagePF2e: ChatMessage) {
const chatRollMode = chatMessagePF2e.isContentVisible
? ChatRollMode.publicroll
: ChatRollMode.selfroll;
let chatType = chatMessagePF2e?.flags?.pf2e?.context?.type;
if (!chatType) {
if (chatMessagePF2e.isDamageRoll) {
Expand All @@ -46,6 +49,7 @@ export default class SetupHooksPF2e {
).result ?? 0,
chatMessagePF2e.token.name,
chatMessagePF2e.actor.id,
chatRollMode,
);
}
if (chatType === "damage-roll") {
Expand All @@ -71,6 +75,7 @@ export default class SetupHooksPF2e {
).result ?? 0,
chatMessagePF2e.token.name,
chatMessagePF2e.actor.id,
chatRollMode,
);
}
},
Expand Down
Loading

0 comments on commit 4374d0d

Please sign in to comment.