mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-07-22 10:29:54 +02:00
Compare commits
8 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82d45cd5fb | ||
|
|
ea3ad2fa11 | ||
|
|
8b8f741b74 | ||
|
|
b73c70aeb8 | ||
|
|
7409a577f6 | ||
|
|
2b725e8760 | ||
|
|
f02e97f0cd | ||
|
|
c5308617e1 |
520 changed files with 5943 additions and 5539 deletions
13
daggerheart.d.ts
vendored
13
daggerheart.d.ts
vendored
|
|
@ -11,9 +11,6 @@ import * as documents from './module/documents/_module.mjs';
|
||||||
import { macros } from './module/_module.mjs';
|
import { macros } from './module/_module.mjs';
|
||||||
import * as dice from './module/dice/_module.mjs';
|
import * as dice from './module/dice/_module.mjs';
|
||||||
import * as fields from './module/data/fields/_module.mjs';
|
import * as fields from './module/data/fields/_module.mjs';
|
||||||
import { gameSettings } from './module/config/settingsConfig.mjs';
|
|
||||||
import DhCountdowns from './module/data/countdowns.mjs';
|
|
||||||
import DhAutomation from './module/data/settings/Automation.mjs';
|
|
||||||
|
|
||||||
|
|
||||||
// Foundry's use of `Object.assign(globalThis) means many globally available objects are not read as such
|
// Foundry's use of `Object.assign(globalThis) means many globally available objects are not read as such
|
||||||
|
|
@ -106,13 +103,3 @@ declare module '@client/packages/system.mjs' {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@client/helpers/client-settings.mjs' {
|
|
||||||
// Add explicit typed overrides for auto complete. These require /** @type {"string"} on the vars themselves to work */
|
|
||||||
export default interface ClientSettings {
|
|
||||||
get(namespace: 'daggerheart', key: typeof gameSettings.Automation): DhAutomation;
|
|
||||||
get(namespace: 'daggerheart', key: typeof gameSettings.Homebrew): DhHomebrew;
|
|
||||||
get(namespace: 'daggerheart', key: typeof gameSettings.Countdowns): DhCountdowns;
|
|
||||||
get(namespace: 'daggerheart', key: string): unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,6 @@ import * as documents from './module/documents/_module.mjs';
|
||||||
import { macros } from './module/_module.mjs';
|
import { macros } from './module/_module.mjs';
|
||||||
import * as collections from './module/documents/collections/_module.mjs';
|
import * as collections from './module/documents/collections/_module.mjs';
|
||||||
import * as dice from './module/dice/_module.mjs';
|
import * as dice from './module/dice/_module.mjs';
|
||||||
import * as die from './module/dice/die/_module.mjs';
|
|
||||||
import * as fields from './module/data/fields/_module.mjs';
|
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';
|
||||||
|
|
@ -24,7 +23,7 @@ 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 = [Roll = BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll];
|
CONFIG.Dice.rolls = [BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll];
|
||||||
CONFIG.Dice.daggerheart = {
|
CONFIG.Dice.daggerheart = {
|
||||||
DHRoll: DHRoll,
|
DHRoll: DHRoll,
|
||||||
DualityRoll: DualityRoll,
|
DualityRoll: DualityRoll,
|
||||||
|
|
@ -39,8 +38,6 @@ CONFIG.RegionBehavior.dataModels = {
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(CONFIG.Dice.termTypes, dice.diceTypes);
|
Object.assign(CONFIG.Dice.termTypes, dice.diceTypes);
|
||||||
CONFIG.Dice.terms.d = die.BaseDie;
|
|
||||||
CONFIG.Dice.types = [die.BaseDie, CONFIG.Dice.terms.f];
|
|
||||||
|
|
||||||
CONFIG.Actor.documentClass = documents.DhpActor;
|
CONFIG.Actor.documentClass = documents.DhpActor;
|
||||||
CONFIG.Actor.dataModels = models.actors.config;
|
CONFIG.Actor.dataModels = models.actors.config;
|
||||||
|
|
@ -270,10 +267,6 @@ Hooks.on('i18nInit', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.on('setup', () => {
|
Hooks.on('setup', () => {
|
||||||
if (game.user.isGM) {
|
|
||||||
document.body.dataset.gm = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
CONFIG.statusEffects = [
|
CONFIG.statusEffects = [
|
||||||
...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)),
|
...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)),
|
||||||
...Object.values(SYSTEM.GENERAL.conditions()).map(x => ({
|
...Object.values(SYSTEM.GENERAL.conditions()).map(x => ({
|
||||||
|
|
@ -397,7 +390,7 @@ const updateActorsRangeDependentEffects = async token => {
|
||||||
).rangeMeasurement;
|
).rangeMeasurement;
|
||||||
|
|
||||||
for (let effect of token.actor?.allApplicableEffects() ?? []) {
|
for (let effect of token.actor?.allApplicableEffects() ?? []) {
|
||||||
if (!effect.system.rangeDependence) continue;
|
if (!effect.system.rangeDependence?.enabled) continue;
|
||||||
const { target, range, type } = effect.system.rangeDependence;
|
const { target, range, type } = effect.system.rangeDependence;
|
||||||
|
|
||||||
// If there are no targets, assume false. Otherwise, start with the effect enabled.
|
// If there are no targets, assume false. Otherwise, start with the effect enabled.
|
||||||
|
|
|
||||||
56
lang/en.json
56
lang/en.json
|
|
@ -104,10 +104,8 @@
|
||||||
"startCountdown": "Start Countdown"
|
"startCountdown": "Start Countdown"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"addResource": "Add Resource",
|
|
||||||
"multiplier": "Multiplier",
|
"multiplier": "Multiplier",
|
||||||
"flatMultiplier": "Flat Multiplier",
|
"flatMultiplier": "Flat Multiplier"
|
||||||
"markResources": "Mark Resources"
|
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"customFormula": "Custom Formula",
|
"customFormula": "Custom Formula",
|
||||||
|
|
@ -126,15 +124,6 @@
|
||||||
"damageOnSave": "Damage on Save",
|
"damageOnSave": "Damage on Save",
|
||||||
"useDefaultItemValues": "Use default Item values"
|
"useDefaultItemValues": "Use default Item values"
|
||||||
},
|
},
|
||||||
"Reload": {
|
|
||||||
"checkReload": "Check Reload",
|
|
||||||
"reloadRequired": "Reload Required!",
|
|
||||||
"notRolled": "Not Rolled",
|
|
||||||
"checkFailed": "Check Failed",
|
|
||||||
"checkPassed": "Check Passed",
|
|
||||||
"rerollConfirmationTitle": "Reroll Reload Check",
|
|
||||||
"rerollConfirmationText": "Are you sure you want to reload the reload check?"
|
|
||||||
},
|
|
||||||
"RollField": {
|
"RollField": {
|
||||||
"diceRolling": {
|
"diceRolling": {
|
||||||
"compare": "Should be",
|
"compare": "Should be",
|
||||||
|
|
@ -178,7 +167,9 @@
|
||||||
},
|
},
|
||||||
"ACTIVEEFFECT": {
|
"ACTIVEEFFECT": {
|
||||||
"Config": {
|
"Config": {
|
||||||
"rangeDependence": { "title": "Range Dependence" },
|
"rangeDependence": {
|
||||||
|
"title": "Range Dependence"
|
||||||
|
},
|
||||||
"stacking": { "title": "Stacking" },
|
"stacking": { "title": "Stacking" },
|
||||||
"targetDispositions": "Affected Dispositions"
|
"targetDispositions": "Affected Dispositions"
|
||||||
},
|
},
|
||||||
|
|
@ -783,7 +774,7 @@
|
||||||
"removeRoll": "Remove Roll",
|
"removeRoll": "Remove Roll",
|
||||||
"resultsHint": "Results will appear when characters roll",
|
"resultsHint": "Results will appear when characters roll",
|
||||||
"selectLeaderHint": "Select one Character to be the leader",
|
"selectLeaderHint": "Select one Character to be the leader",
|
||||||
"selectParticipantsHint": "Select all Characters who will participate in the roll",
|
"selectParticipantsHint": "Select one Character to be the leader",
|
||||||
"startGroupRoll": "Start Group Roll",
|
"startGroupRoll": "Start Group Roll",
|
||||||
"title": "Group Roll"
|
"title": "Group Roll"
|
||||||
},
|
},
|
||||||
|
|
@ -1331,11 +1322,6 @@
|
||||||
"short": "V. Far"
|
"short": "V. Far"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ReloadChoices": {
|
|
||||||
"off": { "label": "Don't Use" },
|
|
||||||
"manual": { "label": "Manual" },
|
|
||||||
"auto": { "label": "Automatic" }
|
|
||||||
},
|
|
||||||
"RollTypes": {
|
"RollTypes": {
|
||||||
"trait": {
|
"trait": {
|
||||||
"name": "Trait"
|
"name": "Trait"
|
||||||
|
|
@ -2235,9 +2221,7 @@
|
||||||
},
|
},
|
||||||
"Resource": {
|
"Resource": {
|
||||||
"single": "Resource",
|
"single": "Resource",
|
||||||
"plural": "Resources",
|
"plural": "Resources"
|
||||||
"unloaded": "The weapon is not loaded",
|
|
||||||
"loaded": "The weapon is loaded"
|
|
||||||
},
|
},
|
||||||
"Roll": {
|
"Roll": {
|
||||||
"attack": "Attack Roll",
|
"attack": "Attack Roll",
|
||||||
|
|
@ -2263,7 +2247,7 @@
|
||||||
"hint": "A used armor slot normally reduces damage by one step. This value increases the number of steps damage is reduced by."
|
"hint": "A used armor slot normally reduces damage by one step. This value increases the number of steps damage is reduced by."
|
||||||
},
|
},
|
||||||
"magical": {
|
"magical": {
|
||||||
"label": "Damage Reduction: Only Magical",
|
"label": "Daamge Reduction: Only Magical",
|
||||||
"hint": "Armor can only be used to reduce magical damage"
|
"hint": "Armor can only be used to reduce magical damage"
|
||||||
},
|
},
|
||||||
"maxArmorMarkedBonus": "Max Armor Used",
|
"maxArmorMarkedBonus": "Max Armor Used",
|
||||||
|
|
@ -2501,6 +2485,7 @@
|
||||||
"reroll": "Reroll",
|
"reroll": "Reroll",
|
||||||
"rerolled": "Rerolled",
|
"rerolled": "Rerolled",
|
||||||
"rerollThing": "Reroll {thing}",
|
"rerollThing": "Reroll {thing}",
|
||||||
|
"resource": "Resource",
|
||||||
"result": {
|
"result": {
|
||||||
"single": "Result",
|
"single": "Result",
|
||||||
"plural": "Results"
|
"plural": "Results"
|
||||||
|
|
@ -2567,9 +2552,6 @@
|
||||||
},
|
},
|
||||||
"identifier": {
|
"identifier": {
|
||||||
"label": "Identifier"
|
"label": "Identifier"
|
||||||
},
|
|
||||||
"gmNotes": {
|
|
||||||
"label": "GM Notes"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Ancestry": {
|
"Ancestry": {
|
||||||
|
|
@ -2585,9 +2567,6 @@
|
||||||
"severe": "Severe Threshold"
|
"severe": "Severe Threshold"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Base": {
|
|
||||||
"addGMNote": "Add GM Note"
|
|
||||||
},
|
|
||||||
"Beastform": {
|
"Beastform": {
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
"beastformType": { "label": "Beastform Type" },
|
"beastformType": { "label": "Beastform Type" },
|
||||||
|
|
@ -2704,9 +2683,6 @@
|
||||||
"displayFear": {
|
"displayFear": {
|
||||||
"label": "Display Fear"
|
"label": "Display Fear"
|
||||||
},
|
},
|
||||||
"fearPosition": {
|
|
||||||
"label": "Fear Position"
|
|
||||||
},
|
|
||||||
"displayCountdownUI": {
|
"displayCountdownUI": {
|
||||||
"label": "Display Countdown UI"
|
"label": "Display Countdown UI"
|
||||||
},
|
},
|
||||||
|
|
@ -2758,13 +2734,6 @@
|
||||||
"token": "Tokens",
|
"token": "Tokens",
|
||||||
"bar": "Bar",
|
"bar": "Bar",
|
||||||
"hide": "Hide"
|
"hide": "Hide"
|
||||||
},
|
|
||||||
"fearPosition": {
|
|
||||||
"free": "Free",
|
|
||||||
"topCenter": "Top + Center",
|
|
||||||
"bottomCenter": "Bottom + Center",
|
|
||||||
"rightTop": "Right + Top",
|
|
||||||
"leftBottom": "Left + Bottom"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Automation": {
|
"Automation": {
|
||||||
|
|
@ -2823,10 +2792,6 @@
|
||||||
"hint": "Effects with defined range dependency will automatically turn on/off depending on range"
|
"hint": "Effects with defined range dependency will automatically turn on/off depending on range"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reload": {
|
|
||||||
"label": "Reload Checking",
|
|
||||||
"hint": "If the system should automatically roll reload checks or wait for the manual press of a button in chat"
|
|
||||||
},
|
|
||||||
"resourceScrollTexts": {
|
"resourceScrollTexts": {
|
||||||
"label": "Show Resource Change Scrolltexts",
|
"label": "Show Resource Change Scrolltexts",
|
||||||
"hint": "When a character is damaged, uses armor etc, a scrolling text will briefly appear by the token to signify this."
|
"hint": "When a character is damaged, uses armor etc, a scrolling text will briefly appear by the token to signify this."
|
||||||
|
|
@ -3270,7 +3235,7 @@
|
||||||
"beastformToManyAdvantages": "You cannot select any more advantages.",
|
"beastformToManyAdvantages": "You cannot select any more advantages.",
|
||||||
"beastformToManyFeatures": "You cannot select any more features.",
|
"beastformToManyFeatures": "You cannot select any more features.",
|
||||||
"beastformEquipWeapon": "You cannot use weapons while in a Beastform.",
|
"beastformEquipWeapon": "You cannot use weapons while in a Beastform.",
|
||||||
"loadoutMaxReached": "You've reached the maximum loadout size. To add more, move at least one domain card to the vault.",
|
"loadoutMaxReached": "You've reached maximum loadout. Move atleast one domain card to the vault, or increase the limit in homebrew settings if desired.",
|
||||||
"domainMaxReached": "You've reached the maximum domains for the class. Increase the limit in homebrew settings if desired.",
|
"domainMaxReached": "You've reached the maximum domains for the class. Increase the limit in homebrew settings if desired.",
|
||||||
"insufficientResources": "You don't have enough resources to use that action.",
|
"insufficientResources": "You don't have enough resources to use that action.",
|
||||||
"actionNoUsesRemaining": "That action doesn't have remaining uses.",
|
"actionNoUsesRemaining": "That action doesn't have remaining uses.",
|
||||||
|
|
@ -3288,8 +3253,7 @@
|
||||||
"knowTheTide": "Know The Tide gained a token",
|
"knowTheTide": "Know The Tide gained a token",
|
||||||
"lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}",
|
"lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}",
|
||||||
"noTokenTargeted": "No token is targeted",
|
"noTokenTargeted": "No token is targeted",
|
||||||
"behaviorRegionRequiresGM": "Creating a Region with an attached Behavior requires an online GM",
|
"behaviorRegionRequiresGM": "Creating a Region with an attached Behavior requires an online GM"
|
||||||
"reloadRequired": "The {weapon} must be reloaded to be used!"
|
|
||||||
},
|
},
|
||||||
"Progress": {
|
"Progress": {
|
||||||
"migrationLabel": "Performing system migration. Please wait and do not close Foundry."
|
"migrationLabel": "Performing system migration. Please wait and do not close Foundry."
|
||||||
|
|
|
||||||
|
|
@ -315,15 +315,15 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
|
|
||||||
const beastformEffect = selected.effects.find(x => x.type === 'beastform');
|
const beastformEffect = selected.effects.find(x => x.type === 'beastform');
|
||||||
for (const traitBonus of app.modifications.traitBonuses) {
|
for (const traitBonus of app.modifications.traitBonuses) {
|
||||||
const existingChange = beastformEffect.system.changes.find(
|
const existingChange = beastformEffect.changes.find(
|
||||||
x => x.key === `system.traits.${traitBonus.trait}.value`
|
x => x.key === `system.traits.${traitBonus.trait}.value`
|
||||||
);
|
);
|
||||||
if (existingChange) {
|
if (existingChange) {
|
||||||
existingChange.value = Number.parseInt(existingChange.value) + traitBonus.bonus;
|
existingChange.value = Number.parseInt(existingChange.value) + traitBonus.bonus;
|
||||||
} else {
|
} else {
|
||||||
beastformEffect.system.changes.push({
|
beastformEffect.changes.push({
|
||||||
key: `system.traits.${traitBonus.trait}.value`,
|
key: `system.traits.${traitBonus.trait}.value`,
|
||||||
type: 'add',
|
mode: 2,
|
||||||
priority: null,
|
priority: null,
|
||||||
value: traitBonus.bonus
|
value: traitBonus.bonus
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.config = CONFIG.DH;
|
context.config = CONFIG.DH;
|
||||||
context.title = this.config.title ?? this.title;
|
context.title = this.config.title ?? this.title;
|
||||||
|
context.formula = this.roll.constructFormula(this.config);
|
||||||
const { damageFormula, resourceFormulas } = this.roll.constructFormulas(this.config);
|
|
||||||
context.damageFormula = damageFormula;
|
|
||||||
context.resourceFormulas = resourceFormulas;
|
|
||||||
|
|
||||||
context.hasHealing = this.config.hasHealing;
|
context.hasHealing = this.config.hasHealing;
|
||||||
context.directDamage = this.config.directDamage;
|
context.directDamage = this.config.directDamage;
|
||||||
context.selectedMessageMode = this.config.selectedMessageMode;
|
context.selectedMessageMode = this.config.selectedMessageMode;
|
||||||
|
|
@ -77,11 +73,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
|
|
||||||
static updateRollConfiguration(_event, _, formData) {
|
static updateRollConfiguration(_event, _, formData) {
|
||||||
const data = foundry.utils.expandObject(formData.object);
|
const data = foundry.utils.expandObject(formData.object);
|
||||||
|
foundry.utils.mergeObject(this.config.roll, data.roll);
|
||||||
if (this.config.damageFormula)
|
|
||||||
foundry.utils.mergeObject(this.config.damageFormula, data.damageFormula);
|
|
||||||
|
|
||||||
foundry.utils.mergeObject(this.config.resourceFormulas, data.resourceFormulas);
|
|
||||||
foundry.utils.mergeObject(this.config.modifiers, data.modifiers);
|
foundry.utils.mergeObject(this.config.modifiers, data.modifiers);
|
||||||
this.config.selectedMessageMode = data.selectedMessageMode;
|
this.config.selectedMessageMode = data.selectedMessageMode;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,15 +139,13 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
|
||||||
name: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.name'),
|
name: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.name'),
|
||||||
description: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.description'),
|
description: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.description'),
|
||||||
img: CONFIG.DH.GENERAL.deathMoves.blazeOfGlory.img,
|
img: CONFIG.DH.GENERAL.deathMoves.blazeOfGlory.img,
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.rules.roll.guaranteedCritical',
|
||||||
key: 'system.rules.roll.guaranteedCritical',
|
mode: 2,
|
||||||
type: 'add',
|
value: 'true'
|
||||||
value: 'true'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -185,6 +183,8 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
|
||||||
|
|
||||||
if (result === undefined) return;
|
if (result === undefined) 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 = {
|
||||||
|
|
@ -200,6 +200,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
|
||||||
img: this.selectedMove.img,
|
img: this.selectedMove.img,
|
||||||
description: game.i18n.localize(this.selectedMove.description),
|
description: game.i18n.localize(this.selectedMove.description),
|
||||||
result: result,
|
result: result,
|
||||||
|
open: autoExpandDescription ? 'open' : '',
|
||||||
showRiskItAllButton: this.showRiskItAllButton,
|
showRiskItAllButton: this.showRiskItAllButton,
|
||||||
riskItAllButtonLabel: this.riskItAllButtonLabel,
|
riskItAllButtonLabel: this.riskItAllButtonLabel,
|
||||||
riskItAllHope: this.riskItAllHope
|
riskItAllHope: this.riskItAllHope
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,9 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
.filter(x => x.testUserPermission(game.user, 'LIMITED'))
|
.filter(x => x.testUserPermission(game.user, 'LIMITED'))
|
||||||
.filter(x => x.uuid !== this.actor.uuid);
|
.filter(x => x.uuid !== this.actor.uuid);
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
@ -216,7 +219,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
actor: { name: this.actor.name, img: this.actor.img },
|
actor: { name: this.actor.name, img: this.actor.img },
|
||||||
moves: moves,
|
moves: moves,
|
||||||
characters: characters,
|
characters: characters,
|
||||||
selfId: this.actor.uuid
|
selfId: this.actor.uuid,
|
||||||
|
open: autoExpandDescription ? 'open' : ''
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
flags: {
|
flags: {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
|
import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
|
||||||
import { shouldUseHopeFearAutomation } from '../../helpers/utils.mjs';
|
import { shouldUseHopeFearAutomation } from '../../helpers/utils.mjs';
|
||||||
import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||||
import PartySheet from '../sheets/actors/party.mjs';
|
import Party from '../sheets/actors/party.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
|
|
||||||
this.party = party;
|
this.party = party;
|
||||||
this.partyMembers = party.system.partyMembers
|
this.partyMembers = party.system.partyMembers
|
||||||
.filter(x => PartySheet.DICE_ROLL_ACTOR_TYPES.includes(x.type))
|
.filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type))
|
||||||
.map(member => ({
|
.map(member => ({
|
||||||
...member.toObject(),
|
...member.toObject(),
|
||||||
uuid: member.uuid,
|
uuid: member.uuid,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
|
import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
|
||||||
import { ChatDamageData } from '../../data/chat-message/chatDamageData.mjs';
|
|
||||||
import { MemberData } from '../../data/tagTeamData.mjs';
|
import { MemberData } from '../../data/tagTeamData.mjs';
|
||||||
import { getCritDamageBonus, shouldUseHopeFearAutomation } from '../../helpers/utils.mjs';
|
import { getCritDamageBonus, shouldUseHopeFearAutomation } from '../../helpers/utils.mjs';
|
||||||
import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||||
import PartySheet from '../sheets/actors/party.mjs';
|
import Party from '../sheets/actors/party.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
|
@ -14,7 +13,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
this.usesTagTeamHopeCost = true;
|
this.usesTagTeamHopeCost = true;
|
||||||
this.party = party;
|
this.party = party;
|
||||||
this.partyMembers = party.system.partyMembers
|
this.partyMembers = party.system.partyMembers
|
||||||
.filter(x => PartySheet.DICE_ROLL_ACTOR_TYPES.includes(x.type))
|
.filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type))
|
||||||
.map(member => ({
|
.map(member => ({
|
||||||
...member.toObject(),
|
...member.toObject(),
|
||||||
uuid: member.uuid,
|
uuid: member.uuid,
|
||||||
|
|
@ -141,7 +140,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
const hasRolled = Boolean(data.rollData);
|
const hasRolled = Boolean(data.rollData);
|
||||||
if (!hasRolled) return false;
|
if (!hasRolled) return false;
|
||||||
|
|
||||||
return !data.rollData.options.hasDamage || data.damageRollData.active;
|
return !data.rollData.options.hasDamage || Boolean(data.rollData.options.damage);
|
||||||
});
|
});
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
|
@ -182,7 +181,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
|
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
|
||||||
const critSelected = !selectedRoll
|
const critSelected = !selectedRoll
|
||||||
? undefined
|
? undefined
|
||||||
: (selectedRoll?.roll?.isCritical ?? false);
|
: (selectedRoll?.rollData?.options?.roll?.isCritical ?? false);
|
||||||
|
|
||||||
partContext.hintText = await this.getInfoTexts(this.party.system.tagTeam.members);
|
partContext.hintText = await this.getInfoTexts(this.party.system.tagTeam.members);
|
||||||
partContext.joinedRoll = await this.getJoinedRoll({
|
partContext.joinedRoll = await this.getJoinedRoll({
|
||||||
|
|
@ -209,16 +208,6 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
|
|
||||||
const rollOptions = [];
|
const rollOptions = [];
|
||||||
const damageRollOptions = [];
|
const damageRollOptions = [];
|
||||||
|
|
||||||
if (actor?.system.usedUnarmed) {
|
|
||||||
damageRollOptions.push({
|
|
||||||
value: actor.system.attack.uuid,
|
|
||||||
label: actor.system.usedUnarmed.name,
|
|
||||||
group: actor.name,
|
|
||||||
baseAction: actor.system.attack
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const item of actor?.items ?? []) {
|
for (const item of actor?.items ?? []) {
|
||||||
if (!item.system.metadata.hasActions) continue;
|
if (!item.system.metadata.hasActions) continue;
|
||||||
const actions = [...item.system.actions, ...(item.system.attack ? [item.system.attack] : [])];
|
const actions = [...item.system.actions, ...(item.system.attack ? [item.system.attack] : [])];
|
||||||
|
|
@ -236,7 +225,8 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
|
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
|
||||||
const critSelected = !selectedRoll ? undefined : (selectedRoll?.roll?.isCritical ?? false);
|
const critSelected = !selectedRoll ? undefined : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false);
|
||||||
|
const damage = data.rollData?.options?.damage;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
|
@ -247,9 +237,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
hasRolled: Boolean(data.rollData),
|
hasRolled: Boolean(data.rollData),
|
||||||
rollOptions,
|
rollOptions,
|
||||||
damageRollOptions,
|
damageRollOptions,
|
||||||
damage: data.damageRollData,
|
damage: damage,
|
||||||
critDamage: await this.getCriticalDamage(data.damageRollData),
|
critDamage: await this.getCriticalDamage(damage),
|
||||||
useCritDamage: critSelected || (critSelected === undefined && data.roll?.isCritical)
|
useCritDamage: critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,7 +369,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
let rollIsSelected = false;
|
let rollIsSelected = false;
|
||||||
for (const member of Object.values(members)) {
|
for (const member of Object.values(members)) {
|
||||||
const rollFinished = Boolean(member.rollData);
|
const rollFinished = Boolean(member.rollData);
|
||||||
const damageFinished = member.rollData?.options?.hasDamage ? member.damageRollData.active : true;
|
const damageFinished = member.rollData?.options?.hasDamage ? Boolean(member.rollData.options.damage) : true;
|
||||||
|
|
||||||
rollsAreFinished = rollsAreFinished && rollFinished && damageFinished;
|
rollsAreFinished = rollsAreFinished && rollFinished && damageFinished;
|
||||||
rollIsSelected = rollIsSelected || member.selected;
|
rollIsSelected = rollIsSelected || member.selected;
|
||||||
|
|
@ -540,10 +530,17 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
|
|
||||||
await action.workflow.get('damage').execute(config, null, true);
|
await action.workflow.get('damage').execute(config, null, true);
|
||||||
if (!config.damage) return;
|
if (!config.damage) return;
|
||||||
|
|
||||||
|
const current = this.party.system.tagTeam.members[memberKey].rollData;
|
||||||
await this.updatePartyData(
|
await this.updatePartyData(
|
||||||
{
|
{
|
||||||
[`system.tagTeam.members.${memberKey}.damageRollData`]: config.damage
|
[`system.tagTeam.members.${memberKey}.rollData`]: {
|
||||||
|
...current,
|
||||||
|
options: {
|
||||||
|
...current.options,
|
||||||
|
damage: config.damage
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
this.getUpdatingParts(button)
|
this.getUpdatingParts(button)
|
||||||
);
|
);
|
||||||
|
|
@ -551,11 +548,15 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
|
|
||||||
static async #removeDamageRoll(_, button) {
|
static async #removeDamageRoll(_, button) {
|
||||||
const { memberKey } = button.dataset;
|
const { memberKey } = button.dataset;
|
||||||
|
const current = this.party.system.tagTeam.members[memberKey].rollData;
|
||||||
this.updatePartyData(
|
this.updatePartyData(
|
||||||
{
|
{
|
||||||
[`system.tagTeam.members.${memberKey}.damageRollData`]: {
|
[`system.tagTeam.members.${memberKey}.rollData`]: {
|
||||||
main: null,
|
...current,
|
||||||
resources: _replace({})
|
options: {
|
||||||
|
...current.options,
|
||||||
|
damage: null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
this.getUpdatingParts(button)
|
this.getUpdatingParts(button)
|
||||||
|
|
@ -563,44 +564,89 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #rerollDamageDice(_, button) {
|
static async #rerollDamageDice(_, button) {
|
||||||
const { isResource, memberKey, damageKey, diceIndex, resultIndex } = button.dataset;
|
const { memberKey, damageKey, part, dice } = button.dataset;
|
||||||
const memberData = this.party.system.tagTeam.members[memberKey];
|
const memberData = this.party.system.tagTeam.members[memberKey];
|
||||||
await memberData.damageRollData.rerollDamageDie(isResource, damageKey, diceIndex, resultIndex);
|
const partData = memberData.rollData.options.damage[damageKey].parts[part];
|
||||||
|
const activeDiceResultKey = Object.keys(partData.dice[dice].results).find(
|
||||||
|
index => partData.dice[dice].results[index].active
|
||||||
|
);
|
||||||
|
const { parsedRoll, rerolledDice } = await game.system.api.dice.DamageRoll.reroll(
|
||||||
|
partData,
|
||||||
|
dice,
|
||||||
|
activeDiceResultKey
|
||||||
|
);
|
||||||
|
|
||||||
|
const rollData = this.party.system.tagTeam.members[memberKey].rollData;
|
||||||
|
rollData.options.damage[damageKey].parts = rollData.options.damage[damageKey].parts.map((damagePart, index) => {
|
||||||
|
if (index !== Number.parseInt(part)) return damagePart;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...damagePart,
|
||||||
|
total: parsedRoll.total,
|
||||||
|
dice: rerolledDice
|
||||||
|
};
|
||||||
|
});
|
||||||
|
rollData.options.damage[damageKey].total = rollData.options.damage[damageKey].parts.reduce((acc, part) => {
|
||||||
|
acc += part.total;
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
const basePath = `system.tagTeam.members.${memberKey}.damageRollData`;
|
|
||||||
const updatePath = isResource ? `${basePath}.resources.${damageKey}` : `${basePath}.main`;
|
|
||||||
const updateValue = isResource ?
|
|
||||||
memberData.damageRollData.resources[damageKey] : memberData.damageRollData.main;
|
|
||||||
this.updatePartyData(
|
this.updatePartyData(
|
||||||
{
|
{
|
||||||
[updatePath]: updateValue.toJSON()
|
[`system.tagTeam.members.${memberKey}.rollData`]: rollData
|
||||||
},
|
},
|
||||||
this.getUpdatingParts(button)
|
this.getUpdatingParts(button)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCriticalDamage(origDamage) {
|
async getCriticalDamage(damage) {
|
||||||
const newDamage = origDamage ? ChatDamageData.fromJSON(JSON.stringify(origDamage)) : null;
|
const newDamage = foundry.utils.deepClone(damage);
|
||||||
if (newDamage?.main) {
|
for (let key in newDamage) {
|
||||||
const criticalDamage = await getCritDamageBonus(newDamage.main.formula);
|
var damage = newDamage[key];
|
||||||
if (criticalDamage) {
|
damage.formula = '';
|
||||||
const criticalTerm = new foundry.dice.terms.NumericTerm({ number: criticalDamage, evaluated: true });
|
damage.total = 0;
|
||||||
criticalTerm.evaluate();
|
|
||||||
newDamage.main = await Roll.fromTerms([
|
for (let part of damage.parts) {
|
||||||
...origDamage.main.terms,
|
const criticalDamage = await getCritDamageBonus(part.formula);
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
if (criticalDamage) {
|
||||||
criticalTerm
|
part.modifierTotal += criticalDamage;
|
||||||
]);
|
part.total += criticalDamage;
|
||||||
newDamage.main.options = foundry.utils.deepClone(origDamage.main.options);
|
part.formula = `${part.dice.map(x => x.formula).join(' + ')} + ${part.modifierTotal}`;
|
||||||
|
part.roll = new Roll(part.formula);
|
||||||
|
}
|
||||||
|
|
||||||
|
damage.formula = [damage.formula, part.formula].filter(x => x).join(' + ');
|
||||||
|
damage.total += part.total;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return newDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getNonCriticalDamage(config) {
|
||||||
|
const newDamage = foundry.utils.deepClone(config.damage);
|
||||||
|
for (let key in newDamage) {
|
||||||
|
var damage = newDamage[key];
|
||||||
|
damage.formula = '';
|
||||||
|
damage.total = 0;
|
||||||
|
|
||||||
|
for (let part of damage.parts) {
|
||||||
|
const critDamageBonus = await getCritDamageBonus(part.formula);
|
||||||
|
part.modifierTotal -= critDamageBonus;
|
||||||
|
part.total -= critDamageBonus;
|
||||||
|
part.formula = `${part.dice.map(x => x.formula).join(' + ')} + ${part.modifierTotal}`;
|
||||||
|
part.roll = new Roll(part.formula);
|
||||||
|
|
||||||
|
damage.formula = [damage.formula, part.formula].filter(x => x).join(' + ');
|
||||||
|
damage.total += part.total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return newDamage;
|
return newDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #selectRoll(_, button) {
|
static async #selectRoll(_, button) {
|
||||||
const { memberKey } = button.dataset;
|
const { memberKey } = button.dataset;
|
||||||
|
|
||||||
this.updatePartyData(
|
this.updatePartyData(
|
||||||
{
|
{
|
||||||
[`system.tagTeam.members`]: Object.entries(this.party.system.tagTeam.members).reduce(
|
[`system.tagTeam.members`]: Object.entries(this.party.system.tagTeam.members).reduce(
|
||||||
|
|
@ -611,12 +657,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
/* Selecting a roll must update all member sections hbs to display the correct damage information incase of a critical */
|
this.getUpdatingParts(button)
|
||||||
[
|
|
||||||
...Object.keys(this.party.system.tagTeam.members),
|
|
||||||
this.constructor.PARTS.rollSelection.id,
|
|
||||||
this.constructor.PARTS.result.id
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -634,65 +675,29 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null;
|
if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null;
|
||||||
|
|
||||||
const mainRoll = new MemberData(baseMainRoll.toObject());
|
const mainRoll = new MemberData(baseMainRoll.toObject());
|
||||||
mainRoll.damageRollData = baseMainRoll.damageRollData ?
|
const secondaryRollData = new MemberData(baseSecondaryRoll.toObject()).rollData;
|
||||||
ChatDamageData.fromJSON(JSON.stringify(baseMainRoll.damageRollData)) : null;
|
const systemData = mainRoll.rollData.options;
|
||||||
const secondaryRoll = new MemberData(baseSecondaryRoll.toObject());
|
const isCritical = overrideIsCritical ?? systemData.roll.isCritical;
|
||||||
secondaryRoll.damageRollData = baseSecondaryRoll.damageRollData ?
|
if (isCritical) systemData.damage = await this.getCriticalDamage(systemData.damage);
|
||||||
ChatDamageData.fromJSON(JSON.stringify(baseSecondaryRoll.damageRollData)) : null;
|
|
||||||
|
|
||||||
const isCritical = overrideIsCritical ?? mainRoll.roll.isCritical;
|
if (secondaryRollData?.options.hasDamage) {
|
||||||
if (isCritical) mainRoll.damageRollData = await this.getCriticalDamage(mainRoll.damageRollData);
|
|
||||||
|
|
||||||
if (secondaryRoll.damageRollData) {
|
|
||||||
const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical)
|
const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical)
|
||||||
? await this.getCriticalDamage(secondaryRoll.damageRollData)
|
? await this.getCriticalDamage(secondaryRollData.options.damage)
|
||||||
: secondaryRoll.damageRollData;
|
: secondaryRollData.options.damage;
|
||||||
if (mainRoll.damageRollData) {
|
if (systemData.damage) {
|
||||||
if (secondaryDamage.main) {
|
for (const [key, damage] of Object.entries(secondaryDamage ?? {})) {
|
||||||
if (mainRoll.damageRollData.main) {
|
if (key in systemData.damage) {
|
||||||
mainRoll.damageRollData.main = Roll.fromTerms([
|
systemData.damage[key].formula = [systemData.damage[key]?.formula, damage.formula]
|
||||||
...baseMainRoll.damageRollData.main.terms,
|
.filter(x => x)
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
.join(' + ');
|
||||||
...baseSecondaryRoll.damageRollData.main.terms
|
systemData.damage[key].total += damage.total;
|
||||||
]);
|
systemData.damage[key].parts.push(...damage.parts);
|
||||||
|
|
||||||
/* Joining the roll.options of both rolls */
|
|
||||||
const joinedDamageTypes = new Set([
|
|
||||||
...baseMainRoll.damageRollData.main.options.damageTypes,
|
|
||||||
...baseSecondaryRoll.damageRollData.main.options.damageTypes
|
|
||||||
]);
|
|
||||||
mainRoll.damageRollData.main.options = {
|
|
||||||
...baseMainRoll.damageRollData.main.options,
|
|
||||||
damageTypes: [...joinedDamageTypes]
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
mainRoll.damageRollData.main = secondaryDamage.main;
|
systemData.damage[key] = damage;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [key, damage] of Object.entries(secondaryDamage.resources ?? {})) {
|
|
||||||
if (key in mainRoll.damageRollData.resources) {
|
|
||||||
mainRoll.damageRollData.resources[key] = Roll.fromTerms([
|
|
||||||
...baseMainRoll.damageRollData.resources[key].terms,
|
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
|
||||||
...baseSecondaryRoll.damageRollData.resources[key].terms
|
|
||||||
]);
|
|
||||||
|
|
||||||
/* Joining the roll.options of both rolls */
|
|
||||||
const joinedDamageTypes = new Set([
|
|
||||||
...baseMainRoll.damageRollData.resources[key].options.damageTypes,
|
|
||||||
...baseSecondaryRoll.damageRollData.resources[key].options.damageTypes
|
|
||||||
]);
|
|
||||||
mainRoll.damageRollData.resources[key].options = {
|
|
||||||
...baseMainRoll.damageRollData.resources[key].options,
|
|
||||||
damageTypes: [...joinedDamageTypes]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
mainRoll.damageRollData.resources[key] = damage;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mainRoll.damageRollData = secondaryDamage;
|
systemData.damage = secondaryDamage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -747,27 +752,13 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
|
|
||||||
const mainActor = this.party.system.partyMembers.find(x => x.uuid === mainRoll.options.source.actor);
|
const mainActor = this.party.system.partyMembers.find(x => x.uuid === mainRoll.options.source.actor);
|
||||||
mainRoll.options.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.chatMessageRollTitle');
|
mainRoll.options.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.chatMessageRollTitle');
|
||||||
|
|
||||||
/* This could assumably be done better. For some reason rolls don't get correctly done through rollData.toJSON */
|
|
||||||
const systemData = {
|
|
||||||
...mainRoll.options,
|
|
||||||
damage: joinedRoll.damageRollData?.toJSON()
|
|
||||||
};
|
|
||||||
|
|
||||||
if (joinedRoll.damageRollData.main) {
|
|
||||||
systemData.damage.main = joinedRoll.damageRollData.toJSON();
|
|
||||||
}
|
|
||||||
for (const type of Object.keys(joinedRoll.damageRollData?.resources ?? {})) {
|
|
||||||
systemData.damage.resources[type] = joinedRoll.damageRollData.resources[type].toJSON();
|
|
||||||
}
|
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage'),
|
const cls = getDocumentClass('ChatMessage'),
|
||||||
msgData = {
|
msgData = {
|
||||||
type: 'dualityRoll',
|
type: 'dualityRoll',
|
||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
title: game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.title'),
|
title: game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.title'),
|
||||||
speaker: cls.getSpeaker({ actor: mainActor }),
|
speaker: cls.getSpeaker({ actor: mainActor }),
|
||||||
system: systemData,
|
system: mainRoll.options,
|
||||||
rolls: [JSON.stringify(joinedRoll.roll)],
|
rolls: [JSON.stringify(joinedRoll.roll)],
|
||||||
sound: null,
|
sound: null,
|
||||||
flags: { core: { RollTable: true } }
|
flags: { core: { RollTable: true } }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { DHDamageData } from '../../data/fields/action/damageField.mjs';
|
import { getUnusedDamageTypes } from '../../helpers/utils.mjs';
|
||||||
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
||||||
|
|
||||||
const { ApplicationV2 } = foundry.applications.api;
|
const { ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
@ -31,10 +31,8 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
removeElement: this.removeElement,
|
removeElement: this.removeElement,
|
||||||
removeTransformActor: this.removeTransformActor,
|
removeTransformActor: this.removeTransformActor,
|
||||||
editEffect: this.editEffect,
|
editEffect: this.editEffect,
|
||||||
addDamage: this.#onAddDamage,
|
addDamage: this.addDamage,
|
||||||
removeDamage: this.#onRemoveDamage,
|
removeDamage: this.removeDamage,
|
||||||
addDamageResource: this.#onAddDamageResource,
|
|
||||||
removeDamageResource: this.#onRemoveDamageResource,
|
|
||||||
editDoc: this.editDoc,
|
editDoc: this.editDoc,
|
||||||
addTrigger: this.addTrigger,
|
addTrigger: this.addTrigger,
|
||||||
removeTrigger: this.removeTrigger,
|
removeTrigger: this.removeTrigger,
|
||||||
|
|
@ -159,9 +157,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
context.tabs = this._getTabs(this.constructor.TABS);
|
context.tabs = this._getTabs(this.constructor.TABS);
|
||||||
context.config = CONFIG.DH;
|
context.config = CONFIG.DH;
|
||||||
if (this.action.damage) {
|
if (this.action.damage) {
|
||||||
const allKeys = Object.keys(CONFIG.DH.GENERAL.healingTypes);
|
context.allDamageTypesUsed = !getUnusedDamageTypes(this.action.damage.parts).length;
|
||||||
context.allDamageTypesUsed = allKeys.every(k => k in this.action._source.damage.resources);
|
|
||||||
if (this.action.damage?.main?.hasOwnProperty('includeBase') && this.action.type === 'attack')
|
if (this.action.damage.hasOwnProperty('includeBase') && this.action.type === 'attack')
|
||||||
context.hasBaseDamage = !!this.action.parent.attack;
|
context.hasBaseDamage = !!this.action.parent.attack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,7 +253,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
const submitData = this._prepareSubmitData(event, formData);
|
const submitData = this._prepareSubmitData(event, formData);
|
||||||
|
|
||||||
const data = foundry.utils.mergeObject(this.action.toObject(), submitData);
|
const data = foundry.utils.mergeObject(this.action.toObject(), submitData);
|
||||||
this.action = (await this.action.update(data)) ?? this.action;
|
this.action = await this.action.update(data);
|
||||||
|
|
||||||
this.sheetUpdate?.(this.action);
|
this.sheetUpdate?.(this.action);
|
||||||
this.render();
|
this.render();
|
||||||
|
|
@ -301,70 +299,53 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @this DHActionBaseConfig */
|
static addDamage(_event) {
|
||||||
static #onAddDamage() {
|
if (!this.action.damage.parts) return;
|
||||||
if (!this.action.damage || this.action.damage?.main) return;
|
|
||||||
|
|
||||||
const data = this.action.toObject();
|
const choices = getUnusedDamageTypes(this.action._source.damage.parts);
|
||||||
data.damage.main = {
|
|
||||||
...DHDamageData.schema.getInitialValue(),
|
|
||||||
applyTo: 'hitPoints',
|
|
||||||
type: 'physical'
|
|
||||||
};
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @this DHActionBaseConfig */
|
|
||||||
static #onRemoveDamage() {
|
|
||||||
if (!this.action.damage?.main) return;
|
|
||||||
const data = this.action.toObject();
|
|
||||||
data.damage.main = null;
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @this DHActionBaseConfig */
|
|
||||||
static #onAddDamageResource(_event) {
|
|
||||||
if (!this.action.damage) return;
|
|
||||||
|
|
||||||
const allKeys = Object.keys(CONFIG.DH.GENERAL.healingTypes);
|
|
||||||
const unused = allKeys.filter(k => !(k in this.action._source.damage.resources));
|
|
||||||
const choices = unused.map(k => ({ value: k, label: _loc(CONFIG.DH.GENERAL.healingTypes[k].label) }));
|
|
||||||
const content = new foundry.data.fields.StringField({
|
const content = new foundry.data.fields.StringField({
|
||||||
label: _loc('DAGGERHEART.GENERAL.Resource.single'),
|
label: game.i18n.localize('Damage Type'),
|
||||||
choices,
|
choices,
|
||||||
required: true
|
required: true
|
||||||
}).toFormGroup({}, {
|
}).toFormGroup(
|
||||||
name: 'type',
|
{},
|
||||||
localize: true,
|
{
|
||||||
nameAttr: 'value',
|
name: 'type',
|
||||||
labelAttr: 'label'
|
localize: true,
|
||||||
}).outerHTML;
|
nameAttr: 'value',
|
||||||
|
labelAttr: 'label'
|
||||||
|
}
|
||||||
|
).outerHTML;
|
||||||
|
|
||||||
const callback = (_, button) => {
|
const callback = (_, button) => {
|
||||||
const data = this.action.toObject();
|
const data = this.action.toObject();
|
||||||
const type = choices[button.form.elements.type.value].value;
|
const type = choices[button.form.elements.type.value].value;
|
||||||
data.damage.resources[type] = {
|
const part = this.action.schema.fields.damage.fields.parts.element.getInitialValue();
|
||||||
...this.action.schema.fields.damage.fields.resources.element.getInitialValue(),
|
part.applyTo = type;
|
||||||
applyTo: type
|
if (type === CONFIG.DH.GENERAL.healingTypes.hitPoints.id)
|
||||||
};
|
part.type = this.action.schema.fields.damage.fields.parts.element.fields.type.element.initial;
|
||||||
|
|
||||||
|
data.damage.parts[type] = part;
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
};
|
};
|
||||||
|
|
||||||
const typeDialog = new foundry.applications.api.DialogV2({
|
const typeDialog = new foundry.applications.api.DialogV2({
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
foundry.utils.mergeObject(
|
||||||
action: 'ok',
|
{
|
||||||
label: 'Confirm',
|
action: 'ok',
|
||||||
icon: 'fas fa-check',
|
label: 'Confirm',
|
||||||
default: true,
|
icon: 'fas fa-check',
|
||||||
callback
|
default: true
|
||||||
}
|
},
|
||||||
|
{ callback: callback }
|
||||||
|
)
|
||||||
],
|
],
|
||||||
content: content,
|
content: content,
|
||||||
rejectClose: false,
|
rejectClose: false,
|
||||||
modal: false,
|
modal: false,
|
||||||
window: {
|
window: {
|
||||||
title: _loc('DAGGERHEART.ACTIONS.Config.damage.addResource')
|
title: game.i18n.localize('Add Damage')
|
||||||
},
|
},
|
||||||
position: { width: 300 }
|
position: { width: 300 }
|
||||||
});
|
});
|
||||||
|
|
@ -372,12 +353,12 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
typeDialog.render(true);
|
typeDialog.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @this DHActionBaseConfig */
|
static removeDamage(_event, button) {
|
||||||
static #onRemoveDamageResource(_event, button) {
|
if (!this.action.damage.parts) return;
|
||||||
if (!this.action.damage?.resources) return;
|
|
||||||
const data = this.action.toObject();
|
const data = this.action.toObject();
|
||||||
const key = button.dataset.key;
|
const key = button.dataset.key;
|
||||||
data.damage.resources[key] = _del;
|
delete data.damage.parts[key];
|
||||||
|
data.damage.parts[`${key}`] = _del;
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,10 +155,6 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
.querySelector('.stacking-change-checkbox')
|
.querySelector('.stacking-change-checkbox')
|
||||||
?.addEventListener('change', this.stackingChangeToggle.bind(this));
|
?.addEventListener('change', this.stackingChangeToggle.bind(this));
|
||||||
|
|
||||||
htmlElement
|
|
||||||
.querySelector('.range-dependence-change-checkbox')
|
|
||||||
?.addEventListener('change', this.rangeDependenceChangeToggle.bind(this));
|
|
||||||
|
|
||||||
htmlElement
|
htmlElement
|
||||||
.querySelector('.armor-change-checkbox')
|
.querySelector('.armor-change-checkbox')
|
||||||
?.addEventListener('change', this.armorChangeToggle.bind(this));
|
?.addEventListener('change', this.armorChangeToggle.bind(this));
|
||||||
|
|
@ -228,20 +224,6 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
return this.submit({ updateData: { system: systemData } });
|
return this.submit({ updateData: { system: systemData } });
|
||||||
}
|
}
|
||||||
|
|
||||||
rangeDependenceChangeToggle(event) {
|
|
||||||
const rangeFields = this.document.system.schema.fields.rangeDependence.fields;
|
|
||||||
const systemData = {
|
|
||||||
rangeDependence: event.target.checked
|
|
||||||
? _replace({
|
|
||||||
type: rangeFields.type.initial,
|
|
||||||
target: rangeFields.target.initial,
|
|
||||||
range: rangeFields.range.initial
|
|
||||||
})
|
|
||||||
: null
|
|
||||||
};
|
|
||||||
return this.submit({ updateData: { system: systemData } });
|
|
||||||
}
|
|
||||||
|
|
||||||
armorChangeToggle(event) {
|
armorChangeToggle(event) {
|
||||||
if (event.target.checked) {
|
if (event.target.checked) {
|
||||||
this.addArmorChange();
|
this.addArmorChange();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { DHDamageData } from '../../data/fields/action/damageField.mjs';
|
|
||||||
import DHBaseActorSettings from '../sheets/api/actor-setting.mjs';
|
import DHBaseActorSettings from '../sheets/api/actor-setting.mjs';
|
||||||
|
|
||||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||||
|
|
@ -9,10 +8,8 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
classes: ['adversary-settings'],
|
classes: ['adversary-settings'],
|
||||||
position: { width: 455, height: 'auto' },
|
position: { width: 455, height: 'auto' },
|
||||||
actions: {
|
actions: {
|
||||||
addExperience: this.#onAddExperience,
|
addExperience: DHAdversarySettings.#addExperience,
|
||||||
removeExperience: this.#onRemoveExperience,
|
removeExperience: DHAdversarySettings.#removeExperience
|
||||||
addDamage: this.#onAddDamage,
|
|
||||||
removeDamage: this.#onRemoveDamage
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -74,7 +71,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
* Adds a new experience entry to the actor.
|
* Adds a new experience entry to the actor.
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #onAddExperience() {
|
static async #addExperience() {
|
||||||
const newExperience = {
|
const newExperience = {
|
||||||
name: 'Experience',
|
name: 'Experience',
|
||||||
modifier: 0
|
modifier: 0
|
||||||
|
|
@ -86,7 +83,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
* Removes an experience entry from the actor.
|
* Removes an experience entry from the actor.
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #onRemoveExperience(_, target) {
|
static async #removeExperience(_, target) {
|
||||||
const experience = this.actor.system.experiences[target.dataset.experience];
|
const experience = this.actor.system.experiences[target.dataset.experience];
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
window: {
|
window: {
|
||||||
|
|
@ -101,28 +98,4 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
|
|
||||||
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
|
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @this DHAdversarySettings
|
|
||||||
* @type {ApplicationClickAction}
|
|
||||||
*/
|
|
||||||
static #onAddDamage() {
|
|
||||||
this.actor.update({
|
|
||||||
'system.attack.damage.main': {
|
|
||||||
...DHDamageData.schema.getInitialValue(),
|
|
||||||
applyTo: 'hitPoints',
|
|
||||||
type: 'physical'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @this DHAdversarySettings
|
|
||||||
* @type {ApplicationClickAction}
|
|
||||||
*/
|
|
||||||
static #onRemoveDamage() {
|
|
||||||
this.actor.update({
|
|
||||||
'system.attack.damage.main': null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
module/applications/sheets/actors/_types.d.ts
vendored
10
module/applications/sheets/actors/_types.d.ts
vendored
|
|
@ -1,18 +1,8 @@
|
||||||
import DhCompanion from '../../../data/actor/companion.mjs';
|
import DhCompanion from '../../../data/actor/companion.mjs';
|
||||||
import DhParty from '../../../data/actor/party.mjs';
|
|
||||||
import DhpActor from '../../../documents/actor.mjs';
|
import DhpActor from '../../../documents/actor.mjs';
|
||||||
|
|
||||||
declare module './companion.mjs' {
|
declare module './companion.mjs' {
|
||||||
export default interface DhCompanionSheet {
|
export default interface DhCompanionSheet {
|
||||||
actor: DhpActor<DhCompanion>;
|
actor: DhpActor<DhCompanion>;
|
||||||
document: DhpActor<DhCompanion>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module './party.mjs' {
|
|
||||||
export default interface PartySheet {
|
|
||||||
actor: DhpActor<DhParty>;
|
|
||||||
document: DhpActor<DhParty>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import DhDeathMove from '../../dialogs/deathMove.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';
|
||||||
import FilterMenu from '../../ux/filter-menu.mjs';
|
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||||
import { getArmorSources, getDocFromElement, getDocFromElementSync, itemAbleRollParse, sortBy } from '../../../helpers/utils.mjs';
|
import { getArmorSources, getDocFromElement, getDocFromElementSync, sortBy } from '../../../helpers/utils.mjs';
|
||||||
|
|
||||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||||
|
|
||||||
|
|
@ -29,7 +29,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
toggleResourceDice: CharacterSheet.#toggleResourceDice,
|
toggleResourceDice: CharacterSheet.#toggleResourceDice,
|
||||||
handleResourceDice: CharacterSheet.#handleResourceDice,
|
handleResourceDice: CharacterSheet.#handleResourceDice,
|
||||||
advanceResourceDie: CharacterSheet.#advanceResourceDie,
|
advanceResourceDie: CharacterSheet.#advanceResourceDie,
|
||||||
toggleItemReload: CharacterSheet.#onToggleItemReload,
|
|
||||||
cancelBeastform: CharacterSheet.#cancelBeastform,
|
cancelBeastform: CharacterSheet.#cancelBeastform,
|
||||||
toggleResourceManagement: CharacterSheet.#toggleResourceManagement,
|
toggleResourceManagement: CharacterSheet.#toggleResourceManagement,
|
||||||
useDowntime: this.useDowntime,
|
useDowntime: this.useDowntime,
|
||||||
|
|
@ -915,7 +914,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
const doc = await getDocFromElement(button);
|
const doc = await getDocFromElement(button);
|
||||||
const { available } = this.document.system.loadoutSlot;
|
const { available } = this.document.system.loadoutSlot;
|
||||||
if (doc.system.inVault && !available && !doc.system.loadoutIgnore) {
|
if (doc.system.inVault && !available && !doc.system.loadoutIgnore) {
|
||||||
return ui.notifications.warn('DAGGERHEART.UI.Notifications.loadoutMaxReached', { localize: true });
|
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached'));
|
||||||
}
|
}
|
||||||
|
|
||||||
await doc?.update({ 'system.inVault': !doc.system.inVault });
|
await doc?.update({ 'system.inVault': !doc.system.inVault });
|
||||||
|
|
@ -955,21 +954,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** */
|
||||||
static #advanceResourceDie(_, target) {
|
static #advanceResourceDie(_, target) {
|
||||||
this.updateResourceDie(target, true);
|
this.updateResourceDie(target, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onToggleItemReload(_, target) {
|
|
||||||
const item = await getDocFromElement(target);
|
|
||||||
if (!item || !item.system.resource?.max)
|
|
||||||
return;
|
|
||||||
|
|
||||||
await item.update({
|
|
||||||
'system.resource.value': item.system.needsReload ?
|
|
||||||
itemAbleRollParse(item.system.resource.max, this.document, item) : 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
lowerResourceDie(event) {
|
lowerResourceDie(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
@ -1217,22 +1206,10 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for same actor drag/drop attempts
|
if (this.document.uuid === item.parent?.uuid) {
|
||||||
const isSameActor = this.document.uuid === item.parent?.uuid;
|
|
||||||
const loadoutFieldset = event.target.closest('[data-in-vault]');
|
|
||||||
const vaulting = loadoutFieldset?.dataset.inVault === 'true';
|
|
||||||
if (isSameActor && loadoutFieldset && item.type === 'domainCard' && vaulting !== item.system.inVault) {
|
|
||||||
// This is likely an attempt to vault or unvault an item
|
|
||||||
const { available } = this.document.system.loadoutSlot;
|
|
||||||
if (!vaulting && !available && !item.system.loadoutIgnore) {
|
|
||||||
return ui.notifications.warn('DAGGERHEART.UI.Notifications.loadoutMaxReached', { localize: true });
|
|
||||||
}
|
|
||||||
return item.update({ 'system.inVault': vaulting });
|
|
||||||
} else if (isSameActor) {
|
|
||||||
return super._onDropItem(event, item);
|
return super._onDropItem(event, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle beastforms
|
|
||||||
if (item.type === 'beastform') {
|
if (item.type === 'beastform') {
|
||||||
if (this.document.effects.find(x => x.type === 'beastform')) {
|
if (this.document.effects.find(x => x.type === 'beastform')) {
|
||||||
return ui.notifications.warn(
|
return ui.notifications.warn(
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ import DaggerheartMenu from '../../sidebar/tabs/daggerheartMenu.mjs';
|
||||||
import { socketEvent } from '../../../systemRegistration/socket.mjs';
|
import { socketEvent } from '../../../systemRegistration/socket.mjs';
|
||||||
import DhpActor from '../../../documents/actor.mjs';
|
import DhpActor from '../../../documents/actor.mjs';
|
||||||
|
|
||||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
export default class Party extends DHBaseActorSheet {
|
||||||
|
|
||||||
export default class PartySheet extends DHBaseActorSheet {
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
|
@ -26,17 +24,17 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
resizable: true
|
resizable: true
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
openDocument: PartySheet.#onOpenDocument,
|
openDocument: Party.#openDocument,
|
||||||
deletePartyMember: PartySheet.#onDeletePartyMember,
|
deletePartyMember: Party.#deletePartyMember,
|
||||||
toggleHope: PartySheet.#onToggleHope,
|
toggleHope: Party.#toggleHope,
|
||||||
toggleHitPoints: PartySheet.#onToggleHitPoints,
|
toggleHitPoints: Party.#toggleHitPoints,
|
||||||
toggleStress: PartySheet.#onToggleStress,
|
toggleStress: Party.#toggleStress,
|
||||||
toggleArmorSlot: PartySheet.#onToggleArmorSlot,
|
toggleArmorSlot: Party.#toggleArmorSlot,
|
||||||
tempBrowser: PartySheet.#onTempBrowser,
|
tempBrowser: Party.#tempBrowser,
|
||||||
refreshActions: PartySheet.#onRefreshActions,
|
refeshActions: Party.#refeshActions,
|
||||||
triggerRest: PartySheet.#onTriggerRest,
|
triggerRest: Party.#triggerRest,
|
||||||
tagTeamRoll: PartySheet.#onTagTeamRoll,
|
tagTeamRoll: Party.#tagTeamRoll,
|
||||||
groupRoll: PartySheet.#onGroupRoll
|
groupRoll: Party.#groupRoll
|
||||||
},
|
},
|
||||||
dragDrop: [{ dragSelector: '[data-item-id]', dropSelector: null }]
|
dragDrop: [{ dragSelector: '[data-item-id]', dropSelector: null }]
|
||||||
};
|
};
|
||||||
|
|
@ -74,23 +72,6 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
this._createSearchFilter();
|
this._createSearchFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
_toggleDisabled(disabled) {
|
|
||||||
// Overriden to only disable text inputs by default if the user is a member
|
|
||||||
const isMember = this.actor.system.partyMembers.some(
|
|
||||||
m => m.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
|
|
||||||
);
|
|
||||||
if (!isMember) return super._toggleDisabled(disabled);
|
|
||||||
|
|
||||||
const form = this.form;
|
|
||||||
for (const input of form.querySelectorAll('input:not([type=search]), .editor.prosemirror')) {
|
|
||||||
input.disabled = disabled;
|
|
||||||
}
|
|
||||||
for (const element of form.querySelectorAll('.input[contenteditable]')) {
|
|
||||||
element.classList.toggle('disabled', disabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Prepare Context */
|
/* Prepare Context */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -217,16 +198,8 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/* Event handlers */
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
static async #openDocument(_, target) {
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
|
||||||
*/
|
|
||||||
static async #onOpenDocument(_, target) {
|
|
||||||
const uuid = target.dataset.uuid;
|
const uuid = target.dataset.uuid;
|
||||||
const document = await foundry.utils.fromUuid(uuid);
|
const document = await foundry.utils.fromUuid(uuid);
|
||||||
document?.sheet?.render(true);
|
document?.sheet?.render(true);
|
||||||
|
|
@ -234,10 +207,9 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles a hope resource value.
|
* Toggles a hope resource value.
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #onToggleHope(_, target) {
|
static async #toggleHope(_, target) {
|
||||||
const hopeValue = Number.parseInt(target.dataset.value);
|
const hopeValue = Number.parseInt(target.dataset.value);
|
||||||
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
||||||
const newValue = actor.system.resources.hope.value >= hopeValue ? hopeValue - 1 : hopeValue;
|
const newValue = actor.system.resources.hope.value >= hopeValue ? hopeValue - 1 : hopeValue;
|
||||||
|
|
@ -247,10 +219,9 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles a hp resource value.
|
* Toggles a hp resource value.
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #onToggleHitPoints(_, target) {
|
static async #toggleHitPoints(_, target) {
|
||||||
const hitPointsValue = Number.parseInt(target.dataset.value);
|
const hitPointsValue = Number.parseInt(target.dataset.value);
|
||||||
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
||||||
const newValue = actor.system.resources.hitPoints.value >= hitPointsValue ? hitPointsValue - 1 : hitPointsValue;
|
const newValue = actor.system.resources.hitPoints.value >= hitPointsValue ? hitPointsValue - 1 : hitPointsValue;
|
||||||
|
|
@ -260,10 +231,9 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles a stress resource value.
|
* Toggles a stress resource value.
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #onToggleStress(_, target) {
|
static async #toggleStress(_, target) {
|
||||||
const stressValue = Number.parseInt(target.dataset.value);
|
const stressValue = Number.parseInt(target.dataset.value);
|
||||||
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
||||||
const newValue = actor.system.resources.stress.value >= stressValue ? stressValue - 1 : stressValue;
|
const newValue = actor.system.resources.stress.value >= stressValue ? stressValue - 1 : stressValue;
|
||||||
|
|
@ -273,10 +243,9 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles a armor slot resource value.
|
* Toggles a armor slot resource value.
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #onToggleArmorSlot(_, target) {
|
static async #toggleArmorSlot(_, target) {
|
||||||
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
||||||
const { value, max } = actor.system.armorScore;
|
const { value, max } = actor.system.armorScore;
|
||||||
const inputValue = Number.parseInt(target.dataset.value);
|
const inputValue = Number.parseInt(target.dataset.value);
|
||||||
|
|
@ -289,19 +258,12 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens Compedium Browser
|
* Opens Compedium Browser
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
|
||||||
*/
|
*/
|
||||||
static async #onTempBrowser(_, target) {
|
static async #tempBrowser(_, target) {
|
||||||
new ItemBrowser().render({ force: true });
|
new ItemBrowser().render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static async #refeshActions() {
|
||||||
* @todo Is this unused?
|
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
|
||||||
*/
|
|
||||||
static async #onRefreshActions() {
|
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
window: {
|
window: {
|
||||||
title: 'New Section',
|
title: 'New Section',
|
||||||
|
|
@ -319,11 +281,7 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static async #triggerRest(_, button) {
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
|
||||||
*/
|
|
||||||
static async #onTriggerRest(_, button) {
|
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
window: {
|
window: {
|
||||||
title: game.i18n.localize(`DAGGERHEART.APPLICATIONS.Downtime.${button.dataset.type}.title`),
|
title: game.i18n.localize(`DAGGERHEART.APPLICATIONS.Downtime.${button.dataset.type}.title`),
|
||||||
|
|
@ -346,30 +304,6 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
|
||||||
*/
|
|
||||||
static async #onDeletePartyMember(event, target) {
|
|
||||||
const doc = await foundry.utils.fromUuid(target.closest('[data-uuid]')?.dataset.uuid);
|
|
||||||
if (!event.shiftKey) {
|
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
|
||||||
window: {
|
|
||||||
title: game.i18n.format('DAGGERHEART.ACTORS.Party.RemoveConfirmation.title', {
|
|
||||||
name: doc.name
|
|
||||||
})
|
|
||||||
},
|
|
||||||
content: game.i18n.format('DAGGERHEART.ACTORS.Party.RemoveConfirmation.text', { name: doc.name })
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!confirmed) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
|
|
||||||
const newMembersList = currentMembers.filter(uuid => uuid !== doc.uuid);
|
|
||||||
await this.document.update({ 'system.partyMembers': newMembersList });
|
|
||||||
}
|
|
||||||
|
|
||||||
static async downtimeMoveQuery({ actorId, downtimeType }) {
|
static async downtimeMoveQuery({ actorId, downtimeType }) {
|
||||||
const actor = await foundry.utils.fromUuid(actorId);
|
const actor = await foundry.utils.fromUuid(actorId);
|
||||||
if (!actor || !actor?.isOwner) return;
|
if (!actor || !actor?.isOwner) return;
|
||||||
|
|
@ -378,19 +312,11 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static async #tagTeamRoll() {
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
|
||||||
*/
|
|
||||||
static async #onTagTeamRoll() {
|
|
||||||
new game.system.api.applications.dialogs.TagTeamDialog(this.document).render({ force: true });
|
new game.system.api.applications.dialogs.TagTeamDialog(this.document).render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static async #groupRoll(_params) {
|
||||||
* @this PartySheet
|
|
||||||
* @type {ApplicationClickAction}
|
|
||||||
*/
|
|
||||||
static async #onGroupRoll(_params) {
|
|
||||||
new game.system.api.applications.dialogs.GroupRollDialog(this.document).render({ force: true });
|
new game.system.api.applications.dialogs.GroupRollDialog(this.document).render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -534,10 +460,11 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
async _onDropActor(event, document) {
|
async _onDropActor(event, document) {
|
||||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||||
if (document instanceof DhpActor && PartySheet.ALLOWED_ACTOR_TYPES.includes(document.type)) {
|
if (document instanceof DhpActor && Party.ALLOWED_ACTOR_TYPES.includes(document.type)) {
|
||||||
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
|
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
|
||||||
if (currentMembers.includes(data.uuid)) {
|
if (currentMembers.includes(data.uuid)) {
|
||||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.duplicateCharacter'));
|
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.duplicateCharacter'));
|
||||||
|
|
@ -550,4 +477,24 @@ export default class PartySheet extends DHBaseActorSheet {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #deletePartyMember(event, target) {
|
||||||
|
const doc = await foundry.utils.fromUuid(target.closest('[data-uuid]')?.dataset.uuid);
|
||||||
|
if (!event.shiftKey) {
|
||||||
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
window: {
|
||||||
|
title: game.i18n.format('DAGGERHEART.ACTORS.Party.RemoveConfirmation.title', {
|
||||||
|
name: doc.name
|
||||||
|
})
|
||||||
|
},
|
||||||
|
content: game.i18n.format('DAGGERHEART.ACTORS.Party.RemoveConfirmation.text', { name: doc.name })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
|
||||||
|
const newMembersList = currentMembers.filter(uuid => uuid !== doc.uuid);
|
||||||
|
await this.document.update({ 'system.partyMembers': newMembersList });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
addFeature: DHBaseItemSheet.#addFeature,
|
addFeature: DHBaseItemSheet.#addFeature,
|
||||||
deleteFeature: DHBaseItemSheet.#deleteFeature,
|
deleteFeature: DHBaseItemSheet.#deleteFeature,
|
||||||
addResource: DHBaseItemSheet.#addResource,
|
addResource: DHBaseItemSheet.#addResource,
|
||||||
removeResource: DHBaseItemSheet.#removeResource,
|
removeResource: DHBaseItemSheet.#removeResource
|
||||||
editGMNote: DHBaseItemSheet.#onEditGMNote
|
|
||||||
},
|
},
|
||||||
dragDrop: [
|
dragDrop: [
|
||||||
{ dragSelector: null, dropSelector: '.drop-section' },
|
{ dragSelector: null, dropSelector: '.drop-section' },
|
||||||
|
|
@ -77,16 +76,10 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _preparePartContext(partId, context, options) {
|
async _preparePartContext(partId, context, options) {
|
||||||
await super._preparePartContext(partId, context, options);
|
await super._preparePartContext(partId, context, options);
|
||||||
const TextEditor = foundry.applications.ux.TextEditor.implementation;
|
|
||||||
|
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
case 'description':
|
case 'description':
|
||||||
context.enrichedDescription = await this.document.system.getEnrichedDescription({ gmNotes: false });
|
context.enrichedDescription = await this.document.system.getEnrichedDescription();
|
||||||
context.enrichedGMNotes = await TextEditor.implementation.enrichHTML(this.item.system.gmNotes, {
|
|
||||||
relativeTo: this.item,
|
|
||||||
rollData: this.item.getRollData(),
|
|
||||||
secrets: this.item.isOwner
|
|
||||||
})
|
|
||||||
break;
|
break;
|
||||||
case 'effects':
|
case 'effects':
|
||||||
await this._prepareEffectsContext(context, options);
|
await this._prepareEffectsContext(context, options);
|
||||||
|
|
@ -338,45 +331,4 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the Add GM Note button being pressed. This is only used when an item has no GM notes.
|
|
||||||
* Later edits to a GM note instead go through the normal editor toggle workflow.
|
|
||||||
* @this DHBaseItemSheet
|
|
||||||
*/
|
|
||||||
static #onEditGMNote() {
|
|
||||||
// Open the editor, which might be hidden. We remove the css class to hide temporarily
|
|
||||||
// so that menu auto resizing functions properly.
|
|
||||||
const editor = this.element.querySelector('prose-mirror[name="system.gmNotes"]');
|
|
||||||
const wasHidden = editor.classList.contains('hide-if-inactive');
|
|
||||||
editor.classList.remove('hide-if-inactive');
|
|
||||||
editor.open = true;
|
|
||||||
window.setTimeout(() => {
|
|
||||||
if (wasHidden) editor.classList.add('hide-if-inactive');
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
async _onRender(context, options) {
|
|
||||||
await super._onRender(context, options);
|
|
||||||
|
|
||||||
// Render an add gmnotes button if there are no set GM notes.
|
|
||||||
// We need to re-render on close since its possible to prosemirror to close *without* triggering a full re-render
|
|
||||||
if (game.user.isGM && !this.item.system.gmNotes) {
|
|
||||||
const description = this.element.querySelector('[name="system.description"]');
|
|
||||||
const addButton = () => {
|
|
||||||
if (description.querySelector('[data-action=editGMNote]')) return;
|
|
||||||
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.type = 'button';
|
|
||||||
button.classList.add('icon', 'toggle', 'fa-regular', 'fa-note-medical');
|
|
||||||
button.dataset.action = 'editGMNote';
|
|
||||||
button.dataset.tooltip = 'DAGGERHEART.ITEMS.Base.addGMNote';
|
|
||||||
description.appendChild(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
addButton();
|
|
||||||
description.addEventListener('close', () => addButton());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { enrichedDualityRoll } from '../../enrichers/DualityRollEnricher.mjs';
|
import { enrichedDualityRoll } from '../../enrichers/DualityRollEnricher.mjs';
|
||||||
import { enrichedFateRoll, getFateTypeData } from '../../enrichers/FateRollEnricher.mjs';
|
import { enrichedFateRoll, getFateTypeData } from '../../enrichers/FateRollEnricher.mjs';
|
||||||
import { getCommandTarget, rollCommandToJSON } from '../../helpers/utils.mjs';
|
import { getCommandTarget, rollCommandToJSON } from '../../helpers/utils.mjs';
|
||||||
import FearTracker from './fearTracker.mjs';
|
|
||||||
|
|
||||||
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
|
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
|
|
@ -111,7 +110,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
const message = game.messages.get(li.dataset.messageId);
|
const message = game.messages.get(li.dataset.messageId);
|
||||||
return message.system.hasRoll && (game.user.isGM || message.isAuthor);
|
return message.system.hasRoll && (game.user.isGM || message.isAuthor);
|
||||||
},
|
},
|
||||||
onClick: async (_event, li) => {
|
callback: async li => {
|
||||||
const message = game.messages.get(li.dataset.messageId);
|
const message = game.messages.get(li.dataset.messageId);
|
||||||
const reroll = await message.rolls[0].reroll({ liveRoll: true });
|
const reroll = await message.rolls[0].reroll({ liveRoll: true });
|
||||||
message.update({ rolls: [reroll] });
|
message.update({ rolls: [reroll] });
|
||||||
|
|
@ -127,7 +126,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
: false;
|
: false;
|
||||||
return (game.user.isGM || message.isAuthor) && hasRolledDamage;
|
return (game.user.isGM || message.isAuthor) && hasRolledDamage;
|
||||||
},
|
},
|
||||||
onClick: async (_event, li) => {
|
callback: async li => {
|
||||||
const message = game.messages.get(li.dataset.messageId);
|
const message = game.messages.get(li.dataset.messageId);
|
||||||
const update = await message.system.getRerolledDamage();
|
const update = await message.system.getRerolledDamage();
|
||||||
message.update(update);
|
message.update(update);
|
||||||
|
|
@ -153,9 +152,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
html.querySelectorAll('.risk-it-all-button').forEach(element =>
|
html.querySelectorAll('.risk-it-all-button').forEach(element =>
|
||||||
element.addEventListener('click', event => this.riskItAllClearStressAndHitPoints(event, data))
|
element.addEventListener('click', event => this.riskItAllClearStressAndHitPoints(event, data))
|
||||||
);
|
);
|
||||||
for (const element of html.querySelectorAll('.roll-reload-check')) {
|
|
||||||
element.addEventListener('click', event => this.onRollReloadCheck(event, message));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setupHooks() {
|
setupHooks() {
|
||||||
|
|
@ -183,15 +179,28 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
}
|
}
|
||||||
|
|
||||||
async onRollSimple(event, message) {
|
async onRollSimple(event, message) {
|
||||||
const buttonType = event.target.dataset.type ?? 'damage';
|
const buttonType = event.target.dataset.type ?? 'damage',
|
||||||
const total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0);
|
total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0),
|
||||||
const targets = Array.from(game.user.targets);
|
damages = {
|
||||||
|
hitPoints: {
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
applyTo: 'hitPoints',
|
||||||
|
damageTypes: [],
|
||||||
|
total
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
targets = Array.from(game.user.targets);
|
||||||
|
|
||||||
if (targets.length === 0)
|
if (targets.length === 0)
|
||||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
|
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
|
||||||
for (const target of targets) {
|
|
||||||
if (buttonType === 'healing') target.actor.takeHealing({ hitPoints: total });
|
targets.forEach(target => {
|
||||||
else target.actor.takeDamage({ total });
|
if (buttonType === 'healing') target.actor.takeHealing(damages);
|
||||||
}
|
else target.actor.takeDamage(damages);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async abilityUseButton(event, message) {
|
async abilityUseButton(event, message) {
|
||||||
|
|
@ -247,17 +256,26 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = game.messages.get(messageData._id);
|
const message = game.messages.get(messageData._id);
|
||||||
const target = event.target.closest('[data-result]');
|
const target = event.target.closest('[data-die-index]');
|
||||||
|
|
||||||
if (target.dataset.type === 'damage') {
|
if (target.dataset.type === 'damage') {
|
||||||
const { isResource, damageType, dice, result } = target.dataset;
|
const { damageType, part, dice, result } = target.dataset;
|
||||||
await message.system.damage.rerollDamageDie(isResource, damageType, dice, result);
|
const damagePart = message.system.damage[damageType].parts[part];
|
||||||
|
const { parsedRoll, rerolledDice } = await game.system.api.dice.DamageRoll.reroll(damagePart, dice, result);
|
||||||
const updatePath = isResource ? `system.damage.resources.${damageType}` : 'system.damage.main';
|
const damageParts = message.system.damage[damageType].parts.map((damagePart, index) => {
|
||||||
const updateValue = isResource ?
|
if (index !== Number(part)) return damagePart;
|
||||||
message.system.damage.resources[damageType] : message.system.damage.main;
|
return {
|
||||||
await message.update({
|
...damagePart,
|
||||||
[updatePath]: updateValue.toJSON()
|
total: parsedRoll.total,
|
||||||
|
dice: rerolledDice
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const updateMessage = game.messages.get(message._id);
|
||||||
|
await updateMessage.update({
|
||||||
|
[`system.damage.${damageType}`]: {
|
||||||
|
total: parsedRoll.total,
|
||||||
|
parts: damageParts
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const rerollDice = message.system.roll.dice[target.dataset.dieIndex];
|
const rerollDice = message.system.roll.dice[target.dataset.dieIndex];
|
||||||
|
|
@ -279,27 +297,4 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
const actor = game.actors.get(event.target.dataset.actorId);
|
const actor = game.actors.get(event.target.dataset.actorId);
|
||||||
new game.system.api.applications.dialogs.RiskItAllDialog(actor, resourceValue).render({ force: true });
|
new game.system.api.applications.dialogs.RiskItAllDialog(actor, resourceValue).render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
_toggleNotifications({ closing = false } = {}) {
|
|
||||||
super._toggleNotifications(closing)
|
|
||||||
FearTracker.handleOffSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
async onRollReloadCheck(_event, messageData) {
|
|
||||||
const message = game.messages.get(messageData._id);
|
|
||||||
|
|
||||||
if (message.system.reloadCheckValue) {
|
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
|
||||||
window: {
|
|
||||||
title: _loc('DAGGERHEART.ACTIONS.Reload.rerollConfirmationTitle')
|
|
||||||
},
|
|
||||||
content: _loc('DAGGERHEART.ACTIONS.Reload.rerollConfirmationText')
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!confirmed) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { rollValue } = await message.system.action.handleReload?.({ awaitRoll: true });
|
|
||||||
await message.update({ 'system.reloadCheckValue': rollValue });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
|
||||||
action: socketEvent.Refresh,
|
action: socketEvent.Refresh,
|
||||||
data: { refreshType: RefreshType.Countdown }
|
data: { refreshType: RefreshType.Countdown }
|
||||||
});
|
});
|
||||||
|
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown });
|
||||||
}
|
}
|
||||||
|
|
||||||
static #addCountdown() {
|
static #addCountdown() {
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,14 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
export default class DhCountdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class DhCountdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
previousCountdownData = null;
|
previousCountdownData = null;
|
||||||
|
changedCountdownsForAnimation = new Set();
|
||||||
|
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
this.previousCountdownData =
|
this.previousCountdownData =
|
||||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).countdowns;
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).countdowns;
|
||||||
|
this.setupHooks();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
@ -77,15 +80,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
async _onFirstRender(context, options) {
|
|
||||||
await super._onFirstRender(context, options);
|
|
||||||
this._createContextMenu(this._getCountdownContextOptions, '.countdown-container[data-countdown]', {
|
|
||||||
parentClassHooks: false, fixed: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
async _onRender(context, options) {
|
async _onRender(context, options) {
|
||||||
await super._onRender(context, options);
|
await super._onRender(context, options);
|
||||||
|
|
||||||
|
|
@ -100,7 +95,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
|
|
||||||
/* Handle animations to draw attention to countdown values changing */
|
/* Handle animations to draw attention to countdown values changing */
|
||||||
const typesToAnimate = new Set();
|
const typesToAnimate = new Set();
|
||||||
for (const countdownKey of options.animate ?? []) {
|
for (const countdownKey of this.changedCountdownsForAnimation) {
|
||||||
const shimmerAnimation = [
|
const shimmerAnimation = [
|
||||||
{ backgroundPositionX: '98%' },
|
{ backgroundPositionX: '98%' },
|
||||||
{ backgroundPositionX: '0%' }
|
{ backgroundPositionX: '0%' }
|
||||||
|
|
@ -132,6 +127,8 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
const element = this.element.querySelector(`.header-type-toggles .header-type[data-type="${type}"]`);
|
const element = this.element.querySelector(`.header-type-toggles .header-type[data-type="${type}"]`);
|
||||||
element?.animate(pulseAnimation, pulseTiming);
|
element?.animate(pulseAnimation, pulseTiming);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.changedCountdownsForAnimation.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns countdown data filtered by ownership */
|
/** Returns countdown data filtered by ownership */
|
||||||
|
|
@ -140,15 +137,17 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
const values = Object.entries(setting.countdowns).map(([key, countdown]) => ({
|
const values = Object.entries(setting.countdowns).map(([key, countdown]) => ({
|
||||||
key,
|
key,
|
||||||
countdown,
|
countdown,
|
||||||
ownership: countdown.getUserLevel(game.user)
|
ownership: DhCountdowns.#getPlayerOwnership(game.user, setting, countdown)
|
||||||
}));
|
}));
|
||||||
return values.filter(v => v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE);
|
return values.filter(v => v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getCountdownData() {
|
_getCountdownData() {
|
||||||
|
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
||||||
|
|
||||||
return this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => {
|
return this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => {
|
||||||
const playersWithAccess = game.users.reduce((acc, user) => {
|
const playersWithAccess = game.users.reduce((acc, user) => {
|
||||||
const ownership = countdown.getUserLevel(user);
|
const ownership = DhCountdowns.#getPlayerOwnership(user, setting, countdown);
|
||||||
if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
||||||
acc.push(user);
|
acc.push(user);
|
||||||
}
|
}
|
||||||
|
|
@ -214,6 +213,19 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static #getPlayerOwnership(user, setting, countdown) {
|
||||||
|
if (user.isGM) return CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
||||||
|
|
||||||
|
const playerOwnership = countdown.ownership[user.id];
|
||||||
|
return playerOwnership === undefined || playerOwnership === CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
|
||||||
|
? setting.defaultOwnership
|
||||||
|
: playerOwnership;
|
||||||
|
}
|
||||||
|
|
||||||
|
cooldownRefresh = ({ refreshType }) => {
|
||||||
|
if (refreshType === RefreshType.Countdown) this.render();
|
||||||
|
};
|
||||||
|
|
||||||
static canPerformEdit() {
|
static canPerformEdit() {
|
||||||
if (game.user.isGM) return true;
|
if (game.user.isGM) return true;
|
||||||
|
|
||||||
|
|
@ -311,12 +323,18 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
action: socketEvent.Refresh,
|
action: socketEvent.Refresh,
|
||||||
data: { refreshType: RefreshType.Countdown }
|
data: { refreshType: RefreshType.Countdown }
|
||||||
});
|
});
|
||||||
|
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown });
|
||||||
|
}
|
||||||
|
|
||||||
|
setupHooks() {
|
||||||
|
Hooks.on(socketEvent.Refresh, this.cooldownRefresh.bind());
|
||||||
}
|
}
|
||||||
|
|
||||||
async close(options) {
|
async close(options) {
|
||||||
/* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */
|
/* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */
|
||||||
if (options.closeKey) return;
|
if (options.closeKey) return;
|
||||||
|
|
||||||
|
Hooks.off(socketEvent.Refresh, this.cooldownRefresh);
|
||||||
return super.close(options);
|
return super.close(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -359,30 +377,4 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
refreshType: RefreshType.Countdown
|
refreshType: RefreshType.Countdown
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]}
|
|
||||||
*/
|
|
||||||
_getCountdownContextOptions() {
|
|
||||||
/** @param {HTMLElement} element */
|
|
||||||
const getCountdownFromElement = element => {
|
|
||||||
const id = element.closest('[data-countdown]').dataset.countdown;
|
|
||||||
if (!id) return null;
|
|
||||||
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
|
||||||
return setting.countdowns[id ?? ''];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: 'CONTROLS.CommonDelete',
|
|
||||||
icon: 'fa-solid fa-trash',
|
|
||||||
visible: element => {
|
|
||||||
return getCountdownFromElement(element)?.isOwner;
|
|
||||||
},
|
|
||||||
onClick: (_, target) => {
|
|
||||||
getCountdownFromElement(target)?.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,6 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
this._dragData = {
|
|
||||||
isDragging: false,
|
|
||||||
startX: 0,
|
|
||||||
startY: 0,
|
|
||||||
startLeft: 0,
|
|
||||||
startTop: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
@ -29,20 +21,19 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV
|
||||||
classes: [],
|
classes: [],
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
window: {
|
window: {
|
||||||
frame: false,
|
frame: true,
|
||||||
title: 'DAGGERHEART.GENERAL.fear',
|
title: 'DAGGERHEART.GENERAL.fear',
|
||||||
positioned: true,
|
positioned: true,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
minimizable: false
|
minimizable: false
|
||||||
},
|
},
|
||||||
classes: ['daggerheart', 'dh-style', 'fear-tracker'],
|
|
||||||
actions: {
|
actions: {
|
||||||
setFear: FearTracker.setFear,
|
setFear: FearTracker.setFear,
|
||||||
increaseFear: FearTracker.increaseFear
|
increaseFear: FearTracker.increaseFear
|
||||||
},
|
},
|
||||||
position: {
|
position: {
|
||||||
width: 540,
|
width: 222,
|
||||||
height: 'auto'
|
height: 222
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -62,10 +53,6 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV
|
||||||
return game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear;
|
return game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fearPosition() {
|
|
||||||
return game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).fearPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Rendering */
|
/* Rendering */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -76,48 +63,15 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV
|
||||||
current = this.currentFear,
|
current = this.currentFear,
|
||||||
max = this.maxFear,
|
max = this.maxFear,
|
||||||
percent = (current / max) * 100,
|
percent = (current / max) * 100,
|
||||||
isGM = game.user.isGM,
|
isGM = game.user.isGM;
|
||||||
locked = false,
|
|
||||||
isFree = this.fearPosition == 'free';
|
|
||||||
|
|
||||||
return { display, current, max, percent, isGM, locked, isFree };
|
return { display, current, max, percent, isGM };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onRender(context, options) {
|
async _preFirstRender(context, options) {
|
||||||
await super._onRender(context, options);
|
options.position =
|
||||||
|
game.user.getFlag(CONFIG.DH.id, 'app.resources.position') ?? FearTracker.DEFAULT_OPTIONS.position;
|
||||||
this.#setupDragging();
|
|
||||||
this.#setupResizing();
|
|
||||||
|
|
||||||
const fearPosition = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).fearPosition;
|
|
||||||
|
|
||||||
if (options.isFirstRender) FearTracker.handleOffSet();
|
|
||||||
if (!options.force) return;
|
|
||||||
|
|
||||||
this.handleStyleElement(fearPosition);
|
|
||||||
|
|
||||||
switch (fearPosition) {
|
|
||||||
case 'topCenter':
|
|
||||||
document.getElementById('ui-top')?.appendChild(this.element);
|
|
||||||
break;
|
|
||||||
case 'bottomCenter':
|
|
||||||
document.getElementById('ui-bottom')?.prepend(this.element);
|
|
||||||
break;
|
|
||||||
case 'rightTop':
|
|
||||||
document.getElementById('ui-right-column-1')?.appendChild(this.element);
|
|
||||||
break;
|
|
||||||
case 'leftBottom':
|
|
||||||
document.getElementById('ui-left-column-1')?.insertBefore(this.element, document.getElementById('players'));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
document.body?.appendChild(this.element);
|
|
||||||
const position =
|
|
||||||
game.user.getFlag(CONFIG.DH.id, 'app.resources.position') ?? FearTracker.DEFAULT_OPTIONS.position;
|
|
||||||
this.setPosition(position);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
|
|
@ -126,31 +80,15 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV
|
||||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, this.maxFear);
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, this.maxFear);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStyleElement(fearPosition) {
|
|
||||||
for (const position of Object.values(CONFIG.DH.GENERAL.fearPosition)) {
|
|
||||||
this.element.classList.remove(position.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.classList.add(fearPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
static handleOffSet() {
|
|
||||||
const fearTracker = document.getElementById('resources');
|
|
||||||
const hotbar = document.getElementById('hotbar');
|
|
||||||
|
|
||||||
if (!fearTracker) return;
|
|
||||||
|
|
||||||
const offset = Number(hotbar.style.getPropertyValue('--offset').replace(/px$/, '')) || 0;
|
|
||||||
|
|
||||||
if (offset > 0) return;
|
|
||||||
|
|
||||||
fearTracker.style.setProperty('--offset', `${offset - 13}px`);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onPosition(position) {
|
_onPosition(position) {
|
||||||
game.user.setFlag(CONFIG.DH.id, 'app.resources.position', position);
|
game.user.setFlag(CONFIG.DH.id, 'app.resources.position', position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async close(options = {}) {
|
||||||
|
if (!options.allowed) return;
|
||||||
|
else super.close(options);
|
||||||
|
}
|
||||||
|
|
||||||
static async setFear(event, target) {
|
static async setFear(event, target) {
|
||||||
if (!game.user.isGM) return;
|
if (!game.user.isGM) return;
|
||||||
const fearCount = Number(target.dataset.index ?? 0);
|
const fearCount = Number(target.dataset.index ?? 0);
|
||||||
|
|
@ -172,119 +110,4 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove methods later to use Foundry's dragger and resize methods
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/* Dragging handlers */
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
#setupDragging() {
|
|
||||||
const dragHandle = this.element.querySelector('.drag-handle');
|
|
||||||
if (!dragHandle) return;
|
|
||||||
dragHandle.addEventListener('mousedown', this.#onDragStart.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
#onDragStart(event) {
|
|
||||||
if (event.button !== 0) return;
|
|
||||||
this._dragData.isDragging = true;
|
|
||||||
this._dragData.startX = event.clientX;
|
|
||||||
this._dragData.startY = event.clientY;
|
|
||||||
const rect = this.element.getBoundingClientRect();
|
|
||||||
this._dragData.startLeft = rect.left;
|
|
||||||
this._dragData.startTop = rect.top;
|
|
||||||
this.element.style.cursor = 'grabbing';
|
|
||||||
|
|
||||||
this._dragHandler = this.#onDragging.bind(this);
|
|
||||||
this._dragEndHandler = this.#onDragEnd.bind(this);
|
|
||||||
window.addEventListener('mousemove', this._dragHandler);
|
|
||||||
window.addEventListener('mouseup', this._dragEndHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
#onDragging(event) {
|
|
||||||
if (!this._dragData.isDragging) return;
|
|
||||||
|
|
||||||
const dragX = event.clientX - this._dragData.startX;
|
|
||||||
const dragY = event.clientY - this._dragData.startY;
|
|
||||||
|
|
||||||
this.element.style.left = `${this._dragData.startLeft + dragX}px`;
|
|
||||||
this.element.style.top = `${this._dragData.startTop + dragY}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
#onDragEnd() {
|
|
||||||
if (!this._dragData.isDragging) return;
|
|
||||||
this._dragData.isDragging = false;
|
|
||||||
this.element.style.cursor = '';
|
|
||||||
|
|
||||||
if (this._dragHandler) window.removeEventListener('mousemove', this._dragHandler);
|
|
||||||
if (this._dragEndHandler) window.removeEventListener('mouseup', this._dragEndHandler);
|
|
||||||
|
|
||||||
const rect = this.element.getBoundingClientRect();
|
|
||||||
const pos = { top: rect.top, left: rect.left };
|
|
||||||
|
|
||||||
this.setPosition(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/* Resize handlers */
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
#setupResizing() {
|
|
||||||
const resizeHandle = this.element.querySelector('.resize-handle');
|
|
||||||
if (!resizeHandle) return;
|
|
||||||
resizeHandle.addEventListener('mousedown', this.#onResizeStart.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
#onResizeStart(e) {
|
|
||||||
if (e.button !== 0) return;
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
let maxAllowedWidth = 10000;
|
|
||||||
|
|
||||||
this._resizeData = {
|
|
||||||
isResizing: true,
|
|
||||||
startX: e.clientX,
|
|
||||||
startY: e.clientY,
|
|
||||||
startWidth: this.element.offsetWidth,
|
|
||||||
startHeight: this.element.offsetHeight,
|
|
||||||
maxAllowedWidth: Math.max(50, maxAllowedWidth)
|
|
||||||
};
|
|
||||||
|
|
||||||
this._resizeHandler = this.#onResizing.bind(this);
|
|
||||||
this._resizeEndHandler = this.#onResizeEnd.bind(this);
|
|
||||||
window.addEventListener('mousemove', this._resizeHandler);
|
|
||||||
window.addEventListener('mouseup', this._resizeEndHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
#onResizing(e) {
|
|
||||||
if (!this._resizeData?.isResizing) return;
|
|
||||||
|
|
||||||
const currentDx = e.clientX - this._resizeData.startX;
|
|
||||||
const potentialWidth = Math.max(50, this._resizeData.startWidth + currentDx);
|
|
||||||
|
|
||||||
const width = Math.min(potentialWidth, this._resizeData.maxAllowedWidth);
|
|
||||||
|
|
||||||
this.element.style.width = `${width}px`;
|
|
||||||
|
|
||||||
if (width < 100) {
|
|
||||||
this.element.classList.add('narrow');
|
|
||||||
} else {
|
|
||||||
this.element.classList.remove('narrow');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#onResizeEnd() {
|
|
||||||
if (!this._resizeData?.isResizing) return;
|
|
||||||
this._resizeData.isResizing = false;
|
|
||||||
|
|
||||||
if (this._resizeHandler) window.removeEventListener('mousemove', this._resizeHandler);
|
|
||||||
if (this._resizeEndHandler) window.removeEventListener('mouseup', this._resizeEndHandler);
|
|
||||||
|
|
||||||
let width = parseFloat(this.element.style.width);
|
|
||||||
|
|
||||||
|
|
||||||
if (isNaN(width)) {
|
|
||||||
width = this.element.getBoundingClientRect().width;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setPosition({ width: width });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,20 +90,18 @@ export const BPModifiers = {
|
||||||
name: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.name',
|
name: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.name',
|
||||||
description: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.description',
|
description: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.description',
|
||||||
img: 'icons/magic/control/buff-flight-wings-red.webp',
|
img: 'icons/magic/control/buff-flight-wings-red.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.bonuses.damage.physical.dice',
|
||||||
key: 'system.bonuses.damage.physical.dice',
|
mode: 2,
|
||||||
type: 'add',
|
value: '1d4'
|
||||||
value: '1d4'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.bonuses.damage.magical.dice',
|
||||||
key: 'system.bonuses.damage.magical.dice',
|
mode: 2,
|
||||||
type: 'add',
|
value: '1d4'
|
||||||
value: '1d4'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -825,7 +825,7 @@ export const refreshTypes = {
|
||||||
export const itemAbilityCosts = {
|
export const itemAbilityCosts = {
|
||||||
resource: {
|
resource: {
|
||||||
id: 'resource',
|
id: 'resource',
|
||||||
label: 'DAGGERHEART.GENERAL.Resource.single',
|
label: 'DAGGERHEART.GENERAL.resource',
|
||||||
group: 'Global'
|
group: 'Global'
|
||||||
},
|
},
|
||||||
quantity: {
|
quantity: {
|
||||||
|
|
@ -924,14 +924,6 @@ export const fearDisplay = {
|
||||||
hide: { value: 'hide', label: 'DAGGERHEART.SETTINGS.Appearance.fearDisplay.hide' }
|
hide: { value: 'hide', label: 'DAGGERHEART.SETTINGS.Appearance.fearDisplay.hide' }
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fearPosition = {
|
|
||||||
free: { value: 'free', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.free' },
|
|
||||||
topCenter: { value: 'topCenter', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.topCenter' },
|
|
||||||
bottomCenter: { value: 'bottomCenter', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.bottomCenter' },
|
|
||||||
rightTop: { value: 'rightTop', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.rightTop' },
|
|
||||||
leftBottom: { value: 'leftBottom', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.leftBottom' }
|
|
||||||
};
|
|
||||||
|
|
||||||
export const basicOwnershiplevels = {
|
export const basicOwnershiplevels = {
|
||||||
0: { value: 0, label: 'OWNERSHIP.NONE' },
|
0: { value: 0, label: 'OWNERSHIP.NONE' },
|
||||||
2: { value: 2, label: 'OWNERSHIP.OBSERVER' },
|
2: { value: 2, label: 'OWNERSHIP.OBSERVER' },
|
||||||
|
|
|
||||||
|
|
@ -37,15 +37,13 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.channeling.effects.channeling.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.channeling.effects.channeling.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.channeling.effects.channeling.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.channeling.effects.channeling.description',
|
||||||
img: 'icons/magic/symbols/rune-sigil-horned-blue.webp',
|
img: 'icons/magic/symbols/rune-sigil-horned-blue.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.bonuses.roll.spellcast',
|
||||||
key: 'system.bonuses.roll.spellcast',
|
mode: 2,
|
||||||
type: 'add',
|
value: '1'
|
||||||
value: '1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -57,45 +55,43 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.difficult.effects.difficult.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.difficult.effects.difficult.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.difficult.effects.difficult.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.difficult.effects.difficult.description',
|
||||||
img: 'icons/magic/control/buff-flight-wings-red.webp',
|
img: 'icons/magic/control/buff-flight-wings-red.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.traits.agility.value',
|
||||||
key: 'system.traits.agility.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.traits.strength.value',
|
||||||
key: 'system.traits.strength.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.traits.finesse.value',
|
||||||
key: 'system.traits.finesse.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.traits.instinct.value',
|
||||||
key: 'system.traits.instinct.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.traits.presence.value',
|
||||||
key: 'system.traits.presence.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.traits.knowledge.value',
|
||||||
key: 'system.traits.knowledge.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.evasion',
|
||||||
key: 'system.evasion',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -107,15 +103,13 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.flexible.effects.flexible.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.flexible.effects.flexible.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.flexible.effects.flexible.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.flexible.effects.flexible.description',
|
||||||
img: 'icons/magic/movement/abstract-ribbons-red-orange.webp',
|
img: 'icons/magic/movement/abstract-ribbons-red-orange.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.evasion',
|
||||||
key: 'system.evasion',
|
mode: 2,
|
||||||
type: 'add',
|
value: '1'
|
||||||
value: '1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -127,15 +121,13 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.fortified.effects.fortified.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.fortified.effects.fortified.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.fortified.effects.fortified.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.fortified.effects.fortified.description',
|
||||||
img: 'icons/magic/defensive/shield-barrier-glowing-blue.webp',
|
img: 'icons/magic/defensive/shield-barrier-glowing-blue.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.rules.damageReduction.increasePerArmorMark',
|
||||||
key: 'system.rules.damageReduction.increasePerArmorMark',
|
mode: 5,
|
||||||
type: 'override',
|
value: '2'
|
||||||
value: '2'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -147,15 +139,13 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.gilded.effects.gilded.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.gilded.effects.gilded.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.gilded.effects.gilded.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.gilded.effects.gilded.description',
|
||||||
img: 'icons/magic/control/control-influence-crown-gold.webp',
|
img: 'icons/magic/control/control-influence-crown-gold.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.traits.presence.value',
|
||||||
key: 'system.traits.presence.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '1'
|
||||||
value: '1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -167,15 +157,13 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.heavy.effects.heavy.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.heavy.effects.heavy.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.heavy.effects.heavy.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.heavy.effects.heavy.description',
|
||||||
img: 'icons/commodities/metal/ingot-worn-iron.webp',
|
img: 'icons/commodities/metal/ingot-worn-iron.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.evasion',
|
||||||
key: 'system.evasion',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -224,15 +212,13 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.magical.effects.magical.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.magical.effects.magical.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.magical.effects.magical.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.magical.effects.magical.description',
|
||||||
img: 'icons/magic/defensive/barrier-shield-dome-blue-purple.webp',
|
img: 'icons/magic/defensive/barrier-shield-dome-blue-purple.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.rules.damageReduction.magical',
|
||||||
key: 'system.rules.damageReduction.magical',
|
mode: 5,
|
||||||
type: 'override',
|
value: 1
|
||||||
value: 1
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -263,15 +249,13 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.physical.effects.physical.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.physical.effects.physical.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.physical.effects.physical.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.physical.effects.physical.description',
|
||||||
img: 'icons/commodities/stone/ore-pile-tan.webp',
|
img: 'icons/commodities/stone/ore-pile-tan.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.rules.damageReduction.physical',
|
||||||
key: 'system.rules.damageReduction.physical',
|
mode: 5,
|
||||||
type: 'override',
|
value: 1
|
||||||
value: 1
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -296,20 +280,18 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.reinforced.effects.reinforced.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.reinforced.effects.reinforced.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.reinforced.effects.reinforced.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.reinforced.effects.reinforced.description',
|
||||||
img: 'icons/magic/defensive/shield-barrier-glowing-triangle-green.webp',
|
img: 'icons/magic/defensive/shield-barrier-glowing-triangle-green.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.bunuses.damageThresholds.major',
|
||||||
key: 'system.bunuses.damageThresholds.major',
|
mode: 2,
|
||||||
type: 'add',
|
value: '2'
|
||||||
value: '2'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.bunuses.damageThresholds.severe',
|
||||||
key: 'system.bunuses.damageThresholds.severe',
|
mode: 2,
|
||||||
type: 'add',
|
value: '2'
|
||||||
value: '2'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -344,20 +326,18 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.sharp.effects.sharp.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.sharp.effects.sharp.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.sharp.effects.sharp.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.sharp.effects.sharp.description',
|
||||||
img: 'icons/magic/defensive/shield-barrier-glowing-triangle-green.webp',
|
img: 'icons/magic/defensive/shield-barrier-glowing-triangle-green.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.bonuses.damage.primaryWeapon.dice',
|
||||||
key: 'system.bonuses.damage.primaryWeapon.dice',
|
mode: 2,
|
||||||
type: 'add',
|
value: '1d4'
|
||||||
value: '1d4'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.bonuses.damage.secondaryWeapon.dice',
|
||||||
key: 'system.bonuses.damage.secondaryWeapon.dice',
|
mode: 2,
|
||||||
type: 'add',
|
value: '1d4'
|
||||||
value: '1d4'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -428,20 +408,18 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.veryHeavy.effects.veryHeavy.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.veryHeavy.effects.veryHeavy.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.veryHeavy.effects.veryHeavy.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.veryHeavy.effects.veryHeavy.description',
|
||||||
img: 'icons/commodities/metal/ingot-stamped-steel.webp',
|
img: 'icons/commodities/metal/ingot-stamped-steel.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.evasion',
|
||||||
key: 'system.evasion',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-2'
|
||||||
value: '-2'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.traits.agility.value',
|
||||||
key: 'system.traits.agility.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -453,16 +431,14 @@ export const armorFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.warded.effects.warded.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.warded.effects.warded.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.warded.effects.warded.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.warded.effects.warded.description',
|
||||||
img: 'icons/magic/defensive/barrier-shield-dome-pink.webp',
|
img: 'icons/magic/defensive/barrier-shield-dome-pink.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.resistance.magical.reduction',
|
||||||
key: 'system.resistance.magical.reduction',
|
mode: 2,
|
||||||
type: 'add',
|
value: '@system.armorScore',
|
||||||
value: '@system.armorScore',
|
priority: 21
|
||||||
priority: 21
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -512,23 +488,21 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.effects.barrier.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.effects.barrier.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.effects.barrier.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.effects.barrier.description',
|
||||||
img: 'icons/skills/melee/shield-block-bash-blue.webp',
|
img: 'icons/skills/melee/shield-block-bash-blue.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.evasion',
|
||||||
key: 'system.evasion',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'Armor',
|
||||||
key: 'Armor',
|
type: 'armor',
|
||||||
|
typeData: {
|
||||||
type: 'armor',
|
type: 'armor',
|
||||||
typeData: {
|
max: 'ITEM.@system.tier + 1'
|
||||||
type: 'armor',
|
|
||||||
max: 'ITEM.@system.tier + 1'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -540,15 +514,13 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.bonded.effects.damage.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.bonded.effects.damage.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.bonded.effects.damage.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.bonded.effects.damage.description',
|
||||||
img: 'icons/magic/symbols/chevron-elipse-circle-blue.webp',
|
img: 'icons/magic/symbols/chevron-elipse-circle-blue.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
||||||
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
mode: 2,
|
||||||
type: 'add',
|
value: '@system.levelData.level.current'
|
||||||
value: '@system.levelData.level.current'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -581,20 +553,18 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.brave.effects.brave.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.brave.effects.brave.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.brave.effects.brave.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.brave.effects.brave.description',
|
||||||
img: 'icons/magic/life/heart-cross-strong-flame-purple-orange.webp',
|
img: 'icons/magic/life/heart-cross-strong-flame-purple-orange.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.evasion',
|
||||||
key: 'system.evasion',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'system.damageThresholds.severe',
|
||||||
key: 'system.damageThresholds.severe',
|
mode: 2,
|
||||||
type: 'add',
|
value: 'ITEM.@system.tier'
|
||||||
value: 'ITEM.@system.tier'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -648,15 +618,13 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.charged.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.charged.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.charged.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.charged.description',
|
||||||
img: 'icons/magic/lightning/claws-unarmed-strike-teal.webp',
|
img: 'icons/magic/lightning/claws-unarmed-strike-teal.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.proficiency',
|
||||||
key: 'system.proficiency',
|
mode: 2,
|
||||||
type: 'add',
|
value: '1'
|
||||||
value: '1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -692,15 +660,13 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.cumbersome.effects.cumbersome.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.cumbersome.effects.cumbersome.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.cumbersome.effects.cumbersome.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.cumbersome.effects.cumbersome.description',
|
||||||
img: 'icons/commodities/metal/mail-plate-steel.webp',
|
img: 'icons/commodities/metal/mail-plate-steel.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.traits.finesse.value',
|
||||||
key: 'system.traits.finesse.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -741,16 +707,14 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.effects.deflecting.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.effects.deflecting.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.effects.deflecting.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.effects.deflecting.description',
|
||||||
img: 'icons/skills/melee/hand-grip-sword-strike-orange.webp',
|
img: 'icons/skills/melee/hand-grip-sword-strike-orange.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.evasion',
|
||||||
key: 'system.evasion',
|
mode: 2,
|
||||||
type: 'add',
|
value: '@system.armorScore',
|
||||||
value: '@system.armorScore',
|
priority: 21
|
||||||
priority: 21
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -790,15 +754,13 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.effects.agility',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.effects.agility',
|
||||||
img: 'icons/skills/melee/strike-flail-spiked-pink.webp',
|
img: 'icons/skills/melee/strike-flail-spiked-pink.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.traits.agility.value',
|
||||||
key: 'system.traits.agility.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -829,14 +791,14 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.doubleDuty.effects.doubleDuty.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.doubleDuty.effects.doubleDuty.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.doubleDuty.effects.doubleDuty.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.doubleDuty.effects.doubleDuty.description',
|
||||||
img: 'icons/skills/melee/sword-shield-stylized-white.webp',
|
img: 'icons/skills/melee/sword-shield-stylized-white.webp',
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
||||||
|
mode: 2,
|
||||||
|
value: '1'
|
||||||
|
}
|
||||||
|
],
|
||||||
system: {
|
system: {
|
||||||
changes: [
|
|
||||||
{
|
|
||||||
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
|
||||||
type: 'add',
|
|
||||||
value: '1'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
rangeDependence: {
|
rangeDependence: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
range: 'melee',
|
range: 'melee',
|
||||||
|
|
@ -940,15 +902,13 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.actions.greed.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.actions.greed.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.actions.greed.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.actions.greed.description',
|
||||||
img: 'icons/commodities/currency/coins-crown-stack-gold.webp',
|
img: 'icons/commodities/currency/coins-crown-stack-gold.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.proficiency',
|
||||||
key: 'system.proficiency',
|
mode: 2,
|
||||||
type: 'add',
|
value: '1'
|
||||||
value: '1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -991,15 +951,13 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.heavy.effects.heavy.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.heavy.effects.heavy.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.heavy.effects.heavy.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.heavy.effects.heavy.description',
|
||||||
img: 'icons/commodities/metal/ingot-worn-iron.webp',
|
img: 'icons/commodities/metal/ingot-worn-iron.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.evasion',
|
||||||
key: 'system.evasion',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -1108,15 +1066,13 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.massive.effects.massive.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.massive.effects.massive.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.massive.effects.massive.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.massive.effects.massive.description',
|
||||||
img: 'icons/skills/melee/strike-flail-destructive-yellow.webp',
|
img: 'icons/skills/melee/strike-flail-destructive-yellow.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.evasion',
|
||||||
key: 'system.evasion',
|
mode: 2,
|
||||||
type: 'add',
|
value: '-1'
|
||||||
value: '-1'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -1147,14 +1103,14 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.paired.effects.paired.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.paired.effects.paired.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.paired.effects.paired.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.paired.effects.paired.description',
|
||||||
img: 'icons/skills/melee/weapons-crossed-swords-yellow-teal.webp',
|
img: 'icons/skills/melee/weapons-crossed-swords-yellow-teal.webp',
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
||||||
|
mode: 2,
|
||||||
|
value: 'ITEM.@system.tier + 1'
|
||||||
|
}
|
||||||
|
],
|
||||||
system: {
|
system: {
|
||||||
changes: [
|
|
||||||
{
|
|
||||||
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
|
||||||
type: 'add',
|
|
||||||
value: 'ITEM.@system.tier + 1'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
rangeDependence: {
|
rangeDependence: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
range: 'melee',
|
range: 'melee',
|
||||||
|
|
@ -1202,15 +1158,13 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.effects.persuasive.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.effects.persuasive.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.effects.persuasive.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.effects.persuasive.description',
|
||||||
img: 'icons/magic/control/hypnosis-mesmerism-eye.webp',
|
img: 'icons/magic/control/hypnosis-mesmerism-eye.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.traits.presence.value',
|
||||||
key: 'system.traits.presence.value',
|
mode: 2,
|
||||||
type: 'add',
|
value: '2'
|
||||||
value: '2'
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1249,20 +1203,17 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.protective.effects.protective.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.protective.effects.protective.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.protective.effects.protective.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.protective.effects.protective.description',
|
||||||
img: 'icons/skills/melee/shield-block-gray-orange.webp',
|
img: 'icons/skills/melee/shield-block-gray-orange.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'Armor',
|
||||||
key: 'Armor',
|
type: 'armor',
|
||||||
|
value: 0,
|
||||||
|
typeData: {
|
||||||
type: 'armor',
|
type: 'armor',
|
||||||
value: 0,
|
max: 'ITEM.@system.tier'
|
||||||
typeData: {
|
|
||||||
type: 'armor',
|
|
||||||
max: 'ITEM.@system.tier'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
]
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -1293,15 +1244,13 @@ export const weaponFeatures = {
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.reliable.effects.reliable.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.reliable.effects.reliable.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.reliable.effects.reliable.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.reliable.effects.reliable.description',
|
||||||
img: 'icons/skills/melee/strike-sword-slashing-red.webp',
|
img: 'icons/skills/melee/strike-sword-slashing-red.webp',
|
||||||
system: {
|
changes: [
|
||||||
changes: [
|
{
|
||||||
{
|
key: 'system.bonuses.roll.primaryWeapon.bonus',
|
||||||
key: 'system.bonuses.roll.primaryWeapon.bonus',
|
mode: 2,
|
||||||
type: 'add',
|
value: 1
|
||||||
value: 1
|
}
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -1392,7 +1341,7 @@ export const weaponFeatures = {
|
||||||
changes: [
|
changes: [
|
||||||
{
|
{
|
||||||
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
||||||
type: 'add',
|
mode: 2,
|
||||||
value: '@system.traits.agility.value',
|
value: '@system.traits.agility.value',
|
||||||
priority: 21
|
priority: 21
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,8 @@ export const menu = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const gameSettings = {
|
export const gameSettings = {
|
||||||
/** @type {'Automation'} */
|
|
||||||
Automation: 'Automation',
|
Automation: 'Automation',
|
||||||
Metagaming: 'Metagaming',
|
Metagaming: 'Metagaming',
|
||||||
/** @type {'Homebrew'} */
|
|
||||||
Homebrew: 'Homebrew',
|
Homebrew: 'Homebrew',
|
||||||
appearance: 'Appearance',
|
appearance: 'Appearance',
|
||||||
GlobalOverrides: 'GlobalOverrides',
|
GlobalOverrides: 'GlobalOverrides',
|
||||||
|
|
@ -39,7 +37,6 @@ export const gameSettings = {
|
||||||
Fear: 'ResourcesFear'
|
Fear: 'ResourcesFear'
|
||||||
},
|
},
|
||||||
LevelTiers: 'LevelTiers',
|
LevelTiers: 'LevelTiers',
|
||||||
/** @type {'Countdowns'} */
|
|
||||||
Countdowns: 'Countdowns',
|
Countdowns: 'Countdowns',
|
||||||
LastMigrationVersion: 'LastMigrationVersion',
|
LastMigrationVersion: 'LastMigrationVersion',
|
||||||
SpotlightRequestQueue: 'SpotlightRequestQueue',
|
SpotlightRequestQueue: 'SpotlightRequestQueue',
|
||||||
|
|
@ -62,18 +59,3 @@ export const actionAutomationChoices = {
|
||||||
label: 'DAGGERHEART.CONFIG.ActionAutomationChoices.always'
|
label: 'DAGGERHEART.CONFIG.ActionAutomationChoices.always'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reloadChoices = {
|
|
||||||
off: {
|
|
||||||
id: 'off',
|
|
||||||
label: 'DAGGERHEART.CONFIG.ReloadChoices.off.label'
|
|
||||||
},
|
|
||||||
manual: {
|
|
||||||
id: 'manual',
|
|
||||||
label: 'DAGGERHEART.CONFIG.ReloadChoices.manual.label'
|
|
||||||
},
|
|
||||||
auto: {
|
|
||||||
id: 'auto',
|
|
||||||
label: 'DAGGERHEART.CONFIG.ReloadChoices.auto.label'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -12,7 +12,6 @@ import * as HOOKS from './hooksConfig.mjs';
|
||||||
import * as TRIGGER from './triggerConfig.mjs';
|
import * as TRIGGER from './triggerConfig.mjs';
|
||||||
import * as ITEMBROWSER from './itemBrowserConfig.mjs';
|
import * as ITEMBROWSER from './itemBrowserConfig.mjs';
|
||||||
|
|
||||||
/** @type {"daggerheart"} */
|
|
||||||
export const SYSTEM_ID = 'daggerheart';
|
export const SYSTEM_ID = 'daggerheart';
|
||||||
|
|
||||||
export const SYSTEM = {
|
export const SYSTEM = {
|
||||||
|
|
|
||||||
7
module/data/_types.d.ts
vendored
7
module/data/_types.d.ts
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
import { DhCountdown } from './countdowns.mjs'
|
|
||||||
|
|
||||||
declare module './countdowns.mjs' {
|
|
||||||
export default interface DhCountdowns {
|
|
||||||
countdowns: Record<string, DhCountdown>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,19 +13,18 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
if (this.damage.includeBase) {
|
if (this.damage.includeBase) {
|
||||||
const baseDamage = this.getParentHitPointDamage();
|
const baseDamage = this.getParentHitPointDamage();
|
||||||
if (baseDamage) {
|
if (baseDamage) {
|
||||||
if (!this.damage.main) {
|
if (!this.damage.parts.hitPoints) {
|
||||||
this.damage.main = baseDamage;
|
this.damage.parts.hitPoints = baseDamage;
|
||||||
} else {
|
} else {
|
||||||
for (const type of baseDamage.type) this.damage.main.type.add(type);
|
for (const type of baseDamage.type) this.damage.parts.hitPoints.type.add(type);
|
||||||
|
|
||||||
this.damage.main.value.custom = {
|
this.damage.parts.hitPoints.value.custom = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
formula: `${baseDamage.value.getFormula()} + ${this.damage.main.value.getFormula()}`
|
formula: `${baseDamage.value.getFormula()} + ${this.damage.parts.hitPoints.value.getFormula()}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.roll.useDefault) {
|
if (this.roll.useDefault) {
|
||||||
this.roll.trait = this.item.system.attack.roll.trait;
|
this.roll.trait = this.item.system.attack.roll.trait;
|
||||||
this.roll.type = 'attack';
|
this.roll.type = 'attack';
|
||||||
|
|
@ -34,28 +33,24 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
getParentHitPointDamage() {
|
getParentHitPointDamage() {
|
||||||
return this.item?.system?.attack.damage.main;
|
return this.item?.system?.attack.damage.parts.hitPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
get damageFormula() {
|
get damageFormula() {
|
||||||
const hitPointsPart = this.damage.main;
|
const hitPointsPart = this.damage.parts.hitPoints;
|
||||||
if (!hitPointsPart) return '0';
|
if (!hitPointsPart) return '0';
|
||||||
|
|
||||||
return hitPointsPart.value.getFormula();
|
return hitPointsPart.value.getFormula();
|
||||||
}
|
}
|
||||||
|
|
||||||
get altDamageFormula() {
|
get altDamageFormula() {
|
||||||
const hitPointsPart = this.damage.main;
|
const hitPointsPart = this.damage.parts.hitPoints;
|
||||||
if (!hitPointsPart) return '0';
|
if (!hitPointsPart) return '0';
|
||||||
|
|
||||||
return hitPointsPart.valueAlt.getFormula();
|
return hitPointsPart.valueAlt.getFormula();
|
||||||
}
|
}
|
||||||
|
|
||||||
async use(event, options) {
|
async use(event, options) {
|
||||||
if (this.item?.system.needsReload) {
|
|
||||||
return ui.notifications.error(_loc('DAGGERHEART.UI.Notifications.reloadRequired', { weapon: this.item.name }));
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await super.use(event, options);
|
const result = await super.use(event, options);
|
||||||
|
|
||||||
if (result?.message?.system.action?.roll?.type === 'attack') {
|
if (result?.message?.system.action?.roll?.type === 'attack') {
|
||||||
|
|
@ -66,23 +61,6 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleReload(options = { awaitRoll: false }) {
|
|
||||||
const roll = await new Roll('1d6').evaluate();
|
|
||||||
if (game.modules.get('dice-so-nice')?.active) {
|
|
||||||
if (options.awaitRoll)
|
|
||||||
await game.dice3d.showForRoll(roll, game.user, true);
|
|
||||||
else
|
|
||||||
game.dice3d.showForRoll(roll, game.user, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const needsReload = roll.total === 1;
|
|
||||||
if (needsReload) {
|
|
||||||
this.item.update({ 'system.resource.value': 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return { needsReload, rollValue: roll.total };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a localized label array for this item subtype.
|
* Generate a localized label array for this item subtype.
|
||||||
* @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects.
|
* @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects.
|
||||||
|
|
@ -95,7 +73,7 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`));
|
if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`));
|
||||||
|
|
||||||
const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active;
|
const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active;
|
||||||
for (const { value, valueAlt, type } of [damage.main, ...damage.resources].filter(d => !!d)) {
|
for (const { value, valueAlt, type } of damage.parts) {
|
||||||
const usedValue = useAltDamage ? valueAlt : value;
|
const usedValue = useAltDamage ? valueAlt : value;
|
||||||
const damageString = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
|
const damageString = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
|
||||||
const str = damageString
|
const str = damageString
|
||||||
|
|
@ -104,7 +82,7 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
x: game.i18n.localize('DAGGERHEART.GENERAL.damage')
|
x: game.i18n.localize('DAGGERHEART.GENERAL.damage')
|
||||||
});
|
});
|
||||||
|
|
||||||
const icons = Array.from(type ?? [])
|
const icons = Array.from(type)
|
||||||
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
|
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -256,8 +256,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
||||||
|
|
||||||
if (this.chatDisplay && !config.skips.createMessage && !config.actionChatMessageHandled)
|
if (this.chatDisplay && !config.skips.createMessage && !config.actionChatMessageHandled) await this.toChat();
|
||||||
await this.toChat(null, config);
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
@ -289,6 +288,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
hasEffect: this.hasEffect,
|
hasEffect: this.hasEffect,
|
||||||
hasSave: this.hasSave,
|
hasSave: this.hasSave,
|
||||||
onSave: this.save?.damageMod,
|
onSave: this.save?.damageMod,
|
||||||
|
isDirect: !!this.damage?.direct,
|
||||||
selectedMessageMode: game.settings.get('core', 'messageMode'),
|
selectedMessageMode: game.settings.get('core', 'messageMode'),
|
||||||
data: this.getRollData(),
|
data: this.getRollData(),
|
||||||
evaluate: this.hasRoll,
|
evaluate: this.hasRoll,
|
||||||
|
|
@ -306,20 +306,20 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.damage) {
|
if (this.damage) {
|
||||||
config.isDirect = !!this.damage.main?.direct;
|
config.isDirect = this.damage.direct;
|
||||||
|
|
||||||
const groupAttackTokens = this.damage.main?.groupAttack
|
const groupAttackTokens = this.damage.groupAttack
|
||||||
? game.system.api.fields.ActionFields.DamageField.getGroupAttackTokens(
|
? game.system.api.fields.ActionFields.DamageField.getGroupAttackTokens(
|
||||||
this.actor.id,
|
this.actor.id,
|
||||||
this.damage.main.groupAttack
|
this.damage.groupAttack
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
config.damageOptions = {
|
config.damageOptions = {
|
||||||
groupAttack: this.damage.main?.groupAttack
|
groupAttack: this.damage.groupAttack
|
||||||
? {
|
? {
|
||||||
numAttackers: Math.max(groupAttackTokens.length, 1),
|
numAttackers: Math.max(groupAttackTokens.length, 1),
|
||||||
range: this.damage.main.groupAttack
|
range: this.damage.groupAttack
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
};
|
};
|
||||||
|
|
@ -429,11 +429,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasDamage() {
|
get hasDamage() {
|
||||||
return this.type !== 'healing' && (Boolean(this.damage?.main) || !foundry.utils.isEmpty(this.damage?.resources));
|
return Boolean(Object.keys(this.damage?.parts ?? {}).length) && this.type !== 'healing';
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasHealing() {
|
get hasHealing() {
|
||||||
return this.type === 'healing' && !foundry.utils.isEmpty(this.damage?.resources);
|
return Boolean(Object.keys(this.damage?.parts ?? {}).length) && this.type === 'healing';
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasSave() {
|
get hasSave() {
|
||||||
|
|
@ -469,25 +469,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.damage?.parts && !source.damage.resources && !source.damage.main) {
|
|
||||||
source.damage.main = null;
|
|
||||||
source.damage.resources = {};
|
|
||||||
for (const [partKey, part] of Object.entries(source.damage.parts)) {
|
|
||||||
if (partKey === 'hitPoints' && source.type !== 'healing') {
|
|
||||||
source.damage.main = {
|
|
||||||
...part,
|
|
||||||
includeBase: source.damage.includeBase,
|
|
||||||
direct: source.damage.direct,
|
|
||||||
groupAttack: source.damage.groupAttack
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
source.damage.resources[partKey] = part;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete source.damage.parts;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,11 @@ export default class DHDamageAction extends DHBaseAction {
|
||||||
* @returns Formula string
|
* @returns Formula string
|
||||||
*/
|
*/
|
||||||
getDamageFormula() {
|
getDamageFormula() {
|
||||||
if (!this.damage.main) return '';
|
const strings = [];
|
||||||
|
for (const { value } of this.damage.parts) {
|
||||||
|
strings.push(Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {}));
|
||||||
|
}
|
||||||
|
|
||||||
return Roll.replaceFormulaData(this.damage.main.value.getFormula(), this.actor?.getRollData() ?? {});
|
return strings.join(' + ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
description: new fields.HTMLField({ label: 'DAGGERHEART.GENERAL.description' })
|
description: new fields.HTMLField({ label: 'DAGGERHEART.GENERAL.description' })
|
||||||
}),
|
}),
|
||||||
rangeDependence: new fields.SchemaField({
|
rangeDependence: new fields.SchemaField({
|
||||||
|
enabled: new fields.BooleanField({
|
||||||
|
required: true,
|
||||||
|
initial: false,
|
||||||
|
label: 'DAGGERHEART.GENERAL.enabled'
|
||||||
|
}),
|
||||||
type: new fields.StringField({
|
type: new fields.StringField({
|
||||||
required: true,
|
required: true,
|
||||||
choices: CONFIG.DH.GENERAL.rangeInclusion,
|
choices: CONFIG.DH.GENERAL.rangeInclusion,
|
||||||
|
|
@ -75,7 +80,7 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
initial: CONFIG.DH.GENERAL.range.melee.id,
|
initial: CONFIG.DH.GENERAL.range.melee.id,
|
||||||
label: 'DAGGERHEART.GENERAL.range'
|
label: 'DAGGERHEART.GENERAL.range'
|
||||||
})
|
})
|
||||||
}, { nullable: true, initial: null }),
|
}),
|
||||||
stacking: new fields.SchemaField(
|
stacking: new fields.SchemaField(
|
||||||
{
|
{
|
||||||
value: new fields.NumberField({
|
value: new fields.NumberField({
|
||||||
|
|
@ -141,8 +146,14 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
description: '',
|
description: '',
|
||||||
transfer: options.transfer,
|
transfer: options.transfer,
|
||||||
statuses: [],
|
statuses: [],
|
||||||
|
changes: [],
|
||||||
system: {
|
system: {
|
||||||
changes: []
|
rangeDependence: {
|
||||||
|
enabled: false,
|
||||||
|
type: CONFIG.DH.GENERAL.rangeInclusion.withinRange.id,
|
||||||
|
target: CONFIG.DH.GENERAL.otherTargetTypes.hostile.id,
|
||||||
|
range: CONFIG.DH.GENERAL.range.melee.id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -174,12 +185,4 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
if (this.parent.actor && options.scrollingTextData)
|
if (this.parent.actor && options.scrollingTextData)
|
||||||
this.parent.actor.queueScrollText(options.scrollingTextData);
|
this.parent.actor.queueScrollText(options.scrollingTextData);
|
||||||
}
|
}
|
||||||
|
|
||||||
static migrateData(source) {
|
|
||||||
if (source.rangeDependence?.enabled === false) {
|
|
||||||
source.rangeDependence = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.migrateData(source);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,11 +84,13 @@ export default class DhpAdversary extends DhCreature {
|
||||||
type: 'attack'
|
type: 'attack'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
main: {
|
parts: {
|
||||||
type: ['physical'],
|
hitPoints: {
|
||||||
applyTo: 'hitPoints',
|
type: ['physical'],
|
||||||
value: {
|
applyTo: 'hitPoints',
|
||||||
multiplier: 'flat'
|
value: {
|
||||||
|
multiplier: 'flat'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,13 +104,15 @@ export default class DhCharacter extends DhCreature {
|
||||||
trait: 'strength'
|
trait: 'strength'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
main: {
|
parts: {
|
||||||
type: ['physical'],
|
hitPoints: {
|
||||||
applyTo: 'hitPoints',
|
type: ['physical'],
|
||||||
value: {
|
applyTo: 'hitPoints',
|
||||||
custom: {
|
value: {
|
||||||
enabled: true,
|
custom: {
|
||||||
formula: '@profd4'
|
enabled: true,
|
||||||
|
formula: '@profd4'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -836,7 +838,7 @@ export default class DhCharacter extends DhCreature {
|
||||||
isReversed: true
|
isReversed: true
|
||||||
};
|
};
|
||||||
|
|
||||||
this.attack.damage.main.value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`;
|
this.attack.damage.parts.hitPoints.value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`;
|
||||||
|
|
||||||
// Clamp resources (must be done last to ensure all updates occur)
|
// Clamp resources (must be done last to ensure all updates occur)
|
||||||
this.resources.clamp();
|
this.resources.clamp();
|
||||||
|
|
|
||||||
|
|
@ -40,17 +40,17 @@ export function getTierAdjustedAdversary(source, tier) {
|
||||||
|
|
||||||
// Store initial attack damage for abilities that have you deal a "standard attack"
|
// Store initial attack damage for abilities that have you deal a "standard attack"
|
||||||
const initialAttack = {
|
const initialAttack = {
|
||||||
type: source.system.attack.damage?.main?.type?.toSorted(),
|
type: source.system.attack.damage?.parts.hitPoints?.type?.toSorted(),
|
||||||
value: getFormula(source.system.attack.damage?.main?.value)
|
value: getFormula(source.system.attack.damage?.parts.hitPoints?.value)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update damage of base attack.
|
// Update damage of base attack.
|
||||||
try {
|
try {
|
||||||
const damage = source.system.attack.damage;
|
const damage = source.system.attack.damage;
|
||||||
if (!damage?.main) throw new Error('Unexpected missing damage in adversary');
|
if (!damage?.parts.hitPoints) throw new Error('Unexpected missing attack in adversary');
|
||||||
|
|
||||||
for (const property of ['value', 'valueAlt']) {
|
for (const property of ['value', 'valueAlt']) {
|
||||||
const data = damage.main[property];
|
const data = damage.parts.hitPoints[property];
|
||||||
const previousFormula = getFormula(data);
|
const previousFormula = getFormula(data);
|
||||||
const value = calculateAdjustedDamage(previousFormula, 'attack', damageMeta);
|
const value = calculateAdjustedDamage(previousFormula, 'attack', damageMeta);
|
||||||
applyAdjustedDamage(data, value);
|
applyAdjustedDamage(data, value);
|
||||||
|
|
@ -82,12 +82,12 @@ export function getTierAdjustedAdversary(source, tier) {
|
||||||
|
|
||||||
// Update damage in item actions and convert all formula matches in the descriptions to the new damage
|
// Update damage in item actions and convert all formula matches in the descriptions to the new damage
|
||||||
for (const action of Object.values(item.system.actions)) {
|
for (const action of Object.values(item.system.actions)) {
|
||||||
if (!action.damage?.main) continue;
|
if (!action.damage?.parts.hitPoints) continue;
|
||||||
try {
|
try {
|
||||||
// Apply conversions and save a record. If it matches attack damage *and* Its not in the description, use attack conversion instead
|
// Apply conversions and save a record. If it matches attack damage *and* Its not in the description, use attack conversion instead
|
||||||
const result = [];
|
const result = [];
|
||||||
for (const property of ['value', 'valueAlt']) {
|
for (const property of ['value', 'valueAlt']) {
|
||||||
const { [property]: data, type: damageType } = action.damage.main;
|
const { [property]: data, type: damageType } = action.damage.parts.hitPoints;
|
||||||
const previousFormula = getFormula(data);
|
const previousFormula = getFormula(data);
|
||||||
const isActuallyAttack =
|
const isActuallyAttack =
|
||||||
previousFormula === initialAttack.value &&
|
previousFormula === initialAttack.value &&
|
||||||
|
|
@ -199,7 +199,7 @@ function calculateAdjustedDamage(formula, type, { currentDamageRange, newDamageR
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get formula from either damage data *or* a simple formula object.
|
* Get formula from either damage parts *or* a simple formula object.
|
||||||
* @returns {string} the new formula data
|
* @returns {string} the new formula data
|
||||||
*/
|
*/
|
||||||
function getFormula(data) {
|
function getFormula(data) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { triggerChatRollFx } from '../../helpers/utils.mjs';
|
import { triggerChatRollFx } from '../../helpers/utils.mjs';
|
||||||
import { ChatDamageData } from './chatDamageData.mjs';
|
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
|
@ -42,7 +41,6 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
hasEffect: new fields.BooleanField({ initial: false }),
|
hasEffect: new fields.BooleanField({ initial: false }),
|
||||||
hasSave: new fields.BooleanField({ initial: false }),
|
hasSave: new fields.BooleanField({ initial: false }),
|
||||||
hasTarget: new fields.BooleanField({ initial: false }),
|
hasTarget: new fields.BooleanField({ initial: false }),
|
||||||
reloadCheckValue: new fields.NumberField({ integer: true, nullable: true, initial: null }),
|
|
||||||
isDirect: new fields.BooleanField({ initial: false }),
|
isDirect: new fields.BooleanField({ initial: false }),
|
||||||
onSave: new fields.StringField(),
|
onSave: new fields.StringField(),
|
||||||
source: new fields.SchemaField({
|
source: new fields.SchemaField({
|
||||||
|
|
@ -51,7 +49,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
originItem: originItemField(),
|
originItem: originItemField(),
|
||||||
action: new fields.StringField()
|
action: new fields.StringField()
|
||||||
}),
|
}),
|
||||||
damage: new fields.EmbeddedDataField(ChatDamageData),
|
damage: new fields.ObjectField(),
|
||||||
damageOptions: new fields.ObjectField(),
|
damageOptions: new fields.ObjectField(),
|
||||||
costs: new fields.ArrayField(new fields.ObjectField()),
|
costs: new fields.ArrayField(new fields.ObjectField()),
|
||||||
successConsumed: new fields.BooleanField({ initial: false })
|
successConsumed: new fields.BooleanField({ initial: false })
|
||||||
|
|
@ -76,14 +74,10 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
return fromUuidSync(this.source.actor);
|
return fromUuidSync(this.source.actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
get item() {
|
get actionItem() {
|
||||||
const actionActor = this.actionActor;
|
const actionActor = this.actionActor;
|
||||||
if (!actionActor || !this.source.item) return null;
|
if (!actionActor || !this.source.item) return null;
|
||||||
|
|
||||||
return actionActor.items.get(this.source.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
get actionItem() {
|
|
||||||
switch (this.source.originItem.type) {
|
switch (this.source.originItem.type) {
|
||||||
case CONFIG.DH.ITEM.originItemType.restMove:
|
case CONFIG.DH.ITEM.originItemType.restMove:
|
||||||
const restMoves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves;
|
const restMoves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves;
|
||||||
|
|
@ -91,18 +85,11 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
this.source.originItem.actionIndex
|
this.source.originItem.actionIndex
|
||||||
];
|
];
|
||||||
default:
|
default:
|
||||||
return this.item?.system.actionsList?.find(a => a.id === this.source.action);
|
const item = actionActor.items.get(this.source.item);
|
||||||
|
return item ? item.system.actionsList?.find(a => a.id === this.source.action) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasReload() {
|
|
||||||
return this.item?.system.hasReload;
|
|
||||||
}
|
|
||||||
|
|
||||||
get reloadCheckFailed() {
|
|
||||||
return this.reloadCheckValue === 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
get action() {
|
get action() {
|
||||||
const { actionActor, actionItem: itemAction } = this;
|
const { actionActor, actionItem: itemAction } = this;
|
||||||
if (!this.source.action) return null;
|
if (!this.source.action) return null;
|
||||||
|
|
@ -145,21 +132,28 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Change how damage data is stored somehow to enable better rerolling */
|
||||||
async getRerolledDamage() {
|
async getRerolledDamage() {
|
||||||
if (!this.damage.active) return;
|
if (!this.damage) return;
|
||||||
|
|
||||||
const rerolls = [];
|
const rerolls = [];
|
||||||
const update = { system: { damage: { main: null, resources: _replace({}) } } };
|
const update = { system: { damage: {} } };
|
||||||
if (this.damage.main) {
|
for (const partKey in this.damage) {
|
||||||
const reroll = await this.damage.main.reroll();
|
const part = this.damage[partKey];
|
||||||
rerolls.push(reroll);
|
const testRoll = Roll.fromData(part.parts[0].roll);
|
||||||
update.system.damage.main = reroll.toJSON();
|
const rerolled = await testRoll.reroll();
|
||||||
}
|
rerolls.push(rerolled);
|
||||||
|
|
||||||
for (const key of Object.keys(this.damage.resources)) {
|
if (!update.system.damage[partKey]) update.system.damage[partKey] = { parts: [part.parts[0]] };
|
||||||
const reroll = await this.damage.resources[key].reroll();
|
const partData = update.system.damage[partKey].parts[0];
|
||||||
rerolls.push(reroll);
|
update.system.damage[partKey].total = rerolled.total;
|
||||||
update.system.damage.resources[key] = reroll.toJSON();
|
partData.modifierTotal = rerolled.terms.reduce((acc, x) => {
|
||||||
|
if (x.isDeterministic && !x.operator) acc += x.total;
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
partData.dice = rerolled.dice.map(d => ({ ...d.toJSON(), dice: d.denomination }));
|
||||||
|
partData.total = rerolled.total;
|
||||||
|
partData.roll = rerolled.toJSON();
|
||||||
}
|
}
|
||||||
|
|
||||||
await triggerChatRollFx(rerolls);
|
await triggerChatRollFx(rerolls);
|
||||||
|
|
@ -204,43 +198,6 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
this.isGM = game.user.isGM; //temp
|
this.isGM = game.user.isGM; //temp
|
||||||
}
|
}
|
||||||
|
|
||||||
static migrateData(source) {
|
|
||||||
const { main, resources, ...flatDamageKeys } = source.damage ?? {};
|
|
||||||
if (source.damage && !main && !resources) {
|
|
||||||
source.damage.main = null;
|
|
||||||
source.damage.resources = {};
|
|
||||||
|
|
||||||
const getRoll = key => {
|
|
||||||
const damageData = source.damage[key];
|
|
||||||
const oldRoll = damageData.parts[0]?.roll;
|
|
||||||
return oldRoll ? JSON.stringify({
|
|
||||||
...oldRoll,
|
|
||||||
class: 'BaseRoll',
|
|
||||||
options: {
|
|
||||||
...oldRoll.options,
|
|
||||||
damageTypes: damageData.parts[0].damageTypes ?? []
|
|
||||||
}
|
|
||||||
}) : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const key of Object.keys(flatDamageKeys)) {
|
|
||||||
if (key === 'hitPoints' && source.hasDamage && !source.hasHealing) {
|
|
||||||
source.damage.main = getRoll('hitPoints');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
source.damage.resources[key] = getRoll(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of Object.keys(flatDamageKeys)) {
|
|
||||||
delete source.damage[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getTargetList() {
|
getTargetList() {
|
||||||
const targets =
|
const targets =
|
||||||
this.targetMode && this.parent.isAuthor
|
this.targetMode && this.parent.isAuthor
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
import { triggerChatRollFx } from '../../helpers/utils.mjs';
|
|
||||||
|
|
||||||
export class ChatDamageData extends foundry.abstract.DataModel {
|
|
||||||
constructor(data = {}, options = {}) {
|
|
||||||
super(data, options);
|
|
||||||
|
|
||||||
this._prepareRolls();
|
|
||||||
}
|
|
||||||
|
|
||||||
static defineSchema() {
|
|
||||||
const fields = foundry.data.fields;
|
|
||||||
|
|
||||||
return {
|
|
||||||
main: new fields.JSONField({ nullable: true, validate: ChatDamageData.#validateRoll}),
|
|
||||||
resources: new fields.TypedObjectField(new fields.JSONField({validate: ChatDamageData.#validateRoll}))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get active() {
|
|
||||||
return !!this.main || Boolean(Object.keys(this.resources).length);
|
|
||||||
}
|
|
||||||
|
|
||||||
static #validateRoll(rollJSON) {
|
|
||||||
if (rollJSON) {
|
|
||||||
const roll = JSON.parse(rollJSON);
|
|
||||||
if (!roll.evaluated) throw new Error('Roll objects added to ChatMessage documents must be evaluated');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_prepareRolls() {
|
|
||||||
this.main &&= Roll.fromData(this.main);
|
|
||||||
for (const key of Object.keys(this.resources)) {
|
|
||||||
this.resources[key] = Roll.fromData(this.resources[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async rerollDamageDie(isResource, damageType, dice, resultIndex) {
|
|
||||||
const reroll = isResource ? this.resources[damageType] : this.main;
|
|
||||||
const rerollDice = reroll.dice[dice];
|
|
||||||
await rerollDice.rerollResult(resultIndex);
|
|
||||||
await reroll._evaluate();
|
|
||||||
|
|
||||||
const rerolledResult = rerollDice.results[rerollDice.results.length - 1];
|
|
||||||
if (rerolledResult) {
|
|
||||||
const fakeRoll = {
|
|
||||||
_evaluated: true,
|
|
||||||
dice: [new foundry.dice.terms.Die({
|
|
||||||
...rerollDice,
|
|
||||||
results: [rerolledResult],
|
|
||||||
total: rerolledResult.value,
|
|
||||||
faces: rerollDice.faces
|
|
||||||
})],
|
|
||||||
options: { appearance: {} }
|
|
||||||
};
|
|
||||||
await triggerChatRollFx([fakeRoll]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
|
||||||
import FormulaField from './fields/formulaField.mjs';
|
import FormulaField from './fields/formulaField.mjs';
|
||||||
|
|
||||||
export default class DhCountdowns extends foundry.abstract.DataModel {
|
export default class DhCountdowns extends foundry.abstract.DataModel {
|
||||||
|
|
@ -15,36 +14,19 @@ export default class DhCountdowns extends foundry.abstract.DataModel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
handleChange() {
|
||||||
_initialize(options) {
|
|
||||||
super._initialize(options);
|
|
||||||
for (const [id, countdown] of Object.entries(this.countdowns)) {
|
|
||||||
countdown.id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleChange() {
|
|
||||||
const previousCountdowns = foundry.ui.countdowns.previousCountdownData;
|
const previousCountdowns = foundry.ui.countdowns.previousCountdownData;
|
||||||
const changedCountdowns = Object.entries(this.countdowns).reduce((acc, [key, countdown]) => {
|
const changedCountdowns = Object.entries(this.countdowns).reduce((acc, [key, countdown]) => {
|
||||||
const previous = previousCountdowns[key];
|
const previousCountdown = previousCountdowns[key];
|
||||||
const currentChanged = !previous || (previous.progress.current !== countdown.progress.current);
|
if (!previousCountdown || (previousCountdown.progress.current !== countdown.progress.current)) {
|
||||||
if (currentChanged && previous?.progress.start === countdown.progress.start) {
|
|
||||||
acc.push(key);
|
acc.push(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Re-render countdowns applications. When the change is due to an actual update, resync the editor
|
for (const countdownKey of changedCountdowns)
|
||||||
if (!foundry.utils.equals(previousCountdowns, this.countdowns)) {
|
foundry.ui.countdowns.changedCountdownsForAnimation.add(countdownKey);
|
||||||
await foundry.ui.countdowns.render({ animate: changedCountdowns });
|
|
||||||
for (const instance of game.system.api.applications.ui.CountdownEdit.instances()) {
|
|
||||||
instance.data = this;
|
|
||||||
await instance.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inform modules of updates
|
|
||||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static migrateData(source) {
|
static migrateData(source) {
|
||||||
|
|
@ -178,14 +160,6 @@ export class DhCountdown extends foundry.abstract.DataModel {
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A boolean indicator for whether the current game User has ownership rights for this countdown
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
get isOwner() {
|
|
||||||
return this.getUserLevel(game.user) === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static migrateData(source) {
|
static migrateData(source) {
|
||||||
if (source.progress.max) {
|
if (source.progress.max) {
|
||||||
|
|
@ -196,29 +170,4 @@ export class DhCountdown extends foundry.abstract.DataModel {
|
||||||
|
|
||||||
return super.migrateData(source);
|
return super.migrateData(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the explicit permission level that a User has over this Document, a value in CONST.DOCUMENT_OWNERSHIP_LEVELS.
|
|
||||||
* Compendium content ignores the ownership field in favor of User role-based ownership. Otherwise, Documents use
|
|
||||||
* granular per-User ownership definitions and Embedded Documents defer to their parent ownership.
|
|
||||||
*
|
|
||||||
* @param {BaseUser} [user=game.user] The User being tested
|
|
||||||
* @returns {DocumentOwnershipNumber} A numeric permission level from {@link CONST.DOCUMENT_OWNERSHIP_LEVELS}
|
|
||||||
*/
|
|
||||||
getUserLevel(user) {
|
|
||||||
if (user.isGM) return CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
|
||||||
|
|
||||||
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
|
||||||
const playerOwnership = this.ownership[user.id];
|
|
||||||
return playerOwnership === undefined || playerOwnership === CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
|
|
||||||
? setting.defaultOwnership
|
|
||||||
: playerOwnership;
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete() {
|
|
||||||
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
|
||||||
const data = foundry.utils.deepClone(setting._source);
|
|
||||||
delete data.countdowns[this.id];
|
|
||||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,20 @@ export default class DamageField extends fields.SchemaField {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
constructor(options, context = {}) {
|
constructor(options, context = {}) {
|
||||||
super({
|
const damageFields = {
|
||||||
main: new fields.EmbeddedDataField(DHDamageData, { nullable: true }),
|
parts: new IterableTypedObjectField(DHDamageData),
|
||||||
resources: new IterableTypedObjectField(DHResourceData)
|
includeBase: new fields.BooleanField({
|
||||||
}, options, context);
|
initial: false,
|
||||||
|
label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label'
|
||||||
|
}),
|
||||||
|
direct: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.CONFIG.DamageType.direct.name' }),
|
||||||
|
groupAttack: new fields.StringField({
|
||||||
|
choices: CONFIG.DH.GENERAL.groupAttackRange,
|
||||||
|
blank: true,
|
||||||
|
label: 'DAGGERHEART.ACTIONS.Settings.groupAttack.label'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
super(damageFields, options, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -31,23 +41,25 @@ export default class DamageField extends fields.SchemaField {
|
||||||
this.hasRoll &&
|
this.hasRoll &&
|
||||||
DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id &&
|
DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id &&
|
||||||
!force
|
!force
|
||||||
) {
|
)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
const damageFormula = this.damage.main ?
|
let formulas = this.damage.parts.map(p => ({
|
||||||
DamageField.formatFormulas.call(this, [this.damage.main], config)[0] : null;
|
formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor),
|
||||||
const resourceFormulas = DamageField.formatFormulas.call(this, this.damage.resources, config);
|
damageTypes: p.applyTo === 'hitPoints' && !p.type.size ? new Set(['physical']) : p.type,
|
||||||
|
applyTo: p.applyTo
|
||||||
|
}));
|
||||||
|
|
||||||
if (!damageFormula && !resourceFormulas.length) return false;
|
if (!formulas.length) return false;
|
||||||
|
|
||||||
|
formulas = DamageField.formatFormulas.call(this, formulas, config);
|
||||||
|
|
||||||
messageId = config.message?._id ?? messageId;
|
messageId = config.message?._id ?? messageId;
|
||||||
const message = game.messages.get(messageId);
|
const message = game.messages.get(messageId);
|
||||||
const damageConfig = {
|
const damageConfig = {
|
||||||
dialog: {},
|
dialog: {},
|
||||||
...config,
|
...config,
|
||||||
damageFormula,
|
roll: formulas,
|
||||||
resourceFormulas,
|
|
||||||
data: this.getRollData(),
|
data: this.getRollData(),
|
||||||
isCritical: Boolean(message?.system.roll?.isCritical)
|
isCritical: Boolean(message?.system.roll?.isCritical)
|
||||||
};
|
};
|
||||||
|
|
@ -80,7 +92,7 @@ export default class DamageField extends fields.SchemaField {
|
||||||
|
|
||||||
const targetDamage = [];
|
const targetDamage = [];
|
||||||
const damagePromises = [];
|
const damagePromises = [];
|
||||||
for (const target of targets) {
|
for (let target of targets) {
|
||||||
const actor = foundry.utils.fromUuidSync(target.actorId);
|
const actor = foundry.utils.fromUuidSync(target.actorId);
|
||||||
if (!actor) continue;
|
if (!actor) continue;
|
||||||
if (!config.hasHealing && config.onSave && target.saved?.success === true) {
|
if (!config.hasHealing && config.onSave && target.saved?.success === true) {
|
||||||
|
|
@ -102,12 +114,13 @@ export default class DamageField extends fields.SchemaField {
|
||||||
actor.takeHealing(config.damage).then(updates => targetDamage.push({ token, updates }))
|
actor.takeHealing(config.damage).then(updates => targetDamage.push({ token, updates }))
|
||||||
);
|
);
|
||||||
else {
|
else {
|
||||||
const configDamage = config.damage.clone();
|
const configDamage = foundry.utils.deepClone(config.damage);
|
||||||
configDamage.main &&= configDamage.main.toJSON();
|
const hpDamageMultiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1;
|
||||||
if (configDamage.main) {
|
const hpDamageTakenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier;
|
||||||
const multiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1;
|
if (configDamage.hitPoints) {
|
||||||
const takenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier;
|
for (const part of configDamage.hitPoints.parts) {
|
||||||
configDamage.main.total = Math.ceil(configDamage.main.total * multiplier * takenMultiplier);
|
part.total = Math.ceil(part.total * hpDamageMultiplier * hpDamageTakenMultiplier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
damagePromises.push(
|
damagePromises.push(
|
||||||
|
|
@ -170,19 +183,13 @@ export default class DamageField extends fields.SchemaField {
|
||||||
/**
|
/**
|
||||||
* Prepare formulas for Damage Roll
|
* Prepare formulas for Damage Roll
|
||||||
* Must be called within Action context or similar.
|
* Must be called within Action context or similar.
|
||||||
* @param {DHResourceData[]} damageData Array of DHResourceData
|
* @param {object[]} formulas Array of formatted formulas object
|
||||||
* @param {object} data Action getRollData
|
* @param {object} data Action getRollData
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
static formatFormulas(damageData, data) {
|
static formatFormulas(formulas, data) {
|
||||||
const formulas = damageData.map(x => ({
|
|
||||||
formula: DamageField.getFormulaValue.call(this, x, data).getFormula(this.actor),
|
|
||||||
damageTypes: x.type ?? new Set(),
|
|
||||||
applyTo: x.applyTo
|
|
||||||
}));
|
|
||||||
|
|
||||||
const formattedFormulas = [];
|
const formattedFormulas = [];
|
||||||
for (const formula of formulas) {
|
formulas.forEach(formula => {
|
||||||
if (isNaN(formula.formula))
|
if (isNaN(formula.formula))
|
||||||
formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(data));
|
formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(data));
|
||||||
const same = formattedFormulas.find(
|
const same = formattedFormulas.find(
|
||||||
|
|
@ -190,8 +197,7 @@ export default class DamageField extends fields.SchemaField {
|
||||||
);
|
);
|
||||||
if (same) same.formula += ` + ${formula.formula}`;
|
if (same) same.formula += ` + ${formula.formula}`;
|
||||||
else formattedFormulas.push(formula);
|
else formattedFormulas.push(formula);
|
||||||
}
|
});
|
||||||
|
|
||||||
return formattedFormulas;
|
return formattedFormulas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,18 +275,10 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string} the formula associated with this damage field
|
|
||||||
*/
|
|
||||||
getFormula() {
|
getFormula() {
|
||||||
if (this.custom.enabled) return this.custom.formula;
|
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`,
|
||||||
|
bonus = this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : '';
|
||||||
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`;
|
return this.custom.enabled ? this.custom.formula : `${multiplier ?? 1}${this.dice}${bonus}`;
|
||||||
if (!multiplier) return String(this.bonus || 0);
|
|
||||||
|
|
||||||
const dice = `${multiplier ?? 1}${this.dice}`;
|
|
||||||
const sign = this.bonus < 0 ? ' - ' : ' + ';
|
|
||||||
return this.bonus ? `${dice} ${sign} ${Math.abs(this.bonus)}` : dice;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,7 +286,6 @@ export class DHResourceData extends foundry.abstract.DataModel {
|
||||||
/** @override */
|
/** @override */
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
|
||||||
applyTo: new fields.StringField({
|
applyTo: new fields.StringField({
|
||||||
choices: CONFIG.DH.GENERAL.healingTypes,
|
choices: CONFIG.DH.GENERAL.healingTypes,
|
||||||
required: true,
|
required: true,
|
||||||
|
|
@ -311,16 +308,7 @@ export class DHDamageData extends DHResourceData {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
includeBase: new fields.BooleanField({
|
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
||||||
initial: false,
|
|
||||||
label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label'
|
|
||||||
}),
|
|
||||||
direct: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.CONFIG.DamageType.direct.name' }),
|
|
||||||
groupAttack: new fields.StringField({
|
|
||||||
choices: CONFIG.DH.GENERAL.groupAttackRange,
|
|
||||||
blank: true,
|
|
||||||
label: 'DAGGERHEART.ACTIONS.Settings.groupAttack.label'
|
|
||||||
}),
|
|
||||||
type: new fields.SetField(
|
type: new fields.SetField(
|
||||||
new fields.StringField({
|
new fields.StringField({
|
||||||
choices: CONFIG.DH.GENERAL.damageTypes,
|
choices: CONFIG.DH.GENERAL.damageTypes,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export default class DHSummonField extends fields.ArrayField {
|
||||||
super(summonFields, options, context);
|
super(summonFields, options, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async execute(config) {
|
static async execute() {
|
||||||
if (!canvas.scene) {
|
if (!canvas.scene) {
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.error'));
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.error'));
|
||||||
return;
|
return;
|
||||||
|
|
@ -36,7 +36,6 @@ export default class DHSummonField extends fields.ArrayField {
|
||||||
|
|
||||||
const rolls = [];
|
const rolls = [];
|
||||||
const summonData = [];
|
const summonData = [];
|
||||||
const chatMessageData = [];
|
|
||||||
for (const summon of this.summon) {
|
for (const summon of this.summon) {
|
||||||
const roll = new Roll(itemAbleRollParse(summon.count, this.actor, this.item));
|
const roll = new Roll(itemAbleRollParse(summon.count, this.actor, this.item));
|
||||||
await roll.evaluate();
|
await roll.evaluate();
|
||||||
|
|
@ -55,18 +54,17 @@ export default class DHSummonField extends fields.ArrayField {
|
||||||
tokenPreviewName: `${actor.prototypeToken.name}${remaining > 1 ? ` (${remaining}x)` : ''}`
|
tokenPreviewName: `${actor.prototypeToken.name}${remaining > 1 ? ` (${remaining}x)` : ''}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
chatMessageData.push({
|
|
||||||
data: actor,
|
|
||||||
quantity: countNumber
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rolls.length) await triggerChatRollFx(rolls);
|
if (rolls.length) await triggerChatRollFx(rolls);
|
||||||
|
|
||||||
this.actor.sheet?.minimize();
|
this.actor.sheet?.minimize();
|
||||||
await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: this.actor.token?.elevation });
|
DHSummonField.handleSummon(summonData, this.actor);
|
||||||
this.actor.sheet?.maximize();
|
}
|
||||||
config.summonData = chatMessageData;
|
|
||||||
|
static async handleSummon(summonData, actionActor) {
|
||||||
|
await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: actionActor.token?.elevation });
|
||||||
|
|
||||||
|
return actionActor.sheet?.maximize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,6 @@ export function ActionMixin(Base) {
|
||||||
result = this.parent;
|
result = this.parent;
|
||||||
} else {
|
} else {
|
||||||
result = await this.item.update({ [path]: updates }, options);
|
result = await this.item.update({ [path]: updates }, options);
|
||||||
if (!result) return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.inCollection
|
return this.inCollection
|
||||||
|
|
@ -270,7 +269,10 @@ export function ActionMixin(Base) {
|
||||||
return this.delete();
|
return this.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
async toChat(origin, config) {
|
async toChat(origin) {
|
||||||
|
const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
|
||||||
|
.expandRollMessage?.desc;
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
const systemData = {
|
const systemData = {
|
||||||
title: game.i18n.localize('DAGGERHEART.CONFIG.FeatureForm.action'),
|
title: game.i18n.localize('DAGGERHEART.CONFIG.FeatureForm.action'),
|
||||||
|
|
@ -280,7 +282,7 @@ export function ActionMixin(Base) {
|
||||||
img: this.baseAction ? this.parent.parent.img : this.img,
|
img: this.baseAction ? this.parent.parent.img : this.img,
|
||||||
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'],
|
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'],
|
||||||
areas: this.areas,
|
areas: this.areas,
|
||||||
summon: config?.summonData
|
summon: this.summon
|
||||||
},
|
},
|
||||||
source: {
|
source: {
|
||||||
actor: this.actor.uuid,
|
actor: this.actor.uuid,
|
||||||
|
|
@ -305,7 +307,7 @@ export function ActionMixin(Base) {
|
||||||
system: systemData,
|
system: systemData,
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/ui/chat/action.hbs',
|
'systems/daggerheart/templates/ui/chat/action.hbs',
|
||||||
systemData
|
{ ...systemData, open: autoExpandDescription ? 'open' : '' }
|
||||||
),
|
),
|
||||||
flags: {
|
flags: {
|
||||||
daggerheart: {
|
daggerheart: {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@ export default class DHArmor extends AttachableItem {
|
||||||
type: 'armor',
|
type: 'armor',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isInventoryItem: true,
|
isInventoryItem: true,
|
||||||
hasActions: true,
|
hasActions: true
|
||||||
hasResource: true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.metadata.hasDescription) {
|
if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||||
schema.description = new fields.HTMLField({ required: true, nullable: true });
|
|
||||||
schema.gmNotes = new fields.HTMLField({ required: true, nullable: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.metadata.hasResource) {
|
if (this.metadata.hasResource) {
|
||||||
schema.resource = new fields.SchemaField(
|
schema.resource = new fields.SchemaField(
|
||||||
|
|
@ -137,7 +134,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
/**
|
/**
|
||||||
* Augments the description for the item with type specific info to display. Implemented in applicable item subtypes.
|
* Augments the description for the item with type specific info to display. Implemented in applicable item subtypes.
|
||||||
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
|
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
|
||||||
* @returns {Promise<{ prefix: string | null; value: string | null; suffix: string | null }>}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
async getDescriptionData(_options) {
|
async getDescriptionData(_options) {
|
||||||
return { prefix: null, value: this.description, suffix: null };
|
return { prefix: null, value: this.description, suffix: null };
|
||||||
|
|
@ -146,26 +143,16 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
/**
|
/**
|
||||||
* Gets the enriched and augmented description for the item.
|
* Gets the enriched and augmented description for the item.
|
||||||
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
|
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
|
||||||
* @returns {Promise<string>}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
async getEnrichedDescription({ gmNotes = true } = {}) {
|
async getEnrichedDescription() {
|
||||||
if (!this.metadata.hasDescription) return '';
|
if (!this.metadata.hasDescription) return '';
|
||||||
|
|
||||||
const { prefix, value, suffix } = await this.getDescriptionData();
|
const { prefix, value, suffix } = await this.getDescriptionData();
|
||||||
let fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n<hr>\n');
|
const fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n<hr>\n');
|
||||||
if (this.gmNotes && gmNotes) {
|
|
||||||
const gmNotesElement = document.createElement('section');
|
|
||||||
gmNotesElement.classList.add('gm-notes-section');
|
|
||||||
gmNotesElement.dataset.visibility = 'gm';
|
|
||||||
const header = document.createElement('header');
|
|
||||||
header.classList.add('gm-notes');
|
|
||||||
header.textContent = _loc('DAGGERHEART.ITEMS.FIELDS.gmNotes.label');
|
|
||||||
gmNotesElement.innerHTML = header.outerHTML + this.gmNotes;
|
|
||||||
fullDescription += gmNotesElement.outerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await foundry.applications.ux.TextEditor.implementation.enrichHTML(fullDescription, {
|
return await foundry.applications.ux.TextEditor.implementation.enrichHTML(fullDescription, {
|
||||||
relativeTo: this.parent,
|
relativeTo: this,
|
||||||
rollData: this.getRollData(),
|
rollData: this.getRollData(),
|
||||||
secrets: this.parent.isOwner
|
secrets: this.parent.isOwner
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,13 @@ export default class DHWeapon extends AttachableItem {
|
||||||
type: 'attack'
|
type: 'attack'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
main: {
|
parts: {
|
||||||
type: ['physical'],
|
hitPoints: {
|
||||||
value: {
|
type: ['physical'],
|
||||||
multiplier: 'prof',
|
value: {
|
||||||
dice: 'd8'
|
multiplier: 'prof',
|
||||||
|
dice: 'd8'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,14 +118,6 @@ export default class DHWeapon extends AttachableItem {
|
||||||
return this.weaponFeatures;
|
return this.weaponFeatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasReload() {
|
|
||||||
return Boolean(this.weaponFeatures.find(x => x.value === 'reloading'));
|
|
||||||
}
|
|
||||||
|
|
||||||
get needsReload() {
|
|
||||||
return this.hasReload && this.resource.value === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async getDescriptionData() {
|
async getDescriptionData() {
|
||||||
const baseDescription = this.description;
|
const baseDescription = this.description;
|
||||||
|
|
@ -236,12 +230,11 @@ export default class DHWeapon extends AttachableItem {
|
||||||
game.i18n.localize(`DAGGERHEART.CONFIG.Burden.${burden}`)
|
game.i18n.localize(`DAGGERHEART.CONFIG.Burden.${burden}`)
|
||||||
];
|
];
|
||||||
|
|
||||||
if (attack.damage.main) {
|
for (const { value, type } of attack.damage.parts) {
|
||||||
const { value, type } = attack.damage.main;
|
|
||||||
const parts = value.custom.enabled ? [game.i18n.localize('DAGGERHEART.GENERAL.custom')] : [value.dice];
|
const parts = value.custom.enabled ? [game.i18n.localize('DAGGERHEART.GENERAL.custom')] : [value.dice];
|
||||||
if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString());
|
if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString());
|
||||||
|
|
||||||
if (type?.size) {
|
if (type.size > 0) {
|
||||||
const typeTags = Array.from(type)
|
const typeTags = Array.from(type)
|
||||||
.map(t => game.i18n.localize(`DAGGERHEART.CONFIG.DamageType.${t}.abbreviation`))
|
.map(t => game.i18n.localize(`DAGGERHEART.CONFIG.DamageType.${t}.abbreviation`))
|
||||||
.join(' | ');
|
.join(' | ');
|
||||||
|
|
@ -250,7 +243,7 @@ export default class DHWeapon extends AttachableItem {
|
||||||
|
|
||||||
tags.push(parts.join(''));
|
tags.push(parts.join(''));
|
||||||
}
|
}
|
||||||
|
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,10 +258,10 @@ export default class DHWeapon extends AttachableItem {
|
||||||
if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`));
|
if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`));
|
||||||
if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`));
|
if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`));
|
||||||
|
|
||||||
for (const { value, type } of [damage.main, ...damage.resources].filter(d => !!d)) {
|
for (const { value, type } of damage.parts) {
|
||||||
const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {});
|
const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {});
|
||||||
|
|
||||||
const icons = Array.from(type ?? [])
|
const icons = Array.from(type)
|
||||||
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
|
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,11 +41,6 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
||||||
choices: CONFIG.DH.GENERAL.fearDisplay,
|
choices: CONFIG.DH.GENERAL.fearDisplay,
|
||||||
initial: CONFIG.DH.GENERAL.fearDisplay.token.value
|
initial: CONFIG.DH.GENERAL.fearDisplay.token.value
|
||||||
}),
|
}),
|
||||||
fearPosition: new StringField({
|
|
||||||
required: true,
|
|
||||||
choices: CONFIG.DH.GENERAL.fearPosition,
|
|
||||||
initial: CONFIG.DH.GENERAL.fearPosition.topCenter.value
|
|
||||||
}),
|
|
||||||
displayCountdownUI: new BooleanField({ initial: true }),
|
displayCountdownUI: new BooleanField({ initial: true }),
|
||||||
diceSoNice: new SchemaField({
|
diceSoNice: new SchemaField({
|
||||||
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
|
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
|
||||||
|
|
|
||||||
|
|
@ -196,13 +196,6 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
reload: new fields.StringField({
|
|
||||||
required: true,
|
|
||||||
choices: CONFIG.DH.SETTINGS.reloadChoices,
|
|
||||||
initial: CONFIG.DH.SETTINGS.reloadChoices.manual.id,
|
|
||||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.reload.label',
|
|
||||||
hint: 'DAGGERHEART.SETTINGS.Automation.FIELDS.reload.hint'
|
|
||||||
}),
|
|
||||||
autoExpireActiveEffects: new fields.BooleanField({
|
autoExpireActiveEffects: new fields.BooleanField({
|
||||||
required: true,
|
required: true,
|
||||||
initial: true,
|
initial: true,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import { ChatDamageData } from './chat-message/chatDamageData.mjs';
|
|
||||||
|
|
||||||
export default class TagTeamData extends foundry.abstract.DataModel {
|
export default class TagTeamData extends foundry.abstract.DataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
@ -39,7 +37,6 @@ export class MemberData extends foundry.abstract.DataModel {
|
||||||
}),
|
}),
|
||||||
rollChoice: new fields.StringField({ nullable: true, initial: null }),
|
rollChoice: new fields.StringField({ nullable: true, initial: null }),
|
||||||
rollData: new fields.JSONField({ nullable: true, initial: null }),
|
rollData: new fields.JSONField({ nullable: true, initial: null }),
|
||||||
damageRollData: new fields.EmbeddedDataField(ChatDamageData),
|
|
||||||
selected: new fields.BooleanField({ initial: false })
|
selected: new fields.BooleanField({ initial: false })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,4 @@ export default class BaseRoll extends Roll {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static TOOLTIP_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRollTooltip.hbs';
|
static TOOLTIP_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRollTooltip.hbs';
|
||||||
|
|
||||||
get modifierTotal() {
|
|
||||||
return this.total - this.dice.reduce((acc, dice) => acc + dice.total, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -185,10 +185,8 @@ export default class D20Roll extends DHRoll {
|
||||||
return changeKeys;
|
return changeKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
static postEvaluate(roll, config = {}) {
|
||||||
await super.buildEvaluate(roll, config, message);
|
const data = super.postEvaluate(roll, config);
|
||||||
|
|
||||||
const data = config.roll;
|
|
||||||
data.type = config.actionType;
|
data.type = config.actionType;
|
||||||
data.difficulty = config.roll.difficulty;
|
data.difficulty = config.roll.difficulty;
|
||||||
if (config.targets?.length) {
|
if (config.targets?.length) {
|
||||||
|
|
@ -224,6 +222,7 @@ export default class D20Roll extends DHRoll {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
data.modifierTotal = roll.modifierTotal;
|
data.modifierTotal = roll.modifierTotal;
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetFormula() {
|
resetFormula() {
|
||||||
|
|
|
||||||
|
|
@ -13,34 +13,30 @@ export default class DamageRoll extends DHRoll {
|
||||||
|
|
||||||
static DefaultDialog = DamageDialog;
|
static DefaultDialog = DamageDialog;
|
||||||
|
|
||||||
static createRollInstance(config) {
|
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||||
return new this(undefined, config.data, config);
|
if (config.dialog.configure === false) roll.constructFormula(config);
|
||||||
}
|
if (config.evaluate !== false) for (const roll of config.roll) await roll.roll.evaluate();
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
static async buildEvaluate(roll, config = {}) {
|
|
||||||
if (config.dialog.configure === false) roll.constructFormulas(config);
|
|
||||||
|
|
||||||
const evaluateRoll = async roll => {
|
|
||||||
await roll.roll.evaluate();
|
|
||||||
roll.roll.options = { damageTypes: roll.damageTypes ? [...roll.damageTypes] : [] };
|
|
||||||
return roll.roll;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.damage) config.damage = { main: null, resources: {} };
|
|
||||||
|
|
||||||
if (config.damageFormula) {
|
|
||||||
config.damage.main = await evaluateRoll(config.damageFormula);
|
|
||||||
config.damage.main.options = { damageTypes:
|
|
||||||
config.damageFormula.damageTypes ? [...config.damageFormula.damageTypes] : []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const roll of config.resourceFormulas) {
|
|
||||||
config.damage.resources[roll.applyTo] = await evaluateRoll(roll);
|
|
||||||
}
|
|
||||||
|
|
||||||
roll._evaluated = true;
|
roll._evaluated = true;
|
||||||
|
|
||||||
|
const parts = [];
|
||||||
|
for (const roll of config.roll) {
|
||||||
|
parts.push(this.postEvaluate(roll));
|
||||||
|
roll.roll = JSON.stringify(roll.roll.toJSON());
|
||||||
|
}
|
||||||
|
|
||||||
|
config.damage = this.unifyDamageRoll(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static postEvaluate(roll, config = {}) {
|
||||||
|
return {
|
||||||
|
...roll,
|
||||||
|
...super.postEvaluate(roll.roll, config),
|
||||||
|
damageTypes: [...(roll.damageTypes ?? [])],
|
||||||
|
roll: roll.roll,
|
||||||
|
type: config.type,
|
||||||
|
modifierTotal: this.calculateTotalModifiers(roll.roll)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static async buildPost(roll, config, message) {
|
static async buildPost(roll, config, message) {
|
||||||
|
|
@ -51,10 +47,9 @@ export default class DamageRoll extends DHRoll {
|
||||||
const diceRolls = [];
|
const diceRolls = [];
|
||||||
if (game.modules.get('dice-so-nice')?.active) {
|
if (game.modules.get('dice-so-nice')?.active) {
|
||||||
config.mute = true;
|
config.mute = true;
|
||||||
const pool = foundry.dice.terms.PoolTerm.fromRolls([
|
const pool = foundry.dice.terms.PoolTerm.fromRolls(
|
||||||
...(config.damage.main ? [config.damage.main] : []),
|
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
|
||||||
...Object.values(config.damage.resources)
|
);
|
||||||
]);
|
|
||||||
diceRolls.push(Roll.fromTerms([pool]));
|
diceRolls.push(Roll.fromTerms([pool]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,14 +60,22 @@ export default class DamageRoll extends DHRoll {
|
||||||
await super.buildPost(roll, config, message);
|
await super.buildPost(roll, config, message);
|
||||||
|
|
||||||
if (config.source?.message) {
|
if (config.source?.message) {
|
||||||
chatMessage.update({ 'system.damage': {
|
chatMessage.update({ 'system.damage': config.damage });
|
||||||
...config.damage.toObject(),
|
|
||||||
main: config.damage.main,
|
|
||||||
resources: config.damage.resources
|
|
||||||
}});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unifyDamageRoll(rolls) {
|
||||||
|
const unified = {};
|
||||||
|
rolls.forEach(r => {
|
||||||
|
const resource = unified[r.applyTo] ?? { formula: '', total: 0, parts: [] };
|
||||||
|
resource.formula += `${resource.formula !== '' ? ' + ' : ''}${r.formula}`;
|
||||||
|
resource.total += r.total;
|
||||||
|
resource.parts.push(r);
|
||||||
|
unified[r.applyTo] = resource;
|
||||||
|
});
|
||||||
|
return unified;
|
||||||
|
}
|
||||||
|
|
||||||
static formatGlobal(rolls) {
|
static formatGlobal(rolls) {
|
||||||
let formula, total;
|
let formula, total;
|
||||||
const applyTo = new Set(rolls.flatMap(r => r.applyTo));
|
const applyTo = new Set(rolls.flatMap(r => r.applyTo));
|
||||||
|
|
@ -121,10 +124,12 @@ export default class DamageRoll extends DHRoll {
|
||||||
const type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage');
|
const type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage');
|
||||||
const changeKeys = [];
|
const changeKeys = [];
|
||||||
|
|
||||||
for (const damageType of this.options.damageFormula?.damageTypes?.values?.() ?? []) {
|
for (const roll of this.options.roll) {
|
||||||
changeKeys.push(`system.bonuses.${type}.${damageType}`);
|
for (const damageType of roll.damageTypes?.values?.() ?? []) {
|
||||||
|
changeKeys.push(`system.bonuses.${type}.${damageType}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = this.data.parent?.items?.get(this.options.source.item);
|
const item = this.data.parent?.items?.get(this.options.source.item);
|
||||||
if (item) {
|
if (item) {
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
|
|
@ -140,69 +145,62 @@ export default class DamageRoll extends DHRoll {
|
||||||
return changeKeys;
|
return changeKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructFormulas(config) {
|
constructFormula(config) {
|
||||||
return {
|
|
||||||
damageFormula: this.constructFormula(this.options.damageFormula, config, true),
|
|
||||||
resourceFormulas: this.options.resourceFormulas.map(x => this.constructFormula(x, config))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructFormula(formulaData, config, isDamage) {
|
|
||||||
if (!formulaData) return null;
|
|
||||||
this.options.isCritical = config.isCritical;
|
this.options.isCritical = config.isCritical;
|
||||||
|
for (const [index, part] of this.options.roll.entries()) {
|
||||||
formulaData.roll = new Roll(Roll.replaceFormulaData(formulaData.formula, config.data));
|
const isHitpointPart = part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id;
|
||||||
formulaData.roll.terms = Roll.parse(formulaData.roll.formula, config.data);
|
part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data));
|
||||||
|
part.roll.terms = Roll.parse(part.roll.formula, config.data);
|
||||||
if (formulaData.extraFormula) {
|
if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||||
formulaData.roll.terms.push(
|
part.modifiers = this.applyBaseBonus(part);
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
this.addModifiers(part);
|
||||||
...this.constructor.parse(formulaData.extraFormula, this.options.data)
|
part.modifiers?.forEach(m => {
|
||||||
);
|
part.roll.terms.push(...this.formatModifier(m.value));
|
||||||
}
|
});
|
||||||
|
|
||||||
if (isDamage && formulaData.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
|
||||||
formulaData.modifiers = this.applyBaseBonus(formulaData);
|
|
||||||
this.addModifiers(formulaData);
|
|
||||||
formulaData.modifiers?.forEach(m => {
|
|
||||||
formulaData.roll.terms.push(...this.formatModifier(m.value));
|
|
||||||
});
|
|
||||||
|
|
||||||
/* To Remove When Reaction System */
|
|
||||||
for (const mod in config.modifiers) {
|
|
||||||
const modifier = config.modifiers[mod];
|
|
||||||
if (
|
|
||||||
modifier.beforeCrit === true &&
|
|
||||||
(modifier.enabled || modifier.value)
|
|
||||||
) modifier.callback(formulaData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To Remove When Reaction System */
|
/* To Remove When Reaction System */
|
||||||
for (const mod in config.modifiers) {
|
if (index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||||
const modifier = config.modifiers[mod];
|
for (const mod in config.modifiers) {
|
||||||
if (!modifier.beforeCrit && (modifier.enabled || modifier.value)) modifier.callback(formulaData);
|
const modifier = config.modifiers[mod];
|
||||||
|
if (modifier.beforeCrit === true && (modifier.enabled || modifier.value)) modifier.callback(part);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.damageOptions.groupAttack?.numAttackers > 1) {
|
if (part.extraFormula) {
|
||||||
|
part.roll.terms.push(
|
||||||
|
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||||
|
...this.constructor.parse(part.extraFormula, this.options.data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.damageOptions.groupAttack?.numAttackers > 1 && isHitpointPart) {
|
||||||
const damageTypes = [foundry.dice.terms.Die, foundry.dice.terms.NumericTerm];
|
const damageTypes = [foundry.dice.terms.Die, foundry.dice.terms.NumericTerm];
|
||||||
for (const term of formulaData.roll.terms) {
|
for (const term of part.roll.terms) {
|
||||||
if (damageTypes.some(type => term instanceof type)) {
|
if (damageTypes.some(type => term instanceof type)) {
|
||||||
term.number *= config.damageOptions.groupAttack.numAttackers;
|
term.number *= config.damageOptions.groupAttack.numAttackers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.isCritical) {
|
if (config.isCritical && isHitpointPart) {
|
||||||
const total = formulaData.roll.dice.reduce((acc, term) => acc + term._faces * term._number, 0);
|
const total = part.roll.dice.reduce((acc, term) => acc + term._faces * term._number, 0);
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
formulaData.roll.terms.push(...this.formatModifier(total));
|
part.roll.terms.push(...this.formatModifier(total));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
formulaData.roll._formula = this.constructor.getFormula(formulaData.roll.terms);
|
/* To Remove When Reaction System */
|
||||||
|
if (index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||||
return formulaData;
|
for (const mod in config.modifiers) {
|
||||||
|
const modifier = config.modifiers[mod];
|
||||||
|
if (!modifier.beforeCrit && (modifier.enabled || modifier.value)) modifier.callback(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
part.roll._formula = this.constructor.getFormula(part.roll.terms);
|
||||||
|
}
|
||||||
|
return this.options.roll;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To Remove When Reaction System */
|
/* To Remove When Reaction System */
|
||||||
|
|
@ -295,4 +293,76 @@ export default class DamageRoll extends DHRoll {
|
||||||
config.modifiers = mods;
|
config.modifiers = mods;
|
||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async reroll(rollPart, dice, result) {
|
||||||
|
let diceIndex = 0;
|
||||||
|
let parsedRoll = game.system.api.dice.DamageRoll.fromData({
|
||||||
|
...rollPart.roll,
|
||||||
|
terms: rollPart.roll.terms.map(term => {
|
||||||
|
const isDie = term.class === 'Die';
|
||||||
|
const fixedTerm = {
|
||||||
|
...term,
|
||||||
|
...(isDie ? { results: rollPart.dice[diceIndex].results } : {})
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isDie) diceIndex++;
|
||||||
|
return fixedTerm;
|
||||||
|
}),
|
||||||
|
class: 'DamageRoll',
|
||||||
|
evaluated: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const parsedDiceTerms = Object.keys(parsedRoll.terms).reduce((acc, key) => {
|
||||||
|
const term = parsedRoll.terms[key];
|
||||||
|
if (term instanceof CONFIG.Dice.termTypes.DiceTerm) acc[Object.keys(acc).length] = term;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
const term = parsedDiceTerms[dice];
|
||||||
|
const termResult = parsedDiceTerms[dice].results[result];
|
||||||
|
|
||||||
|
const newIndex = parsedDiceTerms[dice].results.length;
|
||||||
|
await term.reroll(`/r1=${termResult.result}`);
|
||||||
|
|
||||||
|
const diceRolls = [];
|
||||||
|
if (game.modules.get('dice-so-nice')?.active) {
|
||||||
|
const newResult = parsedDiceTerms[dice].results[newIndex];
|
||||||
|
diceRolls.push({
|
||||||
|
_evaluated: true,
|
||||||
|
dice: [
|
||||||
|
new foundry.dice.terms.Die({
|
||||||
|
...term,
|
||||||
|
total: newResult.result,
|
||||||
|
faces: term._faces,
|
||||||
|
results: [newResult]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
options: { appearance: {} }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await triggerChatRollFx(diceRolls);
|
||||||
|
await parsedRoll.evaluate();
|
||||||
|
|
||||||
|
const results = parsedRoll.dice[dice].results.map(result => ({
|
||||||
|
...result,
|
||||||
|
discarded: !result.active
|
||||||
|
}));
|
||||||
|
const newResult = results.splice(results.length - 1, 1);
|
||||||
|
results.splice(Number(result) + 1, 0, newResult[0]);
|
||||||
|
|
||||||
|
const rerolledDice = parsedRoll.dice.map((x, index) => {
|
||||||
|
const isRerollDice = index === Number(dice);
|
||||||
|
if (!isRerollDice) return { ...x, dice: x.denomination };
|
||||||
|
return {
|
||||||
|
dice: parsedRoll.dice[dice].denomination,
|
||||||
|
total: parsedRoll.dice[dice].total,
|
||||||
|
results: results.map(result => ({
|
||||||
|
...result,
|
||||||
|
hasRerolls: result.hasRerolls || isRerollDice
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return { parsedRoll, rerolledDice };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||||
import { triggerChatRollFx } from '../helpers/utils.mjs';
|
import { triggerChatRollFx } from '../helpers/utils.mjs';
|
||||||
import BaseRoll from './baseRoll.mjs';
|
|
||||||
|
|
||||||
export default class DHRoll extends BaseRoll {
|
export default class DHRoll extends Roll {
|
||||||
baseTerms = [];
|
baseTerms = [];
|
||||||
constructor(formula, data = {}, options = {}) {
|
constructor(formula, data = {}, options = {}) {
|
||||||
super(formula, data, foundry.utils.mergeObject(options, { roll: [] }, { overwrite: false }));
|
super(formula, data, foundry.utils.mergeObject(options, { roll: [] }, { overwrite: false }));
|
||||||
|
|
@ -34,17 +33,11 @@ export default class DHRoll extends BaseRoll {
|
||||||
|
|
||||||
if (config.skips?.createMessage) config.messageRoll = roll;
|
if (config.skips?.createMessage) config.messageRoll = roll;
|
||||||
|
|
||||||
if (config.evaluate !== false) {
|
await this.buildEvaluate(roll, config, (message = {}));
|
||||||
await this.buildEvaluate(roll, config, (message = {}));
|
|
||||||
}
|
|
||||||
await this.buildPost(roll, config, (message = {}));
|
await this.buildPost(roll, config, (message = {}));
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createRollInstance(config) {
|
|
||||||
return new this(config.roll.formula, config.data, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Partial<RollConfig>} config
|
* @param {Partial<RollConfig>} config
|
||||||
* @returns {Promise<RollConfig>}
|
* @returns {Promise<RollConfig>}
|
||||||
|
|
@ -62,7 +55,7 @@ export default class DHRoll extends BaseRoll {
|
||||||
|
|
||||||
this.temporaryModifierBuilder(config);
|
this.temporaryModifierBuilder(config);
|
||||||
|
|
||||||
let roll = this.createRollInstance(config);
|
let roll = new this(config.roll.formula, config.data, config);
|
||||||
if (config.dialog.configure !== false) {
|
if (config.dialog.configure !== false) {
|
||||||
// Open Roll Dialog
|
// Open Roll Dialog
|
||||||
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
||||||
|
|
@ -79,30 +72,13 @@ export default class DHRoll extends BaseRoll {
|
||||||
return roll;
|
return roll;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Evaluates the roll and assigns roll data into the config.
|
|
||||||
* This is only called if config.evaluate is not set to false
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||||
await roll.evaluate();
|
if (config.evaluate !== false) {
|
||||||
config.roll = {
|
await roll.evaluate();
|
||||||
...roll.options.roll,
|
config.roll = this.postEvaluate(roll, config);
|
||||||
total: roll.total,
|
}
|
||||||
formula: roll.formula,
|
|
||||||
dice: roll.dice.map(d => ({
|
|
||||||
dice: d.denomination,
|
|
||||||
total: d.total,
|
|
||||||
formula: d.formula,
|
|
||||||
results: d.results
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs any post configuration events that need to happen towards the end, such as hooks and dice so nice
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
static async buildPost(roll, config, message) {
|
static async buildPost(roll, config, message) {
|
||||||
for (const hook of config.hooks) {
|
for (const hook of config.hooks) {
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.postRoll${hook.capitalize()}`, config, message) === false) return null;
|
if (Hooks.call(`${CONFIG.DH.id}.postRoll${hook.capitalize()}`, config, message) === false) return null;
|
||||||
|
|
@ -115,13 +91,23 @@ export default class DHRoll extends BaseRoll {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static postEvaluate(roll, config = {}) {
|
||||||
|
return {
|
||||||
|
...roll.options.roll,
|
||||||
|
total: roll.total,
|
||||||
|
formula: roll.formula,
|
||||||
|
dice: roll.dice.map(d => ({
|
||||||
|
dice: d.denomination,
|
||||||
|
total: d.total,
|
||||||
|
formula: d.formula,
|
||||||
|
results: d.results
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static async toMessage(roll, config) {
|
static async toMessage(roll, config) {
|
||||||
const item = config.data.parent?.items?.get?.(config.source.item) ?? null;
|
const item = config.data.parent?.items?.get?.(config.source.item) ?? null;
|
||||||
const actions = item ? [
|
const action = item ? item.system.actions.get(config.source.action) : null;
|
||||||
...item.system.actions,
|
|
||||||
...(item.system.attack?.id === config.source.action ? [item.system.attack] : [])
|
|
||||||
] : [];
|
|
||||||
const action = actions.find(x => x.id === config.source.action);
|
|
||||||
let actionDescription = null;
|
let actionDescription = null;
|
||||||
if (action?.chatDisplay) {
|
if (action?.chatDisplay) {
|
||||||
actionDescription = action
|
actionDescription = action
|
||||||
|
|
@ -133,14 +119,6 @@ export default class DHRoll extends BaseRoll {
|
||||||
config.actionChatMessageHandled = true;
|
config.actionChatMessageHandled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reloadSetting =
|
|
||||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).reload;
|
|
||||||
const useReload =
|
|
||||||
item?.system.hasReload &&
|
|
||||||
action?.type === 'attack' &&
|
|
||||||
reloadSetting === CONFIG.DH.SETTINGS.reloadChoices.auto.id;
|
|
||||||
const reloadResult = useReload ? await action?.handleReload?.() : {};
|
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage'),
|
const cls = getDocumentClass('ChatMessage'),
|
||||||
msgData = {
|
msgData = {
|
||||||
type: this.messageType,
|
type: this.messageType,
|
||||||
|
|
@ -148,11 +126,7 @@ export default class DHRoll extends BaseRoll {
|
||||||
title: roll.title,
|
title: roll.title,
|
||||||
speaker: cls.getSpeaker({ actor: roll.data?.parent }),
|
speaker: cls.getSpeaker({ actor: roll.data?.parent }),
|
||||||
sound: config.mute ? null : CONFIG.sounds.dice,
|
sound: config.mute ? null : CONFIG.sounds.dice,
|
||||||
system: {
|
system: { ...config, actionDescription },
|
||||||
...config,
|
|
||||||
actionDescription,
|
|
||||||
reloadCheckValue: reloadResult.rollValue
|
|
||||||
},
|
|
||||||
rolls: [roll]
|
rolls: [roll]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -174,17 +148,14 @@ export default class DHRoll extends BaseRoll {
|
||||||
if (!this._evaluated) return;
|
if (!this._evaluated) return;
|
||||||
|
|
||||||
const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
||||||
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
|
||||||
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
|
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
|
||||||
return foundry.applications.handlebars.renderTemplate(template, {
|
return foundry.applications.handlebars.renderTemplate(template, {
|
||||||
roll: this,
|
roll: this,
|
||||||
...chatData,
|
...chatData,
|
||||||
action: chatData.action,
|
|
||||||
parent: chatData.parent,
|
parent: chatData.parent,
|
||||||
targetMode: chatData.targetMode,
|
targetMode: chatData.targetMode,
|
||||||
areas: chatData.action?.areas,
|
areas: chatData.action?.areas,
|
||||||
metagamingSettings,
|
metagamingSettings
|
||||||
automationSettings
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import HopeDie from './hopeDie.mjs';
|
||||||
import FearDie from './fearDie.mjs';
|
import FearDie from './fearDie.mjs';
|
||||||
import AdvantageDie from './advantageDie.mjs';
|
import AdvantageDie from './advantageDie.mjs';
|
||||||
import DisadvantageDie from './disadvantageDie.mjs';
|
import DisadvantageDie from './disadvantageDie.mjs';
|
||||||
export { default as BaseDie } from './baseDie.mjs';
|
|
||||||
|
|
||||||
export const diceTypes = {
|
export const diceTypes = {
|
||||||
DualityDie,
|
DualityDie,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import BaseDie from './baseDie.mjs';
|
export default class AdvantageDie extends foundry.dice.terms.Die {
|
||||||
|
|
||||||
export default class AdvantageDie extends BaseDie {
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
export default class BaseDie extends foundry.dice.terms.Die {
|
|
||||||
async rerollResult(resultIndex) {
|
|
||||||
const result = this.results[resultIndex];
|
|
||||||
result.rerolled = true;
|
|
||||||
result.active = false;
|
|
||||||
await this.roll({ reroll: true });
|
|
||||||
|
|
||||||
const rerolledResult = this.results[this.results.length - 1];
|
|
||||||
this.results.splice(this.results.length - 1, 1);
|
|
||||||
this.results.splice(resultIndex, 0, rerolledResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import BaseDie from './baseDie.mjs';
|
export default class DisadvantageDie extends foundry.dice.terms.Die {
|
||||||
|
|
||||||
export default class DisadvantageDie extends BaseDie {
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import BaseDie from './baseDie.mjs';
|
|
||||||
import { updateResourcesForDualityReroll } from '../helpers.mjs';
|
import { updateResourcesForDualityReroll } from '../helpers.mjs';
|
||||||
|
|
||||||
export default class DualityDie extends BaseDie {
|
export default class DualityDie extends foundry.dice.terms.Die {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -232,41 +232,9 @@ export default class DualityRoll extends D20Roll {
|
||||||
return changeKeys;
|
return changeKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||||
await super.buildEvaluate(roll, config, message);
|
await super.buildEvaluate(roll, config, message);
|
||||||
config.roll.hope = {
|
|
||||||
dice: roll.dHope.denomination,
|
|
||||||
value: this.guaranteedCritical ? 0 : roll.dHope.total,
|
|
||||||
rerolled: {
|
|
||||||
any: roll.dHope.results.some(x => x.rerolled),
|
|
||||||
rerolls: roll.dHope.results.filter(x => x.rerolled)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
config.roll.fear = {
|
|
||||||
dice: roll.dFear.denomination,
|
|
||||||
value: this.guaranteedCritical ? 0 : roll.dFear.total,
|
|
||||||
rerolled: {
|
|
||||||
any: roll.dFear.results.some(x => x.rerolled),
|
|
||||||
rerolls: roll.dFear.results.filter(x => x.rerolled)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
config.roll.rally = {
|
|
||||||
dice: roll.dRally?.denomination,
|
|
||||||
value: roll.dRally?.total
|
|
||||||
};
|
|
||||||
config.roll.result = {
|
|
||||||
duality: roll.withHope ? 1 : roll.withFear ? -1 : 0,
|
|
||||||
total: this.guaranteedCritical ? 0 : roll.dHope.total + roll.dFear.total,
|
|
||||||
label: roll.totalLabel
|
|
||||||
};
|
|
||||||
|
|
||||||
if (roll._rallyIndex && roll.data?.parent)
|
|
||||||
roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
static async buildPost(roll, config, message) {
|
|
||||||
await setDiceSoNiceForDualityRoll(
|
await setDiceSoNiceForDualityRoll(
|
||||||
roll,
|
roll,
|
||||||
config.roll.advantage.type,
|
config.roll.advantage.type,
|
||||||
|
|
@ -274,7 +242,44 @@ export default class DualityRoll extends D20Roll {
|
||||||
config.roll.fear.dice,
|
config.roll.fear.dice,
|
||||||
config.roll.advantage.dice
|
config.roll.advantage.dice
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static postEvaluate(roll, config = {}) {
|
||||||
|
const data = super.postEvaluate(roll, config);
|
||||||
|
|
||||||
|
data.hope = {
|
||||||
|
dice: roll.dHope.denomination,
|
||||||
|
value: this.guaranteedCritical ? 0 : roll.dHope.total,
|
||||||
|
rerolled: {
|
||||||
|
any: roll.dHope.results.some(x => x.rerolled),
|
||||||
|
rerolls: roll.dHope.results.filter(x => x.rerolled)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
data.fear = {
|
||||||
|
dice: roll.dFear.denomination,
|
||||||
|
value: this.guaranteedCritical ? 0 : roll.dFear.total,
|
||||||
|
rerolled: {
|
||||||
|
any: roll.dFear.results.some(x => x.rerolled),
|
||||||
|
rerolls: roll.dFear.results.filter(x => x.rerolled)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
data.rally = {
|
||||||
|
dice: roll.dRally?.denomination,
|
||||||
|
value: roll.dRally?.total
|
||||||
|
};
|
||||||
|
data.result = {
|
||||||
|
duality: roll.withHope ? 1 : roll.withFear ? -1 : 0,
|
||||||
|
total: this.guaranteedCritical ? 0 : roll.dHope.total + roll.dFear.total,
|
||||||
|
label: roll.totalLabel
|
||||||
|
};
|
||||||
|
|
||||||
|
if (roll._rallyIndex && roll.data?.parent)
|
||||||
|
roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async buildPost(roll, config, message) {
|
||||||
await super.buildPost(roll, config, message);
|
await super.buildPost(roll, config, message);
|
||||||
|
|
||||||
await DualityRoll.dualityUpdate(config);
|
await DualityRoll.dualityUpdate(config);
|
||||||
|
|
@ -409,4 +414,8 @@ export default class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
return rerolled;
|
return rerolled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fromJSON(json) {
|
||||||
|
return super.fromJSON(json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,23 +75,25 @@ export default class FateRoll extends D20Roll {
|
||||||
this.terms[0] = new foundry.dice.terms.Die({ faces: 12 });
|
this.terms[0] = new foundry.dice.terms.Die({ faces: 12 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||||
await super.buildEvaluate(roll, config, message);
|
await super.buildEvaluate(roll, config, message);
|
||||||
config.roll.fate = {
|
|
||||||
dice: roll.fateDie === 'Hope' ? roll.dHope.denomination : roll.dFear.denomination,
|
|
||||||
value: roll.fateDie === 'Hope' ? roll.dHope.total : roll.dFear.total,
|
|
||||||
fateDie: roll.fateDie
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
static async buildPost(roll, config, message) {
|
|
||||||
if (roll.fateDie === 'Hope') {
|
if (roll.fateDie === 'Hope') {
|
||||||
await setDiceSoNiceForHopeFateRoll(roll, config.roll.fate.dice);
|
await setDiceSoNiceForHopeFateRoll(roll, config.roll.fate.dice);
|
||||||
} else {
|
} else {
|
||||||
await setDiceSoNiceForFearFateRoll(roll, config.roll.fate.dice);
|
await setDiceSoNiceForFearFateRoll(roll, config.roll.fate.dice);
|
||||||
}
|
}
|
||||||
return super.buildPost(roll, config, message);
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -622,63 +622,53 @@ export default class DhpActor extends Actor {
|
||||||
return rollData;
|
return rollData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#canReduceDamage(hpDamage, type) {
|
||||||
* Checks to see if damage can be reduced in one way or another.
|
const { stressDamageReduction, disabledArmor } = this.system.rules.damageReduction;
|
||||||
* @param {number} hpDamage the amount of marked hp that will be marked
|
|
||||||
* @param {Set<string>} types a list of damage types
|
|
||||||
*/
|
|
||||||
#canReduceDamage(hpDamage, types) {
|
|
||||||
const { stressDamageReduction, disabledArmor, reduceSeverity, thresholdImmunities } =
|
|
||||||
this.system.rules.damageReduction;
|
|
||||||
if (disabledArmor) return false;
|
if (disabledArmor) return false;
|
||||||
|
|
||||||
const availableStress = this.system.resources.stress.max - this.system.resources.stress.value;
|
const availableStress = this.system.resources.stress.max - this.system.resources.stress.value;
|
||||||
|
|
||||||
const canUseArmor =
|
const canUseArmor =
|
||||||
this.system.armorScore.value < this.system.armorScore.max &&
|
this.system.armorScore.value < this.system.armorScore.max &&
|
||||||
types.every(t => this.system.armorApplicableDamageTypes[t] === true);
|
type.every(t => this.system.armorApplicableDamageTypes[t] === true);
|
||||||
|
|
||||||
const canUseStress = Object.keys(stressDamageReduction).reduce((acc, x) => {
|
const canUseStress = Object.keys(stressDamageReduction).reduce((acc, x) => {
|
||||||
const rule = stressDamageReduction[x];
|
const rule = stressDamageReduction[x];
|
||||||
if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost);
|
if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost);
|
||||||
return acc;
|
return acc;
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
const hasReduceSeverity = types.some(t => reduceSeverity[t]);
|
return canUseArmor || canUseStress;
|
||||||
|
|
||||||
const hasThresholdImmunity = Object.entries(thresholdImmunities)
|
|
||||||
.filter(([key, value]) => Boolean(value) && damageKeyToNumber(key) === hpDamage)
|
|
||||||
.length;
|
|
||||||
|
|
||||||
return canUseArmor || canUseStress || hasReduceSeverity || hasThresholdImmunity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async takeDamage(args, isDirect = false) {
|
async takeDamage(damages, isDirect = false) {
|
||||||
args = this.#parseDamageArgs(args);
|
if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, damages) === false) return null;
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, args) === false) return null;
|
|
||||||
|
|
||||||
if (this.type === 'companion') {
|
if (this.type === 'companion') {
|
||||||
await this.modifyResource([{ value: 1, key: 'stress' }]);
|
await this.modifyResource([{ value: 1, key: 'stress' }]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updates = args.resourceUpdates;
|
const updates = [];
|
||||||
if (args.main) {
|
|
||||||
// todo: avoid side effects, but hook currently requires it
|
|
||||||
args.main.value = this.calculateDamage(args.main.value, args.main.damageTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, args) === false) return null;
|
Object.entries(damages).forEach(([key, damage]) => {
|
||||||
|
damage.parts.forEach(part => {
|
||||||
|
if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id)
|
||||||
|
part.total = this.calculateDamage(part.total, part.damageTypes);
|
||||||
|
const update = updates.find(u => u.key === key);
|
||||||
|
if (update) {
|
||||||
|
update.value += part.total;
|
||||||
|
update.damageTypes.add(...new Set(part.damageTypes));
|
||||||
|
} else updates.push({ value: part.total, key, damageTypes: new Set(part.damageTypes) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Convert deducted resources to a record of updates. Return if nothing to do.
|
if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, damages) === false) return null;
|
||||||
if (!updates.some(u => u.value) && !args.main) return;
|
|
||||||
|
|
||||||
if (args.main) {
|
if (!updates.length) return;
|
||||||
const hpDamage = {
|
|
||||||
value: this.convertDamageToThreshold(args.main.value),
|
const hpDamage = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
||||||
damageTypes: new Set(args.main.damageTypes),
|
if (hpDamage?.value) {
|
||||||
key: CONFIG.DH.GENERAL.healingTypes.hitPoints.id
|
hpDamage.value = this.convertDamageToThreshold(hpDamage.value);
|
||||||
};
|
|
||||||
if (this.type === 'character' && !isDirect && this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)) {
|
if (this.type === 'character' && !isDirect && this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)) {
|
||||||
const armorSlotResult = await this.owner.query(
|
const armorSlotResult = await this.owner.query(
|
||||||
'armorSlot',
|
'armorSlot',
|
||||||
|
|
@ -693,7 +683,7 @@ export default class DhpActor extends Actor {
|
||||||
);
|
);
|
||||||
if (armorSlotResult) {
|
if (armorSlotResult) {
|
||||||
const { modifiedDamage, armorChanges, stressSpent } = armorSlotResult;
|
const { modifiedDamage, armorChanges, stressSpent } = armorSlotResult;
|
||||||
hpDamage.value = modifiedDamage;
|
updates.find(u => u.key === 'hitPoints').value = modifiedDamage;
|
||||||
for (const armorChange of armorChanges) {
|
for (const armorChange of armorChanges) {
|
||||||
updates.push({ value: armorChange.amount, key: 'armor', uuid: armorChange.uuid });
|
updates.push({ value: armorChange.amount, key: 'armor', uuid: armorChange.uuid });
|
||||||
}
|
}
|
||||||
|
|
@ -703,23 +693,19 @@ export default class DhpActor extends Actor {
|
||||||
else updates.push({ value: stressSpent, key: 'stress' });
|
else updates.push({ value: stressSpent, key: 'stress' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.type === 'adversary') {
|
}
|
||||||
|
if (this.type === 'adversary') {
|
||||||
const reducedSeverity = hpDamage.damageTypes.reduce((value, curr) => {
|
const reducedSeverity = hpDamage.damageTypes.reduce((value, curr) => {
|
||||||
return Math.max(this.system.rules.damageReduction.reduceSeverity[curr], value);
|
return Math.max(this.system.rules.damageReduction.reduceSeverity[curr], value);
|
||||||
}, 0);
|
}, 0);
|
||||||
hpDamage.value = Math.max(hpDamage.value - reducedSeverity, 0);
|
hpDamage.value = Math.max(hpDamage.value - reducedSeverity, 0);
|
||||||
if (this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)]) {
|
|
||||||
hpDamage.value = Math.max(0, hpDamage.value - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge existing hitPoint deduction with finalised damage deduction
|
if (
|
||||||
const existing = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
hpDamage.value &&
|
||||||
if (existing) {
|
this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)]
|
||||||
existing.value += hpDamage.value;
|
) {
|
||||||
existing.damageTypes = hpDamage.damageTypes;
|
hpDamage.value -= 1;
|
||||||
} else {
|
}
|
||||||
updates.push(hpDamage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -735,10 +721,12 @@ export default class DhpActor extends Actor {
|
||||||
for (var result of results) resourceMap.addResources(result.updates);
|
for (var result of results) resourceMap.addResources(result.updates);
|
||||||
resourceMap.updateResources();
|
resourceMap.updateResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const u of updates) {
|
updates.forEach(
|
||||||
u.value = u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value;
|
u =>
|
||||||
}
|
(u.value =
|
||||||
|
u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value)
|
||||||
|
);
|
||||||
|
|
||||||
await this.modifyResource(updates);
|
await this.modifyResource(updates);
|
||||||
|
|
||||||
|
|
@ -747,36 +735,6 @@ export default class DhpActor extends Actor {
|
||||||
return updates;
|
return updates;
|
||||||
}
|
}
|
||||||
|
|
||||||
async takeHealing(args) {
|
|
||||||
args = this.#parseDamageArgs({ resources: 'resources' in args ? args.resources : args });
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, args) === false) return null;
|
|
||||||
|
|
||||||
const updates = args.resourceUpdates;
|
|
||||||
for (const u of updates) {
|
|
||||||
const shouldFlip = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false);
|
|
||||||
u.value = shouldFlip ? u.value * -1 : u.value;
|
|
||||||
}
|
|
||||||
await this.modifyResource(updates);
|
|
||||||
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null;
|
|
||||||
|
|
||||||
return updates;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Parse damage args that may be coming from takeHealing or takeDamage. Used to simplify macro usage and roll vs non-roll usage */
|
|
||||||
#parseDamageArgs(args = {}) {
|
|
||||||
const damageRoll = 'total' in args ? args : (args.main ?? args.damage);
|
|
||||||
const damageValue = typeof damageRoll === 'number' ? damageRoll : damageRoll?.total;
|
|
||||||
const damageTypes = Array.from(damageRoll?.options?.damageTypes ?? damageRoll?.damageTypes ?? []);
|
|
||||||
const main = typeof damageValue === 'number' ? { key: 'damage', value: damageValue, damageTypes } : null;
|
|
||||||
const resourceUpdates = Object.entries(args.resources ?? {}).map(([key, damage]) => ({
|
|
||||||
key,
|
|
||||||
value: typeof damage === 'number' ? damage : damage?.total ?? 0
|
|
||||||
}));
|
|
||||||
|
|
||||||
return { main, resourceUpdates };
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateDamage(baseDamage, type) {
|
calculateDamage(baseDamage, type) {
|
||||||
if (this.canResist(type, 'immunity')) return 0;
|
if (this.canResist(type, 'immunity')) return 0;
|
||||||
if (this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
|
if (this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
|
||||||
|
|
@ -801,6 +759,32 @@ export default class DhpActor extends Actor {
|
||||||
return reduction === Infinity ? 0 : reduction;
|
return reduction === Infinity ? 0 : reduction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async takeHealing(healings) {
|
||||||
|
if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, healings) === false) return null;
|
||||||
|
|
||||||
|
const updates = [];
|
||||||
|
Object.entries(healings).forEach(([key, healing]) => {
|
||||||
|
healing.parts.forEach(part => {
|
||||||
|
const update = updates.find(u => u.key === key);
|
||||||
|
if (update) update.value += part.total;
|
||||||
|
else updates.push({ value: part.total, key });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
updates.forEach(
|
||||||
|
u =>
|
||||||
|
(u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false)
|
||||||
|
? u.value * -1
|
||||||
|
: u.value)
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.modifyResource(updates);
|
||||||
|
|
||||||
|
if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null;
|
||||||
|
|
||||||
|
return updates;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resources are modified asynchronously, so be careful not to update the same resource in
|
* Resources are modified asynchronously, so be careful not to update the same resource in
|
||||||
* quick succession.
|
* quick succession.
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,6 @@ import { emitGMUpdate, emitGMCreate, GMUpdateEvent } from '../systemRegistration
|
||||||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
targetHook = null;
|
targetHook = null;
|
||||||
|
|
||||||
static #EXPAND_SECTIONS = [
|
|
||||||
{ selector: 'roll-section [data-action="expandRoll"]', key: 'roll' },
|
|
||||||
{ selector: 'damage-section', key: 'damage' },
|
|
||||||
{ selector: 'target-section', key: 'target' },
|
|
||||||
{ selector: 'description-section', key: 'desc' }
|
|
||||||
];
|
|
||||||
|
|
||||||
async renderHTML() {
|
async renderHTML() {
|
||||||
const actor = game.actors.get(this.speaker.actor);
|
const actor = game.actors.get(this.speaker.actor);
|
||||||
const actorData =
|
const actorData =
|
||||||
|
|
@ -96,26 +89,23 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check registered selectors and the main item section for expanding
|
|
||||||
// Preserving during re-render is handled by core foundry on anything with [data-action=expandRoll]
|
|
||||||
const autoExpandRoll = game.settings.get(
|
const autoExpandRoll = game.settings.get(
|
||||||
CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance
|
CONFIG.DH.id,
|
||||||
).expandRollMessage;
|
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||||
for (const { selector, key } of DhpChatMessage.#EXPAND_SECTIONS) {
|
).expandRollMessage,
|
||||||
const elements = html.querySelectorAll(selector);
|
rollSections = html.querySelectorAll('.roll-part'),
|
||||||
for (const element of elements) {
|
itemDesc = html.querySelector('.domain-card-move');
|
||||||
element.classList.toggle('expanded', autoExpandRoll[key]);
|
rollSections.forEach(s => {
|
||||||
}
|
if (s.classList.contains('roll-section')) {
|
||||||
}
|
const toExpand = s.querySelector('[data-action="expandRoll"]');
|
||||||
|
toExpand.classList.toggle('expanded', autoExpandRoll.roll);
|
||||||
// Auto expand the item description. These are not preserved by foundry during re-renders
|
} else if (s.classList.contains('damage-section'))
|
||||||
const itemDesc = html.querySelector('details');
|
s.classList.toggle('expanded', autoExpandRoll.damage);
|
||||||
if (itemDesc) {
|
else if (s.classList.contains('target-section')) s.classList.toggle('expanded', autoExpandRoll.target);
|
||||||
const existing = document.querySelector(`.chat-message[data-message-id="${this.id}"] details`);
|
else if (s.classList.contains('description-section'))
|
||||||
if (existing?.hasAttribute('open') ?? autoExpandRoll.desc) {
|
s.classList.toggle('expanded', autoExpandRoll.desc);
|
||||||
itemDesc.setAttribute('open', '');
|
});
|
||||||
}
|
if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', '');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isAuthor && !this.speakerActor?.isOwner) {
|
if (!this.isAuthor && !this.speakerActor?.isOwner) {
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,10 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
tags: this._getTags()
|
tags: this._getTags()
|
||||||
},
|
},
|
||||||
actions: item.system.actionsList,
|
actions: item.system.actionsList,
|
||||||
description: await this.system.getEnrichedDescription()
|
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.system.description, {
|
||||||
|
relativeTo: this.parent,
|
||||||
|
rollData: this.parent?.getRollData() ?? {}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const msg = {
|
const msg = {
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ export default class DhTokenManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const placedData = await canvas.tokens.placeTokens(
|
return await canvas.tokens.placeTokens(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
...(await actor.getTokenDocument()).toObject(),
|
...actor.prototypeToken.toObject(),
|
||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
displayName: 50,
|
displayName: 50,
|
||||||
...tokenData
|
...tokenData
|
||||||
|
|
@ -30,8 +30,6 @@ export default class DhTokenManager {
|
||||||
],
|
],
|
||||||
{ create: false }
|
{ create: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
return placedData?.[0] ?? null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -48,24 +46,22 @@ export default class DhTokenManager {
|
||||||
|
|
||||||
const createElevation = elevation ?? level.elevation.bottom;
|
const createElevation = elevation ?? level.elevation.bottom;
|
||||||
for (const tokenData of tokensData) {
|
for (const tokenData of tokensData) {
|
||||||
const previewToken = await this.createPreview(tokenData.actor, {
|
const previewTokens = await this.createPreview(tokenData.actor, {
|
||||||
name: tokenData.tokenPreviewName,
|
name: tokenData.tokenPreviewName,
|
||||||
level: game.user.viewedLevel,
|
level: game.user.viewedLevel,
|
||||||
elevation: createElevation,
|
elevation: createElevation,
|
||||||
flags: { daggerheart: { createPlacement: true } }
|
flags: { daggerheart: { createPlacement: true } }
|
||||||
});
|
});
|
||||||
if (!previewToken) return null;
|
if (!previewTokens?.length) return null;
|
||||||
|
|
||||||
const finalTokenData = {
|
|
||||||
...previewToken.toObject(),
|
|
||||||
name: tokenData.actor.prototypeToken.name,
|
|
||||||
displayName: tokenData.actor.prototypeToken.displayName,
|
|
||||||
flags: tokenData.actor.prototypeToken.flags
|
|
||||||
};
|
|
||||||
|
|
||||||
await canvas.scene.createEmbeddedDocuments(
|
await canvas.scene.createEmbeddedDocuments(
|
||||||
'Token',
|
'Token',
|
||||||
[finalTokenData],
|
previewTokens.map(x => ({
|
||||||
|
...x.toObject(),
|
||||||
|
name: tokenData.actor.prototypeToken.name,
|
||||||
|
displayName: tokenData.actor.prototypeToken.displayName,
|
||||||
|
flags: tokenData.actor.prototypeToken.flags
|
||||||
|
})),
|
||||||
{ controlObject: true, parent: canvas.scene }
|
{ controlObject: true, parent: canvas.scene }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,234 +4,199 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
#wide = false;
|
#wide = false;
|
||||||
#bordered = false;
|
#bordered = false;
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
async activate(element, options = {}) {
|
async activate(element, options = {}) {
|
||||||
this.#wide = false;
|
const { TextEditor } = foundry.applications.ux;
|
||||||
this.#bordered = false;
|
|
||||||
|
|
||||||
const isMacro = document.getElementById('action-bar').contains(element);
|
|
||||||
const macro = isMacro ? game.macros.get(game.user.hotbar[Number(element.dataset.slot)] ?? null) : null;
|
|
||||||
const macroItemUuid = macro?.type === 'script' ? macro.command.match(/await game\.system\.api\.applications\.ui\.DhHotbar\.useItem\("([^"]+)"\);/)?.[1] : null;
|
|
||||||
if (macroItemUuid && await fromUuid(macroItemUuid, { strict: false })) {
|
|
||||||
element.dataset.tooltip = `#item#${macroItemUuid}`;
|
|
||||||
options.direction = this.constructor.TOOLTIP_DIRECTIONS.UP;
|
|
||||||
}
|
|
||||||
|
|
||||||
let html = options.html;
|
let html = options.html;
|
||||||
const key = element.dataset.tooltip?.match(/^#([\w-]+)#/)?.[1];
|
if (element.dataset.tooltip?.startsWith('#battlepoints#')) {
|
||||||
switch (key) {
|
this.#wide = true;
|
||||||
case 'battlepoints':
|
this.#bordered = true;
|
||||||
return this.#activateBattlepoints(element, options);
|
|
||||||
case 'effect-display':
|
html = await this.getBattlepointHTML(element.dataset.combatId);
|
||||||
html = await this.#activateEffectDisplay(element, options);
|
options.direction = this._determineItemTooltipDirection(element);
|
||||||
break;
|
super.activate(element, { ...options, html: html });
|
||||||
case 'item':
|
|
||||||
html = await this.#activateItem(element, options);
|
const lockedTooltip = this.lockTooltip();
|
||||||
break;
|
lockedTooltip.querySelectorAll('.battlepoint-toggle-container input').forEach(element => {
|
||||||
case 'attack':
|
element.addEventListener('input', this.toggleModifier.bind(this));
|
||||||
html = await this.#activateAttack(element, options);
|
});
|
||||||
break;
|
return;
|
||||||
case 'shortRest':
|
} else {
|
||||||
case 'longRest':
|
this.#wide = false;
|
||||||
html = await this.#activateRest(element, options);
|
this.#bordered = false;
|
||||||
break;
|
}
|
||||||
case 'advantage':
|
|
||||||
case 'disadvantage':
|
if (element.dataset.tooltip === '#effect-display#') {
|
||||||
html = await this.#activateAdvantageDisadvantage(element, options);
|
this.#bordered = true;
|
||||||
break;
|
let effect = {};
|
||||||
case 'deathMove':
|
if (element.dataset.uuid) {
|
||||||
html = await this.#activateDeathMove(element, options);
|
const effectItem = await foundry.utils.fromUuid(element.dataset.uuid);
|
||||||
break;
|
const effectData = effectItem.toObject();
|
||||||
|
|
||||||
|
effect = {
|
||||||
|
...effectData,
|
||||||
|
name: game.i18n.localize(effectData.name)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (effectData.type === 'beastform') {
|
||||||
|
const beastformData = {
|
||||||
|
features: [],
|
||||||
|
advantageOn: effectData.system.advantageOn,
|
||||||
|
beastformAttackData: game.system.api.data.items.DHBeastform.getBeastformAttackData(effectItem)
|
||||||
|
};
|
||||||
|
|
||||||
|
const features = effectItem.parent.items.filter(x => effectItem.system.featureIds.includes(x.id));
|
||||||
|
for (const feature of features) {
|
||||||
|
const featureData = feature.toObject();
|
||||||
|
featureData.enrichedDescription = await feature.system.getEnrichedDescription();
|
||||||
|
beastformData.features.push(featureData);
|
||||||
|
}
|
||||||
|
|
||||||
|
effect.description = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/ui/tooltip/parts/beastformData.hbs',
|
||||||
|
{
|
||||||
|
item: { system: beastformData }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
effect.description = game.i18n.localize(
|
||||||
|
effectData.description ?? effectData.parent.system.description
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const conditions = CONFIG.DH.GENERAL.conditions();
|
||||||
|
const condition = conditions[element.dataset.condition];
|
||||||
|
effect = {
|
||||||
|
...condition,
|
||||||
|
name: game.i18n.localize(condition.name),
|
||||||
|
description: game.i18n.localize(condition.description),
|
||||||
|
appliedBy: element.dataset.appliedBy,
|
||||||
|
isLockedCondition: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
html = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/daggerheart/templates/ui/tooltip/effect-display.hbs`,
|
||||||
|
{
|
||||||
|
effect
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.tooltip.innerHTML = html;
|
||||||
|
options.direction = this._determineItemTooltipDirection(element);
|
||||||
|
} else {
|
||||||
|
this.#bordered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.dataset.tooltip?.startsWith('#item#')) {
|
||||||
|
const itemUuid = element.dataset.tooltip.slice(6);
|
||||||
|
const item = await foundry.utils.fromUuid(itemUuid);
|
||||||
|
if (item) {
|
||||||
|
const isAction = item instanceof game.system.api.models.actions.actionsTypes.base;
|
||||||
|
const isEffect = item instanceof ActiveEffect;
|
||||||
|
await this.enrichText(item);
|
||||||
|
|
||||||
|
const type = isAction ? 'action' : isEffect ? 'effect' : item.type;
|
||||||
|
html = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/daggerheart/templates/ui/tooltip/${type}.hbs`,
|
||||||
|
{
|
||||||
|
item: item,
|
||||||
|
description: item.system?.enrichedDescription ?? item.enrichedDescription,
|
||||||
|
config: CONFIG.DH,
|
||||||
|
allDomains: CONFIG.DH.DOMAIN.allDomains()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.tooltip.innerHTML = html;
|
||||||
|
options.direction = this._determineItemTooltipDirection(element);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const attack = element.dataset.tooltip?.startsWith('#attack#');
|
||||||
|
if (attack) {
|
||||||
|
const actorUuid = element.dataset.tooltip.slice(8);
|
||||||
|
const actor = await foundry.utils.fromUuid(actorUuid);
|
||||||
|
const attack = actor.system.attack;
|
||||||
|
|
||||||
|
const description = await TextEditor.enrichHTML(attack.description);
|
||||||
|
html = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/daggerheart/templates/ui/tooltip/attack.hbs`,
|
||||||
|
{
|
||||||
|
attack: attack,
|
||||||
|
description: description,
|
||||||
|
parent: actor,
|
||||||
|
config: CONFIG.DH
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.tooltip.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shortRest = element.dataset.tooltip?.startsWith('#shortRest#');
|
||||||
|
const longRest = element.dataset.tooltip?.startsWith('#longRest#');
|
||||||
|
if (shortRest || longRest) {
|
||||||
|
const key = element.dataset.tooltip.slice(shortRest ? 11 : 10);
|
||||||
|
|
||||||
|
const moves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves[
|
||||||
|
element.dataset.restType
|
||||||
|
].moves;
|
||||||
|
const move = moves[key];
|
||||||
|
const description = await TextEditor.enrichHTML(move.description);
|
||||||
|
html = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/daggerheart/templates/ui/tooltip/downtime.hbs`,
|
||||||
|
{
|
||||||
|
move: move,
|
||||||
|
description: description
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.tooltip.innerHTML = html;
|
||||||
|
options.direction = this._determineItemTooltipDirection(
|
||||||
|
element,
|
||||||
|
this.constructor.TOOLTIP_DIRECTIONS.RIGHT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAdvantage = element.dataset.tooltip?.startsWith('#advantage#');
|
||||||
|
const isDisadvantage = element.dataset.tooltip?.startsWith('#disadvantage#');
|
||||||
|
if (isAdvantage || isDisadvantage) {
|
||||||
|
const actorUuid = element.dataset.tooltip.slice(isAdvantage ? 11 : 14);
|
||||||
|
const actor = await foundry.utils.fromUuid(actorUuid);
|
||||||
|
|
||||||
|
if (actor) {
|
||||||
|
html = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/daggerheart/templates/ui/tooltip/advantage.hbs`,
|
||||||
|
{
|
||||||
|
sources: isAdvantage ? actor.system.advantageSources : actor.system.disadvantageSources
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.tooltip.innerHTML = html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deathMove = element.dataset.tooltip?.startsWith('#deathMove#');
|
||||||
|
if (deathMove) {
|
||||||
|
const name = element.dataset.deathName;
|
||||||
|
const img = element.dataset.deathImg;
|
||||||
|
const description = element.dataset.deathDescription;
|
||||||
|
|
||||||
|
html = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/daggerheart/templates/ui/tooltip/death-move.hbs`,
|
||||||
|
{
|
||||||
|
move: { name: name, img: img, description: description }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.tooltip.innerHTML = html;
|
||||||
|
options.direction = this._determineItemTooltipDirection(
|
||||||
|
element,
|
||||||
|
this.constructor.TOOLTIP_DIRECTIONS.RIGHT
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.noOffset = options.noOffset;
|
this.noOffset = options.noOffset;
|
||||||
super.activate(element, { ...options, html });
|
|
||||||
}
|
|
||||||
|
|
||||||
async #activateBattlepoints(element, options) {
|
|
||||||
this.#wide = true;
|
|
||||||
this.#bordered = true;
|
|
||||||
|
|
||||||
const html = await this.getBattlepointHTML(element.dataset.combatId);
|
|
||||||
options.direction = this._determineItemTooltipDirection(element);
|
|
||||||
super.activate(element, { ...options, html: html });
|
super.activate(element, { ...options, html: html });
|
||||||
|
|
||||||
const lockedTooltip = this.lockTooltip();
|
|
||||||
lockedTooltip.querySelectorAll('.battlepoint-toggle-container input').forEach(element => {
|
|
||||||
element.addEventListener('input', this.toggleModifier.bind(this));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async #activateEffectDisplay(element, options) {
|
|
||||||
this.#bordered = true;
|
|
||||||
let effect = {};
|
|
||||||
if (element.dataset.uuid) {
|
|
||||||
const effectItem = await foundry.utils.fromUuid(element.dataset.uuid);
|
|
||||||
const effectData = effectItem.toObject();
|
|
||||||
|
|
||||||
effect = {
|
|
||||||
...effectData,
|
|
||||||
name: game.i18n.localize(effectData.name)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (effectData.type === 'beastform') {
|
|
||||||
const beastformData = {
|
|
||||||
features: [],
|
|
||||||
advantageOn: effectData.system.advantageOn,
|
|
||||||
beastformAttackData: game.system.api.data.items.DHBeastform.getBeastformAttackData(effectItem)
|
|
||||||
};
|
|
||||||
|
|
||||||
const features = effectItem.parent.items.filter(x => effectItem.system.featureIds.includes(x.id));
|
|
||||||
for (const feature of features) {
|
|
||||||
const featureData = feature.toObject();
|
|
||||||
featureData.enrichedDescription = await feature.system.getEnrichedDescription();
|
|
||||||
beastformData.features.push(featureData);
|
|
||||||
}
|
|
||||||
|
|
||||||
effect.description = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/ui/tooltip/parts/beastformData.hbs',
|
|
||||||
{
|
|
||||||
item: { system: beastformData }
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
effect.description = game.i18n.localize(
|
|
||||||
effectData.description ?? effectData.parent.system.description
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const conditions = CONFIG.DH.GENERAL.conditions();
|
|
||||||
const condition = conditions[element.dataset.condition];
|
|
||||||
effect = {
|
|
||||||
...condition,
|
|
||||||
name: game.i18n.localize(condition.name),
|
|
||||||
description: game.i18n.localize(condition.description),
|
|
||||||
appliedBy: element.dataset.appliedBy,
|
|
||||||
isLockedCondition: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const html = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
`systems/daggerheart/templates/ui/tooltip/effect-display.hbs`,
|
|
||||||
{
|
|
||||||
effect
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tooltip.innerHTML = html;
|
|
||||||
options.direction = this._determineItemTooltipDirection(element);
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
async #activateItem(element, options) {
|
|
||||||
const itemUuid = element.dataset.tooltip.slice(6);
|
|
||||||
const item = await foundry.utils.fromUuid(itemUuid);
|
|
||||||
if (item) {
|
|
||||||
const isAction = item instanceof game.system.api.models.actions.actionsTypes.base;
|
|
||||||
const isEffect = item instanceof ActiveEffect;
|
|
||||||
await this.enrichText(item);
|
|
||||||
|
|
||||||
const type = isAction ? 'action' : isEffect ? 'effect' : item.type;
|
|
||||||
const html = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
`systems/daggerheart/templates/ui/tooltip/${type}.hbs`,
|
|
||||||
{
|
|
||||||
item: item,
|
|
||||||
description: item.system?.enrichedDescription ?? item.enrichedDescription,
|
|
||||||
config: CONFIG.DH,
|
|
||||||
allDomains: CONFIG.DH.DOMAIN.allDomains()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tooltip.innerHTML = html;
|
|
||||||
options.direction ??= this._determineItemTooltipDirection(element);
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async #activateAttack(element, options) {
|
|
||||||
const actorUuid = element.dataset.tooltip.slice(8);
|
|
||||||
const actor = await foundry.utils.fromUuid(actorUuid);
|
|
||||||
const attack = actor.system.attack;
|
|
||||||
|
|
||||||
const description = await foundry.applications.ux.TextEditor.enrichHTML(attack.description);
|
|
||||||
const html = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
`systems/daggerheart/templates/ui/tooltip/attack.hbs`,
|
|
||||||
{
|
|
||||||
attack: attack,
|
|
||||||
description: description,
|
|
||||||
parent: actor,
|
|
||||||
config: CONFIG.DH
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tooltip.innerHTML = html;
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
async #activateAdvantageDisadvantage(element, options) {
|
|
||||||
const isAdvantage = element.dataset.tooltip?.startsWith('#advantage#');
|
|
||||||
const actorUuid = element.dataset.tooltip.slice(isAdvantage ? 11 : 14);
|
|
||||||
const actor = await foundry.utils.fromUuid(actorUuid);
|
|
||||||
|
|
||||||
if (actor) {
|
|
||||||
const html = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
`systems/daggerheart/templates/ui/tooltip/advantage.hbs`,
|
|
||||||
{
|
|
||||||
sources: isAdvantage ? actor.system.advantageSources : actor.system.disadvantageSources
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tooltip.innerHTML = html;
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async #activateDeathMove(element, options) {
|
|
||||||
const name = element.dataset.deathName;
|
|
||||||
const img = element.dataset.deathImg;
|
|
||||||
const description = element.dataset.deathDescription;
|
|
||||||
|
|
||||||
const html = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
`systems/daggerheart/templates/ui/tooltip/death-move.hbs`,
|
|
||||||
{
|
|
||||||
move: { name: name, img: img, description: description }
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tooltip.innerHTML = html;
|
|
||||||
options.direction = this._determineItemTooltipDirection(
|
|
||||||
element,
|
|
||||||
this.constructor.TOOLTIP_DIRECTIONS.RIGHT
|
|
||||||
);
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
async #activateRest(element, options) {
|
|
||||||
const isShortRest = element.dataset.tooltip?.startsWith('#shortRest#');
|
|
||||||
const key = element.dataset.tooltip.slice(isShortRest ? 11 : 10);
|
|
||||||
const moves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves[
|
|
||||||
element.dataset.restType
|
|
||||||
].moves;
|
|
||||||
const move = moves[key];
|
|
||||||
const description = await foundry.applications.ux.TextEditor.enrichHTML(move.description);
|
|
||||||
const html = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
`systems/daggerheart/templates/ui/tooltip/downtime.hbs`,
|
|
||||||
{
|
|
||||||
move: move,
|
|
||||||
description: description
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tooltip.innerHTML = html;
|
|
||||||
options.direction = this._determineItemTooltipDirection(
|
|
||||||
element,
|
|
||||||
this.constructor.TOOLTIP_DIRECTIONS.RIGHT
|
|
||||||
);
|
|
||||||
return html;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_setAnchor(direction) {
|
_setAnchor(direction) {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ export const renderMeasuredTemplate = async event => {
|
||||||
if (!type || !range || !game.canvas.scene) return;
|
if (!type || !range || !game.canvas.scene) return;
|
||||||
|
|
||||||
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({
|
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({
|
||||||
shapeType: type,
|
shapetype: type,
|
||||||
angle,
|
angle,
|
||||||
range,
|
range,
|
||||||
direction,
|
direction,
|
||||||
|
|
|
||||||
|
|
@ -35,24 +35,23 @@ export const enricherConfig = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export const enricherRenderSetup = element => {
|
export const enricherRenderSetup = element => {
|
||||||
const clickWrapper = func => event => {
|
|
||||||
event.stopPropagation();
|
|
||||||
func(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
element
|
element
|
||||||
.querySelectorAll('.enriched-damage-button')
|
.querySelectorAll('.enriched-damage-button')
|
||||||
.forEach(element => element.addEventListener('click', clickWrapper(renderDamageButton)));
|
.forEach(element => element.addEventListener('click', renderDamageButton));
|
||||||
|
|
||||||
element
|
element
|
||||||
.querySelectorAll('.duality-roll-button')
|
.querySelectorAll('.duality-roll-button')
|
||||||
.forEach(element => element.addEventListener('click', clickWrapper(renderDualityButton)));
|
.forEach(element => element.addEventListener('click', renderDualityButton));
|
||||||
|
|
||||||
element
|
element
|
||||||
.querySelectorAll('.fate-roll-button')
|
.querySelectorAll('.fate-roll-button')
|
||||||
.forEach(element => element.addEventListener('click', clickWrapper(renderFateButton)));
|
.forEach(element => element.addEventListener('click', renderFateButton));
|
||||||
|
|
||||||
element
|
element
|
||||||
.querySelectorAll('.measured-template-button')
|
.querySelectorAll('.measured-template-button')
|
||||||
.forEach(element => element.addEventListener('click', clickWrapper(renderMeasuredTemplate)));
|
.forEach(element => element.addEventListener('click', renderMeasuredTemplate));
|
||||||
|
|
||||||
|
// element
|
||||||
|
// .querySelectorAll('.enriched-effect')
|
||||||
|
// .forEach(element => element.addEventListener('dragstart', dragEnrichedEffect));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,9 @@ export default class RegisterHandlebarsHelpers {
|
||||||
return formula;
|
return formula;
|
||||||
}
|
}
|
||||||
|
|
||||||
static damageSymbols(damageData) {
|
static damageSymbols(damageParts) {
|
||||||
const symbols = damageData.type.map(p => CONFIG.DH.GENERAL.damageTypes[p].icon);
|
const allTypes = [...new Set([...damageParts].flatMap(x => Array.from(x.type)))];
|
||||||
|
const symbols = allTypes.map(p => CONFIG.DH.GENERAL.damageTypes[p].icon);
|
||||||
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
|
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -700,6 +700,19 @@ export async function RefreshFeatures(
|
||||||
return refreshedActors;
|
return refreshedActors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getUnusedDamageTypes(parts) {
|
||||||
|
const usedKeys = Object.keys(parts);
|
||||||
|
return Object.keys(CONFIG.DH.GENERAL.healingTypes).reduce((acc, key) => {
|
||||||
|
if (!usedKeys.includes(key))
|
||||||
|
acc.push({
|
||||||
|
value: key,
|
||||||
|
label: game.i18n.localize(CONFIG.DH.GENERAL.healingTypes[key].label)
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns resolved armor sources ordered by application order */
|
/** Returns resolved armor sources ordered by application order */
|
||||||
export function getArmorSources(actor) {
|
export function getArmorSources(actor) {
|
||||||
const rawArmorSources = Array.from(actor.allApplicableEffects()).filter(x => x.system.armorData);
|
const rawArmorSources = Array.from(actor.allApplicableEffects()).filter(x => x.system.armorData);
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
import { MigrationHandlerBase } from './base.mjs';
|
|
||||||
|
|
||||||
export class Migration_2_5_2 extends MigrationHandlerBase {
|
|
||||||
version = '2.5.2';
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
async updateActiveEffectSource(effectSource, item) {
|
|
||||||
let shouldUpdate = false;
|
|
||||||
const newChanges = [];
|
|
||||||
|
|
||||||
const srdItem = item?._stats?.compendiumSource ?
|
|
||||||
await foundry.utils.fromUuid(item?._stats.compendiumSource) :
|
|
||||||
null;
|
|
||||||
for (let i = 0; i < effectSource.system.changes.length; i++) {
|
|
||||||
const change = effectSource.system.changes[i];
|
|
||||||
const srdEffect = srdItem?.effects.find(x => x.name === effectSource.name);
|
|
||||||
if (change.type === 'custom') {
|
|
||||||
const srdChange = srdEffect ? srdEffect.system.changes[i] : null;
|
|
||||||
if (srdChange &&
|
|
||||||
change.key === srdChange.key &&
|
|
||||||
change.value === srdChange.value &&
|
|
||||||
change.type !== srdChange.type
|
|
||||||
) {
|
|
||||||
shouldUpdate = true;
|
|
||||||
newChanges.push(srdChange);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newChanges.push(change);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldUpdate) {
|
|
||||||
return {
|
|
||||||
_id: effectSource._id,
|
|
||||||
system: { changes: newChanges }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import { MigrationHandlerBase } from './base.mjs';
|
|
||||||
|
|
||||||
export class Migration_2_6_0 extends MigrationHandlerBase {
|
|
||||||
version = '2.6.0';
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
async updateActorSource(actor) {
|
|
||||||
if (actor.type === 'party' && Object.keys(actor.system.tagTeam.members).length) {
|
|
||||||
return {
|
|
||||||
_id: actor._id,
|
|
||||||
system: {
|
|
||||||
tagTeam: {
|
|
||||||
initiator: null,
|
|
||||||
members: _replace({})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
/**
|
|
||||||
* @import DHItem from "../../documents/item.mjs";
|
|
||||||
* @import DhActor from "../../documents/actor.mjs";
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
|
|
||||||
* The base class of an async migration.
|
|
||||||
* These are generally run between versions for things that require compendiums or must be done in post.
|
|
||||||
* The migrate() functions calls the various updateXSource() functions.
|
|
||||||
* Generally a subclass will override the version and the updateXSource() functions.
|
|
||||||
*/
|
|
||||||
export class MigrationHandlerBase {
|
|
||||||
version = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets change data for an active effect's source, or null if no changes
|
|
||||||
* @param {object} effectSource
|
|
||||||
* @param {DHItem} item
|
|
||||||
* @returns {Promise<object>}
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
async updateActiveEffectSource(effectSource, item) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a world actor
|
|
||||||
* @param {DhActor} actor
|
|
||||||
* @returns {Promise<object>}
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
async updateActorSource(actor) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async migrate() {
|
|
||||||
// todo: handle more than just migrating effects. Right now this can only migrate effects
|
|
||||||
// NOTE: the preload is hardcoded, we should not hardcode it
|
|
||||||
|
|
||||||
const numActors = game.actors.size;
|
|
||||||
const numItems = game.items.size;
|
|
||||||
const finalUpdateProgress = 5;
|
|
||||||
const DhProgress = game.system.api.applications.ui.DhProgress;
|
|
||||||
const preRunProgress = game.packs.size;
|
|
||||||
|
|
||||||
const progress = DhProgress.createMigrationProgress(
|
|
||||||
preRunProgress + numActors + numItems + finalUpdateProgress
|
|
||||||
);
|
|
||||||
|
|
||||||
// Preload. Avoid hardcoding in the future
|
|
||||||
for (const pack of game.packs) {
|
|
||||||
await pack.getDocuments();
|
|
||||||
progress.advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
const batch = [];
|
|
||||||
|
|
||||||
const updateItem = async item => {
|
|
||||||
const itemUpdates = [];
|
|
||||||
for (const effect of item.effects) {
|
|
||||||
const changes = await this.updateActiveEffectSource(effect.toObject(), item);
|
|
||||||
if (changes) itemUpdates.push(changes);
|
|
||||||
}
|
|
||||||
if (itemUpdates.length) {
|
|
||||||
batch.push({
|
|
||||||
action: 'update',
|
|
||||||
documentName: 'ActiveEffect',
|
|
||||||
updates: itemUpdates,
|
|
||||||
parent: item
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateActor = async actor => {
|
|
||||||
const actorUpdate = await this.updateActorSource(actor);
|
|
||||||
if (actorUpdate) {
|
|
||||||
batch.push({
|
|
||||||
action: 'update',
|
|
||||||
documentName: 'Actor',
|
|
||||||
updates: [actorUpdate]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const aeUpdates = [];
|
|
||||||
for (const item of actor.items) {
|
|
||||||
await updateItem(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const effect of actor.effects) {
|
|
||||||
const changes = await this.updateActiveEffectSource(effect.toObject(), { parent: actor });
|
|
||||||
if (changes) aeUpdates.push(changes);
|
|
||||||
}
|
|
||||||
if (aeUpdates.length) {
|
|
||||||
batch.push({
|
|
||||||
action: 'update',
|
|
||||||
documentName: 'ActiveEffect',
|
|
||||||
updates: aeUpdates,
|
|
||||||
parent: actor
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (const actor of game.actors) {
|
|
||||||
await updateActor(actor);
|
|
||||||
|
|
||||||
progress.advance();
|
|
||||||
}
|
|
||||||
for (const item of game.items) {
|
|
||||||
await updateItem(item);
|
|
||||||
progress.advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
await foundry.documents.modifyBatch(batch);
|
|
||||||
progress.advance({ by: finalUpdateProgress });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import { defaultRestOptions } from '../config/generalConfig.mjs';
|
import { defaultRestOptions } from '../config/generalConfig.mjs';
|
||||||
import { Migration_2_5_2 } from './migration-handlers/2_5_2.mjs';
|
|
||||||
import { Migration_2_6_0 } from './migration-handlers/2_6_0.mjs';
|
|
||||||
|
|
||||||
export async function runMigrations() {
|
export async function runMigrations() {
|
||||||
let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion);
|
let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion);
|
||||||
|
|
@ -322,20 +320,7 @@ export async function runMigrations() {
|
||||||
|
|
||||||
lastMigrationVersion = '2.1.0';
|
lastMigrationVersion = '2.1.0';
|
||||||
}
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/* New Style migrations below this point */
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
const migrations = [
|
|
||||||
new Migration_2_5_2(),
|
|
||||||
new Migration_2_6_0()
|
|
||||||
].filter(m => m.version && foundry.utils.isNewerVersion(m.version, lastMigrationVersion));
|
|
||||||
|
|
||||||
for (const handler of migrations) {
|
|
||||||
await handler.migrate();
|
|
||||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, handler.version);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import DamageReductionDialog from '../applications/dialogs/damageReductionDialog.mjs';
|
import DamageReductionDialog from '../applications/dialogs/damageReductionDialog.mjs';
|
||||||
import PartySheet from '../applications/sheets/actors/party.mjs';
|
import Party from '../applications/sheets/actors/party.mjs';
|
||||||
|
|
||||||
export function handleSocketEvent({ action = null, data = {} } = {}) {
|
export function handleSocketEvent({ action = null, data = {} } = {}) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
|
@ -16,7 +16,7 @@ export function handleSocketEvent({ action = null, data = {} } = {}) {
|
||||||
Hooks.call(socketEvent.Refresh, data);
|
Hooks.call(socketEvent.Refresh, data);
|
||||||
break;
|
break;
|
||||||
case socketEvent.DowntimeTrigger:
|
case socketEvent.DowntimeTrigger:
|
||||||
PartySheet.downtimeMoveQuery(data);
|
Party.downtimeMoveQuery(data);
|
||||||
break;
|
break;
|
||||||
case socketEvent.TagTeamStart:
|
case socketEvent.TagTeamStart:
|
||||||
Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.tagTeamStart, data);
|
Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.tagTeamStart, data);
|
||||||
|
|
|
||||||
|
|
@ -403,6 +403,12 @@
|
||||||
"_id": "9PsnogEPsp1OOK64",
|
"_id": "9PsnogEPsp1OOK64",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "act"
|
"type": "act"
|
||||||
|
|
|
||||||
|
|
@ -322,23 +322,32 @@
|
||||||
"_id": "ia0NUIOxE3RHb45P",
|
"_id": "ia0NUIOxE3RHb45P",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.evasion",
|
"type": "withinRange",
|
||||||
"type": "override",
|
"target": "hostile",
|
||||||
"value": "@system.evasion / 2",
|
"range": "melee"
|
||||||
"priority": 10
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"duration": {
|
|
||||||
"value": null,
|
|
||||||
"units": "seconds",
|
|
||||||
"expiry": null,
|
|
||||||
"expired": false
|
|
||||||
},
|
},
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.evasion",
|
||||||
|
"mode": 5,
|
||||||
|
"value": "@system.evasion / 2",
|
||||||
|
"priority": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"description": "<p>When the Flickerfly makes an attack, the target’s Evasion is halved against the attack.</p>",
|
"duration": {
|
||||||
|
"startTime": null,
|
||||||
|
"combat": null,
|
||||||
|
"seconds": null,
|
||||||
|
"rounds": null,
|
||||||
|
"turns": null,
|
||||||
|
"startRound": null,
|
||||||
|
"startTurn": null
|
||||||
|
},
|
||||||
|
"description": "<p>When the Flickerfl y makes an attack, the target’s Evasion is halved against the attack.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"statuses": [],
|
"statuses": [],
|
||||||
"sort": 0,
|
"sort": 0,
|
||||||
|
|
|
||||||
|
|
@ -491,6 +491,12 @@
|
||||||
"_id": "KGdf2eqcXkdigg0u",
|
"_id": "KGdf2eqcXkdigg0u",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -325,15 +325,21 @@
|
||||||
"_id": "wGuxOLokMqdxVSOo",
|
"_id": "wGuxOLokMqdxVSOo",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.disadvantageSources",
|
"type": "withinRange",
|
||||||
"type": "add",
|
"target": "hostile",
|
||||||
"value": "Agility Rolls",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.disadvantageSources",
|
||||||
|
"mode": 2,
|
||||||
|
"value": "Agility Rolls",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
|
||||||
|
|
@ -472,8 +472,14 @@
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"_id": "bq8hTzQoCXmA3xad",
|
"_id": "bq8hTzQoCXmA3xad",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": []
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": 0,
|
"startTime": 0,
|
||||||
"combat": null
|
"combat": null
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,12 @@
|
||||||
"_id": "2iBVUGHtGW3I9VIj",
|
"_id": "2iBVUGHtGW3I9VIj",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
@ -335,17 +341,23 @@
|
||||||
"name": "Out of Nowhere",
|
"name": "Out of Nowhere",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.advantageSources",
|
"type": "withinRange",
|
||||||
"type": "add",
|
"target": "hostile",
|
||||||
"value": "On attacks if they are Hidden.",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"_id": "qT5nh7OcQ6aGdiWS",
|
"_id": "qT5nh7OcQ6aGdiWS",
|
||||||
"img": "icons/magic/perception/shadow-stealth-eyes-purple.webp",
|
"img": "icons/magic/perception/shadow-stealth-eyes-purple.webp",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.advantageSources",
|
||||||
|
"mode": 2,
|
||||||
|
"value": "On attacks if they are Hidden.",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
@ -440,6 +452,12 @@
|
||||||
"_id": "yP4ot8VqS56RnxnE",
|
"_id": "yP4ot8VqS56RnxnE",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": []
|
"changes": []
|
||||||
},
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
|
|
|
||||||
|
|
@ -680,6 +680,12 @@
|
||||||
"_id": "9NQcCXMhjyBReJRd",
|
"_id": "9NQcCXMhjyBReJRd",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
@ -921,6 +927,12 @@
|
||||||
"_id": "S7kJlhnV8Nexzi8l",
|
"_id": "S7kJlhnV8Nexzi8l",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -363,6 +363,12 @@
|
||||||
"_id": "U50Ccm9emMqAxma6",
|
"_id": "U50Ccm9emMqAxma6",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,12 @@
|
||||||
"_id": "LmzztuktRkwOCy1a",
|
"_id": "LmzztuktRkwOCy1a",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -447,6 +447,12 @@
|
||||||
"_id": "CjMrSdL6kgD8mKRQ",
|
"_id": "CjMrSdL6kgD8mKRQ",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary"
|
"type": "temporary"
|
||||||
|
|
|
||||||
|
|
@ -232,17 +232,23 @@
|
||||||
"name": "Levitation",
|
"name": "Levitation",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.rules.conditionImmunities.restrained",
|
"type": "withinRange",
|
||||||
"type": "override",
|
"target": "hostile",
|
||||||
"value": "1",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"_id": "k6iaQVfMZhrpwYQj",
|
"_id": "k6iaQVfMZhrpwYQj",
|
||||||
"img": "icons/magic/air/fog-gas-smoke-blue-gray.webp",
|
"img": "icons/magic/air/fog-gas-smoke-blue-gray.webp",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.rules.conditionImmunities.restrained",
|
||||||
|
"mode": 5,
|
||||||
|
"value": "1",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
@ -295,17 +301,23 @@
|
||||||
"name": "Wards",
|
"name": "Wards",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.resistance.magical.resistance",
|
"type": "withinRange",
|
||||||
"type": "override",
|
"target": "hostile",
|
||||||
"value": "1",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"_id": "4rj6PM0AGrrlbxSc",
|
"_id": "4rj6PM0AGrrlbxSc",
|
||||||
"img": "icons/magic/defensive/barrier-shield-dome-blue-purple.webp",
|
"img": "icons/magic/defensive/barrier-shield-dome-blue-purple.webp",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.resistance.magical.resistance",
|
||||||
|
"mode": 5,
|
||||||
|
"value": "1",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
|
||||||
|
|
@ -459,15 +459,21 @@
|
||||||
"_id": "xkDIZk9u2ipDHvOL",
|
"_id": "xkDIZk9u2ipDHvOL",
|
||||||
"img": "icons/creatures/magical/construct-golem-stone-blue.webp",
|
"img": "icons/creatures/magical/construct-golem-stone-blue.webp",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.bonuses.damage.physical.bonus",
|
"type": "withinRange",
|
||||||
"type": "add",
|
"target": "hostile",
|
||||||
"value": "10",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.bonuses.damage.physical.bonus",
|
||||||
|
"mode": 2,
|
||||||
|
"value": "10",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
|
||||||
|
|
@ -315,6 +315,12 @@
|
||||||
"_id": "blcRqns0PHqiuPac",
|
"_id": "blcRqns0PHqiuPac",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -331,6 +331,12 @@
|
||||||
"_id": "YNMhgBZW8ndrCjIp",
|
"_id": "YNMhgBZW8ndrCjIp",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "scene"
|
"type": "scene"
|
||||||
|
|
|
||||||
|
|
@ -415,6 +415,12 @@
|
||||||
"_id": "U9lWz1LgeAiK5L85",
|
"_id": "U9lWz1LgeAiK5L85",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"key": "system.resistance.magical.resistance",
|
"key": "system.resistance.magical.resistance",
|
||||||
|
|
@ -540,6 +546,12 @@
|
||||||
"_id": "lNH6srSPyEprXZ4o",
|
"_id": "lNH6srSPyEprXZ4o",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -379,6 +379,12 @@
|
||||||
"_id": "LwWxRz7FTMA80VdA",
|
"_id": "LwWxRz7FTMA80VdA",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary"
|
"type": "temporary"
|
||||||
|
|
|
||||||
|
|
@ -410,6 +410,12 @@
|
||||||
"_id": "F3E7fiz01AbF2kr5",
|
"_id": "F3E7fiz01AbF2kr5",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -270,17 +270,23 @@
|
||||||
"name": "Numbers Must Go Up",
|
"name": "Numbers Must Go Up",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.bonuses.roll.attack.bonus",
|
"type": "withinRange",
|
||||||
"type": "add",
|
"target": "hostile",
|
||||||
"value": "ITEM.@system.resource.value",
|
"range": "melee"
|
||||||
"priority": 21
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"_id": "iLeiOlUtWvYsgbDQ",
|
"_id": "iLeiOlUtWvYsgbDQ",
|
||||||
"img": "icons/magic/control/fear-fright-monster-grin-red-orange.webp",
|
"img": "icons/magic/control/fear-fright-monster-grin-red-orange.webp",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.bonuses.roll.attack.bonus",
|
||||||
|
"mode": 2,
|
||||||
|
"value": "ITEM.@system.resource.value",
|
||||||
|
"priority": 21
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
|
||||||
|
|
@ -238,17 +238,23 @@
|
||||||
"name": "Depths Of Despair",
|
"name": "Depths Of Despair",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.rules.attack.damage.hpDamageMultiplier",
|
"type": "withinRange",
|
||||||
"type": "override",
|
"target": "hostile",
|
||||||
"value": "2",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"_id": "nofxm1vGZ2TmceA2",
|
"_id": "nofxm1vGZ2TmceA2",
|
||||||
"img": "icons/magic/death/skull-horned-worn-fire-blue.webp",
|
"img": "icons/magic/death/skull-horned-worn-fire-blue.webp",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.rules.attack.damage.hpDamageMultiplier",
|
||||||
|
"mode": 5,
|
||||||
|
"value": "2",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
@ -350,6 +356,12 @@
|
||||||
"_id": "6WSx03mFbpbPWnOI",
|
"_id": "6WSx03mFbpbPWnOI",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"key": "system.rules.dualityRoll.defaultHopeDice",
|
"key": "system.rules.dualityRoll.defaultHopeDice",
|
||||||
|
|
|
||||||
|
|
@ -319,6 +319,12 @@
|
||||||
"_id": "gFeHLGgeRoDdd3VG",
|
"_id": "gFeHLGgeRoDdd3VG",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"key": "system.rules.dualityRoll.defaultFearDice",
|
"key": "system.rules.dualityRoll.defaultFearDice",
|
||||||
|
|
|
||||||
|
|
@ -440,8 +440,14 @@
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"_id": "dPZ7PwvTERjtG92P",
|
"_id": "dPZ7PwvTERjtG92P",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": []
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": 0,
|
"startTime": 0,
|
||||||
"combat": null
|
"combat": null
|
||||||
|
|
|
||||||
|
|
@ -238,6 +238,12 @@
|
||||||
"name": "Flying",
|
"name": "Flying",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"key": "system.difficulty",
|
"key": "system.difficulty",
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,12 @@
|
||||||
"_id": "YNKHEFQ4ucGr4Rmc",
|
"_id": "YNKHEFQ4ucGr4Rmc",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -358,8 +358,14 @@
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"_id": "xoal60Ed3Ih7saBJ",
|
"_id": "xoal60Ed3Ih7saBJ",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": []
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": 0,
|
"startTime": 0,
|
||||||
"combat": null
|
"combat": null
|
||||||
|
|
|
||||||
|
|
@ -240,17 +240,23 @@
|
||||||
"name": "Warped Fortitude",
|
"name": "Warped Fortitude",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.resistance.physical.resistance",
|
"type": "withinRange",
|
||||||
"type": "override",
|
"target": "hostile",
|
||||||
"value": "1",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"_id": "3QLH2EEtcUEZolFz",
|
"_id": "3QLH2EEtcUEZolFz",
|
||||||
"img": "icons/creatures/magical/humanoid-silhouette-glowing-pink.webp",
|
"img": "icons/creatures/magical/humanoid-silhouette-glowing-pink.webp",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.resistance.physical.resistance",
|
||||||
|
"mode": 5,
|
||||||
|
"value": "1",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
@ -303,17 +309,23 @@
|
||||||
"name": "Overwhelm",
|
"name": "Overwhelm",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.rules.attack.damage.hpDamageMultiplier",
|
"type": "withinRange",
|
||||||
"type": "override",
|
"target": "hostile",
|
||||||
"value": "2",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"_id": "eGB9G0ljYCcdGbOx",
|
"_id": "eGB9G0ljYCcdGbOx",
|
||||||
"img": "icons/skills/melee/strike-slashes-orange.webp",
|
"img": "icons/skills/melee/strike-slashes-orange.webp",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.rules.attack.damage.hpDamageMultiplier",
|
||||||
|
"mode": 5,
|
||||||
|
"value": "2",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
|
||||||
|
|
@ -388,6 +388,12 @@
|
||||||
"_id": "Q99saHj6NOY2PSXf",
|
"_id": "Q99saHj6NOY2PSXf",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
@ -590,6 +596,12 @@
|
||||||
"_id": "pWDg0MADoohKu40O",
|
"_id": "pWDg0MADoohKu40O",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -656,6 +656,12 @@
|
||||||
"vulnerable"
|
"vulnerable"
|
||||||
],
|
],
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|
|
||||||
|
|
@ -347,6 +347,12 @@
|
||||||
"_id": "vb1sZCOLwDNLsr3j",
|
"_id": "vb1sZCOLwDNLsr3j",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "temporary",
|
"type": "temporary",
|
||||||
|
|
|
||||||
|
|
@ -265,17 +265,23 @@
|
||||||
"name": "Flying",
|
"name": "Flying",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.difficulty",
|
"type": "withinRange",
|
||||||
"type": "add",
|
"target": "hostile",
|
||||||
"value": "3",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"_id": "odAyKY0BSAkcO3Dd",
|
"_id": "odAyKY0BSAkcO3Dd",
|
||||||
"img": "icons/creatures/birds/raptor-hawk-flying.webp",
|
"img": "icons/creatures/birds/raptor-hawk-flying.webp",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.difficulty",
|
||||||
|
"mode": 2,
|
||||||
|
"value": "3",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
@ -419,6 +425,12 @@
|
||||||
"_id": "m6qqQZxukDPVcGdQ",
|
"_id": "m6qqQZxukDPVcGdQ",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"type": "act"
|
"type": "act"
|
||||||
|
|
@ -567,8 +579,14 @@
|
||||||
"_id": "Sd34xywRqiF5w9sX",
|
"_id": "Sd34xywRqiF5w9sX",
|
||||||
"type": "base",
|
"type": "base",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": []
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
"changes": [],
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
|
||||||
|
|
@ -269,15 +269,21 @@
|
||||||
"_id": "aATxfjeOzUYtKuU6",
|
"_id": "aATxfjeOzUYtKuU6",
|
||||||
"img": "icons/commodities/biological/wing-insect-green.webp",
|
"img": "icons/commodities/biological/wing-insect-green.webp",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": [
|
"rangeDependence": {
|
||||||
{
|
"enabled": false,
|
||||||
"key": "system.difficulty",
|
"type": "withinRange",
|
||||||
"type": "add",
|
"target": "hostile",
|
||||||
"value": "2",
|
"range": "melee"
|
||||||
"priority": null
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"key": "system.difficulty",
|
||||||
|
"mode": 2,
|
||||||
|
"value": "2",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
|
|
@ -379,8 +385,14 @@
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"_id": "dQgcYTz5vmV25Y6G",
|
"_id": "dQgcYTz5vmV25Y6G",
|
||||||
"system": {
|
"system": {
|
||||||
"changes": []
|
"rangeDependence": {
|
||||||
|
"enabled": false,
|
||||||
|
"type": "withinRange",
|
||||||
|
"target": "hostile",
|
||||||
|
"range": "melee"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
"changes": [],
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": 0,
|
"startTime": 0,
|
||||||
"combat": null
|
"combat": null
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue