Skip to content

Commit 97e08b7

Browse files
authored
Roll crits and fumbles exactly once so the dice roll shown matches the table result. (#113)
* Roll crits and fumbles exactly once so the dice roll shown matches the table result. Ensure crits and fumbles are displayed correctly when using standard cards with no tables setup. Add localised string for 'Fumble'. Closes #109. * Fix formatting and tests.
1 parent 49a080e commit 97e08b7

File tree

6 files changed

+55
-21
lines changed

6 files changed

+55
-21
lines changed

lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@
248248
"DCC.FlagsTitle": "Configure Special Traits",
249249
"DCC.ForgeDocument": "Forge Document",
250250
"DCC.Formula": "Formula",
251+
"DCC.Fumble": "Fumble",
251252
"DCC.FumbleDie": "Fumble Die",
252253
"DCC.Generic": "Generic",
253254
"DCC.GrantedAbilities": "Granted Abilities",

module/__tests__/actor.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ test('roll weapon attack', () => {
111111
user: 1,
112112
speaker: { alias: 'test character', _id: 1 },
113113
type: 'emote',
114-
content: 'AttackRollEmote,weaponName:longsword,rollHTML:<a class="inline-roll inline-result" data-roll="%7B%22dice%22%3A%5B%7B%22results%22%3A%5B10%5D%2C%22options%22%3A%7B%7D%7D%5D%7D" title="undefined"><i class="fas fa-dice-d20"></i> undefined</a>,damageRollHTML:<a class="inline-roll inline-result damage-applyable" data-roll="%7B%22dice%22%3A%5B%7B%22results%22%3A%5B10%5D%2C%22options%22%3A%7B%7D%7D%5D%7D" data-damage="undefined" title="undefined"><i class="fas fa-dice-d20"></i> undefined</a>,crit:,fumble:[object Object]',
114+
content: 'AttackRollEmote,weaponName:longsword,rollHTML:<a class="inline-roll inline-result" data-roll="%7B%22dice%22%3A%5B%7B%22results%22%3A%5B10%5D%2C%22options%22%3A%7B%22dcc%22%3A%7B%22upperThreshold%22%3A20%7D%7D%7D%5D%7D" title="undefined"><i class="fas fa-dice-d20"></i> undefined</a>,damageRollHTML:<a class="inline-roll inline-result damage-applyable" data-roll="%7B%22dice%22%3A%5B%7B%22results%22%3A%5B10%5D%2C%22options%22%3A%7B%7D%7D%5D%7D" data-damage="undefined" title="undefined"><i class="fas fa-dice-d20"></i> undefined</a>,crit:,fumble:[object Object]',
115115
sound: 'diceSound'
116116
})
117117

@@ -134,7 +134,7 @@ test('roll weapon attack', () => {
134134
user: 1,
135135
speaker: { alias: 'test character', _id: 1 },
136136
type: 'emote',
137-
content: 'AttackRollEmote,weaponName:longsword,rollHTML:<a class="inline-roll inline-result" data-roll="%7B%22dice%22%3A%5B%7B%22results%22%3A%5B10%5D%2C%22options%22%3A%7B%7D%7D%5D%7D" title="undefined"><i class="fas fa-dice-d20"></i> undefined</a>,damageRollHTML:<a class="inline-roll inline-result damage-applyable" data-roll="%7B%22dice%22%3A%5B%7B%22results%22%3A%5B10%5D%2C%22options%22%3A%7B%7D%7D%5D%7D" data-damage="undefined" title="undefined"><i class="fas fa-dice-d20"></i> undefined</a>,crit:,fumble:[object Object]',
137+
content: 'AttackRollEmote,weaponName:longsword,rollHTML:<a class="inline-roll inline-result" data-roll="%7B%22dice%22%3A%5B%7B%22results%22%3A%5B10%5D%2C%22options%22%3A%7B%22dcc%22%3A%7B%22upperThreshold%22%3A20%7D%7D%7D%5D%7D" title="undefined"><i class="fas fa-dice-d20"></i> undefined</a>,damageRollHTML:<a class="inline-roll inline-result damage-applyable" data-roll="%7B%22dice%22%3A%5B%7B%22results%22%3A%5B10%5D%2C%22options%22%3A%7B%7D%7D%5D%7D" data-damage="undefined" title="undefined"><i class="fas fa-dice-d20"></i> undefined</a>,crit:,fumble:[object Object]',
138138
sound: 'diceSound'
139139
})
140140

module/actor-sheets-dcc.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/* global game */
2+
13
/**
24
* DCC specific character sheet overrides
35
*/

module/actor.js

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,9 @@ class DCCActor extends Actor {
424424
const roll = new Roll(formula, { ab: attackBonus, critical: critRange })
425425
roll.roll()
426426
const d20RollResult = roll.dice[0].total
427+
roll.dice[0].options.dcc = {
428+
upperThreshold: critRange
429+
}
427430

428431
if (displayStandardCards) {
429432
roll.toMessage({
@@ -484,12 +487,8 @@ class DCCActor extends Actor {
484487
// Display standard cards in chat?
485488
const displayStandardCards = game.settings.get('dcc', 'useStandardDiceRoller')
486489

487-
// Roll the crit
488-
const roll = new Roll(`${this.data.data.attributes.critical.die} + ${this.data.data.abilities.lck.mod}`)
489-
roll.roll()
490-
const rollData = escape(JSON.stringify(roll))
491-
const rollTotal = roll.total
492-
const rollHTML = `<a class="inline-roll inline-result" data-roll="${rollData}" data-damage="${rollTotal}" title="${Roll.cleanFormula(roll.terms || roll.formula)}"><i class="fas fa-dice-d20"></i> ${rollTotal}</a>`
490+
// Roll object for the crit die
491+
let roll = new Roll(`${this.data.data.attributes.critical.die} + ${this.data.data.abilities.lck.mod}`)
493492

494493
// Lookup the crit table if available
495494
let critResult = null
@@ -507,13 +506,31 @@ class DCCActor extends Actor {
507506
}
508507
}
509508

509+
// Either roll the die or grab the roll from the table lookup
510+
if (!critResult) {
511+
roll.roll()
512+
} else {
513+
roll = critResult.roll
514+
}
515+
510516
if (!displayStandardCards) {
517+
// Create the roll emote
518+
const rollData = escape(JSON.stringify(roll))
519+
const rollTotal = roll.total
520+
const rollHTML = `<a class="inline-roll inline-result" data-roll="${rollData}" data-damage="${rollTotal}" title="${Roll.cleanFormula(roll.terms || roll.formula)}"><i class="fas fa-dice-d20"></i> ${rollTotal}</a>`
521+
511522
// Display crit result or just a notification of the crit
512523
if (critResult) {
513524
return ` <br/><br/><span style='color:#ff0000; font-weight: bolder'>${game.i18n.localize('DCC.CriticalHit')}!</span> ${rollHTML}<br/>${critResult.results[0].text}`
514525
} else {
515526
return ` <br/><br/><span style='color:#ff0000; font-weight: bolder'>${game.i18n.localize('DCC.CriticalHit')}!</span> ${rollHTML}`
516527
}
528+
} else if (!critResult) {
529+
// Display the raw crit roll
530+
await roll.toMessage({
531+
speaker: ChatMessage.getSpeaker({ actor: this }),
532+
flavor: `${game.i18n.localize('DCC.CriticalHit')}!`
533+
})
517534
}
518535
}
519536

@@ -531,12 +548,8 @@ class DCCActor extends Actor {
531548
fumbleDie = '1d4'
532549
}
533550

534-
// Roll the fumble
535-
const roll = new Roll(`${fumbleDie} - ${this.data.data.abilities.lck.mod}`)
536-
roll.roll()
537-
const rollData = escape(JSON.stringify(roll))
538-
const rollTotal = roll.total
539-
const rollHTML = `<a class="inline-roll inline-result" data-roll="${rollData}" data-damage="${rollTotal}" title="${Roll.cleanFormula(roll.terms || roll.formula)}"><i class="fas fa-dice-d20"></i> ${rollTotal}</a>`
551+
// Roll object for the fumble die
552+
let roll = new Roll(`${fumbleDie} - ${this.data.data.abilities.lck.mod}`)
540553

541554
// Lookup the fumble table if available
542555
let fumbleResult = null
@@ -557,13 +570,31 @@ class DCCActor extends Actor {
557570
}
558571
}
559572

573+
// Either roll the die or grab the roll from the table lookup
574+
if (!fumbleResult) {
575+
roll.roll()
576+
} else {
577+
roll = fumbleResult.roll
578+
}
579+
560580
if (!displayStandardCards) {
581+
// Create the roll emote
582+
const rollData = escape(JSON.stringify(roll))
583+
const rollTotal = roll.total
584+
const rollHTML = `<a class="inline-roll inline-result" data-roll="${rollData}" data-damage="${rollTotal}" title="${Roll.cleanFormula(roll.terms || roll.formula)}"><i class="fas fa-dice-d20"></i> ${rollTotal}</a>`
585+
561586
// Display fumble result or just a notification of the fumble
562587
if (fumbleResult) {
563-
return ` <br/><br/><span style='color:red; font-weight: bolder'>Fumble!</span> ${rollHTML}<br/>${fumbleResult.results[0].text}`
588+
return ` <br/><br/><span style='color:red; font-weight: bolder'>${game.i18n.localize('DCC.Fumble')}!</span> ${rollHTML}<br/>${fumbleResult.results[0].text}`
564589
} else {
565-
return ` <br/><br/><span style='color:red; font-weight: bolder'>Fumble!</span> ${rollHTML}`
590+
return ` <br/><br/><span style='color:red; font-weight: bolder'>${game.i18n.localize('DCC.Fumble')}!</span> ${rollHTML}`
566591
}
592+
} else if (!fumbleResult) {
593+
// Display the raw fumble roll
594+
await roll.toMessage({
595+
speaker: ChatMessage.getSpeaker({ actor: this }),
596+
flavor: `${game.i18n.localize('DCC.Fumble')}!`
597+
})
567598
}
568599
}
569600

module/item.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ class DCCItem extends Item {
1717

1818
// Set default inherit crit range for legacy items
1919
if (this.data.data.config.inheritCritRange === undefined) {
20-
this.data.data.config.inheritCritRange == true
20+
this.data.data.config.inheritCritRange = true
2121
this.update({
22-
"data.config.inheritCritRange": true
22+
'data.config.inheritCritRange': true
2323
})
2424
}
2525

@@ -30,11 +30,11 @@ class DCCItem extends Item {
3030
// If not inheriting crit range make sure there is a value (for legacy items)
3131
if (this.data.data.critRange === null || this.data.data.critRange === undefined) {
3232
this.update({
33-
"data.critRange": 20
33+
'data.critRange': 20
3434
})
3535
}
3636
}
37-
37+
3838
// Spells can inherit the owner's spell check
3939
if (this.data.data.config.inheritSpellCheck) {
4040
this.data.data.spellCheck.value = this.actor.data.data.class.spellCheck

module/migrations.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const migrateActorData = function (actor) {
110110
// Add useDisapprovalRange to cleric skills
111111
'data.skills.divineAid.useDisapprovalRange': true,
112112
'data.skills.turnUnholy.useDisapprovalRange': true,
113-
'data.skills.layOnHands.useDisapprovalRange': true,
113+
'data.skills.layOnHands.useDisapprovalRange': true
114114
}
115115

116116
// Migrate Owned Items

0 commit comments

Comments
 (0)