mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-07-22 02:19:54 +02:00
Merge 0301d6f134 into 8d68166e4c
This commit is contained in:
commit
15b67d016c
6 changed files with 215 additions and 5 deletions
|
|
@ -3258,7 +3258,8 @@
|
|||
"knowTheTide": "Know The Tide gained a token",
|
||||
"lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}",
|
||||
"noTokenTargeted": "No token is targeted",
|
||||
"behaviorRegionRequiresGM": "Creating a Region with an attached Behavior requires an online GM"
|
||||
"behaviorRegionRequiresGM": "Creating a Region with an attached Behavior requires an online GM",
|
||||
"comboDiceOnlyTwoDiceError": "Combo dice functionality only works on a single pair of two dice"
|
||||
},
|
||||
"Progress": {
|
||||
"migrationLabel": "Performing system migration. Please wait and do not close Foundry."
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ export { default as DamageRoll } from './damageRoll.mjs';
|
|||
export { default as DHRoll } from './dhRoll.mjs';
|
||||
export { default as DualityRoll } from './dualityRoll.mjs';
|
||||
export { default as FateRoll } from './fateRoll.mjs';
|
||||
export { BaseDie } from './die/_module.mjs';
|
||||
export { diceTypes } from './die/_module.mjs';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
import { adjustDice, triggerChatRollFx } from '../../helpers/utils.mjs';
|
||||
|
||||
export default class BaseDie extends foundry.dice.terms.Die {
|
||||
async rerollResult(resultIndex) {
|
||||
static MODIFIERS = {
|
||||
...foundry.dice.terms.Die.MODIFIERS,
|
||||
cc: 'compoundComboDice',
|
||||
c: 'comboDice'
|
||||
};
|
||||
|
||||
async rerollResult(resultToReroll) {
|
||||
const resultIndex = Number(resultToReroll);
|
||||
const result = this.results[resultIndex];
|
||||
result.rerolled = true;
|
||||
result.active = false;
|
||||
|
|
@ -8,5 +17,199 @@ export default class BaseDie extends foundry.dice.terms.Die {
|
|||
const rerolledResult = this.results[this.results.length - 1];
|
||||
this.results.splice(this.results.length - 1, 1);
|
||||
this.results.splice(resultIndex, 0, rerolledResult);
|
||||
|
||||
if (['c', 'cc'].some(x => this.modifiers.includes(x))) {
|
||||
await this.handleComboDiceReroll(resultIndex, result);
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
getResultCSS(result) {
|
||||
const hasSuccess = result.success !== undefined;
|
||||
const hasFailure = result.failure !== undefined;
|
||||
const isMax = result.result === this.faces;
|
||||
const isMin = result.result === 1;
|
||||
return [
|
||||
this.constructor.name.toLowerCase(),
|
||||
result.denomination ?? this.denomination, // Accomodating ComboDie as a result can have a different denomination than the die as a whole
|
||||
result.success ? 'success' : null,
|
||||
result.failure ? 'failure' : null,
|
||||
result.rerolled ? 'rerolled' : null,
|
||||
result.exploded ? 'exploded' : null,
|
||||
result.discarded ? 'discarded' : null,
|
||||
!(hasSuccess || hasFailure) && isMin ? 'min' : null,
|
||||
!(hasSuccess || hasFailure) && isMax ? 'max' : null
|
||||
];
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Modifier Logic */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async compoundComboDice() {
|
||||
return this.handleComboDice({ maxIncreasesDiceSize: true});
|
||||
}
|
||||
|
||||
async comboDice() {
|
||||
return this.handleComboDice({ maxIncreasesDiceSize: false });
|
||||
}
|
||||
|
||||
async handleComboDice(maxIncreasesDiceSize) {
|
||||
/* ComboDice only works with exactly two dice and both have to be the same denomination */
|
||||
if (this.number !== 2) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.comboDiceOnlyTwoDiceError'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.rollComboDice(maxIncreasesDiceSize);
|
||||
}
|
||||
|
||||
async rollComboDice(options) {
|
||||
const { maxIncreasesDiceSize, rerollStartIndex } = options;
|
||||
const initialResultsLength = this.results.filter(x => x.active).length;
|
||||
const result = await this.continueCombo(maxIncreasesDiceSize);
|
||||
|
||||
/* The flow of DiceSoNice has no way of knowing that some of the results of a Die should be a different denomination
|
||||
We solve this by marking the results as hidden so they're not picked up by the auto roll of DiceSoNice.
|
||||
The actual rolls are done here in place so every dice gets the correct denomination.
|
||||
*/
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const resultsToRoll = this.results.filter((x, index) => {
|
||||
if (!x.active) return false;
|
||||
if (rerollStartIndex)
|
||||
return index === rerollStartIndex || index > initialResultsLength - 1;
|
||||
|
||||
return true;
|
||||
});
|
||||
const rolls = [];
|
||||
for (const result of resultsToRoll) {
|
||||
const roll = await (new Roll(`1${result.denomination ?? this.denomination}`)).evaluate();
|
||||
roll.terms[0].results = [result];
|
||||
roll._evaluateTotal();
|
||||
rolls.push(roll);
|
||||
}
|
||||
|
||||
/* If there are other dice that will be rolled we cannot await here. The other dice will be awaited in the normal flow */
|
||||
if (rerollStartIndex === undefined && this._root.dice.length > 1) {
|
||||
for (const roll of rolls) {
|
||||
game.dice3d.showForRoll(roll, game.user, true);
|
||||
}
|
||||
} else {
|
||||
await Promise.allSettled(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true)));
|
||||
}
|
||||
}
|
||||
|
||||
for (const result of this.results)
|
||||
result.hidden = true;
|
||||
|
||||
const modifier = maxIncreasesDiceSize ? 'cc' : 'c';
|
||||
if (!this.modifiers.includes(modifier))
|
||||
this.modifiers.push(modifier);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async continueCombo(maxIncreasesDiceSize) {
|
||||
const activeResults = this.results.filter(x => x.active);
|
||||
const lastIndex = activeResults.length - 1;
|
||||
const lastResult = activeResults[lastIndex];
|
||||
|
||||
/* The Combo only continues if the latest roll was higher or equal to the previous */
|
||||
if (lastResult.result < activeResults[lastIndex - 1].result) return false;
|
||||
|
||||
const lastFaces = lastResult.denomination ?
|
||||
Number(lastResult.denomination.slice(1)) : this.faces;
|
||||
const currentDenomination = `d${lastFaces}`
|
||||
const increaseDiceSize =
|
||||
maxIncreasesDiceSize && lastFaces < 12 &&
|
||||
lastResult.result === lastFaces;
|
||||
|
||||
const denomination = increaseDiceSize ? adjustDice(currentDenomination, false) : currentDenomination;
|
||||
|
||||
const newRoll = await (new Roll(`1${denomination}`)).evaluate();
|
||||
this.results.push({ result: newRoll.total, denomination: denomination, active: true });
|
||||
this.number += 1;
|
||||
|
||||
return this.continueCombo(maxIncreasesDiceSize);
|
||||
}
|
||||
|
||||
async handleComboDiceReroll(rerolledIndex, originalResult) {
|
||||
/* Potentially sliced when correcting compound dice at (1) */
|
||||
let resultGroupingIndexes = this.results.map((x, index) => ({ index, active: x.active }))
|
||||
.filter(x => x.active).map(x => x.index);
|
||||
if (resultGroupingIndexes.length <= 1) return;
|
||||
|
||||
const rerolledResult = this.results[rerolledIndex];
|
||||
const compoundCombo = this.modifiers.includes('cc');
|
||||
const rerollGroupingIndex = resultGroupingIndexes.indexOf(rerolledIndex);
|
||||
|
||||
const previousIndex = resultGroupingIndexes[rerollGroupingIndex - 1];
|
||||
const previousResult = this.results[previousIndex];
|
||||
const nextIndex = resultGroupingIndexes[rerollGroupingIndex + 1];
|
||||
const nextResult = this.results[nextIndex];
|
||||
|
||||
const dropDice = preceeding => {
|
||||
const cutoffIndex = preceeding ? resultGroupingIndexes[rerollGroupingIndex + 1] + 1 : rerolledIndex + 1;
|
||||
this.results = this.results.slice(0, cutoffIndex);
|
||||
this.number = this.results.filter(x => x.active).length;
|
||||
};
|
||||
|
||||
/* (1) If subsequent size increases from the compound effect are now incorrect, drop subsequent dice */
|
||||
const lastFaces = Number((originalResult.denomination ?? this.denomination).slice(1));
|
||||
if (
|
||||
compoundCombo &&
|
||||
(
|
||||
(originalResult.result === lastFaces &&
|
||||
rerolledResult.result !== lastFaces)
|
||||
||
|
||||
(originalResult.result !== lastFaces &&
|
||||
rerolledResult.result === lastFaces)
|
||||
)
|
||||
) {
|
||||
dropDice(false);
|
||||
resultGroupingIndexes = resultGroupingIndexes.slice(0, this.number);
|
||||
}
|
||||
|
||||
/* (2) Rerolling any of the last two dice might introduce new results */
|
||||
if (
|
||||
rerollGroupingIndex === resultGroupingIndexes.length - 1 &&
|
||||
rerolledResult.result >= previousResult?.result
|
||||
) {
|
||||
return await this.rollComboDice({
|
||||
maxIncreasesDiceSize: compoundCombo,
|
||||
rerollStartIndex: rerollGroupingIndex
|
||||
});
|
||||
} else if (
|
||||
rerollGroupingIndex === resultGroupingIndexes.length - 2 &&
|
||||
rerolledResult.result < nextResult?.result
|
||||
) {
|
||||
return await this.rollComboDice({
|
||||
maxIncreasesDiceSize: compoundCombo,
|
||||
rerollStartIndex: rerollGroupingIndex
|
||||
});
|
||||
}
|
||||
/* (3) Rerolling a subsequent dice might invalidate later dice which should then be dropped */
|
||||
else if (rerolledResult.result < previousResult?.result){
|
||||
dropDice(false);
|
||||
}
|
||||
/* (4) Rerolling a preceeding dice might invalidate later dice which should then be dropped */
|
||||
else if (rerolledResult.result >= nextResult?.result) {
|
||||
dropDice(true);
|
||||
}
|
||||
|
||||
const fakeRollFaces =
|
||||
rerolledResult.denomination ? rerolledResult.denomination.slice(1) : this.faces;
|
||||
const fakeRoll = {
|
||||
_evaluated: true,
|
||||
dice: [new foundry.dice.terms.Die({
|
||||
...this,
|
||||
results: [rerolledResult],
|
||||
total: rerolledResult.result,
|
||||
faces: fakeRollFaces
|
||||
})],
|
||||
options: { appearance: {} }
|
||||
};
|
||||
await triggerChatRollFx([fakeRoll]);
|
||||
rerolledResult.hidden = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import BaseDie from './baseDie.mjs';
|
||||
import { updateResourcesForDualityReroll } from '../helpers.mjs';
|
||||
import BaseDie from './baseDie.mjs';
|
||||
|
||||
export default class DualityDie extends BaseDie {
|
||||
constructor(options) {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
{{#if active}}
|
||||
<div class="roll-die{{#unless @../first}} has-plus{{/unless}}">
|
||||
<div
|
||||
class="dice reroll-button {{../denomination}}"
|
||||
class="dice reroll-button {{ifThen denomination denomination ../denomination}}"
|
||||
data-type="damage" data-damage-type="{{@../../key}}" data-dice="{{@../key}}" data-result="{{@key}}" {{#if ../../isResource}}data-is-resource="true"{{/if}}
|
||||
>
|
||||
{{#if hasRerolls}}<i class="fa-solid fa-dice dice-rerolled" data-tooltip="{{localize "DAGGERHEART.GENERAL.rerolled"}}"></i>{{/if}}
|
||||
|
|
|
|||
|
|
@ -84,8 +84,13 @@
|
|||
{{else}}
|
||||
{{#each roll.dice}}
|
||||
{{#each results}}
|
||||
{{debug this}}
|
||||
<div class="roll-die {{#unless (or @../first discarded)}} has-plus{{/unless}}">
|
||||
<div class="dice {{../denomination}}{{#if discarded}} discarded{{else}}{{#if (and @../first ../../roll.advantage.type)}}{{#if (eq ../../roll.advantage.type 1)}} color-adv{{else}} color-dis{{/if}}{{/if}}{{#if success}} color-adv{{/if}}{{/if}}">
|
||||
<div class="
|
||||
dice
|
||||
{{ifThen denomination denomination ../denomination}}
|
||||
{{#if discarded}} discarded{{else}}{{#if (and @../first ../../roll.advantage.type)}}{{#if (eq ../../roll.advantage.type 1)}} color-adv{{else}} color-dis{{/if}}{{/if}}{{#if success}} color-adv{{/if}}{{/if}}
|
||||
">
|
||||
{{result}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue