Skip to content

Commit

Permalink
[#3156] Add setting to show attack roll result (AC check) card to pla…
Browse files Browse the repository at this point in the history
…yers (#3157)

Co-authored-by: Pavel Basov <[email protected]>
Co-authored-by: Kim Mantas <[email protected]>
  • Loading branch information
3 people authored Jul 22, 2024
1 parent 2f14e7f commit b6ca06a
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 2 deletions.
7 changes: 7 additions & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2119,6 +2119,13 @@
"SETTINGS.5eAutoCollapseCardN": "Collapse Item Cards in Chat",
"SETTINGS.5eAutoSpellTemplateL": "When a spell is cast, defaults to begin the process to create the corresponding Measured Template if any (requires TRUSTED or higher player role)",
"SETTINGS.5eAutoSpellTemplateN": "Always place Spell Template",
"SETTINGS.5eAttackRollVisibility": {
"Name": "Attack Result Visibility",
"Hint": "Control visibility of attack roll results in chat cards for players.",
"All": "Show results & target ACs",
"HideAC": "Show only results",
"None": "Hide all"
},
"SETTINGS.5eChallengeVisibility": {
"Name": "Challenge Visibility",
"Hint": "Control what roll DCs are visible to the players and whether successes/failures are highlighted.",
Expand Down
12 changes: 10 additions & 2 deletions module/documents/chat-message.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export default class ChatMessage5e extends ChatMessage {
if ( !this.isContentVisible || !this.rolls.length ) return;
const originatingMessage = game.messages.get(this.getFlag("dnd5e", "originatingMessage")) ?? this;
const displayChallenge = originatingMessage?.shouldDisplayChallenge;
const displayAttackResult = game.user.isGM || (game.settings.get("dnd5e", "attackRollVisibility") !== "none");

/**
* Create an icon to indicate success or failure.
Expand Down Expand Up @@ -202,7 +203,9 @@ export default class ChatMessage5e extends ChatMessage {
if ( !total ) continue;
// Only attack rolls and death saves can crit or fumble.
const canCrit = ["attack", "death"].includes(this.getFlag("dnd5e", "roll.type"));
if ( d.options.target && displayChallenge ) {
const isAttack = this.getFlag("dnd5e", "roll.type") === "attack";
const showResult = isAttack ? displayAttackResult : displayChallenge;
if ( d.options.target && showResult ) {
if ( d20Roll.total >= d.options.target ) total.classList.add("success");
else total.classList.add("failure");
}
Expand Down Expand Up @@ -359,7 +362,9 @@ export default class ChatMessage5e extends ChatMessage {
_enrichAttackTargets(html) {
const attackRoll = this.rolls[0];
const targets = this.getFlag("dnd5e", "targets");
if ( !game.user.isGM || !(attackRoll instanceof dnd5e.dice.D20Roll) || !targets?.length ) return;
const visibility = game.settings.get("dnd5e", "attackRollVisibility");
const isVisible = game.user.isGM || (visibility !== "none");
if ( !isVisible || !(attackRoll instanceof dnd5e.dice.D20Roll) || !targets?.length ) return;
const tray = document.createElement("div");
tray.classList.add("dnd5e2");
tray.innerHTML = `
Expand All @@ -376,15 +381,18 @@ export default class ChatMessage5e extends ChatMessage {
`;
const evaluation = tray.querySelector("ul");
evaluation.innerHTML = targets.map(({ name, ac, uuid }) => {
if ( !game.user.isGM && (visibility !== "all") ) ac = "";
const isMiss = !attackRoll.isCritical && ((attackRoll.total < ac) || attackRoll.isFumble);
return [`
<li data-uuid="${uuid}" class="target ${isMiss ? "miss" : "hit"}">
<i class="fas ${isMiss ? "fa-times" : "fa-check"}"></i>
<div class="name">${name}</div>
${ac ? `
<div class="ac">
<i class="fas fa-shield-halved"></i>
<span>${ac}</span>
</div>
` : ""}
</li>
`, isMiss];
}).sort((a, b) => (a[1] === b[1]) ? 0 : a[1] ? 1 : -1).reduce((str, [li]) => str + li, "");
Expand Down
14 changes: 14 additions & 0 deletions module/settings.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ export function registerSystemSettings() {
}
});

game.settings.register("dnd5e", "attackRollVisibility", {
name: "SETTINGS.5eAttackRollVisibility.Name",
hint: "SETTINGS.5eAttackRollVisibility.Hint",
scope: "world",
config: true,
default: "none",
type: String,
choices: {
all: "SETTINGS.5eAttackRollVisibility.All",
hideAC: "SETTINGS.5eAttackRollVisibility.HideAC",
none: "SETTINGS.5eAttackRollVisibility.None"
}
});

// Encumbrance tracking
game.settings.register("dnd5e", "encumbrance", {
name: "SETTINGS.5eEncumbrance.Name",
Expand Down

0 comments on commit b6ca06a

Please sign in to comment.