mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-18 16:09:03 +01:00
[Feature] Death moves and Fate rolls (#1463)
* Update the death move descriptions * Renamed to DhDeathMove * Partial Fate Roll creation and Fate Roll Enricher (/fr) * Hide stuff not required for fate roll * Hide formula display; code removal; start to add Fear die as a choice for Fate roll * Fix chat message display; start moving towards supporting Hope and Fear for Fate roll * /fr now supports type=X, where X is Hope or Fear, if not supplied, defaults to Hope * Fixed DSN rolling; removed console messages; chat message clean up * Add localisation entry * Trying to sort out the button for the fate roll * Style the fate message based on Hope/Fear colors. * Partial improvement on the fate template buttons - chat display is correct, but the roll dialog is wrong * Fixed enricher button; localization fixes; debug cleanup * Error checking for the fate type parsing in all potential problem locations * Added localization for the fate type parsing error * Start on Avoid Death death move * debug stuff * More death moves setup/testing * Avoid fate scars update in place, with scars migrating to an integer value. * Remove some debug code; add Blaze Of Glory shell * Start on Guaranteed Critical for Blaze of Glory * Partial implementation of Blaze of Glory * Dice/critical checks/tests * Moved detection of guaranteed critical to before the roll dialog is created, so it can be skipped; removed debug code * Remove debug * Update Blaze of Glory effect description * Risk It All - critical roll - clear all stress and HP * Auto remove all marked stress and HP for Risk It All, if Hope value rolled covers it. * Display the Death Move description in chat expanded if the appropriate config setting is on * Made the Blaze of Glory ActiveEffect image use configured version * Update the current Hope value if the scar value change affects it * Scars management in the Character details editor * Separate less file for the Death Moves instead of reusing Downtime * Added result messages to the Death Move chat output and removed debug statements * Some localization, style and smaller changes * Fixed RiskItAll resource handling method * Risk It All success chat message start * [Add] Hope/Scar Interplay (#1531) * Migrated character.maxHope to homebrew settings * Added a visual for scars * . * . * Pass the hope value in the button data; skeleton risk it all dialog to fill out. * Start on risk it dialog * More dialog stuff * Remove non-existent field * Dialog templating and logic * . * Ensure effect is Applied to Actor (#1547) Co-authored-by: Chris Ryan <chrisr@blackhole> * [Fix] 1548 - Standalone Item Add Actions (#1549) * Fixed so that items not on an actor don't error out on creating actions * Fixed deletion of items error * Raised version * Fix the sliders to do the correct maximums * Pass the actor id through the button; fix /dr and /fr flavor text * Remove debug message --------- Co-authored-by: Chris Ryan <chrisr@blackhole> Co-authored-by: WBHarry <williambjrklund@gmail.com> Co-authored-by: WBHarry <89362246+WBHarry@users.noreply.github.com>
This commit is contained in:
parent
3103a40c26
commit
9d75157e17
38 changed files with 1166 additions and 258 deletions
|
|
@ -8,8 +8,9 @@ import * as fields from './module/data/fields/_module.mjs';
|
||||||
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
|
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
|
||||||
import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module.mjs';
|
import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module.mjs';
|
||||||
import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs';
|
import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs';
|
||||||
import { BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll } from './module/dice/_module.mjs';
|
import { BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll } from './module/dice/_module.mjs';
|
||||||
import { enrichedDualityRoll } from './module/enrichers/DualityRollEnricher.mjs';
|
import { enrichedDualityRoll } from './module/enrichers/DualityRollEnricher.mjs';
|
||||||
|
import { enrichedFateRoll, getFateTypeData } from './module/enrichers/FateRollEnricher.mjs';
|
||||||
import {
|
import {
|
||||||
handlebarsRegistration,
|
handlebarsRegistration,
|
||||||
runMigrations,
|
runMigrations,
|
||||||
|
|
@ -24,12 +25,13 @@ import TokenManager from './module/documents/tokenManager.mjs';
|
||||||
CONFIG.DH = SYSTEM;
|
CONFIG.DH = SYSTEM;
|
||||||
CONFIG.TextEditor.enrichers.push(...enricherConfig);
|
CONFIG.TextEditor.enrichers.push(...enricherConfig);
|
||||||
|
|
||||||
CONFIG.Dice.rolls = [BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll];
|
CONFIG.Dice.rolls = [BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll];
|
||||||
CONFIG.Dice.daggerheart = {
|
CONFIG.Dice.daggerheart = {
|
||||||
DHRoll: DHRoll,
|
DHRoll: DHRoll,
|
||||||
DualityRoll: DualityRoll,
|
DualityRoll: DualityRoll,
|
||||||
D20Roll: D20Roll,
|
D20Roll: D20Roll,
|
||||||
DamageRoll: DamageRoll
|
DamageRoll: DamageRoll,
|
||||||
|
FateRoll: FateRoll
|
||||||
};
|
};
|
||||||
|
|
||||||
CONFIG.Actor.documentClass = documents.DhpActor;
|
CONFIG.Actor.documentClass = documents.DhpActor;
|
||||||
|
|
@ -298,8 +300,8 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
const difficulty = rollCommand.difficulty;
|
const difficulty = rollCommand.difficulty;
|
||||||
|
|
||||||
const target = getCommandTarget({ allowNull: true });
|
const target = getCommandTarget({ allowNull: true });
|
||||||
const title = traitValue
|
const title = flavor ??
|
||||||
? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
traitValue ? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||||
ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label)
|
ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label)
|
||||||
})
|
})
|
||||||
: game.i18n.localize('DAGGERHEART.GENERAL.duality');
|
: game.i18n.localize('DAGGERHEART.GENERAL.duality');
|
||||||
|
|
@ -316,6 +318,34 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.startsWith('/fr')) {
|
||||||
|
const result =
|
||||||
|
message.trim().toLowerCase() === '/fr' ? { result: {} } : rollCommandToJSON(message.replace(/\/fr\s?/, ''));
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateParsing'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result: rollCommand, flavor } = result;
|
||||||
|
const fateTypeData = getFateTypeData(rollCommand?.type);
|
||||||
|
|
||||||
|
if (!fateTypeData)
|
||||||
|
return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing'));
|
||||||
|
|
||||||
|
const { value: fateType, label: fateTypeLabel } = fateTypeData;
|
||||||
|
const target = getCommandTarget({ allowNull: true });
|
||||||
|
const title = flavor ?? game.i18n.localize('DAGGERHEART.GENERAL.fateRoll');
|
||||||
|
|
||||||
|
enrichedFateRoll({
|
||||||
|
target,
|
||||||
|
title,
|
||||||
|
label: fateTypeLabel,
|
||||||
|
fateType
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateActorsRangeDependentEffects = async token => {
|
const updateActorsRangeDependentEffects = async token => {
|
||||||
|
|
|
||||||
35
lang/en.json
35
lang/en.json
|
|
@ -619,6 +619,13 @@
|
||||||
"title": "{name} Resource",
|
"title": "{name} Resource",
|
||||||
"rerollDice": "Reroll Dice"
|
"rerollDice": "Reroll Dice"
|
||||||
},
|
},
|
||||||
|
"RiskItAllDialog": {
|
||||||
|
"title": "{name} - Risk It All",
|
||||||
|
"subtitle": "Clear Stress and Hit Points",
|
||||||
|
"remainingTitle": "Remaining Points",
|
||||||
|
"clearResource": "Clear {resource}",
|
||||||
|
"finalTitle": "Final Character Resources"
|
||||||
|
},
|
||||||
"TagTeamSelect": {
|
"TagTeamSelect": {
|
||||||
"title": "Tag Team Roll",
|
"title": "Tag Team Roll",
|
||||||
"leaderTitle": "Initiating Character",
|
"leaderTitle": "Initiating Character",
|
||||||
|
|
@ -1017,15 +1024,15 @@
|
||||||
"DeathMoves": {
|
"DeathMoves": {
|
||||||
"avoidDeath": {
|
"avoidDeath": {
|
||||||
"name": "Avoid Death",
|
"name": "Avoid Death",
|
||||||
"description": "You drop unconscious temporarily and work with the GM to describe how the situation gets much worse because of it. Then roll your Fear die; if its value is equal to or under your Level, take a Scar."
|
"description": "Your character avoids death and faces the consequences. They temporarily drop unconscious, and then you work with the GM to describe how the situation worsens. While unconscious, your character can't move or act, and they can't be targeted by an attack. They return to consciousness when an ally clears 1 or more of their marked Hit Points or when the party finishes a long rest. After your character falls unconscious, roll your Hope Die. If its value is equal to or less than your character's level, they gain a scar: permanently cross out a Hope slot and work with the GM to determine its lasting narrative impact and how, if possible, it can be restored. If you ever cross out your last Hope slot, your character's journey ends."
|
||||||
},
|
},
|
||||||
"riskItAll": {
|
"riskItAll": {
|
||||||
"name": "Risk It All",
|
"name": "Risk It All",
|
||||||
"description": "Roll your Duality Dice. If Hope is higher, you stay on your feet and clear an amount of Hit Points and/or Stress equal to the value of the Hope die (divide the Hope die value up between these however you’d prefer). If your Fear die is higher, you cross through the veil of death. If the Duality Dice are tied, you stay on your feet and clear all Hit Points and Stress."
|
"description": "Roll your Duality Dice. If the Hope Die is higher, your character stays on their feet and clears a number of Hit Points or Stress equal to the value of the Hope Die (you can divide the Hope Die value between Hit Points and Stress however you'd prefer). If the Fear Die is higher, your character crosses through the veil of death. If the Duality Dice show matching results, your character stays up and clears all Hit Points and Stress."
|
||||||
},
|
},
|
||||||
"blazeOfGlory": {
|
"blazeOfGlory": {
|
||||||
"name": "Blaze Of Glory",
|
"name": "Blaze Of Glory",
|
||||||
"description": "With Blaze of Glory, the player is accepting death for the character. Take one action (at GM discretion), which becomes an automatic critical success, then cross through the veil of death."
|
"description": "Your character embraces death and goes out in a blaze of glory. Take one final action. It automatically critically succeeds (with GM approval), and then you cross through the veil of death. NOTE: A Blaze of Glory effect has been added to your character. Any Duality Roll will automatically be a critical."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DomainCardTypes": {
|
"DomainCardTypes": {
|
||||||
|
|
@ -2066,6 +2073,7 @@
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"main": "Data",
|
"main": "Data",
|
||||||
"information": "Information",
|
"information": "Information",
|
||||||
|
"itemFeatures": "Item Features",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"inventory": "Inventory",
|
"inventory": "Inventory",
|
||||||
"loadout": "Loadout",
|
"loadout": "Loadout",
|
||||||
|
|
@ -2141,6 +2149,7 @@
|
||||||
"dropActorsHere": "Drop Actors here",
|
"dropActorsHere": "Drop Actors here",
|
||||||
"dropFeaturesHere": "Drop Features here",
|
"dropFeaturesHere": "Drop Features here",
|
||||||
"duality": "Duality",
|
"duality": "Duality",
|
||||||
|
"dualityDice": "Duality Dice",
|
||||||
"dualityRoll": "Duality Roll",
|
"dualityRoll": "Duality Roll",
|
||||||
"enabled": "Enabled",
|
"enabled": "Enabled",
|
||||||
"evasion": "Evasion",
|
"evasion": "Evasion",
|
||||||
|
|
@ -2150,11 +2159,14 @@
|
||||||
"plural": "Experiences"
|
"plural": "Experiences"
|
||||||
},
|
},
|
||||||
"failure": "Failure",
|
"failure": "Failure",
|
||||||
|
"fate": "Fate",
|
||||||
|
"fateRoll": "Fate Roll",
|
||||||
"fear": "Fear",
|
"fear": "Fear",
|
||||||
"features": "Features",
|
"features": "Features",
|
||||||
"formula": "Formula",
|
"formula": "Formula",
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"gm": "GM",
|
"gm": "GM",
|
||||||
|
"guaranteedCriticalSuccess": "Guaranteed Critical Success",
|
||||||
"healing": "Healing",
|
"healing": "Healing",
|
||||||
"healingRoll": "Healing Roll",
|
"healingRoll": "Healing Roll",
|
||||||
"hit": {
|
"hit": {
|
||||||
|
|
@ -2214,6 +2226,7 @@
|
||||||
"rollWith": "{roll} Roll",
|
"rollWith": "{roll} Roll",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"scalable": "Scalable",
|
"scalable": "Scalable",
|
||||||
|
"scars": "Scars",
|
||||||
"situationalBonus": "Situational Bonus",
|
"situationalBonus": "Situational Bonus",
|
||||||
"spent": "Spent",
|
"spent": "Spent",
|
||||||
"step": "Step",
|
"step": "Step",
|
||||||
|
|
@ -2510,6 +2523,7 @@
|
||||||
"resetMovesText": "Are you sure you want to reset?",
|
"resetMovesText": "Are you sure you want to reset?",
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
"maxFear": { "label": "Max Fear" },
|
"maxFear": { "label": "Max Fear" },
|
||||||
|
"maxHope": { "label": "Max Hope" },
|
||||||
"traitArray": { "label": "Initial Trait Modifiers" },
|
"traitArray": { "label": "Initial Trait Modifiers" },
|
||||||
"maxLoadout": {
|
"maxLoadout": {
|
||||||
"label": "Max Cards in Loadout",
|
"label": "Max Cards in Loadout",
|
||||||
|
|
@ -2657,7 +2671,16 @@
|
||||||
"currentTarget": "Current"
|
"currentTarget": "Current"
|
||||||
},
|
},
|
||||||
"deathMove": {
|
"deathMove": {
|
||||||
"title": "Death Move"
|
"title": "Death Move",
|
||||||
|
"gainScar": "You gained a scar.",
|
||||||
|
"avoidScar": "You have avoided a new scar.",
|
||||||
|
"journeysEnd": "You have {scars} Scars and have crossed out your last Hope slot. Your character's journey ends.",
|
||||||
|
"riskItAllCritical": "Critical Rolled, clearing all marked Stress and Hit Points.",
|
||||||
|
"riskItAllFailure": "The fear die rolled higher. You have crossed through the veil of death.",
|
||||||
|
"blazeOfGlory": "Blaze of Glory Effect Added!",
|
||||||
|
"riskItAllDialogButton": "Clear Stress And Hit Points.",
|
||||||
|
"riskItAllSuccessWithEnoughHope": "The Hope value is more than the marked Stress and Hit Points. Both are cleared fully.",
|
||||||
|
"riskItAllSuccess": "The hope die rolled higher, clear up to {hope} Stress And Hit Points."
|
||||||
},
|
},
|
||||||
"dicePool": {
|
"dicePool": {
|
||||||
"title": "Dice Pool"
|
"title": "Dice Pool"
|
||||||
|
|
@ -2779,7 +2802,9 @@
|
||||||
"noAssignedPlayerCharacter": "You have no assigned character.",
|
"noAssignedPlayerCharacter": "You have no assigned character.",
|
||||||
"noSelectedToken": "You have no selected token",
|
"noSelectedToken": "You have no selected token",
|
||||||
"onlyUseableByPC": "This can only be used with a PC token",
|
"onlyUseableByPC": "This can only be used with a PC token",
|
||||||
"dualityParsing": "Duality roll not properly formated",
|
"dualityParsing": "Duality roll not properly formatted",
|
||||||
|
"fateParsing": "Fate roll not properly formatted",
|
||||||
|
"fateTypeParsing": "Fate roll not properly formatted, bad fate type. Valid types are 'Hope' and 'Fear'",
|
||||||
"attributeFaulty": "The supplied Attribute doesn't exist",
|
"attributeFaulty": "The supplied Attribute doesn't exist",
|
||||||
"domainCardWrongDomain": "You don't have access to that Domain",
|
"domainCardWrongDomain": "You don't have access to that Domain",
|
||||||
"domainCardToHighLevel": "The Domain Card is too high level to be selected",
|
"domainCardToHighLevel": "The Domain Card is too high level to be selected",
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,4 @@ export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs';
|
||||||
export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs';
|
export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs';
|
||||||
export { default as GroupRollDialog } from './group-roll-dialog.mjs';
|
export { default as GroupRollDialog } from './group-roll-dialog.mjs';
|
||||||
export { default as TagTeamDialog } from './tagTeamDialog.mjs';
|
export { default as TagTeamDialog } from './tagTeamDialog.mjs';
|
||||||
|
export { default as RiskItAllDialog } from './riskItAllDialog.mjs';
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
context.formula = this.roll.constructFormula(this.config);
|
context.formula = this.roll.constructFormula(this.config);
|
||||||
if (this.actor?.system?.traits) context.abilities = this.getTraitModifiers();
|
if (this.actor?.system?.traits) context.abilities = this.getTraitModifiers();
|
||||||
|
|
||||||
context.showReaction = !this.config.roll?.type || context.rollType === 'DualityRoll';
|
context.showReaction = !this.config.skips?.reaction && context.rollType === 'DualityRoll';
|
||||||
context.reactionOverride = this.reactionOverride;
|
context.reactionOverride = this.reactionOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
import { enrichedFateRoll } from '../../enrichers/FateRollEnricher.mjs';
|
||||||
|
import { enrichedDualityRoll } from '../../enrichers/DualityRollEnricher.mjs';
|
||||||
|
|
||||||
export default class DhpDeathMove extends HandlebarsApplicationMixin(ApplicationV2) {
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
constructor(actor) {
|
constructor(actor) {
|
||||||
super({});
|
super({});
|
||||||
|
|
||||||
this.actor = actor;
|
this.actor = actor;
|
||||||
this.selectedMove = null;
|
this.selectedMove = null;
|
||||||
|
this.showRiskItAllButton = false;
|
||||||
|
this.riskItAllButtonLabel = '';
|
||||||
|
this.riskItAllHope = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
|
|
@ -38,6 +43,107 @@ export default class DhpDeathMove extends HandlebarsApplicationMixin(Application
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleAvoidDeath() {
|
||||||
|
const target = this.actor.uuid;
|
||||||
|
const config = await enrichedFateRoll({
|
||||||
|
target,
|
||||||
|
title: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.avoidDeath.name'),
|
||||||
|
label: `${game.i18n.localize('DAGGERHEART.GENERAL.hope')} ${game.i18n.localize('DAGGERHEART.GENERAL.fateRoll')}`,
|
||||||
|
fateType: 'Hope'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!config.roll.fate) return;
|
||||||
|
|
||||||
|
if (config.roll.fate.value <= this.actor.system.levelData.level.current) {
|
||||||
|
// apply scarring - for now directly apply - later add a button.
|
||||||
|
const newScarAmount = this.actor.system.scars + 1;
|
||||||
|
|
||||||
|
await this.actor.update({
|
||||||
|
system: {
|
||||||
|
scars: newScarAmount
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newScarAmount >= this.actor.system.resources.hope.max) {
|
||||||
|
return game.i18n.format('DAGGERHEART.UI.Chat.deathMove.journeysEnd', { scars: newScarAmount });
|
||||||
|
}
|
||||||
|
|
||||||
|
return game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.gainScar');
|
||||||
|
}
|
||||||
|
|
||||||
|
return game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.avoidScar');
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleRiskItAll() {
|
||||||
|
const config = await enrichedDualityRoll({
|
||||||
|
reaction: true,
|
||||||
|
traitValue: null,
|
||||||
|
target: this.actor,
|
||||||
|
difficulty: null,
|
||||||
|
title: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.riskItAll.name'),
|
||||||
|
label: game.i18n.localize('DAGGERHEART.GENERAL.dualityDice'),
|
||||||
|
actionType: null,
|
||||||
|
advantage: null,
|
||||||
|
customConfig: { skips: { resources: true, reaction: true } }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!config.roll.result) return;
|
||||||
|
|
||||||
|
const clearAllStressAndHitpointsUpdates = [
|
||||||
|
{ key: 'hitPoints', clear: true },
|
||||||
|
{ key: 'stress', clear: true }
|
||||||
|
];
|
||||||
|
|
||||||
|
let chatMessage = '';
|
||||||
|
if (config.roll.isCritical) {
|
||||||
|
config.resourceUpdates.addResources(clearAllStressAndHitpointsUpdates);
|
||||||
|
chatMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.riskItAllCritical');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.roll.result.duality == 1) {
|
||||||
|
if (
|
||||||
|
config.roll.hope.value >=
|
||||||
|
this.actor.system.resources.hitPoints.value + this.actor.system.resources.stress.value
|
||||||
|
) {
|
||||||
|
config.resourceUpdates.addResources(clearAllStressAndHitpointsUpdates);
|
||||||
|
chatMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.riskItAllSuccessWithEnoughHope');
|
||||||
|
} else {
|
||||||
|
chatMessage = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllSuccess', {
|
||||||
|
hope: config.roll.hope.value
|
||||||
|
});
|
||||||
|
this.showRiskItAllButton = true;
|
||||||
|
this.riskItAllHope = config.roll.hope.value;
|
||||||
|
this.riskItAllButtonLabel = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllDialogButton');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.roll.result.duality == -1) {
|
||||||
|
chatMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.riskItAllFailure');
|
||||||
|
}
|
||||||
|
|
||||||
|
await config.resourceUpdates.updateResources();
|
||||||
|
return chatMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleBlazeOfGlory() {
|
||||||
|
this.actor.createEmbeddedDocuments('ActiveEffect', [
|
||||||
|
{
|
||||||
|
name: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.name'),
|
||||||
|
description: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.description'),
|
||||||
|
img: CONFIG.DH.GENERAL.deathMoves.blazeOfGlory.img,
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
key: 'system.rules.roll.guaranteedCritical',
|
||||||
|
mode: 2,
|
||||||
|
value: 'true'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
return game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.blazeOfGlory');
|
||||||
|
}
|
||||||
|
|
||||||
static selectMove(_, button) {
|
static selectMove(_, button) {
|
||||||
const move = button.dataset.move;
|
const move = button.dataset.move;
|
||||||
this.selectedMove = CONFIG.DH.GENERAL.deathMoves[move];
|
this.selectedMove = CONFIG.DH.GENERAL.deathMoves[move];
|
||||||
|
|
@ -46,23 +152,49 @@ export default class DhpDeathMove extends HandlebarsApplicationMixin(Application
|
||||||
}
|
}
|
||||||
|
|
||||||
static async takeMove() {
|
static async takeMove() {
|
||||||
|
this.close();
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
if (CONFIG.DH.GENERAL.deathMoves.blazeOfGlory === this.selectedMove) {
|
||||||
|
result = await this.handleBlazeOfGlory();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CONFIG.DH.GENERAL.deathMoves.avoidDeath === this.selectedMove) {
|
||||||
|
result = await this.handleAvoidDeath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CONFIG.DH.GENERAL.deathMoves.riskItAll === this.selectedMove) {
|
||||||
|
result = await this.handleRiskItAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
|
||||||
|
.expandRollMessage?.desc;
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
|
||||||
const msg = {
|
const msg = {
|
||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/ui/chat/deathMove.hbs',
|
'systems/daggerheart/templates/ui/chat/deathMove.hbs',
|
||||||
{
|
{
|
||||||
player: this.actor.name,
|
player: this.actor.name,
|
||||||
actor: { name: this.actor.name, img: this.actor.img },
|
actor: this.actor,
|
||||||
|
actorId: this.actor._id,
|
||||||
author: game.users.get(game.user.id),
|
author: game.users.get(game.user.id),
|
||||||
title: game.i18n.localize(this.selectedMove.name),
|
title: game.i18n.localize(this.selectedMove.name),
|
||||||
img: this.selectedMove.img,
|
img: this.selectedMove.img,
|
||||||
description: game.i18n.localize(this.selectedMove.description)
|
description: game.i18n.localize(this.selectedMove.description),
|
||||||
|
result: result,
|
||||||
|
open: autoExpandDescription ? 'open' : '',
|
||||||
|
chevron: autoExpandDescription ? 'fa-chevron-up' : 'fa-chevron-down',
|
||||||
|
showRiskItAllButton: this.showRiskItAllButton,
|
||||||
|
riskItAllButtonLabel: this.riskItAllButtonLabel,
|
||||||
|
riskItAllHope: this.riskItAllHope
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
title: game.i18n.localize(
|
title: game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.title'),
|
||||||
'DAGGERHEART.UI.Chat.deathMove.title'
|
|
||||||
),
|
|
||||||
speaker: cls.getSpeaker(),
|
speaker: cls.getSpeaker(),
|
||||||
flags: {
|
flags: {
|
||||||
daggerheart: {
|
daggerheart: {
|
||||||
|
|
@ -72,7 +204,5 @@ export default class DhpDeathMove extends HandlebarsApplicationMixin(Application
|
||||||
};
|
};
|
||||||
|
|
||||||
cls.create(msg);
|
cls.create(msg);
|
||||||
|
|
||||||
this.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
94
module/applications/dialogs/riskItAllDialog.mjs
Normal file
94
module/applications/dialogs/riskItAllDialog.mjs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
export default class RiskItAllDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(actor, resourceValue) {
|
||||||
|
super({});
|
||||||
|
|
||||||
|
this.actor = actor;
|
||||||
|
this.resourceValue = resourceValue;
|
||||||
|
this.choices = {
|
||||||
|
hitPoints: 0,
|
||||||
|
stress: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return game.i18n.format('DAGGERHEART.APPLICATIONS.RiskItAllDialog.title', { name: this.actor.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ['daggerheart', 'dh-style', 'dialog', 'views', 'risk-it-all'],
|
||||||
|
position: { width: 280, height: 'auto' },
|
||||||
|
window: { icon: 'fa-solid fa-dice fa-xl' },
|
||||||
|
actions: {
|
||||||
|
finish: RiskItAllDialog.#finish
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
application: {
|
||||||
|
id: 'risk-it-all',
|
||||||
|
template: 'systems/daggerheart/templates/dialogs/riskItAllDialog.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
|
for (const input of htmlElement.querySelectorAll('.resource-container input'))
|
||||||
|
input.addEventListener('change', this.updateChoice.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.resourceValue = this.resourceValue;
|
||||||
|
context.maxHitPointsValue = Math.min(this.resourceValue, this.actor.system.resources.hitPoints.max);
|
||||||
|
context.maxStressValue = Math.min(this.resourceValue, this.actor.system.resources.stress.max);
|
||||||
|
context.remainingResource = this.resourceValue - this.choices.hitPoints - this.choices.stress;
|
||||||
|
context.unfinished = context.remainingResource !== 0;
|
||||||
|
|
||||||
|
context.choices = this.choices;
|
||||||
|
context.final = {
|
||||||
|
hitPoints: {
|
||||||
|
value: this.actor.system.resources.hitPoints.value - this.choices.hitPoints,
|
||||||
|
max: this.actor.system.resources.hitPoints.max
|
||||||
|
},
|
||||||
|
stress: {
|
||||||
|
value: this.actor.system.resources.stress.value - this.choices.stress,
|
||||||
|
max: this.actor.system.resources.stress.max
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
context;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChoice(event) {
|
||||||
|
let value = Number.parseInt(event.target.value);
|
||||||
|
const choiceKey = event.target.dataset.choice;
|
||||||
|
const actorValue = this.actor.system.resources[choiceKey].value;
|
||||||
|
const remaining = this.resourceValue - this.choices.hitPoints - this.choices.stress;
|
||||||
|
const changeAmount = value - this.choices[choiceKey];
|
||||||
|
|
||||||
|
/* If trying to increase beyond remaining resource points, just increase to max available */
|
||||||
|
if (remaining - changeAmount < 0) value = this.choices[choiceKey] + remaining;
|
||||||
|
else if (actorValue - value < 0) value = actorValue;
|
||||||
|
|
||||||
|
this.choices[choiceKey] = value;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #finish() {
|
||||||
|
const resourceUpdate = Object.keys(this.choices).reduce((acc, resourceKey) => {
|
||||||
|
const value = this.actor.system.resources[resourceKey].value - this.choices[resourceKey];
|
||||||
|
acc[resourceKey] = { value };
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
await this.actor.update({
|
||||||
|
'system.resources': resourceUpdate
|
||||||
|
});
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import DHBaseActorSheet from '../api/base-actor.mjs';
|
import DHBaseActorSheet from '../api/base-actor.mjs';
|
||||||
import DhpDeathMove from '../../dialogs/deathMove.mjs';
|
import DhDeathMove from '../../dialogs/deathMove.mjs';
|
||||||
import { abilities } from '../../../config/actorConfig.mjs';
|
import { abilities } from '../../../config/actorConfig.mjs';
|
||||||
import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs';
|
import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs';
|
||||||
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
||||||
|
|
@ -696,7 +696,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #makeDeathMove() {
|
static async #makeDeathMove() {
|
||||||
await new DhpDeathMove(this.document).render({ force: true });
|
await new DhDeathMove(this.document).render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -753,9 +753,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
|
|
||||||
/* This could be avoided by baking config.costs into config.resourceUpdates. Didn't feel like messing with it at the time */
|
/* This could be avoided by baking config.costs into config.resourceUpdates. Didn't feel like messing with it at the time */
|
||||||
const costResources = result.costs
|
const costResources = result.costs?.filter(x => x.enabled)
|
||||||
.filter(x => x.enabled)
|
.map(cost => ({ ...cost, value: -cost.value, total: -cost.total })) || {};
|
||||||
.map(cost => ({ ...cost, value: -cost.value, total: -cost.total }));
|
|
||||||
config.resourceUpdates.addResources(costResources);
|
config.resourceUpdates.addResources(costResources);
|
||||||
await config.resourceUpdates.updateResources();
|
await config.resourceUpdates.updateResources();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
html.querySelectorAll('.group-roll-header-expand-section').forEach(element =>
|
html.querySelectorAll('.group-roll-header-expand-section').forEach(element =>
|
||||||
element.addEventListener('click', this.groupRollExpandSection)
|
element.addEventListener('click', this.groupRollExpandSection)
|
||||||
);
|
);
|
||||||
|
html.querySelectorAll('.risk-it-all-button').forEach(element =>
|
||||||
|
element.addEventListener('click', event => this.riskItAllClearStressAndHitPoints(event, data))
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
setupHooks() {
|
setupHooks() {
|
||||||
|
|
@ -94,15 +97,17 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
|
|
||||||
/** Ensure the chat theme inherits the interface theme */
|
/** Ensure the chat theme inherits the interface theme */
|
||||||
_replaceHTML(result, content, options) {
|
_replaceHTML(result, content, options) {
|
||||||
const themedElement = result.log?.querySelector(".chat-log");
|
const themedElement = result.log?.querySelector('.chat-log');
|
||||||
themedElement?.classList.remove("themed", "theme-light", "theme-dark");
|
themedElement?.classList.remove('themed', 'theme-light', 'theme-dark');
|
||||||
super._replaceHTML(result, content, options);
|
super._replaceHTML(result, content, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove chat log theme from notifications area */
|
/** Remove chat log theme from notifications area */
|
||||||
async _onFirstRender(result, content) {
|
async _onFirstRender(result, content) {
|
||||||
await super._onFirstRender(result, content);
|
await super._onFirstRender(result, content);
|
||||||
document.querySelector("#chat-notifications .chat-log")?.classList.remove("themed", "theme-light", "theme-dark")
|
document
|
||||||
|
.querySelector('#chat-notifications .chat-log')
|
||||||
|
?.classList.remove('themed', 'theme-light', 'theme-dark');
|
||||||
}
|
}
|
||||||
|
|
||||||
async onRollSimple(event, message) {
|
async onRollSimple(event, message) {
|
||||||
|
|
@ -383,4 +388,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
});
|
});
|
||||||
event.target.closest('.group-roll-section').querySelector('.group-roll-content').classList.toggle('closed');
|
event.target.closest('.group-roll-section').querySelector('.group-roll-content').classList.toggle('closed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async riskItAllClearStressAndHitPoints(event, data) {
|
||||||
|
const resourceValue = event.target.dataset.resourceValue;
|
||||||
|
const actor = game.actors.get(event.target.dataset.actorId);
|
||||||
|
new game.system.api.applications.dialogs.RiskItAllDialog(actor, resourceValue).render({ force: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -376,14 +376,14 @@ export class ResourceUpdateMap extends Map {
|
||||||
if (!resource.key) continue;
|
if (!resource.key) continue;
|
||||||
|
|
||||||
const existing = this.get(resource.key);
|
const existing = this.get(resource.key);
|
||||||
if (existing) {
|
if (!existing || resource.clear) {
|
||||||
|
this.set(resource.key, resource);
|
||||||
|
} else if (!existing?.clear) {
|
||||||
this.set(resource.key, {
|
this.set(resource.key, {
|
||||||
...existing,
|
...existing,
|
||||||
value: existing.value + (resource.value ?? 0),
|
value: existing.value + (resource.value ?? 0),
|
||||||
total: existing.total + (resource.total ?? 0)
|
total: existing.total + (resource.total ?? 0)
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
this.set(resource.key, resource);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,14 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||||
),
|
),
|
||||||
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
|
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||||
hope: resourceField(6, 2, 'DAGGERHEART.GENERAL.hope')
|
hope: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({
|
||||||
|
initial: 2,
|
||||||
|
min: 0,
|
||||||
|
integer: true,
|
||||||
|
label: 'DAGGERHEART.GENERAL.hope'
|
||||||
|
})
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
traits: new fields.SchemaField({
|
traits: new fields.SchemaField({
|
||||||
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
||||||
|
|
@ -78,12 +85,7 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
bags: new fields.NumberField({ initial: 0, integer: true }),
|
bags: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
chests: new fields.NumberField({ initial: 0, integer: true })
|
chests: new fields.NumberField({ initial: 0, integer: true })
|
||||||
}),
|
}),
|
||||||
scars: new fields.TypedObjectField(
|
scars: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.scars' }),
|
||||||
new fields.SchemaField({
|
|
||||||
name: new fields.StringField({}),
|
|
||||||
description: new fields.StringField()
|
|
||||||
})
|
|
||||||
),
|
|
||||||
biography: new fields.SchemaField({
|
biography: new fields.SchemaField({
|
||||||
background: new fields.HTMLField(),
|
background: new fields.HTMLField(),
|
||||||
connections: new fields.HTMLField(),
|
connections: new fields.HTMLField(),
|
||||||
|
|
@ -301,6 +303,9 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
runeWard: new fields.BooleanField({ initial: false }),
|
runeWard: new fields.BooleanField({ initial: false }),
|
||||||
burden: new fields.SchemaField({
|
burden: new fields.SchemaField({
|
||||||
ignore: new fields.BooleanField()
|
ignore: new fields.BooleanField()
|
||||||
|
}),
|
||||||
|
roll: new fields.SchemaField({
|
||||||
|
guaranteedCritical: new fields.BooleanField()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
@ -642,7 +647,9 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
? armor.system.baseThresholds.severe + this.levelData.level.current
|
? armor.system.baseThresholds.severe + this.levelData.level.current
|
||||||
: this.levelData.level.current * 2
|
: this.levelData.level.current * 2
|
||||||
};
|
};
|
||||||
this.resources.hope.max -= Object.keys(this.scars).length;
|
|
||||||
|
const globalHopeMax = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxHope;
|
||||||
|
this.resources.hope.max = globalHopeMax - this.scars;
|
||||||
this.resources.hitPoints.max += this.class.value?.system?.hitPoints ?? 0;
|
this.resources.hitPoints.max += this.class.value?.system?.hitPoints ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -699,6 +706,20 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
changes.system.experiences[experience].core = true;
|
changes.system.experiences[experience].core = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Scars can alter the amount of current hope */
|
||||||
|
if (changes.system?.scars) {
|
||||||
|
const diff = this.system.scars - changes.system.scars;
|
||||||
|
const newHopeMax = this.system.resources.hope.max + diff;
|
||||||
|
const newHopeValue = Math.min(newHopeMax, this.system.resources.hope.value);
|
||||||
|
if (newHopeValue != this.system.resources.hope.value) {
|
||||||
|
if (!changes.system.resources) changes.system.resources = { hope: { value: 0 } };
|
||||||
|
changes.system.resources.hope = {
|
||||||
|
...changes.system.resources.hope,
|
||||||
|
value: changes.system.resources.hope.value + newHopeValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preDelete() {
|
async _preDelete() {
|
||||||
|
|
@ -714,4 +735,11 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
t => !!t
|
t => !!t
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static migrateData(source) {
|
||||||
|
if (typeof source.scars === 'object') source.scars = 0;
|
||||||
|
if (source.resources?.hope?.max) source.scars = Math.max(6 - source.resources.hope.max, 0);
|
||||||
|
|
||||||
|
return super.migrateData(source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ export const config = {
|
||||||
adversaryRoll: DHActorRoll,
|
adversaryRoll: DHActorRoll,
|
||||||
damageRoll: DHActorRoll,
|
damageRoll: DHActorRoll,
|
||||||
dualityRoll: DHActorRoll,
|
dualityRoll: DHActorRoll,
|
||||||
|
fateRoll: DHActorRoll,
|
||||||
groupRoll: DHGroupRoll,
|
groupRoll: DHGroupRoll,
|
||||||
systemMessage: DHSystemMessage
|
systemMessage: DHSystemMessage
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,13 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
initial: 12,
|
initial: 12,
|
||||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxFear.label'
|
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxFear.label'
|
||||||
}),
|
}),
|
||||||
|
maxHope: new fields.NumberField({
|
||||||
|
required: true,
|
||||||
|
integer: true,
|
||||||
|
min: 0,
|
||||||
|
initial: 6,
|
||||||
|
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxHope.label'
|
||||||
|
}),
|
||||||
maxLoadout: new fields.NumberField({
|
maxLoadout: new fields.NumberField({
|
||||||
required: true,
|
required: true,
|
||||||
integer: true,
|
integer: true,
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@ export { default as D20Roll } from './d20Roll.mjs';
|
||||||
export { default as DamageRoll } from './damageRoll.mjs';
|
export { default as DamageRoll } from './damageRoll.mjs';
|
||||||
export { default as DHRoll } from './dhRoll.mjs';
|
export { default as DHRoll } from './dhRoll.mjs';
|
||||||
export { default as DualityRoll } from './dualityRoll.mjs';
|
export { default as DualityRoll } from './dualityRoll.mjs';
|
||||||
|
export { default as FateRoll } from './fateRoll.mjs';
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export default class DualityRoll extends D20Roll {
|
||||||
constructor(formula, data = {}, options = {}) {
|
constructor(formula, data = {}, options = {}) {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
this.rallyChoices = this.setRallyChoices();
|
this.rallyChoices = this.setRallyChoices();
|
||||||
|
this.guaranteedCritical = options.guaranteedCritical;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messageType = 'dualityRoll';
|
static messageType = 'dualityRoll';
|
||||||
|
|
@ -25,29 +26,23 @@ export default class DualityRoll extends D20Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
get dHope() {
|
get dHope() {
|
||||||
// if ( !(this.terms[0] instanceof foundry.dice.terms.Die) ) return;
|
|
||||||
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
return this.dice[0];
|
return this.dice[0];
|
||||||
// return this.#hopeDice;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set dHope(faces) {
|
set dHope(faces) {
|
||||||
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
this.terms[0].faces = this.getFaces(faces);
|
this.dice[0].faces = this.getFaces(faces);
|
||||||
// this.#hopeDice = `d${face}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get dFear() {
|
get dFear() {
|
||||||
// if ( !(this.terms[1] instanceof foundry.dice.terms.Die) ) return;
|
|
||||||
if (!(this.dice[1] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
if (!(this.dice[1] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
return this.dice[1];
|
return this.dice[1];
|
||||||
// return this.#fearDice;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set dFear(faces) {
|
set dFear(faces) {
|
||||||
if (!(this.dice[1] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
if (!(this.dice[1] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
this.dice[1].faces = this.getFaces(faces);
|
this.dice[1].faces = this.getFaces(faces);
|
||||||
// this.#fearDice = `d${face}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get dAdvantage() {
|
get dAdvantage() {
|
||||||
|
|
@ -90,26 +85,29 @@ export default class DualityRoll extends D20Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
get isCritical() {
|
get isCritical() {
|
||||||
|
if (this.guaranteedCritical) return true;
|
||||||
if (!this.dHope._evaluated || !this.dFear._evaluated) return;
|
if (!this.dHope._evaluated || !this.dFear._evaluated) return;
|
||||||
return this.dHope.total === this.dFear.total;
|
return this.dHope.total === this.dFear.total;
|
||||||
}
|
}
|
||||||
|
|
||||||
get withHope() {
|
get withHope() {
|
||||||
if (!this._evaluated) return;
|
if (!this._evaluated || this.guaranteedCritical) return;
|
||||||
return this.dHope.total > this.dFear.total;
|
return this.dHope.total > this.dFear.total;
|
||||||
}
|
}
|
||||||
|
|
||||||
get withFear() {
|
get withFear() {
|
||||||
if (!this._evaluated) return;
|
if (!this._evaluated || this.guaranteedCritical) return;
|
||||||
return this.dHope.total < this.dFear.total;
|
return this.dHope.total < this.dFear.total;
|
||||||
}
|
}
|
||||||
|
|
||||||
get totalLabel() {
|
get totalLabel() {
|
||||||
const label = this.withHope
|
const label = this.guaranteedCritical
|
||||||
? 'DAGGERHEART.GENERAL.hope'
|
? 'DAGGERHEART.GENERAL.guaranteedCriticalSuccess'
|
||||||
: this.withFear
|
: this.isCritical
|
||||||
? 'DAGGERHEART.GENERAL.fear'
|
? 'DAGGERHEART.GENERAL.criticalSuccess'
|
||||||
: 'DAGGERHEART.GENERAL.criticalSuccess';
|
: this.withHope
|
||||||
|
? 'DAGGERHEART.GENERAL.hope'
|
||||||
|
: 'DAGGERHEART.GENERAL.fear';
|
||||||
|
|
||||||
return game.i18n.localize(label);
|
return game.i18n.localize(label);
|
||||||
}
|
}
|
||||||
|
|
@ -178,6 +176,21 @@ export default class DualityRoll extends D20Roll {
|
||||||
return modifiers;
|
return modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async buildConfigure(config = {}, message = {}) {
|
||||||
|
config.dialog ??= {};
|
||||||
|
config.guaranteedCritical = config.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||||
|
const change = c.changes.find(ch => ch.key === 'system.rules.roll.guaranteedCritical');
|
||||||
|
if (change) a = true;
|
||||||
|
return a;
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
if (config.guaranteedCritical) {
|
||||||
|
config.dialog.configure = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.buildConfigure(config, message);
|
||||||
|
}
|
||||||
|
|
||||||
getActionChangeKeys() {
|
getActionChangeKeys() {
|
||||||
const changeKeys = new Set([`system.bonuses.roll.${this.options.actionType}`]);
|
const changeKeys = new Set([`system.bonuses.roll.${this.options.actionType}`]);
|
||||||
|
|
||||||
|
|
@ -223,7 +236,7 @@ export default class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
data.hope = {
|
data.hope = {
|
||||||
dice: roll.dHope.denomination,
|
dice: roll.dHope.denomination,
|
||||||
value: roll.dHope.total,
|
value: this.guaranteedCritical ? 0 : roll.dHope.total,
|
||||||
rerolled: {
|
rerolled: {
|
||||||
any: roll.dHope.results.some(x => x.rerolled),
|
any: roll.dHope.results.some(x => x.rerolled),
|
||||||
rerolls: roll.dHope.results.filter(x => x.rerolled)
|
rerolls: roll.dHope.results.filter(x => x.rerolled)
|
||||||
|
|
@ -231,7 +244,7 @@ export default class DualityRoll extends D20Roll {
|
||||||
};
|
};
|
||||||
data.fear = {
|
data.fear = {
|
||||||
dice: roll.dFear.denomination,
|
dice: roll.dFear.denomination,
|
||||||
value: roll.dFear.total,
|
value: this.guaranteedCritical ? 0 : roll.dFear.total,
|
||||||
rerolled: {
|
rerolled: {
|
||||||
any: roll.dFear.results.some(x => x.rerolled),
|
any: roll.dFear.results.some(x => x.rerolled),
|
||||||
rerolls: roll.dFear.results.filter(x => x.rerolled)
|
rerolls: roll.dFear.results.filter(x => x.rerolled)
|
||||||
|
|
@ -243,7 +256,7 @@ export default class DualityRoll extends D20Roll {
|
||||||
};
|
};
|
||||||
data.result = {
|
data.result = {
|
||||||
duality: roll.withHope ? 1 : roll.withFear ? -1 : 0,
|
duality: roll.withHope ? 1 : roll.withFear ? -1 : 0,
|
||||||
total: roll.dHope.total + roll.dFear.total,
|
total: this.guaranteedCritical ? 0 : roll.dHope.total + roll.dFear.total,
|
||||||
label: roll.totalLabel
|
label: roll.totalLabel
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
85
module/dice/fateRoll.mjs
Normal file
85
module/dice/fateRoll.mjs
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||||
|
import D20Roll from './d20Roll.mjs';
|
||||||
|
import { setDiceSoNiceForHopeFateRoll, setDiceSoNiceForFearFateRoll } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
|
export default class FateRoll extends D20Roll {
|
||||||
|
constructor(formula, data = {}, options = {}) {
|
||||||
|
super(formula, data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static messageType = 'fateRoll';
|
||||||
|
|
||||||
|
static DefaultDialog = D20RollDialog;
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return game.i18n.localize(`DAGGERHEART.GENERAL.fateRoll`);
|
||||||
|
}
|
||||||
|
|
||||||
|
get dHope() {
|
||||||
|
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
|
return this.dice[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
set dHope(faces) {
|
||||||
|
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
|
this.dice[0].faces = this.getFaces(faces);
|
||||||
|
}
|
||||||
|
|
||||||
|
get dFear() {
|
||||||
|
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
|
return this.dice[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
set dFear(faces) {
|
||||||
|
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
|
this.dice[0].faces = this.getFaces(faces);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCritical() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fateDie() {
|
||||||
|
return this.data.fateType;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getHooks(hooks) {
|
||||||
|
return [...(hooks ?? []), 'Fate'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
static fromData(data) {
|
||||||
|
data.terms[0].class = foundry.dice.terms.Die.name;
|
||||||
|
return super.fromData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
createBaseDice() {
|
||||||
|
if (this.dice[0] instanceof foundry.dice.terms.Die) {
|
||||||
|
this.terms = [this.terms[0]];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.terms[0] = new foundry.dice.terms.Die({ faces: 12 });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||||
|
await super.buildEvaluate(roll, config, message);
|
||||||
|
|
||||||
|
if (roll.fateDie === 'Hope') {
|
||||||
|
await setDiceSoNiceForHopeFateRoll(roll, config.roll.fate.dice);
|
||||||
|
} else {
|
||||||
|
await setDiceSoNiceForFearFateRoll(roll, config.roll.fate.dice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static postEvaluate(roll, config = {}) {
|
||||||
|
const data = super.postEvaluate(roll, config);
|
||||||
|
|
||||||
|
data.fate = {
|
||||||
|
dice: roll.fateDie === 'Hope' ? roll.dHope.denomination : roll.dFear.denomination,
|
||||||
|
value: roll.fateDie === 'Hope' ? roll.dHope.total : roll.dFear.total,
|
||||||
|
fateDie: roll.fateDie
|
||||||
|
};
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -764,16 +764,24 @@ export default class DhpActor extends Actor {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const valueFunc = (base, resource, baseMax) => {
|
||||||
|
if (resource.clear) return baseMax && base.inverted ? baseMax : 0;
|
||||||
|
|
||||||
|
return (base.value ?? base) + resource.value;
|
||||||
|
};
|
||||||
switch (r.key) {
|
switch (r.key) {
|
||||||
case 'fear':
|
case 'fear':
|
||||||
ui.resources.updateFear(
|
ui.resources.updateFear(
|
||||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear) + r.value
|
valueFunc(
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
|
||||||
|
r
|
||||||
|
)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'armor':
|
case 'armor':
|
||||||
if (this.system.armor?.system?.marks) {
|
if (this.system.armor?.system?.marks) {
|
||||||
updates.armor.resources['system.marks.value'] = Math.max(
|
updates.armor.resources['system.marks.value'] = Math.max(
|
||||||
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
Math.min(valueFunc(this.system.armor.system.marks, r), this.system.armorScore),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -782,7 +790,7 @@ export default class DhpActor extends Actor {
|
||||||
if (this.system.resources?.[r.key]) {
|
if (this.system.resources?.[r.key]) {
|
||||||
updates.actor.resources[`system.resources.${r.key}.value`] = Math.max(
|
updates.actor.resources[`system.resources.${r.key}.value`] = Math.max(
|
||||||
Math.min(
|
Math.min(
|
||||||
this.system.resources[r.key].value + r.value,
|
valueFunc(this.system.resources[r.key], r, this.system.resources[r.key].max),
|
||||||
this.system.resources[r.key].max
|
this.system.resources[r.key].max
|
||||||
),
|
),
|
||||||
0
|
0
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,15 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.type === 'fateRoll') {
|
||||||
|
html.classList.add('fate');
|
||||||
|
if (this.system.roll?.fate.fateDie == 'Hope') {
|
||||||
|
html.classList.add('hope');
|
||||||
|
}
|
||||||
|
if (this.system.roll?.fate.fateDie == 'Fear') {
|
||||||
|
html.classList.add('fear');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const autoExpandRoll = game.settings.get(
|
const autoExpandRoll = game.settings.get(
|
||||||
CONFIG.DH.id,
|
CONFIG.DH.id,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { abilities } from '../config/actorConfig.mjs';
|
||||||
import { getCommandTarget, rollCommandToJSON } from '../helpers/utils.mjs';
|
import { getCommandTarget, rollCommandToJSON } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
export default function DhDualityRollEnricher(match, _options) {
|
export default function DhDualityRollEnricher(match, _options) {
|
||||||
const roll = rollCommandToJSON(match[1], match[0]);
|
const roll = rollCommandToJSON(match[0]);
|
||||||
if (!roll) return match[0];
|
if (!roll) return match[0];
|
||||||
|
|
||||||
return getDualityMessage(roll.result, roll.flavor);
|
return getDualityMessage(roll.result, roll.flavor);
|
||||||
|
|
@ -80,7 +80,7 @@ export const renderDualityButton = async event => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const enrichedDualityRoll = async (
|
export const enrichedDualityRoll = async (
|
||||||
{ reaction, traitValue, target, difficulty, title, label, advantage },
|
{ reaction, traitValue, target, difficulty, title, label, advantage, customConfig },
|
||||||
event
|
event
|
||||||
) => {
|
) => {
|
||||||
const config = {
|
const config = {
|
||||||
|
|
@ -94,7 +94,8 @@ export const enrichedDualityRoll = async (
|
||||||
type: reaction ? 'reaction' : null
|
type: reaction ? 'reaction' : null
|
||||||
},
|
},
|
||||||
type: 'trait',
|
type: 'trait',
|
||||||
hasRoll: true
|
hasRoll: true,
|
||||||
|
...(customConfig ?? {})
|
||||||
};
|
};
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
|
|
@ -105,4 +106,5 @@ export const enrichedDualityRoll = async (
|
||||||
config.source = { actor: null };
|
config.source = { actor: null };
|
||||||
await CONFIG.Dice.daggerheart.DualityRoll.build(config);
|
await CONFIG.Dice.daggerheart.DualityRoll.build(config);
|
||||||
}
|
}
|
||||||
|
return config;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
80
module/enrichers/FateRollEnricher.mjs
Normal file
80
module/enrichers/FateRollEnricher.mjs
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { getCommandTarget, rollCommandToJSON } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
|
export default function DhFateRollEnricher(match, _options) {
|
||||||
|
const roll = rollCommandToJSON(match[0]);
|
||||||
|
if (!roll) return match[0];
|
||||||
|
|
||||||
|
return getFateMessage(roll.result, roll?.flavor);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFateTypeData(fateTypeValue) {
|
||||||
|
const value = fateTypeValue ? fateTypeValue.capitalize() : 'Hope';
|
||||||
|
const lowercased = fateTypeValue?.toLowerCase?.() ?? 'hope';
|
||||||
|
switch (lowercased) {
|
||||||
|
case 'hope':
|
||||||
|
case 'fear':
|
||||||
|
return { value, label: game.i18n.localize(`DAGGERHEART.GENERAL.${lowercased}`) };
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFateMessage(roll, flavor) {
|
||||||
|
const fateTypeData = getFateTypeData(roll?.type);
|
||||||
|
|
||||||
|
if (!fateTypeData)
|
||||||
|
return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing'));
|
||||||
|
|
||||||
|
const { value: fateType, label: fateTypeLabel } = fateTypeData;
|
||||||
|
const title = flavor ?? game.i18n.localize('DAGGERHEART.GENERAL.fateRoll');
|
||||||
|
|
||||||
|
const fateElement = document.createElement('span');
|
||||||
|
fateElement.innerHTML = `
|
||||||
|
<button type="button" class="fate-roll-button${roll?.inline ? ' inline' : ''}"
|
||||||
|
data-title="${title}"
|
||||||
|
data-label="${fateTypeLabel}"
|
||||||
|
data-fateType="${fateType}"
|
||||||
|
>
|
||||||
|
${title}
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return fateElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const renderFateButton = async event => {
|
||||||
|
const button = event.currentTarget,
|
||||||
|
target = getCommandTarget({ allowNull: true });
|
||||||
|
|
||||||
|
const fateTypeData = getFateTypeData(button.dataset?.fatetype);
|
||||||
|
|
||||||
|
if (!fateTypeData) ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing'));
|
||||||
|
const { value: fateType, label: fateTypeLabel } = fateTypeData;
|
||||||
|
|
||||||
|
await enrichedFateRoll(
|
||||||
|
{
|
||||||
|
target,
|
||||||
|
title: button.dataset.title,
|
||||||
|
label: button.dataset.label,
|
||||||
|
fateType: fateType
|
||||||
|
},
|
||||||
|
event
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const enrichedFateRoll = async ({ target, title, label, fateType }, event) => {
|
||||||
|
const config = {
|
||||||
|
event: event ?? {},
|
||||||
|
title: title,
|
||||||
|
headerTitle: label,
|
||||||
|
roll: {},
|
||||||
|
hasRoll: true,
|
||||||
|
fateType: fateType,
|
||||||
|
skips: { reaction: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
config.data = { experiences: {}, traits: {}, fateType: fateType };
|
||||||
|
config.source = { actor: target?.uuid };
|
||||||
|
await CONFIG.Dice.daggerheart.FateRoll.build(config);
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { default as DhDamageEnricher, renderDamageButton } from './DamageEnricher.mjs';
|
import { default as DhDamageEnricher, renderDamageButton } from './DamageEnricher.mjs';
|
||||||
import { default as DhDualityRollEnricher, renderDualityButton } from './DualityRollEnricher.mjs';
|
import { default as DhDualityRollEnricher, renderDualityButton } from './DualityRollEnricher.mjs';
|
||||||
|
import { default as DhFateRollEnricher, renderFateButton } from './FateRollEnricher.mjs';
|
||||||
import { default as DhEffectEnricher } from './EffectEnricher.mjs';
|
import { default as DhEffectEnricher } from './EffectEnricher.mjs';
|
||||||
import { default as DhTemplateEnricher, renderMeasuredTemplate } from './TemplateEnricher.mjs';
|
import { default as DhTemplateEnricher, renderMeasuredTemplate } from './TemplateEnricher.mjs';
|
||||||
import { default as DhLookupEnricher } from './LookupEnricher.mjs';
|
import { default as DhLookupEnricher } from './LookupEnricher.mjs';
|
||||||
|
|
||||||
export { DhDamageEnricher, DhDualityRollEnricher, DhEffectEnricher, DhTemplateEnricher };
|
export { DhDamageEnricher, DhDualityRollEnricher, DhEffectEnricher, DhTemplateEnricher, DhFateRollEnricher };
|
||||||
|
|
||||||
export const enricherConfig = [
|
export const enricherConfig = [
|
||||||
{
|
{
|
||||||
|
|
@ -15,6 +16,10 @@ export const enricherConfig = [
|
||||||
pattern: /\[\[\/dr\s?(.*?)\]\]({[^}]*})?/g,
|
pattern: /\[\[\/dr\s?(.*?)\]\]({[^}]*})?/g,
|
||||||
enricher: DhDualityRollEnricher
|
enricher: DhDualityRollEnricher
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pattern: /\[\[\/fr\s?(.*?)\]\]({[^}]*})?/g,
|
||||||
|
enricher: DhFateRollEnricher
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pattern: /@Effect\[([^\[\]]*)\]({[^}]*})?/g,
|
pattern: /@Effect\[([^\[\]]*)\]({[^}]*})?/g,
|
||||||
enricher: DhEffectEnricher
|
enricher: DhEffectEnricher
|
||||||
|
|
@ -38,6 +43,10 @@ export const enricherRenderSetup = element => {
|
||||||
.querySelectorAll('.duality-roll-button')
|
.querySelectorAll('.duality-roll-button')
|
||||||
.forEach(element => element.addEventListener('click', renderDualityButton));
|
.forEach(element => element.addEventListener('click', renderDualityButton));
|
||||||
|
|
||||||
|
element
|
||||||
|
.querySelectorAll('.fate-roll-button')
|
||||||
|
.forEach(element => element.addEventListener('click', renderFateButton));
|
||||||
|
|
||||||
element
|
element
|
||||||
.querySelectorAll('.measured-template-button')
|
.querySelectorAll('.measured-template-button')
|
||||||
.forEach(element => element.addEventListener('click', renderMeasuredTemplate));
|
.forEach(element => element.addEventListener('click', renderMeasuredTemplate));
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { diceTypes, getDiceSoNicePresets, range } from '../config/generalConfig.mjs';
|
import { diceTypes, getDiceSoNicePresets, getDiceSoNicePreset, range } from '../config/generalConfig.mjs';
|
||||||
import Tagify from '@yaireo/tagify';
|
import Tagify from '@yaireo/tagify';
|
||||||
|
|
||||||
export const capitalize = string => {
|
export const capitalize = string => {
|
||||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function rollCommandToJSON(text, raw) {
|
export function rollCommandToJSON(text) {
|
||||||
if (!text) return {};
|
if (!text) return {};
|
||||||
|
|
||||||
const flavorMatch = raw?.match(/{(.*)}$/);
|
const flavorMatch = text?.match(/{(.*)}$/);
|
||||||
const flavor = flavorMatch ? flavorMatch[1] : null;
|
const flavor = flavorMatch ? flavorMatch[1] : null;
|
||||||
|
|
||||||
// Match key="quoted string" OR key=unquotedValue
|
// Match key="quoted string" OR key=unquotedValue
|
||||||
|
|
@ -31,7 +31,7 @@ export function rollCommandToJSON(text, raw) {
|
||||||
}
|
}
|
||||||
result[key] = value;
|
result[key] = value;
|
||||||
}
|
}
|
||||||
return Object.keys(result).length > 0 ? { result, flavor } : null;
|
return { result, flavor };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getCommandTarget = (options = {}) => {
|
export const getCommandTarget = (options = {}) => {
|
||||||
|
|
@ -69,6 +69,20 @@ export const setDiceSoNiceForDualityRoll = async (rollResult, advantageState, ho
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setDiceSoNiceForHopeFateRoll = async (rollResult, hopeFaces) => {
|
||||||
|
if (!game.modules.get('dice-so-nice')?.active) return;
|
||||||
|
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
||||||
|
const diceSoNicePresets = await getDiceSoNicePreset(diceSoNice.hope, hopeFaces);
|
||||||
|
rollResult.dice[0].options = diceSoNicePresets;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setDiceSoNiceForFearFateRoll = async (rollResult, fearFaces) => {
|
||||||
|
if (!game.modules.get('dice-so-nice')?.active) return;
|
||||||
|
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
||||||
|
const diceSoNicePresets = await getDiceSoNicePreset(diceSoNice.fear, fearFaces);
|
||||||
|
rollResult.dice[0].options = diceSoNicePresets;
|
||||||
|
};
|
||||||
|
|
||||||
export const chunkify = (array, chunkSize, mappingFunc) => {
|
export const chunkify = (array, chunkSize, mappingFunc) => {
|
||||||
var chunkifiedArray = [];
|
var chunkifiedArray = [];
|
||||||
for (let i = 0; i < array.length; i += chunkSize) {
|
for (let i = 0; i < array.length; i += chunkSize) {
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,56 @@
|
||||||
@import '../../utils/spacing.less';
|
@import '../../utils/spacing.less';
|
||||||
@import '../../utils/colors.less';
|
@import '../../utils/colors.less';
|
||||||
@import '../../utils/fonts.less';
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
.daggerheart.dh-style.dialog.death-move {
|
.daggerheart.dh-style.dialog.death-move {
|
||||||
.death-move-container {
|
.death-move-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|
||||||
.moves-list {
|
.moves-list {
|
||||||
.move-item {
|
.move-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
padding: 5px;
|
||||||
&:hover {
|
border-radius: 5px;
|
||||||
background-color: light-dark(@soft-shadow, @soft-white-shadow);
|
transition: background-color 0.3s ease-in-out;
|
||||||
cursor: pointer;
|
height: 37px;
|
||||||
}
|
|
||||||
padding: 5px;
|
&:hover {
|
||||||
border-radius: 5px;
|
background-color: light-dark(@soft-shadow, @soft-white-shadow);
|
||||||
transition: background-color 0.3s ease-in-out;
|
cursor: pointer;
|
||||||
|
}
|
||||||
.label {
|
|
||||||
display: flex;
|
.label {
|
||||||
align-items: center;
|
display: flex;
|
||||||
gap: 10px;
|
align-items: center;
|
||||||
cursor: pointer;
|
gap: 10px;
|
||||||
flex: 1;
|
cursor: pointer;
|
||||||
i {
|
flex: 1;
|
||||||
text-align: center;
|
i {
|
||||||
width: 30px;
|
text-align: center;
|
||||||
}
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
input[type='radio'] {
|
|
||||||
margin-left: auto;
|
input[type='radio'] {
|
||||||
}
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
footer {
|
|
||||||
margin-top: 8px;
|
footer {
|
||||||
display: flex;
|
margin-top: 8px;
|
||||||
gap: 8px;
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
button {
|
|
||||||
flex: 1;
|
button {
|
||||||
height: 40px;
|
flex: 1;
|
||||||
font-weight: 600;
|
height: 40px;
|
||||||
}
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,6 @@
|
||||||
|
|
||||||
@import './item-transfer/sheet.less';
|
@import './item-transfer/sheet.less';
|
||||||
|
|
||||||
@import './settings/change-currency-icon.less';
|
@import './settings/change-currency-icon.less';
|
||||||
|
|
||||||
|
@import './risk-it-all/sheet.less';
|
||||||
|
|
|
||||||
60
styles/less/dialog/risk-it-all/sheet.less
Normal file
60
styles/less/dialog/risk-it-all/sheet.less
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
.daggerheart.dialog.dh-style.views.risk-it-all {
|
||||||
|
.risk-it-all-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
header {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: var(--font-size-20);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-label {
|
||||||
|
font-size: var(--font-size-18);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remaining-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-section {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.final-section {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.final-section-values-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.final-section-value-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.measured-template-button,
|
.measured-template-button,
|
||||||
.enriched-damage-button,
|
.enriched-damage-button,
|
||||||
|
.fate-roll-button,
|
||||||
.duality-roll-button {
|
.duality-roll-button {
|
||||||
display: inline;
|
display: inline;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,11 @@
|
||||||
.hope-value {
|
.hope-value {
|
||||||
display: flex;
|
display: flex;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.scar {
|
||||||
|
cursor: initial;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
color: @dark;
|
color: @dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fate,
|
||||||
&.duality {
|
&.duality {
|
||||||
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||||
|
|
||||||
|
|
@ -66,6 +67,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fate,
|
||||||
&.critical {
|
&.critical {
|
||||||
--text-color: @chat-purple;
|
--text-color: @chat-purple;
|
||||||
--bg-color: @chat-purple-40;
|
--bg-color: @chat-purple-40;
|
||||||
|
|
@ -80,7 +82,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.duality) {
|
&:not(.duality .fate) {
|
||||||
.font-20 {
|
.font-20 {
|
||||||
color: @dark;
|
color: @dark;
|
||||||
}
|
}
|
||||||
|
|
@ -173,6 +175,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fate {
|
||||||
|
|
||||||
|
&.hope {
|
||||||
|
--text-color: @golden;
|
||||||
|
--bg-color: @golden-40;
|
||||||
|
.message-header,
|
||||||
|
.message-content {
|
||||||
|
background-color: @golden-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fear {
|
||||||
|
--text-color: @chat-blue;
|
||||||
|
--bg-color: @chat-blue-40;
|
||||||
|
.message-header,
|
||||||
|
.message-content {
|
||||||
|
background-color: @chat-blue-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
&.duality {
|
&.duality {
|
||||||
&.hope {
|
&.hope {
|
||||||
--text-color: @golden;
|
--text-color: @golden;
|
||||||
|
|
|
||||||
152
styles/less/ui/chat/deathmoves.less
Normal file
152
styles/less/ui/chat/deathmoves.less
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
@import '../../utils/spacing.less';
|
||||||
|
|
||||||
|
#interface.theme-light {
|
||||||
|
.daggerheart.chat.death-moves {
|
||||||
|
.death-moves-list .death-move {
|
||||||
|
&:hover {
|
||||||
|
background: @dark-blue-10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.death-label {
|
||||||
|
border-bottom: 1px solid @dark-blue;
|
||||||
|
|
||||||
|
.header-label .title {
|
||||||
|
color: @dark-blue;
|
||||||
|
}
|
||||||
|
.header-label .label {
|
||||||
|
color: @dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-chevron-down {
|
||||||
|
color: @dark-blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: @dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
color: @dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.risk-it-all-button {
|
||||||
|
color: @dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.daggerheart.chat {
|
||||||
|
&.death-moves {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
details[open] {
|
||||||
|
.fa-chevron-down {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.death-moves-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.fa-chevron-down {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.death-move {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.death-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
border-bottom: 1px solid @golden;
|
||||||
|
margin: 0 8px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
width: -webkit-fill-available;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: light-dark(@dark-blue-10, @golden-10);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.death-image {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-label {
|
||||||
|
padding: 8px;
|
||||||
|
.title {
|
||||||
|
font-size: var(--font-size-16);
|
||||||
|
color: @golden;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
font-size: var(--font-size-12);
|
||||||
|
color: @beige;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-use-button-parent {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.action-use-target {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 4px 8px 10px 40px;
|
||||||
|
font-size: var(--font-size-12);
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-use-button {
|
||||||
|
width: -webkit-fill-available;
|
||||||
|
margin: 0 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
padding: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.risk-it-all-button {
|
||||||
|
width: -webkit-fill-available;
|
||||||
|
margin: 0 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
@import './chat/effect-summary.less';
|
@import './chat/effect-summary.less';
|
||||||
@import './chat/group-roll.less';
|
@import './chat/group-roll.less';
|
||||||
@import './chat/refresh-message.less';
|
@import './chat/refresh-message.less';
|
||||||
|
@import './chat/deathmoves.less';
|
||||||
@import './chat/sheet.less';
|
@import './chat/sheet.less';
|
||||||
|
|
||||||
@import './combat-sidebar/combat-sidebar.less';
|
@import './combat-sidebar/combat-sidebar.less';
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,7 @@
|
||||||
},
|
},
|
||||||
"ChatMessage": {
|
"ChatMessage": {
|
||||||
"dualityRoll": {},
|
"dualityRoll": {},
|
||||||
|
"fateRoll": {},
|
||||||
"adversaryRoll": {},
|
"adversaryRoll": {},
|
||||||
"damageRoll": {},
|
"damageRoll": {},
|
||||||
"abilityUse": {},
|
"abilityUse": {},
|
||||||
|
|
|
||||||
|
|
@ -68,95 +68,127 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if (eq @root.rollType 'FateRoll')}}
|
||||||
|
{{#if (eq @root.roll.fateDie 'Hope')}}
|
||||||
|
|
||||||
|
<div class="dice-option">
|
||||||
|
<img class="dice-icon" src="{{concat 'systems/daggerheart/assets/icons/dice/hope/' @root.roll.dHope.denomination '.svg'}}" alt="">
|
||||||
|
<div class="dice-select">
|
||||||
|
<span class="label">{{localize "DAGGERHEART.GENERAL.hope"}}</span>
|
||||||
|
<select name="roll.dice.dHope">
|
||||||
|
{{selectOptions diceOptions selected=@root.roll.dHope.denomination}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (eq @root.roll.fateDie 'Fear')}}
|
||||||
|
<div class="dice-option">
|
||||||
|
<img class="dice-icon" src="{{concat 'systems/daggerheart/assets/icons/dice/fear/' @root.roll.dFear.denomination '.svg'}}" alt="">
|
||||||
|
<div class="dice-select">
|
||||||
|
<span class="label">{{localize "DAGGERHEART.GENERAL.fear"}}</span>
|
||||||
|
<select name="roll.dice.dFear">
|
||||||
|
{{selectOptions diceOptions selected=@root.roll.dFear.denomination}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if hasSelectedEffects}}
|
{{#if (ne @root.rollType 'FateRoll')}}
|
||||||
<fieldset class="experience-container">
|
{{#if hasSelectedEffects}}
|
||||||
<legend>{{localize "DAGGERHEART.GENERAL.Effect.plural"}}</legend>
|
<fieldset class="experience-container">
|
||||||
|
<legend>{{localize "DAGGERHEART.GENERAL.Effect.plural"}}</legend>
|
||||||
|
|
||||||
{{#each selectedEffects as |effect id|}}
|
{{#each selectedEffects as |effect id|}}
|
||||||
<div class="experience-chip {{#if effect.selected}}selected{{/if}}" data-action="toggleSelectedEffect" data-key="{{id}}" data-tooltip="{{this.description}}">
|
<div class="experience-chip {{#if effect.selected}}selected{{/if}}" data-action="toggleSelectedEffect" data-key="{{id}}" data-tooltip="{{this.description}}">
|
||||||
<span><i class="{{ifThen effect.selected "fa-solid" "fa-regular"}} fa-circle"></i></span>
|
<span><i class="{{ifThen effect.selected "fa-solid" "fa-regular"}} fa-circle"></i></span>
|
||||||
<span class="label">{{effect.name}}</span>
|
<span class="label">{{effect.name}}</span>
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</fieldset>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if experiences.length}}
|
|
||||||
<fieldset class="experience-container">
|
|
||||||
<legend>{{localize "DAGGERHEART.GENERAL.experience.plural"}}</legend>
|
|
||||||
{{#each experiences}}
|
|
||||||
{{#if name}}
|
|
||||||
<div class="experience-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}" data-tooltip="{{this.description}}">
|
|
||||||
<span><i class="{{ifThen (includes ../selectedExperiences id) "fa-solid" "fa-regular"}} fa-circle"></i></span>
|
|
||||||
<span class="label">{{name}} +{{value}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/each}}
|
||||||
{{/each}}
|
</fieldset>
|
||||||
</fieldset>
|
{{/if}}
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<fieldset class="modifier-container {{#if (eq @root.rollType 'DualityRoll')}}two-columns{{else}}one-column{{/if}}">
|
{{#if experiences.length}}
|
||||||
<legend>{{localize "DAGGERHEART.GENERAL.Modifier.plural"}}</legend>
|
<fieldset class="experience-container">
|
||||||
<div class="nest-inputs">
|
<legend>{{localize "DAGGERHEART.GENERAL.experience.plural"}}</legend>
|
||||||
<button class="advantage-chip flex1 {{#if (eq advantage 1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="1">
|
{{#each experiences}}
|
||||||
{{#if (eq advantage 1)}}
|
{{#if name}}
|
||||||
<span><i class="fa-solid fa-circle"></i></span>
|
<div class="experience-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}" data-tooltip="{{this.description}}">
|
||||||
{{else}}
|
<span><i class="{{ifThen (includes ../selectedExperiences id) "fa-solid" "fa-regular"}} fa-circle"></i></span>
|
||||||
<span><i class="fa-regular fa-circle"></i></span>
|
<span class="label">{{name}} +{{value}}</span>
|
||||||
{{/if}}
|
</div>
|
||||||
<span class="label">{{localize "DAGGERHEART.GENERAL.Advantage.full"}}</span>
|
{{/if}}
|
||||||
{{#if @root.rollConfig.data.advantageSources.length}}
|
{{/each}}
|
||||||
<span class="advantage-chip-tooltip" data-tooltip="{{concat "#advantage#" @root.rollConfig.source.actor}}"><i class="fa-solid fa-circle-info"></i></span>
|
</fieldset>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</button>
|
<fieldset class="modifier-container {{#if (eq @root.rollType 'DualityRoll')}}two-columns{{else}}one-column{{/if}}">
|
||||||
<button class="disadvantage-chip flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">
|
{{#if @root.advantage}}
|
||||||
{{#if (eq advantage -1)}}
|
<legend>{{localize "DAGGERHEART.GENERAL.Modifier.plural"}}</legend>
|
||||||
<span><i class="fa-solid fa-circle"></i></span>
|
|
||||||
{{else}}
|
|
||||||
<span><i class="fa-regular fa-circle"></i></span>
|
|
||||||
{{/if}}
|
|
||||||
<span class="label">{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}</span>
|
|
||||||
{{#if @root.rollConfig.data.disadvantageSources.length}}
|
|
||||||
<span class="advantage-chip-tooltip" data-tooltip="{{concat "#disadvantage#" @root.rollConfig.source.actor}}"><i class="fa-solid fa-circle-info"></i></span>
|
|
||||||
{{/if}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{{#unless (eq @root.rollType 'D20Roll')}}
|
|
||||||
<div class="nest-inputs">
|
<div class="nest-inputs">
|
||||||
<select name="roll.dice.advantageNumber"{{#unless advantage}} disabled{{/unless}}>
|
<button class="advantage-chip flex1 {{#if (eq advantage 1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="1">
|
||||||
{{#times 10}}
|
{{#if (eq advantage 1)}}
|
||||||
<option value="{{add this 1}}" {{#if (eq @root.roll.advantageNumber (add this 1))}} selected{{/if}}>{{add this 1}}</option>
|
<span><i class="fa-solid fa-circle"></i></span>
|
||||||
{{/times}}
|
{{else}}
|
||||||
</select>
|
<span><i class="fa-regular fa-circle"></i></span>
|
||||||
<select name="roll.dice.advantageFaces"{{#unless advantage}} disabled{{/unless}}>
|
{{/if}}
|
||||||
{{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}}
|
<span class="label">{{localize "DAGGERHEART.GENERAL.Advantage.full"}}</span>
|
||||||
</select>
|
{{#if @root.rollConfig.data.advantageSources.length}}
|
||||||
|
<span class="advantage-chip-tooltip" data-tooltip="{{concat "#advantage#" @root.rollConfig.source.actor}}"><i class="fa-solid fa-circle-info"></i></span>
|
||||||
|
{{/if}}
|
||||||
|
</button>
|
||||||
|
<button class="disadvantage-chip flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">
|
||||||
|
{{#if (eq advantage -1)}}
|
||||||
|
<span><i class="fa-solid fa-circle"></i></span>
|
||||||
|
{{else}}
|
||||||
|
<span><i class="fa-regular fa-circle"></i></span>
|
||||||
|
{{/if}}
|
||||||
|
<span class="label">{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}</span>
|
||||||
|
{{#if @root.rollConfig.data.disadvantageSources.length}}
|
||||||
|
<span class="advantage-chip-tooltip" data-tooltip="{{concat "#disadvantage#" @root.rollConfig.source.actor}}"><i class="fa-solid fa-circle-info"></i></span>
|
||||||
|
{{/if}}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{{#if abilities}}
|
{{#unless (eq @root.rollType 'D20Roll')}}
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.traitModifier"}}</span>
|
<div class="nest-inputs">
|
||||||
<select name="trait">
|
<select name="roll.dice.advantageNumber"{{#unless advantage}} disabled{{/unless}}>
|
||||||
{{selectOptions abilities selected=@root.rollConfig.roll.trait valueAttr="id" labelAttr="label" blank="" localize=true}}
|
{{#times 10}}
|
||||||
|
<option value="{{add this 1}}" {{#if (eq @root.roll.advantageNumber (add this 1))}} selected{{/if}}>{{add this 1}}</option>
|
||||||
|
{{/times}}
|
||||||
|
</select>
|
||||||
|
<select name="roll.dice.advantageFaces"{{#unless advantage}} disabled{{/unless}}>
|
||||||
|
{{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{{#if abilities}}
|
||||||
|
<span>{{localize "DAGGERHEART.GENERAL.traitModifier"}}</span>
|
||||||
|
<select name="trait">
|
||||||
|
{{selectOptions abilities selected=@root.rollConfig.roll.trait valueAttr="id" labelAttr="label" blank="" localize=true}}
|
||||||
|
</select>
|
||||||
|
{{/if}}
|
||||||
|
{{/unless}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if @root.rallyDie.length}}
|
||||||
|
<span class="formula-label">{{localize "DAGGERHEART.CLASS.Feature.rallyDice"}}</span>
|
||||||
|
<select name="roll.dice._rallyIndex">
|
||||||
|
{{selectOptions @root.rallyDie blank="" selected=@root.roll._rallyIndex}}
|
||||||
</select>
|
</select>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/unless}}
|
{{#if (eq @root.rollType 'DualityRoll')}}<span class="formula-label">{{localize "DAGGERHEART.GENERAL.situationalBonus"}}</span>{{/if}}
|
||||||
{{#if @root.rallyDie.length}}
|
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="{{#if (eq @root.rollType 'DualityRoll')}}Ex: 1d6 + 5{{else}}Situational Bonus{{/if}}">
|
||||||
<span class="formula-label">{{localize "DAGGERHEART.CLASS.Feature.rallyDice"}}</span>
|
</fieldset>
|
||||||
<select name="roll.dice._rallyIndex">
|
{{/if}}
|
||||||
{{selectOptions @root.rallyDie blank="" selected=@root.roll._rallyIndex}}
|
|
||||||
</select>
|
|
||||||
{{/if}}
|
|
||||||
{{#if (eq @root.rollType 'DualityRoll')}}<span class="formula-label">{{localize "DAGGERHEART.GENERAL.situationalBonus"}}</span>{{/if}}
|
|
||||||
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="{{#if (eq @root.rollType 'DualityRoll')}}Ex: 1d6 + 5{{else}}Situational Bonus{{/if}}">
|
|
||||||
</fieldset>
|
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{#if (or costs uses)}}
|
{{#if (or costs uses)}}
|
||||||
{{> 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'}}
|
{{> 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<span class="formula-label"><b>{{localize "DAGGERHEART.GENERAL.formula"}}:</b> {{@root.formula}}</span>
|
{{#if (ne @root.rollType 'FateRoll')}}
|
||||||
|
<span class="formula-label"><b>{{localize "DAGGERHEART.GENERAL.formula"}}:</b> {{@root.formula}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class="roll-dialog-controls">
|
<div class="roll-dialog-controls">
|
||||||
<select class="roll-mode-select" name="selectedRollMode">
|
<select class="roll-mode-select" name="selectedRollMode">
|
||||||
|
|
|
||||||
39
templates/dialogs/riskItAllDialog.hbs
Normal file
39
templates/dialogs/riskItAllDialog.hbs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<div>
|
||||||
|
<div class="risk-it-all-container">
|
||||||
|
<header>{{localize "DAGGERHEART.APPLICATIONS.RiskItAllDialog.subtitle"}}</header>
|
||||||
|
|
||||||
|
<div class="remaining-section">
|
||||||
|
<label class="section-label">{{localize "DAGGERHEART.APPLICATIONS.RiskItAllDialog.remainingTitle"}}</label>
|
||||||
|
<div>{{this.remainingResource}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resource-section">
|
||||||
|
<div class="resource-container">
|
||||||
|
<label>{{localize "DAGGERHEART.APPLICATIONS.RiskItAllDialog.clearResource" resource=(localize "DAGGERHEART.GENERAL.HitPoints.short")}}: {{this.choices.hitPoints}}</label>
|
||||||
|
<input type="range" step="1" min="0" max="{{this.maxHitPointsValue}}" value="{{this.choices.hitPoints}}" name="choices.hitPoints" data-choice="hitPoints" />
|
||||||
|
</div>
|
||||||
|
<div class="resource-container">
|
||||||
|
<label>{{localize "DAGGERHEART.APPLICATIONS.RiskItAllDialog.clearResource" resource=(localize "DAGGERHEART.GENERAL.stress")}}: {{this.choices.stress}}</label>
|
||||||
|
<input type="range" step="1" min="0" max="{{this.maxStressValue}}" value="{{this.choices.stress}}" name="choices.stress" data-choice="stress" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="final-section">
|
||||||
|
<label class="section-label">{{localize "DAGGERHEART.APPLICATIONS.RiskItAllDialog.finalTitle"}}</label>
|
||||||
|
<div class="final-section-values-container">
|
||||||
|
<div class="final-section-value-container">
|
||||||
|
<label>{{localize "DAGGERHEART.GENERAL.HitPoints.plural"}}</label>
|
||||||
|
<span>{{this.final.hitPoints.value}}/{{this.final.hitPoints.max}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="final-section-value-container">
|
||||||
|
<label>{{localize "DAGGERHEART.GENERAL.stress"}}</label>
|
||||||
|
<span>{{this.final.stress.value}}/{{this.final.stress.max}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<button type="button" data-action="finish" {{disabled this.unfinished}}>{{localize "Submit"}}</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
<h1>{{localize 'DAGGERHEART.SETTINGS.Menu.homebrew.name'}}</h1>
|
<h1>{{localize 'DAGGERHEART.SETTINGS.Menu.homebrew.name'}}</h1>
|
||||||
</header>
|
</header>
|
||||||
{{formGroup settingFields.schema.fields.maxFear value=settingFields._source.maxFear localize=true}}
|
{{formGroup settingFields.schema.fields.maxFear value=settingFields._source.maxFear localize=true}}
|
||||||
|
{{formGroup settingFields.schema.fields.maxHope value=settingFields._source.maxHope localize=true}}
|
||||||
{{formGroup settingFields.schema.fields.maxDomains value=settingFields._source.maxDomains localize=true}}
|
{{formGroup settingFields.schema.fields.maxDomains value=settingFields._source.maxDomains localize=true}}
|
||||||
{{formGroup settingFields.schema.fields.maxLoadout value=settingFields._source.maxLoadout localize=true}}
|
{{formGroup settingFields.schema.fields.maxLoadout value=settingFields._source.maxLoadout localize=true}}
|
||||||
<div class="settings-hint"><label>{{localize "DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxLoadout.hint"}}</label></div>
|
<div class="settings-hint"><label>{{localize "DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxLoadout.hint"}}</label></div>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="code-mirror-wrapper {{#if trigger.revealed}}revealed{{/if}}">
|
<div class="code-mirror-wrapper {{#if trigger.revealed}}revealed{{/if}}">
|
||||||
{{formInput @root.fields.triggers.element.fields.command value=trigger.command elementType="code-mirror" name=(concat "triggers." index ".command") aria=(object label=(localize "Test")) }}
|
{{formInput @root.fields.triggers.element.fields.command value=trigger.command elementType="code-mirror" name=(concat "triggers." index ".command") }}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
{{formGroup systemFields.resources.fields.stress.fields.max value=document._source.system.resources.stress.max localize=true}}
|
{{formGroup systemFields.resources.fields.stress.fields.max value=document._source.system.resources.stress.max localize=true}}
|
||||||
|
|
||||||
{{formGroup systemFields.resources.fields.hope.fields.value value=document._source.system.resources.hope.value localize=true}}
|
{{formGroup systemFields.resources.fields.hope.fields.value value=document._source.system.resources.hope.value localize=true}}
|
||||||
{{formGroup systemFields.resources.fields.hope.fields.max value=document._source.system.resources.hope.max localize=true}}
|
{{formGroup systemFields.scars value=document._source.system.scars localize=true}}
|
||||||
|
|
||||||
{{formGroup systemFields.proficiency value=document._source.system.proficiency localize=true}}
|
{{formGroup systemFields.proficiency value=document._source.system.proficiency localize=true}}
|
||||||
<span data-tooltip-text="{{localize "DAGGERHEART.UI.Tooltip.maxEvasionClassBound"}}">
|
<span data-tooltip-text="{{localize "DAGGERHEART.UI.Tooltip.maxEvasionClassBound"}}">
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,11 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</span>
|
</span>
|
||||||
{{/times}}
|
{{/times}}
|
||||||
|
{{#times document.system.scars}}
|
||||||
|
<span class='hope-value scar'>
|
||||||
|
<i class='fa-regular fa-ban'></i>
|
||||||
|
</span>
|
||||||
|
{{/times}}
|
||||||
</div>
|
</div>
|
||||||
{{#if document.system.class.value}}
|
{{#if document.system.class.value}}
|
||||||
<div class="domains-section">
|
<div class="domains-section">
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,32 @@
|
||||||
<div class="daggerheart chat downtime">
|
<div class="daggerheart chat death-moves">
|
||||||
<ul class="downtime-moves-list">
|
<ul class="death-moves-list">
|
||||||
<details class="downtime-move">
|
<details class="death-move" {{this.open}}>
|
||||||
<summary class="downtime-label">
|
<summary class="death-label">
|
||||||
<img class="downtime-image" src="{{this.img}}" />
|
<img class="death-image" src="{{this.img}}" />
|
||||||
<div class="header-label">
|
<div class="header-label">
|
||||||
<h2 class="title">{{this.title}}</h2>
|
<h2 class="title">{{this.title}}</h2>
|
||||||
<span class="label">{{localize 'DAGGERHEART.UI.Chat.deathMove.title'}}</span>
|
<span class="label">{{localize 'DAGGERHEART.UI.Chat.deathMove.title'}}</span>
|
||||||
</div>
|
</div>
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid {{this.chevron}}"></i>
|
||||||
</summary>
|
</summary>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
{{{this.description}}}
|
{{{this.description}}}
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="result">
|
||||||
|
{{{this.result}}}
|
||||||
|
</div>
|
||||||
|
{{#if this.showRiskItAllButton}}
|
||||||
|
<div>
|
||||||
|
<button class="risk-it-all-button" data-resource-value="{{this.riskItAllHope}}" data-actor-id="{{this.actorId}}">
|
||||||
|
<span>
|
||||||
|
{{this.riskItAllButtonLabel}}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -29,64 +29,86 @@
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="roll-dice">
|
<div class="roll-dice">
|
||||||
{{#if roll.hope}}
|
{{#if roll.fate}}
|
||||||
<div class="roll-die">
|
{{#if (eq roll.fate.fateDie "Hope")}}
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.hope"}}</label>
|
<div class="roll-die">
|
||||||
<div class="dice {{roll.hope.dice}} color-hope reroll-button" data-die-index="0" data-type="hope" data-tooltip="{{localize "DAGGERHEART.GENERAL.rerollThing" thing=(localize "DAGGERHEART.GENERAL.hope")}}">
|
<label>{{localize "DAGGERHEART.GENERAL.hope"}}</label>
|
||||||
{{#if roll.hope.rerolled.any}}<i class="fa-solid fa-dice dice-rerolled" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.diceIsRerolled" times=roll.hope.rerolled.rerolls.length}}"></i>{{/if}}
|
<div class="dice {{roll.fate.dice}} color-hope" data-die-index="0" data-type="hope">
|
||||||
{{roll.hope.value}}
|
{{roll.fate.value}}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="roll-die has-plus">
|
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.fear"}}</label>
|
|
||||||
<div class="dice {{roll.fear.dice}} color-fear reroll-button" data-die-index="2" data-type="fear" style="--svg-folder: 'fear';" data-tooltip="{{localize "DAGGERHEART.GENERAL.rerollThing" thing=(localize "DAGGERHEART.GENERAL.fear")}}">
|
|
||||||
{{#if roll.fear.rerolled.any}}<i class="fa-solid fa-dice dice-rerolled" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.diceIsRerolled" times=roll.fear.rerolled.rerolls.length}}"></i>{{/if}}
|
|
||||||
{{roll.fear.value}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{#if roll.advantage.type}}
|
|
||||||
<div class="roll-die has-plus">
|
|
||||||
{{#if (eq roll.advantage.type 1)}}
|
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.Advantage.short"}}</label>
|
|
||||||
<div class="dice {{roll.advantage.dice}} color-adv">{{roll.advantage.value}}</div>
|
|
||||||
{{else}}
|
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.Disadvantage.short"}}</label>
|
|
||||||
<div class="dice {{roll.advantage.dice}} color-dis">{{roll.advantage.value}}</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{#if roll.rally.dice}}
|
|
||||||
<div class="roll-die has-plus">
|
|
||||||
<label>{{localize "DAGGERHEART.CLASS.Feature.short"}}</label>
|
|
||||||
<div class="dice {{roll.rally.dice}}">{{roll.rally.value}}</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{#each roll.extra}}
|
|
||||||
{{#each results}}
|
|
||||||
{{#unless discarded}}
|
|
||||||
<div class="roll-die has-plus">
|
|
||||||
<label></label>
|
|
||||||
<div class="dice {{../dice}}">{{result}}</div>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
{{/each}}
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
{{#each roll.dice}}
|
|
||||||
{{#each results}}
|
|
||||||
<div class="roll-die {{#unless (or @../first discarded)}} has-plus{{/unless}}">
|
|
||||||
<div class="dice {{../dice}}{{#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>
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (eq roll.fate.fateDie "Fear")}}
|
||||||
|
<div class="roll-die">
|
||||||
|
<label>{{localize "DAGGERHEART.GENERAL.fear"}}</label>
|
||||||
|
<div class="dice {{roll.fate.dice}} color-fear" data-die-index="0" data-type="fear">
|
||||||
|
{{roll.fate.value}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#if roll.hope}}
|
||||||
|
<div class="roll-die">
|
||||||
|
<label>{{localize "DAGGERHEART.GENERAL.hope"}}</label>
|
||||||
|
<div class="dice {{roll.hope.dice}} color-hope reroll-button" data-die-index="0" data-type="hope" data-tooltip="{{localize "DAGGERHEART.GENERAL.rerollThing" thing=(localize "DAGGERHEART.GENERAL.hope")}}">
|
||||||
|
{{#if roll.hope.rerolled.any}}<i class="fa-solid fa-dice dice-rerolled" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.diceIsRerolled" times=roll.hope.rerolled.rerolls.length}}"></i>{{/if}}
|
||||||
|
{{roll.hope.value}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="roll-die has-plus">
|
||||||
|
<label>{{localize "DAGGERHEART.GENERAL.fear"}}</label>
|
||||||
|
<div class="dice {{roll.fear.dice}} color-fear reroll-button" data-die-index="2" data-type="fear" style="--svg-folder: 'fear';" data-tooltip="{{localize "DAGGERHEART.GENERAL.rerollThing" thing=(localize "DAGGERHEART.GENERAL.fear")}}">
|
||||||
|
{{#if roll.fear.rerolled.any}}<i class="fa-solid fa-dice dice-rerolled" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.diceIsRerolled" times=roll.fear.rerolled.rerolls.length}}"></i>{{/if}}
|
||||||
|
{{roll.fear.value}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#if roll.advantage.type}}
|
||||||
|
<div class="roll-die has-plus">
|
||||||
|
{{#if (eq roll.advantage.type 1)}}
|
||||||
|
<label>{{localize "DAGGERHEART.GENERAL.Advantage.short"}}</label>
|
||||||
|
<div class="dice {{roll.advantage.dice}} color-adv">{{roll.advantage.value}}</div>
|
||||||
|
{{else}}
|
||||||
|
<label>{{localize "DAGGERHEART.GENERAL.Disadvantage.short"}}</label>
|
||||||
|
<div class="dice {{roll.advantage.dice}} color-dis">{{roll.advantage.value}}</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if roll.rally.dice}}
|
||||||
|
<div class="roll-die has-plus">
|
||||||
|
<label>{{localize "DAGGERHEART.CLASS.Feature.short"}}</label>
|
||||||
|
<div class="dice {{roll.rally.dice}}">{{roll.rally.value}}</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#each roll.extra}}
|
||||||
|
{{#each results}}
|
||||||
|
{{#unless discarded}}
|
||||||
|
<div class="roll-die has-plus">
|
||||||
|
<label></label>
|
||||||
|
<div class="dice {{../dice}}">{{result}}</div>
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
{{/each}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/each}}
|
{{else}}
|
||||||
|
{{#each roll.dice}}
|
||||||
|
{{#each results}}
|
||||||
|
<div class="roll-die {{#unless (or @../first discarded)}} has-plus{{/unless}}">
|
||||||
|
<div class="dice {{../dice}}{{#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>
|
||||||
|
{{/each}}
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="roll-formula">{{roll.formula}}</div>
|
{{#if roll.fate}}
|
||||||
|
{{else}}
|
||||||
|
<div class="roll-formula">{{roll.formula}}</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue