mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-22 07:23:37 +02:00
Merged with v14-Dev
This commit is contained in:
commit
8d84b8da48
70 changed files with 1076 additions and 936 deletions
|
|
@ -1,3 +1,3 @@
|
||||||
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M6.12012 0.5H51.8799C55.2901 0.500041 57.8779 3.57175 57.2998 6.93262L50.4639 46.6777C50.1604 48.4411 49.0179 49.9467 47.4014 50.7139L31.3584 58.3271C29.8661 59.0354 28.1339 59.0354 26.6416 58.3271L10.5986 50.7139C8.98214 49.9467 7.83959 48.4411 7.53613 46.6777L0.700195 6.93262C0.122088 3.57175 2.7099 0.500042 6.12012 0.5Z" fill="transparent" stroke="#18162e"/>
|
<path d="M 7.12 0.5 H 52.88 C 56.29 0.5 58.88 3.57 58.3 6.93 L 51.46 46.68 C 51.16 48.44 50.02 49.95 48.4 50.71 L 32.36 58.33 C 30.87 59.04 29.13 59.04 27.64 58.33 L 11.6 50.71 C 9.98 49.95 8.84 48.44 8.54 46.68 L 1.7 6.93 C 1.12 3.57 3.71 0.5 7.12 0.5 Z" fill="transparent" stroke="#18162e"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 476 B After Width: | Height: | Size: 397 B |
|
|
@ -1,3 +1,3 @@
|
||||||
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M6.12012 0.5H51.8799C55.2901 0.500041 57.8779 3.57175 57.2998 6.93262L50.4639 46.6777C50.1604 48.4411 49.0179 49.9467 47.4014 50.7139L31.3584 58.3271C29.8661 59.0354 28.1339 59.0354 26.6416 58.3271L10.5986 50.7139C8.98214 49.9467 7.83959 48.4411 7.53613 46.6777L0.700195 6.93262C0.122088 3.57175 2.7099 0.500042 6.12012 0.5Z" fill="#18152E" stroke="#F3C267"/>
|
<path d="M 7.12 0.5 H 52.88 C 56.29 0.5 58.88 3.57 58.3 6.93 L 51.46 46.68 C 51.16 48.44 50.02 49.95 48.4 50.71 L 32.36 58.33 C 30.87 59.04 29.13 59.04 27.64 58.33 L 11.6 50.71 C 9.98 49.95 8.84 48.44 8.54 46.68 L 1.7 6.93 C 1.12 3.57 3.71 0.5 7.12 0.5 Z" fill="#18152E" stroke="#F3C267"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 472 B After Width: | Height: | Size: 393 B |
|
|
@ -3,15 +3,13 @@ import * as applications from './module/applications/_module.mjs';
|
||||||
import * as data from './module/data/_module.mjs';
|
import * as data from './module/data/_module.mjs';
|
||||||
import * as models from './module/data/_module.mjs';
|
import * as models from './module/data/_module.mjs';
|
||||||
import * as documents from './module/documents/_module.mjs';
|
import * as documents from './module/documents/_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 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';
|
||||||
import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs';
|
|
||||||
import { BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll } from './module/dice/_module.mjs';
|
import { BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll } from './module/dice/_module.mjs';
|
||||||
import { enrichedDualityRoll } from './module/enrichers/DualityRollEnricher.mjs';
|
|
||||||
import { enrichedFateRoll, getFateTypeData } from './module/enrichers/FateRollEnricher.mjs';
|
|
||||||
import {
|
import {
|
||||||
handlebarsRegistration,
|
handlebarsRegistration,
|
||||||
runMigrations,
|
runMigrations,
|
||||||
|
|
@ -34,6 +32,8 @@ CONFIG.Dice.daggerheart = {
|
||||||
FateRoll: FateRoll
|
FateRoll: FateRoll
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Object.assign(CONFIG.Dice.termTypes, dice.diceTypes);
|
||||||
|
|
||||||
CONFIG.Actor.documentClass = documents.DhpActor;
|
CONFIG.Actor.documentClass = documents.DhpActor;
|
||||||
CONFIG.Actor.dataModels = models.actors.config;
|
CONFIG.Actor.dataModels = models.actors.config;
|
||||||
CONFIG.Actor.collection = collections.DhActorCollection;
|
CONFIG.Actor.collection = collections.DhActorCollection;
|
||||||
|
|
@ -94,6 +94,7 @@ Hooks.once('init', () => {
|
||||||
data,
|
data,
|
||||||
models,
|
models,
|
||||||
documents,
|
documents,
|
||||||
|
macros,
|
||||||
dice,
|
dice,
|
||||||
fields
|
fields
|
||||||
};
|
};
|
||||||
|
|
@ -331,78 +332,6 @@ Hooks.on('renderHandlebarsApplication', (_, element) => {
|
||||||
enricherRenderSetup(element);
|
enricherRenderSetup(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.on('chatMessage', (_, message) => {
|
|
||||||
if (message.startsWith('/dr')) {
|
|
||||||
const result =
|
|
||||||
message.trim().toLowerCase() === '/dr' ? { result: {} } : rollCommandToJSON(message.replace(/\/dr\s?/, ''));
|
|
||||||
if (!result) {
|
|
||||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.dualityParsing'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { result: rollCommand, flavor } = result;
|
|
||||||
|
|
||||||
const reaction = rollCommand.reaction;
|
|
||||||
const traitValue = rollCommand.trait?.toLowerCase();
|
|
||||||
const advantage = rollCommand.advantage
|
|
||||||
? CONFIG.DH.ACTIONS.advantageState.advantage.value
|
|
||||||
: rollCommand.disadvantage
|
|
||||||
? CONFIG.DH.ACTIONS.advantageState.disadvantage.value
|
|
||||||
: undefined;
|
|
||||||
const difficulty = rollCommand.difficulty;
|
|
||||||
const grantResources = rollCommand.grantResources;
|
|
||||||
|
|
||||||
const target = getCommandTarget({ allowNull: true });
|
|
||||||
const title =
|
|
||||||
(flavor ?? traitValue)
|
|
||||||
? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
|
||||||
ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label)
|
|
||||||
})
|
|
||||||
: game.i18n.localize('DAGGERHEART.GENERAL.duality');
|
|
||||||
|
|
||||||
enrichedDualityRoll({
|
|
||||||
reaction,
|
|
||||||
traitValue,
|
|
||||||
target,
|
|
||||||
difficulty,
|
|
||||||
title,
|
|
||||||
label: game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll'),
|
|
||||||
actionType: null,
|
|
||||||
advantage,
|
|
||||||
grantResources
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.startsWith('/fr')) {
|
|
||||||
const result =
|
|
||||||
message.trim().toLowerCase() === '/fr' ? { result: {} } : rollCommandToJSON(message.replace(/\/fr\s?/, ''));
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateParsing'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { result: rollCommand, flavor } = result;
|
|
||||||
const fateTypeData = getFateTypeData(rollCommand?.type);
|
|
||||||
|
|
||||||
if (!fateTypeData)
|
|
||||||
return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing'));
|
|
||||||
|
|
||||||
const { value: fateType, label: fateTypeLabel } = fateTypeData;
|
|
||||||
const target = getCommandTarget({ allowNull: true });
|
|
||||||
const title = flavor ?? game.i18n.localize('DAGGERHEART.GENERAL.fateRoll');
|
|
||||||
|
|
||||||
enrichedFateRoll({
|
|
||||||
target,
|
|
||||||
title,
|
|
||||||
label: fateTypeLabel,
|
|
||||||
fateType
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Hooks.on(CONFIG.DH.HOOKS.hooksConfig.tagTeamStart, async data => {
|
Hooks.on(CONFIG.DH.HOOKS.hooksConfig.tagTeamStart, async data => {
|
||||||
if (data.openForAllPlayers && data.partyId) {
|
if (data.openForAllPlayers && data.partyId) {
|
||||||
const party = game.actors.get(data.partyId);
|
const party = game.actors.get(data.partyId);
|
||||||
|
|
|
||||||
33
lang/en.json
33
lang/en.json
|
|
@ -89,9 +89,14 @@
|
||||||
},
|
},
|
||||||
"Config": {
|
"Config": {
|
||||||
"beastform": {
|
"beastform": {
|
||||||
"exact": "Beastform Max Tier",
|
"exact": { "label": "Beastform Max Tier", "hint": "The Character's Tier is used if empty" },
|
||||||
"exactHint": "The Character's Tier is used if empty",
|
"modifications": {
|
||||||
"label": "Beastform"
|
"traitBonuses": {
|
||||||
|
"label": { "single": "Trait Bonus", "plural": "Trait Bonuses" },
|
||||||
|
"hint": "Pick bonuses you apply to freely chosen traits at the time of transforming",
|
||||||
|
"bonus": "Bonus Amount"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"countdown": {
|
"countdown": {
|
||||||
"defaultOwnership": "Default Ownership",
|
"defaultOwnership": "Default Ownership",
|
||||||
|
|
@ -448,7 +453,8 @@
|
||||||
},
|
},
|
||||||
"DaggerheartMenu": {
|
"DaggerheartMenu": {
|
||||||
"title": "GM Tools",
|
"title": "GM Tools",
|
||||||
"refreshFeatures": "Refresh Features"
|
"refreshFeatures": "Refresh Features",
|
||||||
|
"fallingAndCollision": "Falling And Collision Damage"
|
||||||
},
|
},
|
||||||
"DeleteConfirmation": {
|
"DeleteConfirmation": {
|
||||||
"title": "Delete {type} - {name}",
|
"title": "Delete {type} - {name}",
|
||||||
|
|
@ -1163,6 +1169,12 @@
|
||||||
"description": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fallAndCollision": {
|
||||||
|
"veryClose": { "label": "Very Close", "chatTitle": "Fall Damage: Very Close" },
|
||||||
|
"close": { "label": "Close", "chatTitle": "Fall Damage: Close" },
|
||||||
|
"far": { "label": "Far", "chatTitle": "Fall Damage: Far" },
|
||||||
|
"collision": { "label": "Collision", "chatTitle": "Dangerous Collision" }
|
||||||
|
},
|
||||||
"FeatureForm": {
|
"FeatureForm": {
|
||||||
"label": "Feature Form",
|
"label": "Feature Form",
|
||||||
"passive": "Passive",
|
"passive": "Passive",
|
||||||
|
|
@ -2567,6 +2579,13 @@
|
||||||
"secondaryWeapon": "Secondary Weapon"
|
"secondaryWeapon": "Secondary Weapon"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"MACROS": {
|
||||||
|
"Spotlight": {
|
||||||
|
"errors": {
|
||||||
|
"noTokenSelected": "A token on the canvas must either be selected or hovered to spotlight it"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ROLLTABLES": {
|
"ROLLTABLES": {
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
"formulaName": { "label": "Formula Name" }
|
"formulaName": { "label": "Formula Name" }
|
||||||
|
|
@ -2810,6 +2829,12 @@
|
||||||
"setResourceIdentifier": "Set Resource Identifier"
|
"setResourceIdentifier": "Set Resource Identifier"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Keybindings": {
|
||||||
|
"spotlight": {
|
||||||
|
"name": "Spotlight Combatant",
|
||||||
|
"hint": "Move the spotlight to a hovered or selected token that's present in an active encounter"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Menu": {
|
"Menu": {
|
||||||
"title": "Daggerheart Game Settings",
|
"title": "Daggerheart Game Settings",
|
||||||
"automation": {
|
"automation": {
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@ export * as documents from './documents/_module.mjs';
|
||||||
export * as enrichers from './enrichers/_module.mjs';
|
export * as enrichers from './enrichers/_module.mjs';
|
||||||
export * as helpers from './helpers/_module.mjs';
|
export * as helpers from './helpers/_module.mjs';
|
||||||
export * as systemRegistration from './systemRegistration/_module.mjs';
|
export * as systemRegistration from './systemRegistration/_module.mjs';
|
||||||
|
export * as macros from './macros/_modules.mjs';
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
this.selected = null;
|
this.selected = null;
|
||||||
this.evolved = { form: null };
|
this.evolved = { form: null };
|
||||||
this.hybrid = { forms: {}, advantages: {}, features: {} };
|
this.hybrid = { forms: {}, advantages: {}, features: {} };
|
||||||
|
this.modifications = {
|
||||||
|
traitBonuses: configData.modifications.traitBonuses.map(x => ({
|
||||||
|
trait: null,
|
||||||
|
bonus: x.bonus
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
this._dragDrop = this._createDragDropHandlers();
|
this._dragDrop = this._createDragDropHandlers();
|
||||||
}
|
}
|
||||||
|
|
@ -28,6 +34,7 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
selectBeastform: this.selectBeastform,
|
selectBeastform: this.selectBeastform,
|
||||||
toggleHybridFeature: this.toggleHybridFeature,
|
toggleHybridFeature: this.toggleHybridFeature,
|
||||||
toggleHybridAdvantage: this.toggleHybridAdvantage,
|
toggleHybridAdvantage: this.toggleHybridAdvantage,
|
||||||
|
toggleTraitBonus: this.toggleTraitBonus,
|
||||||
submitBeastform: this.submitBeastform
|
submitBeastform: this.submitBeastform
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
|
|
@ -48,6 +55,7 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
tabs: { template: 'systems/daggerheart/templates/dialogs/beastform/tabs.hbs' },
|
tabs: { template: 'systems/daggerheart/templates/dialogs/beastform/tabs.hbs' },
|
||||||
beastformTier: { template: 'systems/daggerheart/templates/dialogs/beastform/beastformTier.hbs' },
|
beastformTier: { template: 'systems/daggerheart/templates/dialogs/beastform/beastformTier.hbs' },
|
||||||
advanced: { template: 'systems/daggerheart/templates/dialogs/beastform/advanced.hbs' },
|
advanced: { template: 'systems/daggerheart/templates/dialogs/beastform/advanced.hbs' },
|
||||||
|
modifications: { template: 'systems/daggerheart/templates/dialogs/beastform/modifications.hbs' },
|
||||||
footer: { template: 'systems/daggerheart/templates/dialogs/beastform/footer.hbs' }
|
footer: { template: 'systems/daggerheart/templates/dialogs/beastform/footer.hbs' }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -146,6 +154,9 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
context.modifications = this.modifications;
|
||||||
|
context.traits = CONFIG.DH.ACTOR.abilities;
|
||||||
|
|
||||||
context.tier = beastformTiers[this.tabGroups.primary];
|
context.tier = beastformTiers[this.tabGroups.primary];
|
||||||
context.tierKey = this.tabGroups.primary;
|
context.tierKey = this.tabGroups.primary;
|
||||||
|
|
||||||
|
|
@ -155,6 +166,9 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
}
|
}
|
||||||
|
|
||||||
canSubmit() {
|
canSubmit() {
|
||||||
|
const modificationsFinished = this.modifications.traitBonuses.every(x => x.trait);
|
||||||
|
if (!modificationsFinished) return false;
|
||||||
|
|
||||||
if (this.selected) {
|
if (this.selected) {
|
||||||
switch (this.selected.system.beastformType) {
|
switch (this.selected.system.beastformType) {
|
||||||
case 'normal':
|
case 'normal':
|
||||||
|
|
@ -261,6 +275,13 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static toggleTraitBonus(_, button) {
|
||||||
|
const { index, trait } = button.dataset;
|
||||||
|
this.modifications.traitBonuses[index].trait =
|
||||||
|
this.modifications.traitBonuses[index].trait === trait ? null : trait;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
static async submitBeastform() {
|
static async submitBeastform() {
|
||||||
await this.close({ submitted: true });
|
await this.close({ submitted: true });
|
||||||
}
|
}
|
||||||
|
|
@ -292,6 +313,23 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const beastformEffect = selected.effects.find(x => x.type === 'beastform');
|
||||||
|
for (const traitBonus of app.modifications.traitBonuses) {
|
||||||
|
const existingChange = beastformEffect.changes.find(
|
||||||
|
x => x.key === `system.traits.${traitBonus.trait}.value`
|
||||||
|
);
|
||||||
|
if (existingChange) {
|
||||||
|
existingChange.value = Number.parseInt(existingChange.value) + traitBonus.bonus;
|
||||||
|
} else {
|
||||||
|
beastformEffect.changes.push({
|
||||||
|
key: `system.traits.${traitBonus.trait}.value`,
|
||||||
|
mode: 2,
|
||||||
|
priority: null,
|
||||||
|
value: traitBonus.bonus
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
selected: selected,
|
selected: selected,
|
||||||
evolved: { ...app.evolved, form: evolved },
|
evolved: { ...app.evolved, form: evolved },
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
|
|
||||||
partContext.members[partId] = {
|
partContext.members[partId] = {
|
||||||
...data,
|
...data,
|
||||||
|
roll: data.roll,
|
||||||
isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
|
isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
|
||||||
key: partId,
|
key: partId,
|
||||||
readyToRoll: Boolean(data.rollChoice),
|
readyToRoll: Boolean(data.rollChoice),
|
||||||
|
|
@ -448,23 +449,19 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
const { member, diceType } = button.dataset;
|
const { member, diceType } = button.dataset;
|
||||||
const memberData = this.party.system.tagTeam.members[member];
|
const memberData = this.party.system.tagTeam.members[member];
|
||||||
|
|
||||||
const dieIndex = diceType === 'hope' ? 0 : diceType === 'fear' ? 2 : 4;
|
const dieIndex = diceType === 'hope' ? 0 : diceType === 'fear' ? 1 : 2;
|
||||||
|
const newRoll = game.system.api.dice.DualityRoll.fromData(memberData.rollData);
|
||||||
const { parsedRoll, newRoll } = await game.system.api.dice.DualityRoll.reroll(
|
const dice = newRoll.dice[dieIndex];
|
||||||
memberData.rollData,
|
await dice.reroll(`/r1=${dice.total}`, {
|
||||||
dieIndex,
|
liveRoll: {
|
||||||
diceType
|
roll: newRoll,
|
||||||
);
|
isReaction: true
|
||||||
const rollData = parsedRoll.toJSON();
|
}
|
||||||
|
});
|
||||||
|
const rollData = newRoll.toJSON();
|
||||||
this.updatePartyData(
|
this.updatePartyData(
|
||||||
{
|
{
|
||||||
[`system.tagTeam.members.${member}.rollData`]: {
|
[`system.tagTeam.members.${member}.rollData`]: rollData
|
||||||
...rollData,
|
|
||||||
options: {
|
|
||||||
...rollData.options,
|
|
||||||
roll: newRoll
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
this.getUpdatingParts(button)
|
this.getUpdatingParts(button)
|
||||||
);
|
);
|
||||||
|
|
@ -699,7 +696,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
const error = this.checkInitiatorHopeError(this.party.system.tagTeam.initiator);
|
const error = this.checkInitiatorHopeError(this.party.system.tagTeam.initiator);
|
||||||
if (error) return error;
|
if (error) return error;
|
||||||
|
|
||||||
const mainRoll = (await this.getJoinedRoll()).rollData;
|
const joinedRoll = await this.getJoinedRoll();
|
||||||
|
const mainRoll = joinedRoll.rollData;
|
||||||
|
const finalRoll = foundry.utils.deepClone(joinedRoll.roll);
|
||||||
|
|
||||||
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');
|
||||||
|
|
@ -710,7 +709,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
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: mainRoll.options,
|
system: mainRoll.options,
|
||||||
rolls: [mainRoll],
|
rolls: [JSON.stringify(joinedRoll.roll)],
|
||||||
sound: null,
|
sound: null,
|
||||||
flags: { core: { RollTable: true } }
|
flags: { core: { RollTable: true } }
|
||||||
};
|
};
|
||||||
|
|
@ -722,7 +721,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
const fearUpdate = { key: 'fear', value: null, total: null, enabled: true };
|
const fearUpdate = { key: 'fear', value: null, total: null, enabled: true };
|
||||||
for (let memberId in tagTeamData.members) {
|
for (let memberId in tagTeamData.members) {
|
||||||
const resourceUpdates = [];
|
const resourceUpdates = [];
|
||||||
const rollGivesHope = mainRoll.options.roll.isCritical || mainRoll.options.roll.result.duality === 1;
|
const rollGivesHope = finalRoll.isCritical || finalRoll.withHope;
|
||||||
if (memberId === tagTeamData.initiator.memberId) {
|
if (memberId === tagTeamData.initiator.memberId) {
|
||||||
const value = tagTeamData.initiator.cost
|
const value = tagTeamData.initiator.cost
|
||||||
? rollGivesHope
|
? rollGivesHope
|
||||||
|
|
@ -733,9 +732,8 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
} else if (rollGivesHope) {
|
} else if (rollGivesHope) {
|
||||||
resourceUpdates.push({ key: 'hope', value: 1, total: -1, enabled: true });
|
resourceUpdates.push({ key: 'hope', value: 1, total: -1, enabled: true });
|
||||||
}
|
}
|
||||||
if (mainRoll.options.roll.isCritical)
|
if (finalRoll.isCritical) resourceUpdates.push({ key: 'stress', value: -1, total: 1, enabled: true });
|
||||||
resourceUpdates.push({ key: 'stress', value: -1, total: 1, enabled: true });
|
if (finalRoll.withFear) {
|
||||||
if (mainRoll.options.roll.result.duality === -1) {
|
|
||||||
fearUpdate.value = fearUpdate.value === null ? 1 : fearUpdate.value + 1;
|
fearUpdate.value = fearUpdate.value === null ? 1 : fearUpdate.value + 1;
|
||||||
fearUpdate.total = fearUpdate.total === null ? -1 : fearUpdate.total - 1;
|
fearUpdate.total = fearUpdate.total === null ? -1 : fearUpdate.total - 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
editDoc: this.editDoc,
|
editDoc: this.editDoc,
|
||||||
addTrigger: this.addTrigger,
|
addTrigger: this.addTrigger,
|
||||||
removeTrigger: this.removeTrigger,
|
removeTrigger: this.removeTrigger,
|
||||||
expandTrigger: this.expandTrigger
|
expandTrigger: this.expandTrigger,
|
||||||
|
addBeastformTraitBonus: this.addBeastformTraitBonus,
|
||||||
|
removeBeastformTraitBonus: this.removeBeastformTraitBonus
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
handler: this.updateForm,
|
handler: this.updateForm,
|
||||||
|
|
@ -412,6 +414,21 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async addBeastformTraitBonus() {
|
||||||
|
const data = this.action.toObject();
|
||||||
|
data.beastform.modifications.traitBonuses = [
|
||||||
|
...data.beastform.modifications.traitBonuses,
|
||||||
|
this.action.schema.fields.beastform.fields.modifications.fields.traitBonuses.element.getInitialValue()
|
||||||
|
];
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async removeBeastformTraitBonus(_event, button) {
|
||||||
|
const data = this.action.toObject();
|
||||||
|
data.beastform.modifications.traitBonuses.splice(button.dataset.index, 1);
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
updateSummonCount(event) {
|
updateSummonCount(event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const wrapper = event.target.closest('.summon-count-wrapper');
|
const wrapper = event.target.closest('.summon-count-wrapper');
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,6 @@ export default function DHApplicationMixin(Base) {
|
||||||
*/
|
*/
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
this._setupDragDrop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -175,9 +173,6 @@ export default function DHApplicationMixin(Base) {
|
||||||
_attachPartListeners(partId, htmlElement, options) {
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
/* Core dragDrop from ActorDocument is always only 1. Possible we could refactor our own */
|
|
||||||
if (Array.isArray(this._dragDrop)) this._dragDrop.forEach(d => d.bind(htmlElement));
|
|
||||||
|
|
||||||
// Handle delta inputs
|
// Handle delta inputs
|
||||||
for (const deltaInput of htmlElement.querySelectorAll('input[data-allow-delta]')) {
|
for (const deltaInput of htmlElement.querySelectorAll('input[data-allow-delta]')) {
|
||||||
deltaInput.dataset.numValue = deltaInput.value;
|
deltaInput.dataset.numValue = deltaInput.value;
|
||||||
|
|
@ -289,6 +284,16 @@ export default function DHApplicationMixin(Base) {
|
||||||
async _onRender(context, options) {
|
async _onRender(context, options) {
|
||||||
await super._onRender(context, options);
|
await super._onRender(context, options);
|
||||||
this._createTagifyElements(this.options.tagifyConfigs);
|
this._createTagifyElements(this.options.tagifyConfigs);
|
||||||
|
|
||||||
|
for (const d of this.options.dragDrop) {
|
||||||
|
new foundry.applications.ux.DragDrop.implementation({
|
||||||
|
...d,
|
||||||
|
callbacks: {
|
||||||
|
dragstart: this._onDragStart.bind(this),
|
||||||
|
drop: this._onDrop.bind(this)
|
||||||
|
}
|
||||||
|
}).bind(this.element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -349,26 +354,6 @@ export default function DHApplicationMixin(Base) {
|
||||||
/* Drag and Drop */
|
/* Drag and Drop */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates drag-drop handlers from the configured options.
|
|
||||||
* @returns {foundry.applications.ux.DragDrop[]}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_setupDragDrop() {
|
|
||||||
if (this._dragDrop) {
|
|
||||||
this._dragDrop.callbacks.dragStart = this._onDragStart;
|
|
||||||
this._dragDrop.callback.drop = this._onDrop;
|
|
||||||
} else {
|
|
||||||
this._dragDrop = this.options.dragDrop.map(d => {
|
|
||||||
d.callbacks = {
|
|
||||||
dragstart: this._onDragStart.bind(this),
|
|
||||||
drop: this._onDrop.bind(this)
|
|
||||||
};
|
|
||||||
return new foundry.applications.ux.DragDrop.implementation(d);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle dragStart event.
|
* Handle dragStart event.
|
||||||
* @param {DragEvent} event
|
* @param {DragEvent} event
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
'systems/daggerheart/templates/ui/chat/action.hbs',
|
'systems/daggerheart/templates/ui/chat/action.hbs',
|
||||||
systemData
|
systemData
|
||||||
),
|
),
|
||||||
title: game.i18n.localize('DAGGERHEART.ACTIONS.Config.displayInChat'),
|
|
||||||
speaker: cls.getSpeaker(),
|
speaker: cls.getSpeaker(),
|
||||||
flags: {
|
flags: {
|
||||||
daggerheart: {
|
daggerheart: {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
selectRefreshable: DaggerheartMenu.#selectRefreshable,
|
selectRefreshable: DaggerheartMenu.#selectRefreshable,
|
||||||
refreshActors: DaggerheartMenu.#refreshActors
|
refreshActors: DaggerheartMenu.#refreshActors,
|
||||||
|
createFallCollisionDamage: DaggerheartMenu.#createFallCollisionDamage
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -50,6 +51,7 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
||||||
const context = await super._prepareContext(options);
|
const context = await super._prepareContext(options);
|
||||||
context.refreshables = this.refreshSelections;
|
context.refreshables = this.refreshSelections;
|
||||||
context.disableRefresh = Object.values(this.refreshSelections).every(x => !x.selected);
|
context.disableRefresh = Object.values(this.refreshSelections).every(x => !x.selected);
|
||||||
|
context.fallAndCollision = CONFIG.DH.GENERAL.fallAndCollisionDamage;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -71,4 +73,22 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
||||||
this.refreshSelections = DaggerheartMenu.defaultRefreshSelections();
|
this.refreshSelections = DaggerheartMenu.defaultRefreshSelections();
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #createFallCollisionDamage(_event, button) {
|
||||||
|
const data = CONFIG.DH.GENERAL.fallAndCollisionDamage[button.dataset.key];
|
||||||
|
const roll = new Roll(data.damageFormula);
|
||||||
|
await roll.evaluate();
|
||||||
|
|
||||||
|
/* class BaseRoll needed to get rendered by foundryRoll.hbs */
|
||||||
|
const rollJSON = roll.toJSON();
|
||||||
|
rollJSON.class = 'BaseRoll';
|
||||||
|
|
||||||
|
foundry.documents.ChatMessage.implementation.create({
|
||||||
|
title: game.i18n.localize(data.chatTitle),
|
||||||
|
author: game.user.id,
|
||||||
|
speaker: foundry.documents.ChatMessage.implementation.getSpeaker(),
|
||||||
|
rolls: [rollJSON],
|
||||||
|
sound: CONFIG.sounds.dice
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import { abilities } from '../../config/actorConfig.mjs';
|
import { abilities } from '../../config/actorConfig.mjs';
|
||||||
import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
import { enrichedDualityRoll } from '../../enrichers/DualityRollEnricher.mjs';
|
||||||
|
import { enrichedFateRoll, getFateTypeData } from '../../enrichers/FateRollEnricher.mjs';
|
||||||
|
import { getCommandTarget, rollCommandToJSON } from '../../helpers/utils.mjs';
|
||||||
|
import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.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) {
|
||||||
|
|
@ -21,6 +24,84 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
classes: ['daggerheart']
|
classes: ['daggerheart']
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static CHAT_COMMANDS = {
|
||||||
|
...super.CHAT_COMMANDS,
|
||||||
|
dr: {
|
||||||
|
rgx: /^(?:\/dr)((?:\s)[^]*)?/,
|
||||||
|
fn: (_, match) => {
|
||||||
|
const argString = match[1]?.trim();
|
||||||
|
const result = argString ? rollCommandToJSON(argString) : { result: {} };
|
||||||
|
if (!result) {
|
||||||
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.dualityParsing'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result: rollCommand, flavor } = result;
|
||||||
|
|
||||||
|
const reaction = rollCommand.reaction;
|
||||||
|
const traitValue = rollCommand.trait?.toLowerCase();
|
||||||
|
const advantage = rollCommand.advantage
|
||||||
|
? CONFIG.DH.ACTIONS.advantageState.advantage.value
|
||||||
|
: rollCommand.disadvantage
|
||||||
|
? CONFIG.DH.ACTIONS.advantageState.disadvantage.value
|
||||||
|
: undefined;
|
||||||
|
const difficulty = rollCommand.difficulty;
|
||||||
|
const grantResources = rollCommand.grantResources;
|
||||||
|
|
||||||
|
const target = getCommandTarget({ allowNull: true });
|
||||||
|
const title =
|
||||||
|
(flavor ?? traitValue)
|
||||||
|
? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||||
|
ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label)
|
||||||
|
})
|
||||||
|
: game.i18n.localize('DAGGERHEART.GENERAL.duality');
|
||||||
|
|
||||||
|
enrichedDualityRoll({
|
||||||
|
reaction,
|
||||||
|
traitValue,
|
||||||
|
target,
|
||||||
|
difficulty,
|
||||||
|
title,
|
||||||
|
label: game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll'),
|
||||||
|
actionType: null,
|
||||||
|
advantage,
|
||||||
|
grantResources
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
rgx: /^(?:\/fr)((?:\s)[^]*)?/,
|
||||||
|
fn: (_, match) => {
|
||||||
|
const argString = match[1]?.trim();
|
||||||
|
const result = argString ? rollCommandToJSON(argString) : { result: {} };
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateParsing'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result: rollCommand, flavor } = result;
|
||||||
|
const fateTypeData = getFateTypeData(rollCommand?.type);
|
||||||
|
|
||||||
|
if (!fateTypeData)
|
||||||
|
return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing'));
|
||||||
|
|
||||||
|
const { value: fateType, label: fateTypeLabel } = fateTypeData;
|
||||||
|
const target = getCommandTarget({ allowNull: true });
|
||||||
|
const title = flavor ?? game.i18n.localize('DAGGERHEART.GENERAL.fateRoll');
|
||||||
|
|
||||||
|
enrichedFateRoll({
|
||||||
|
target,
|
||||||
|
title,
|
||||||
|
label: fateTypeLabel,
|
||||||
|
fateType
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
_getEntryContextOptions() {
|
_getEntryContextOptions() {
|
||||||
return [
|
return [
|
||||||
...super._getEntryContextOptions(),
|
...super._getEntryContextOptions(),
|
||||||
|
|
@ -175,7 +256,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
action.use(event);
|
action.use(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
async rerollEvent(event, message) {
|
async rerollEvent(event, messageData) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (!event.shiftKey) {
|
if (!event.shiftKey) {
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
|
@ -187,6 +268,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const message = game.messages.get(messageData._id);
|
||||||
const target = event.target.closest('[data-die-index]');
|
const target = event.target.closest('[data-die-index]');
|
||||||
|
|
||||||
if (target.dataset.type === 'damage') {
|
if (target.dataset.type === 'damage') {
|
||||||
|
|
@ -209,27 +291,16 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0];
|
const rerollDice = message.system.roll.dice[target.dataset.dieIndex];
|
||||||
const rollClass =
|
await rerollDice.reroll(`/r1=${rerollDice.total}`, {
|
||||||
game.system.api.dice[
|
liveRoll: {
|
||||||
message.type === 'dualityRoll'
|
roll: message.system.roll,
|
||||||
? 'DualityRoll'
|
actor: message.system.actionActor,
|
||||||
: target.dataset.type === 'damage'
|
isReaction: message.system.roll.options.actionType === 'reaction'
|
||||||
? 'DHRoll'
|
}
|
||||||
: 'D20Roll'
|
});
|
||||||
];
|
await message.update({
|
||||||
|
rolls: [message.system.roll.toJSON()]
|
||||||
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
|
||||||
|
|
||||||
const { newRoll, parsedRoll } = await rollClass.reroll(
|
|
||||||
originalRoll_parsed,
|
|
||||||
target.dataset.dieIndex,
|
|
||||||
target.dataset.type
|
|
||||||
);
|
|
||||||
|
|
||||||
await game.messages.get(message._id).update({
|
|
||||||
'system.roll': newRoll,
|
|
||||||
'rolls': [parsedRoll]
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
|
import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
|
||||||
import { expireActiveEffects } from '../../helpers/utils.mjs';
|
import { expireActiveEffects } from '../../helpers/utils.mjs';
|
||||||
|
import { clearPreviousSpotlight } from '../../macros/spotlightCombatant.mjs';
|
||||||
|
|
||||||
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
|
|
@ -150,13 +151,13 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
||||||
}
|
}
|
||||||
|
|
||||||
async setCombatantSpotlight(combatantId) {
|
async setCombatantSpotlight(combatantId) {
|
||||||
|
const combatant = this.viewed.combatants.get(combatantId);
|
||||||
const update = {
|
const update = {
|
||||||
system: {
|
system: {
|
||||||
'spotlight.requesting': false,
|
'spotlight.requesting': false,
|
||||||
'spotlight.requestOrderIndex': 0
|
'spotlight.requestOrderIndex': 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const combatant = this.viewed.combatants.get(combatantId);
|
|
||||||
|
|
||||||
const toggleTurn = this.viewed.combatants.contents
|
const toggleTurn = this.viewed.combatants.contents
|
||||||
.sort(this.viewed._sortCombatants)
|
.sort(this.viewed._sortCombatants)
|
||||||
|
|
@ -187,6 +188,14 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
||||||
round: this.viewed.round + 1
|
round: this.viewed.round + 1
|
||||||
});
|
});
|
||||||
await combatant.update(update);
|
await combatant.update(update);
|
||||||
|
if (combatant.token) clearPreviousSpotlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearTurn() {
|
||||||
|
await this.viewed.update({
|
||||||
|
turn: null,
|
||||||
|
round: this.viewed.round + 1
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async requestSpotlight(_, target) {
|
static async requestSpotlight(_, target) {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,36 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
this.previewHelp ||= this.addChild(this.#drawPreviewHelp());
|
this.previewHelp ||= this.addChild(this.#drawPreviewHelp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**@inheritdoc */
|
||||||
|
_refreshTurnMarker() {
|
||||||
|
// Should a Turn Marker be active?
|
||||||
|
const { turnMarker } = this.document;
|
||||||
|
const markersEnabled =
|
||||||
|
CONFIG.Combat.settings.turnMarker.enabled && turnMarker.mode !== CONST.TOKEN_TURN_MARKER_MODES.DISABLED;
|
||||||
|
const spotlighted = game.settings
|
||||||
|
.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker)
|
||||||
|
.spotlightedTokens.has(this.document.uuid);
|
||||||
|
|
||||||
|
const turnIsSet = typeof game.combat?.turn === 'number';
|
||||||
|
const isTurn = game.combat?.combatant?.tokenId === this.id;
|
||||||
|
const markerActive = markersEnabled && turnIsSet ? isTurn : spotlighted;
|
||||||
|
|
||||||
|
// Activate a Turn Marker
|
||||||
|
if (markerActive) {
|
||||||
|
if (!this.turnMarker)
|
||||||
|
this.turnMarker = this.addChildAt(new foundry.canvas.placeables.tokens.TokenTurnMarker(this), 0);
|
||||||
|
canvas.tokens.turnMarkers.add(this);
|
||||||
|
this.turnMarker.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a Turn Marker
|
||||||
|
else if (this.turnMarker) {
|
||||||
|
canvas.tokens.turnMarkers.delete(this);
|
||||||
|
this.turnMarker.destroy();
|
||||||
|
this.turnMarker = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
async _drawEffects() {
|
async _drawEffects() {
|
||||||
this.effects.renderable = false;
|
this.effects.renderable = false;
|
||||||
|
|
|
||||||
|
|
@ -1102,3 +1102,29 @@ export const comparator = {
|
||||||
label: 'DAGGERHEART.CONFIG.Comparator.lte'
|
label: 'DAGGERHEART.CONFIG.Comparator.lte'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
export const fallAndCollisionDamage = {
|
||||||
|
veryClose: {
|
||||||
|
id: 'veryClose',
|
||||||
|
label: 'DAGGERHEART.CONFIG.fallAndCollision.veryClose.label',
|
||||||
|
chatTitle: 'DAGGERHEART.CONFIG.fallAndCollision.veryClose.chatTitle',
|
||||||
|
damageFormula: '1d10 + 3'
|
||||||
|
},
|
||||||
|
close: {
|
||||||
|
id: 'veryClose',
|
||||||
|
label: 'DAGGERHEART.CONFIG.fallAndCollision.close.label',
|
||||||
|
chatTitle: 'DAGGERHEART.CONFIG.fallAndCollision.close.chatTitle',
|
||||||
|
damageFormula: '1d20 + 5'
|
||||||
|
},
|
||||||
|
far: {
|
||||||
|
id: 'veryClose',
|
||||||
|
label: 'DAGGERHEART.CONFIG.fallAndCollision.far.label',
|
||||||
|
chatTitle: 'DAGGERHEART.CONFIG.fallAndCollision.far.chatTitle',
|
||||||
|
damageFormula: '1d100 + 15'
|
||||||
|
},
|
||||||
|
collision: {
|
||||||
|
id: 'veryClose',
|
||||||
|
label: 'DAGGERHEART.CONFIG.fallAndCollision.collision.label',
|
||||||
|
chatTitle: 'DAGGERHEART.CONFIG.fallAndCollision.collision.chatTitle',
|
||||||
|
damageFormula: '1d20 + 5'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
export const keybindings = {
|
||||||
|
spotlight: 'DHSpotlight'
|
||||||
|
};
|
||||||
|
|
||||||
export const menu = {
|
export const menu = {
|
||||||
Automation: {
|
Automation: {
|
||||||
Name: 'GameSettingsAutomation',
|
Name: 'GameSettingsAutomation',
|
||||||
|
|
@ -35,7 +39,8 @@ export const gameSettings = {
|
||||||
Countdowns: 'Countdowns',
|
Countdowns: 'Countdowns',
|
||||||
LastMigrationVersion: 'LastMigrationVersion',
|
LastMigrationVersion: 'LastMigrationVersion',
|
||||||
SpotlightRequestQueue: 'SpotlightRequestQueue',
|
SpotlightRequestQueue: 'SpotlightRequestQueue',
|
||||||
CompendiumBrowserSettings: 'CompendiumBrowserSettings'
|
CompendiumBrowserSettings: 'CompendiumBrowserSettings',
|
||||||
|
SpotlightTracker: 'SpotlightTracker'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actionAutomationChoices = {
|
export const actionAutomationChoices = {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ export { default as DhRollTable } from './rollTable.mjs';
|
||||||
export { default as RegisteredTriggers } from './registeredTriggers.mjs';
|
export { default as RegisteredTriggers } from './registeredTriggers.mjs';
|
||||||
export { default as CompendiumBrowserSettings } from './compendiumBrowserSettings.mjs';
|
export { default as CompendiumBrowserSettings } from './compendiumBrowserSettings.mjs';
|
||||||
export { default as TagTeamData } from './tagTeamData.mjs';
|
export { default as TagTeamData } from './tagTeamData.mjs';
|
||||||
|
export { default as SpotlightTracker } from './spotlightTracker.mjs';
|
||||||
|
|
||||||
export * as countdowns from './countdowns.mjs';
|
export * as countdowns from './countdowns.mjs';
|
||||||
export * as actions from './action/_module.mjs';
|
export * as actions from './action/_module.mjs';
|
||||||
|
|
|
||||||
|
|
@ -166,12 +166,11 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
this.parent.actor?.type === 'character' &&
|
this.parent.actor?.type === 'character' &&
|
||||||
this.parent.actor.system.resources.armor
|
this.parent.actor.system.resources.armor
|
||||||
) {
|
) {
|
||||||
const newArmorTotal = (changed.system?.changes ?? []).reduce((acc, change) => {
|
const armorEffect = changed.system?.changes?.find(x => x.type === 'armor');
|
||||||
if (change.type === 'armor') acc += change.value.current;
|
const newArmorTotal =
|
||||||
return acc;
|
armorEffect?.value?.current + (this.parent.actor.system.armor?.system?.armor?.current ?? 0);
|
||||||
}, this.parent.actor.system.armor?.system?.armor?.current ?? 0);
|
|
||||||
|
|
||||||
if (newArmorTotal !== this.parent.actor.system.armorScore.value) {
|
if (armorEffect && newArmorTotal !== this.parent.actor.system.armorScore.value) {
|
||||||
const armorData = getScrollTextData(this.parent.actor, { value: newArmorTotal }, 'armor');
|
const armorData = getScrollTextData(this.parent.actor, { value: newArmorTotal }, 'armor');
|
||||||
options.scrollingTextData = [armorData];
|
options.scrollingTextData = [armorData];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export default class BeastformEffect extends BaseEffect {
|
||||||
width: new fields.NumberField({ integer: false, nullable: true })
|
width: new fields.NumberField({ integer: false, nullable: true })
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
advantageOn: new fields.ArrayField(new fields.StringField()),
|
advantageOn: new fields.TypedObjectField(new fields.SchemaField({ value: new fields.StringField() })),
|
||||||
featureIds: new fields.ArrayField(new fields.StringField()),
|
featureIds: new fields.ArrayField(new fields.StringField()),
|
||||||
effectIds: new fields.ArrayField(new fields.StringField())
|
effectIds: new fields.ArrayField(new fields.StringField())
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -757,7 +757,6 @@ export default class DhCharacter extends DhCreature {
|
||||||
|
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
super.prepareDerivedData();
|
super.prepareDerivedData();
|
||||||
let baseHope = this.resources.hope.value;
|
|
||||||
if (this.companion) {
|
if (this.companion) {
|
||||||
for (let levelKey in this.companion.system.levelData.levelups) {
|
for (let levelKey in this.companion.system.levelData.levelups) {
|
||||||
const level = this.companion.system.levelData.levelups[levelKey];
|
const level = this.companion.system.levelData.levelups[levelKey];
|
||||||
|
|
@ -772,7 +771,6 @@ export default class DhCharacter extends DhCreature {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resources.hope.max -= this.scars;
|
this.resources.hope.max -= this.scars;
|
||||||
this.resources.hope.value = Math.min(baseHope, this.resources.hope.max);
|
|
||||||
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
||||||
|
|
||||||
this.resources.armor = {
|
this.resources.armor = {
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,14 @@ export default class DhCreature extends BaseDataActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareDerivedData() {
|
||||||
|
const minLimitResource = resource => {
|
||||||
|
if (resource) resource.value = Math.min(resource.value, resource.max);
|
||||||
|
};
|
||||||
|
|
||||||
|
minLimitResource(this.resources.stress);
|
||||||
|
minLimitResource(this.resources.hitPoints);
|
||||||
|
minLimitResource(this.resources.hope);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
return {
|
return {
|
||||||
title: new fields.StringField(),
|
title: new fields.StringField(),
|
||||||
actionDescription: new fields.HTMLField(),
|
actionDescription: new fields.HTMLField(),
|
||||||
roll: new fields.ObjectField(),
|
|
||||||
targets: targetsField(),
|
targets: targetsField(),
|
||||||
hasRoll: new fields.BooleanField({ initial: false }),
|
hasRoll: new fields.BooleanField({ initial: false }),
|
||||||
hasDamage: new fields.BooleanField({ initial: false }),
|
hasDamage: new fields.BooleanField({ initial: false }),
|
||||||
|
|
@ -55,6 +54,16 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get roll() {
|
||||||
|
if (this.parent.type === 'dualityRoll')
|
||||||
|
return this.parent.rolls.find(x => x instanceof game.system.api.dice.DualityRoll);
|
||||||
|
|
||||||
|
if (this.parent.type === 'fateRoll')
|
||||||
|
return this.parent.rolls.find(x => x instanceof game.system.api.dice.FateRoll);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
get actionActor() {
|
get actionActor() {
|
||||||
if (!this.source.actor) return null;
|
if (!this.source.actor) return null;
|
||||||
return fromUuidSync(this.source.actor);
|
return fromUuidSync(this.source.actor);
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,21 @@ export default class BeastformField extends fields.SchemaField {
|
||||||
{ 1: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') }
|
{ 1: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') }
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
hint: 'DAGGERHEART.ACTIONS.Config.beastform.exactHint'
|
label: 'DAGGERHEART.ACTIONS.Config.beastform.exact.label',
|
||||||
|
hint: 'DAGGERHEART.ACTIONS.Config.beastform.exact.hint'
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
modifications: new fields.SchemaField({
|
||||||
|
traitBonuses: new fields.ArrayField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
bonus: new fields.NumberField({
|
||||||
|
integer: true,
|
||||||
|
initial: 1,
|
||||||
|
min: 1,
|
||||||
|
label: 'DAGGERHEART.ACTIONS.Config.beastform.modifications.traitBonuses.bonus'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
super(beastformFields, options, context);
|
super(beastformFields, options, context);
|
||||||
|
|
@ -66,15 +79,9 @@ export default class BeastformField extends fields.SchemaField {
|
||||||
) ?? 1;
|
) ?? 1;
|
||||||
|
|
||||||
config.tierLimit = this.beastform.tierAccess.exact ?? actorTier;
|
config.tierLimit = this.beastform.tierAccess.exact ?? actorTier;
|
||||||
|
config.modifications = this.beastform.modifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO by Harry
|
|
||||||
* @param {*} selectedForm
|
|
||||||
* @param {*} evolvedData
|
|
||||||
* @param {*} hybridData
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
static async transform(selectedForm, evolvedData, hybridData) {
|
static async transform(selectedForm, evolvedData, hybridData) {
|
||||||
const formData = evolvedData?.form ?? selectedForm;
|
const formData = evolvedData?.form ?? selectedForm;
|
||||||
const beastformEffect = formData.effects.find(x => x.type === 'beastform');
|
const beastformEffect = formData.effects.find(x => x.type === 'beastform');
|
||||||
|
|
|
||||||
|
|
@ -99,10 +99,14 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
|
|
||||||
get beastformAttackData() {
|
get beastformAttackData() {
|
||||||
const effect = this.parent.effects.find(x => x.type === 'beastform');
|
const effect = this.parent.effects.find(x => x.type === 'beastform');
|
||||||
|
return DHBeastform.getBeastformAttackData(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getBeastformAttackData(effect) {
|
||||||
if (!effect) return null;
|
if (!effect) return null;
|
||||||
|
|
||||||
const traitBonus =
|
const mainTrait = effect.system.changes.find(x => x.key === 'system.rules.attack.roll.trait')?.value;
|
||||||
effect.system.changes.find(x => x.key === `system.traits.${this.mainTrait}.value`)?.value ?? 0;
|
const traitBonus = effect.system.changes.find(x => x.key === `system.traits.${mainTrait}.value`)?.value ?? 0;
|
||||||
const evasionBonus = effect.system.changes.find(x => x.key === 'system.evasion')?.value ?? 0;
|
const evasionBonus = effect.system.changes.find(x => x.key === 'system.evasion')?.value ?? 0;
|
||||||
|
|
||||||
const damageDiceIndex = effect.system.changes.find(x => x.key === 'system.rules.attack.damage.diceIndex');
|
const damageDiceIndex = effect.system.changes.find(x => x.key === 'system.rules.attack.damage.diceIndex');
|
||||||
|
|
@ -110,7 +114,7 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
const damageBonus = effect.system.changes.find(x => x.key === 'system.rules.attack.damage.bonus')?.value ?? 0;
|
const damageBonus = effect.system.changes.find(x => x.key === 'system.rules.attack.damage.bonus')?.value ?? 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
trait: game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.mainTrait].label),
|
trait: game.i18n.localize(CONFIG.DH.ACTOR.abilities[mainTrait]?.label),
|
||||||
traitBonus: traitBonus ? Number(traitBonus).signedString() : '',
|
traitBonus: traitBonus ? Number(traitBonus).signedString() : '',
|
||||||
evasionBonus: evasionBonus ? Number(evasionBonus).signedString() : '',
|
evasionBonus: evasionBonus ? Number(evasionBonus).signedString() : '',
|
||||||
damageDice: damageDice,
|
damageDice: damageDice,
|
||||||
|
|
|
||||||
9
module/data/spotlightTracker.mjs
Normal file
9
module/data/spotlightTracker.mjs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
export default class SpotlightTracker extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
spotlightedTokens: new fields.SetField(new fields.DocumentUUIDField())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,3 +4,4 @@ export { default as DamageRoll } from './damageRoll.mjs';
|
||||||
export { default as DHRoll } from './dhRoll.mjs';
|
export { default as DHRoll } from './dhRoll.mjs';
|
||||||
export { default as DualityRoll } from './dualityRoll.mjs';
|
export { default as DualityRoll } from './dualityRoll.mjs';
|
||||||
export { default as FateRoll } from './fateRoll.mjs';
|
export { default as FateRoll } from './fateRoll.mjs';
|
||||||
|
export { diceTypes } from './die/_module.mjs';
|
||||||
|
|
|
||||||
|
|
@ -217,49 +217,11 @@ export default class D20Roll extends DHRoll {
|
||||||
results: d.results
|
results: d.results
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
data.modifierTotal = this.calculateTotalModifiers(roll);
|
data.modifierTotal = roll.modifierTotal;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetFormula() {
|
resetFormula() {
|
||||||
return (this._formula = this.constructor.getFormula(this.terms));
|
return (this._formula = this.constructor.getFormula(this.terms));
|
||||||
}
|
}
|
||||||
|
|
||||||
static async reroll(rollString, _target, message) {
|
|
||||||
let parsedRoll = game.system.api.dice.D20Roll.fromData(rollString);
|
|
||||||
parsedRoll = await parsedRoll.reroll();
|
|
||||||
const newRoll = game.system.api.dice.D20Roll.postEvaluate(parsedRoll, {
|
|
||||||
targets: message.system.targets,
|
|
||||||
roll: {
|
|
||||||
advantage: message.system.roll.advantage?.type,
|
|
||||||
difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (game.modules.get('dice-so-nice')?.active) {
|
|
||||||
await game.dice3d.showForRoll(parsedRoll, game.user, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rerolled = {
|
|
||||||
any: true,
|
|
||||||
rerolls: [
|
|
||||||
...(message.system.roll.dice[0].rerolled?.rerolls?.length > 0
|
|
||||||
? [message.system.roll.dice[0].rerolled?.rerolls]
|
|
||||||
: []),
|
|
||||||
rollString.terms[0].results
|
|
||||||
]
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
newRoll: {
|
|
||||||
...newRoll,
|
|
||||||
dice: [
|
|
||||||
{
|
|
||||||
...newRoll.dice[0],
|
|
||||||
rerolled: rerolled
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
parsedRoll
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@ export default class DHRoll extends Roll {
|
||||||
return game.i18n.localize('DAGGERHEART.GENERAL.Roll.basic');
|
return game.i18n.localize('DAGGERHEART.GENERAL.Roll.basic');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get modifierTotal() {
|
||||||
|
return this.constructor.calculateTotalModifiers(this);
|
||||||
|
}
|
||||||
|
|
||||||
static messageType = 'adversaryRoll';
|
static messageType = 'adversaryRoll';
|
||||||
|
|
||||||
static CHAT_TEMPLATE = 'systems/daggerheart/templates/ui/chat/roll.hbs';
|
static CHAT_TEMPLATE = 'systems/daggerheart/templates/ui/chat/roll.hbs';
|
||||||
|
|
@ -122,10 +126,6 @@ export default class DHRoll extends Roll {
|
||||||
if (roll._evaluated) {
|
if (roll._evaluated) {
|
||||||
const message = await cls.create(msgData, { messageMode: config.selectedMessageMode });
|
const message = await cls.create(msgData, { messageMode: config.selectedMessageMode });
|
||||||
|
|
||||||
if (config.tagTeamSelected) {
|
|
||||||
game.system.api.applications.dialogs.TagTeamDialog.assignRoll(message.speakerActor, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roll.formula !== '' && game.modules.get('dice-so-nice')?.active) {
|
if (roll.formula !== '' && game.modules.get('dice-so-nice')?.active) {
|
||||||
await game.dice3d.waitFor3DAnimationByMessageID(message.id);
|
await game.dice3d.waitFor3DAnimationByMessageID(message.id);
|
||||||
}
|
}
|
||||||
|
|
@ -142,6 +142,7 @@ export default class DHRoll extends Roll {
|
||||||
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, {
|
||||||
...chatData,
|
...chatData,
|
||||||
|
roll: this,
|
||||||
parent: chatData.parent,
|
parent: chatData.parent,
|
||||||
targetMode: chatData.targetMode,
|
targetMode: chatData.targetMode,
|
||||||
metagamingSettings
|
metagamingSettings
|
||||||
|
|
@ -245,16 +246,21 @@ export default class DHRoll extends Roll {
|
||||||
return (this._formula = this.constructor.getFormula(this.terms));
|
return (this._formula = this.constructor.getFormula(this.terms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate total modifiers of any rolls, including non-dh rolls.
|
||||||
|
* This exists because damage rolls still may receive base roll classes
|
||||||
|
*/
|
||||||
static calculateTotalModifiers(roll) {
|
static calculateTotalModifiers(roll) {
|
||||||
let modifierTotal = 0;
|
let modifierTotal = 0;
|
||||||
for (let i = 0; i < roll.terms.length; i++) {
|
for (let i = 0; i < roll.terms.length; i++) {
|
||||||
if (
|
if (!roll.terms[i].isDeterministic) continue;
|
||||||
roll.terms[i] instanceof foundry.dice.terms.NumericTerm &&
|
const termTotal = roll.terms[i].total;
|
||||||
!!roll.terms[i - 1] &&
|
if (typeof termTotal === 'number') {
|
||||||
roll.terms[i - 1] instanceof foundry.dice.terms.OperatorTerm
|
const multiplier = roll.terms[i - 1]?.operator === ' - ' ? -1 : 1;
|
||||||
)
|
modifierTotal += multiplier * termTotal;
|
||||||
modifierTotal += Number(`${roll.terms[i - 1].operator}${roll.terms[i].total}`);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return modifierTotal;
|
return modifierTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
9
module/dice/die/_module.mjs
Normal file
9
module/dice/die/_module.mjs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import DualityDie from './dualityDie.mjs';
|
||||||
|
import AdvantageDie from './advantageDie.mjs';
|
||||||
|
import DisadvantageDie from './disadvantageDie.mjs';
|
||||||
|
|
||||||
|
export const diceTypes = {
|
||||||
|
DualityDie,
|
||||||
|
AdvantageDie,
|
||||||
|
DisadvantageDie
|
||||||
|
};
|
||||||
7
module/dice/die/advantageDie.mjs
Normal file
7
module/dice/die/advantageDie.mjs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
export default class AdvantageDie extends foundry.dice.terms.Die {
|
||||||
|
constructor(options) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
this.modifiers = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
7
module/dice/die/disadvantageDie.mjs
Normal file
7
module/dice/die/disadvantageDie.mjs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
export default class DisadvantageDie extends foundry.dice.terms.Die {
|
||||||
|
constructor(options) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
this.modifiers = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
62
module/dice/die/dualityDie.mjs
Normal file
62
module/dice/die/dualityDie.mjs
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
|
||||||
|
|
||||||
|
export default class DualityDie extends foundry.dice.terms.Die {
|
||||||
|
constructor(options) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
this.modifiers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
#getDualityState(roll) {
|
||||||
|
if (!roll) return null;
|
||||||
|
return roll.withHope ? 1 : roll.withFear ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateResources(oldDuality, newDuality, actor) {
|
||||||
|
const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
||||||
|
if (game.user.isGM ? !hopeFear.gm : !hopeFear.players) return;
|
||||||
|
|
||||||
|
const updates = [];
|
||||||
|
const hope = (newDuality >= 0 ? 1 : 0) - (oldDuality >= 0 ? 1 : 0);
|
||||||
|
const stress = (newDuality === 0 ? 1 : 0) - (oldDuality === 0 ? 1 : 0);
|
||||||
|
const fear = (newDuality === -1 ? 1 : 0) - (oldDuality === -1 ? 1 : 0);
|
||||||
|
|
||||||
|
if (hope !== 0) updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true });
|
||||||
|
if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true });
|
||||||
|
if (fear !== 0) updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true });
|
||||||
|
|
||||||
|
const resourceUpdates = new ResourceUpdateMap(actor);
|
||||||
|
resourceUpdates.addResources(updates);
|
||||||
|
resourceUpdates.updateResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
async reroll(modifier, options) {
|
||||||
|
const oldDuality = this.#getDualityState(options.liveRoll.roll);
|
||||||
|
await super.reroll(modifier, options);
|
||||||
|
|
||||||
|
if (options?.liveRoll) {
|
||||||
|
/* Can't currently test since DiceSoNice is not v14. Might need to set the appearance earlier if a roll is triggered by super.reroll */
|
||||||
|
if (game.modules.get('dice-so-nice')?.active) {
|
||||||
|
const diceSoNiceRoll = {
|
||||||
|
_evaluated: true,
|
||||||
|
dice: [this],
|
||||||
|
options: { appearance: {} }
|
||||||
|
};
|
||||||
|
|
||||||
|
const preset = await getDiceSoNicePreset(diceSoNice[key], faces);
|
||||||
|
diceSoNiceRoll.dice[0].options.appearance = preset.appearance;
|
||||||
|
diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile;
|
||||||
|
|
||||||
|
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||||
|
} else {
|
||||||
|
foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||||
|
}
|
||||||
|
|
||||||
|
await options.liveRoll.roll._evaluate();
|
||||||
|
if (options.liveRoll.isReaction) return;
|
||||||
|
|
||||||
|
const newDuality = this.#getDualityState(options.liveRoll.roll);
|
||||||
|
this.#updateResources(oldDuality, newDuality, options.liveRoll.actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||||
import D20Roll from './d20Roll.mjs';
|
import D20Roll from './d20Roll.mjs';
|
||||||
import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||||
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
|
|
||||||
import { ResourceUpdateMap } from '../data/action/baseAction.mjs';
|
|
||||||
|
|
||||||
export default class DualityRoll extends D20Roll {
|
export default class DualityRoll extends D20Roll {
|
||||||
_advantageFaces = 6;
|
_advantageFaces = 6;
|
||||||
|
|
@ -26,27 +24,31 @@ export default class DualityRoll extends D20Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
get dHope() {
|
get dHope() {
|
||||||
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
if (!(this.dice[0] instanceof game.system.api.dice.diceTypes.DualityDie)) this.createBaseDice();
|
||||||
return this.dice[0];
|
return this.dice[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
set dHope(faces) {
|
set dHope(faces) {
|
||||||
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
// TODO this should not be asymmetrical with the getter. updateRollConfiguration() should use dHope.faces
|
||||||
this.dice[0].faces = this.getFaces(faces);
|
this.dHope.faces = this.getFaces(faces);
|
||||||
}
|
}
|
||||||
|
|
||||||
get dFear() {
|
get dFear() {
|
||||||
if (!(this.dice[1] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
if (!(this.dice[1] instanceof game.system.api.dice.diceTypes.DualityDie)) this.createBaseDice();
|
||||||
return this.dice[1];
|
return this.dice[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
set dFear(faces) {
|
set dFear(faces) {
|
||||||
if (!(this.dice[1] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
// TODO this should not be asymmetrical with the getter. updateRollConfiguration() should use dFear.faces
|
||||||
this.dice[1].faces = this.getFaces(faces);
|
this.dFear.faces = this.getFaces(faces);
|
||||||
}
|
}
|
||||||
|
|
||||||
get dAdvantage() {
|
get dAdvantage() {
|
||||||
return this.dice[2];
|
return this.dice[2] instanceof game.system.api.dice.diceTypes.AdvantageDie ? this.dice[2] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dDisadvantage() {
|
||||||
|
return this.dice[2] instanceof game.system.api.dice.diceTypes.DisadvantageDie ? this.dice[2] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get advantageFaces() {
|
get advantageFaces() {
|
||||||
|
|
@ -65,6 +67,11 @@ export default class DualityRoll extends D20Roll {
|
||||||
this._advantageNumber = Number(value);
|
this._advantageNumber = Number(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get extraDice() {
|
||||||
|
const { DualityDie, AdvantageDie, DisadvantageDie } = game.system.api.dice.diceTypes;
|
||||||
|
return this.dice.filter(x => ![DualityDie, AdvantageDie, DisadvantageDie].some(die => x instanceof die));
|
||||||
|
}
|
||||||
|
|
||||||
setRallyChoices() {
|
setRallyChoices() {
|
||||||
return this.data?.parent?.appliedEffects.reduce((a, c) => {
|
return this.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||||
const change = c.system.changes.find(ch => ch.key === 'system.bonuses.rally');
|
const change = c.system.changes.find(ch => ch.key === 'system.bonuses.rally');
|
||||||
|
|
@ -118,22 +125,28 @@ export default class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static fromData(data) {
|
static fromData(data) {
|
||||||
data.terms[0].class = foundry.dice.terms.Die.name;
|
data.terms[0].class = 'DualityDie';
|
||||||
data.terms[2].class = foundry.dice.terms.Die.name;
|
data.terms[2].class = 'DualityDie';
|
||||||
|
if (data.options.roll.advantage?.type && data.terms[4]?.faces) {
|
||||||
|
data.terms[4].class = data.options.roll.advantage.type === 1 ? 'AdvantageDie' : 'DisadvantageDie';
|
||||||
|
}
|
||||||
return super.fromData(data);
|
return super.fromData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
createBaseDice() {
|
createBaseDice() {
|
||||||
if (this.dice[0] instanceof foundry.dice.terms.Die && this.dice[1] instanceof foundry.dice.terms.Die) {
|
if (
|
||||||
|
this.dice[0] instanceof game.system.api.dice.diceTypes.DualityDie &&
|
||||||
|
this.dice[1] instanceof game.system.api.dice.diceTypes.DualityDie
|
||||||
|
) {
|
||||||
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
|
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.terms[0] = new foundry.dice.terms.Die({
|
this.terms[0] = new game.system.api.dice.diceTypes.DualityDie({
|
||||||
faces: this.data.rules.dualityRoll?.defaultHopeDice ?? 12
|
faces: this.data.rules.dualityRoll?.defaultHopeDice ?? 12
|
||||||
});
|
});
|
||||||
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
|
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
|
||||||
this.terms[2] = new foundry.dice.terms.Die({
|
this.terms[2] = new game.system.api.dice.diceTypes.DualityDie({
|
||||||
faces: this.data.rules.dualityRoll?.defaultFearDice ?? 12
|
faces: this.data.rules.dualityRoll?.defaultFearDice ?? 12
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -305,7 +318,6 @@ export default class DualityRoll extends D20Roll {
|
||||||
!config.source?.actor ||
|
!config.source?.actor ||
|
||||||
(game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
|
(game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
|
||||||
config.actionType === 'reaction' ||
|
config.actionType === 'reaction' ||
|
||||||
config.tagTeamSelected ||
|
|
||||||
config.skips?.resources
|
config.skips?.resources
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
@ -346,7 +358,6 @@ export default class DualityRoll extends D20Roll {
|
||||||
if (
|
if (
|
||||||
automationSettings.countdownAutomation &&
|
automationSettings.countdownAutomation &&
|
||||||
config.actionType !== 'reaction' &&
|
config.actionType !== 'reaction' &&
|
||||||
!config.tagTeamSelected &&
|
|
||||||
!config.skips?.updateCountdowns
|
!config.skips?.updateCountdowns
|
||||||
) {
|
) {
|
||||||
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
||||||
|
|
@ -373,61 +384,4 @@ export default class DualityRoll extends D20Roll {
|
||||||
if (currentCombatant?.actorId == config.data.id) ui.combat.setCombatantSpotlight(currentCombatant.id);
|
if (currentCombatant?.actorId == config.data.id) ui.combat.setCombatantSpotlight(currentCombatant.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async reroll(rollBase, dieIndex, diceType) {
|
|
||||||
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollBase, evaluated: false });
|
|
||||||
const term = parsedRoll.terms[dieIndex];
|
|
||||||
await term.reroll(`/r1=${term.total}`);
|
|
||||||
const result = await parsedRoll.evaluate();
|
|
||||||
|
|
||||||
if (game.modules.get('dice-so-nice')?.active) {
|
|
||||||
const diceSoNiceRoll = {
|
|
||||||
_evaluated: true,
|
|
||||||
dice: [
|
|
||||||
new foundry.dice.terms.Die({
|
|
||||||
...term,
|
|
||||||
faces: term._faces,
|
|
||||||
results: term.results.filter(x => !x.rerolled)
|
|
||||||
})
|
|
||||||
],
|
|
||||||
options: { appearance: {} }
|
|
||||||
};
|
|
||||||
|
|
||||||
const diceSoNicePresets = await getDiceSoNicePresets(`d${term._faces}`, `d${term._faces}`);
|
|
||||||
if (diceSoNicePresets[diceType]) {
|
|
||||||
diceSoNiceRoll.dice[0].options = diceSoNicePresets[diceType];
|
|
||||||
}
|
|
||||||
|
|
||||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
|
||||||
} else {
|
|
||||||
foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
|
||||||
}
|
|
||||||
|
|
||||||
const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, {
|
|
||||||
targets: parsedRoll.options.targets ?? [],
|
|
||||||
roll: {
|
|
||||||
advantage: parsedRoll.options.roll.advantage?.type,
|
|
||||||
difficulty: parsedRoll.options.roll.difficulty ? Number(parsedRoll.options.roll.difficulty) : null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const extraIndex = newRoll.advantage ? 3 : 2;
|
|
||||||
newRoll.extra = newRoll.extra.slice(extraIndex);
|
|
||||||
|
|
||||||
const actor = parsedRoll.options.source.actor
|
|
||||||
? await foundry.utils.fromUuid(parsedRoll.options.source.actor)
|
|
||||||
: null;
|
|
||||||
const config = {
|
|
||||||
source: { actor: parsedRoll.options.source.actor ?? '' },
|
|
||||||
targets: parsedRoll.targets,
|
|
||||||
roll: newRoll,
|
|
||||||
rerolledRoll: parsedRoll.roll,
|
|
||||||
resourceUpdates: new ResourceUpdateMap(actor)
|
|
||||||
};
|
|
||||||
|
|
||||||
await DualityRoll.addDualityResourceUpdates(config);
|
|
||||||
await config.resourceUpdates.updateResources();
|
|
||||||
|
|
||||||
return { newRoll, parsedRoll };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ export default class FateRoll extends D20Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
set dHope(faces) {
|
set dHope(faces) {
|
||||||
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
// TODO this should not be asymmetrical with the getter. updateRollConfiguration() should use dHope.faces
|
||||||
this.dice[0].faces = this.getFaces(faces);
|
this.dHope.faces = this.getFaces(faces);
|
||||||
}
|
}
|
||||||
|
|
||||||
get dFear() {
|
get dFear() {
|
||||||
|
|
@ -31,8 +31,8 @@ export default class FateRoll extends D20Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
set dFear(faces) {
|
set dFear(faces) {
|
||||||
if (!(this.dice[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
// TODO this should not be asymmetrical with the getter. updateRollConfiguration() should use dFear.faces
|
||||||
this.dice[0].faces = this.getFaces(faces);
|
this.dFear.faces = this.getFaces(faces);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isCritical() {
|
get isCritical() {
|
||||||
|
|
@ -43,6 +43,20 @@ export default class FateRoll extends D20Roll {
|
||||||
return this.data.fateType;
|
return this.data.fateType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get withHope() {
|
||||||
|
return this.data.fateType === 'Hope';
|
||||||
|
}
|
||||||
|
|
||||||
|
get withFear() {
|
||||||
|
return this.data.fateType === 'Fear';
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalLabel() {
|
||||||
|
const label = this.withHope ? 'DAGGERHEART.GENERAL.hope' : 'DAGGERHEART.GENERAL.fear';
|
||||||
|
|
||||||
|
return game.i18n.localize(label);
|
||||||
|
}
|
||||||
|
|
||||||
static getHooks(hooks) {
|
static getHooks(hooks) {
|
||||||
return [...(hooks ?? []), 'Fate'];
|
return [...(hooks ?? []), 'Fate'];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { itemAbleRollParse } from '../helpers/utils.mjs';
|
import { itemAbleRollParse } from '../helpers/utils.mjs';
|
||||||
import { RefreshType } from '../systemRegistration/socket.mjs';
|
|
||||||
|
|
||||||
export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -111,6 +110,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
update.img = 'icons/magic/life/heart-cross-blue.webp';
|
update.img = 'icons/magic/life/heart-cross-blue.webp';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.actor && data.origin) {
|
||||||
const existingEffect = this.actor.effects.find(x => x.origin === data.origin);
|
const existingEffect = this.actor.effects.find(x => x.origin === data.origin);
|
||||||
const stacks = Boolean(data.system?.stacking);
|
const stacks = Boolean(data.system?.stacking);
|
||||||
if (existingEffect && !stacks) return false;
|
if (existingEffect && !stacks) return false;
|
||||||
|
|
@ -122,7 +122,9 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.parent) {
|
||||||
const statuses = Object.keys(data.statuses ?? {});
|
const statuses = Object.keys(data.statuses ?? {});
|
||||||
const immuneStatuses =
|
const immuneStatuses =
|
||||||
statuses.filter(
|
statuses.filter(
|
||||||
|
|
@ -144,6 +146,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
this.parent.queueScrollText(scrollingTexts);
|
this.parent.queueScrollText(scrollingTexts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(update).length > 0) {
|
if (Object.keys(update).length > 0) {
|
||||||
await this.updateSource(update);
|
await this.updateSource(update);
|
||||||
|
|
@ -152,20 +155,6 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
await super._preCreate(data, options, user);
|
await super._preCreate(data, options, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
_onCreate(data, options, userId) {
|
|
||||||
super._onCreate(data, options, userId);
|
|
||||||
|
|
||||||
Hooks.callAll(RefreshType.EffectsDisplay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
_onDelete(data, options, userId) {
|
|
||||||
super._onDelete(data, options, userId);
|
|
||||||
|
|
||||||
Hooks.callAll(RefreshType.EffectsDisplay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Methods */
|
/* Methods */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,18 @@ export default class DhpActor extends Actor {
|
||||||
return this.system.metadata.isNPC;
|
return this.system.metadata.isNPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareData() {
|
||||||
|
super.prepareData();
|
||||||
|
|
||||||
|
// Update effects if it is the user's character or is controlled
|
||||||
|
if (canvas.ready) {
|
||||||
|
const controlled = canvas.tokens.controlled.some(t => t.actor === this);
|
||||||
|
if (game.user.character === this || controlled) {
|
||||||
|
ui.effectsDisplay.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
@ -122,14 +134,6 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId) {
|
|
||||||
if (collection === 'effects') {
|
|
||||||
ui.effectsDisplay.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
super._onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateLevel(newLevel) {
|
async updateLevel(newLevel) {
|
||||||
if (!['character', 'companion'].includes(this.type) || newLevel === this.system.levelData.level.changed) return;
|
if (!['character', 'companion'].includes(this.type) || newLevel === this.system.levelData.level.changed) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
|
||||||
|
|
||||||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
targetHook = null;
|
targetHook = null;
|
||||||
|
|
@ -78,25 +78,14 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
if (this.isContentVisible) {
|
if (this.isContentVisible) {
|
||||||
if (this.type === 'dualityRoll') {
|
if (this.type === 'dualityRoll') {
|
||||||
html.classList.add('duality');
|
html.classList.add('duality');
|
||||||
switch (this.system.roll?.result?.duality) {
|
if (this.system.roll.withHope) html.classList.add('hope');
|
||||||
case 1:
|
else if (this.system.roll.withFear) html.classList.add('fear');
|
||||||
html.classList.add('hope');
|
else html.classList.add('critical');
|
||||||
break;
|
|
||||||
case -1:
|
|
||||||
html.classList.add('fear');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
html.classList.add('critical');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (this.type === 'fateRoll') {
|
if (this.type === 'fateRoll') {
|
||||||
html.classList.add('fate');
|
html.classList.add('fate');
|
||||||
if (this.system.roll?.fate.fateDie == 'Hope') {
|
if (this.system.roll?.fateDie) {
|
||||||
html.classList.add('hope');
|
html.classList.add(this.system.roll.fateDie.toLowerCase());
|
||||||
}
|
|
||||||
if (this.system.roll?.fate.fateDie == 'Fear') {
|
|
||||||
html.classList.add('fear');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,6 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
actor: item.parent,
|
actor: item.parent,
|
||||||
speaker: cls.getSpeaker(),
|
speaker: cls.getSpeaker(),
|
||||||
system: systemData,
|
system: systemData,
|
||||||
title: game.i18n.localize('DAGGERHEART.ACTIONS.Config.displayInChat'),
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/ui/chat/ability-use.hbs',
|
'systems/daggerheart/templates/ui/chat/ability-use.hbs',
|
||||||
systemData
|
systemData
|
||||||
|
|
|
||||||
|
|
@ -494,62 +494,4 @@ export default class DHToken extends CONFIG.Token.documentClass {
|
||||||
game.system.registeredTriggers.unregisterItemTriggers(this.actor.items);
|
game.system.registeredTriggers.unregisterItemTriggers(this.actor.items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* V14 TEMP until foundry fixes: https://discord.com/channels/170995199584108546/1421197211194228907/1467296028700049566 */
|
|
||||||
_onRelatedUpdate(update = {}, operation = {}) {
|
|
||||||
this.#refreshOverrides(operation);
|
|
||||||
this._prepareBars();
|
|
||||||
|
|
||||||
// Update tracked Combat resource
|
|
||||||
const combatant = this.combatant;
|
|
||||||
if (combatant) {
|
|
||||||
const isActorUpdate = [this, null, undefined].includes(operation.parent);
|
|
||||||
const resource = game.combat.settings.resource;
|
|
||||||
const updates = Array.isArray(update) ? update : [update];
|
|
||||||
if (isActorUpdate && resource && updates.some(u => foundry.utils.hasProperty(u.system ?? {}, resource))) {
|
|
||||||
combatant.updateResource();
|
|
||||||
}
|
|
||||||
ui.combat.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger redraws on the token
|
|
||||||
if (this.parent.isView) {
|
|
||||||
if (this.object?.hasActiveHUD) canvas.tokens.hud.render();
|
|
||||||
this.object?.renderFlags.set({ redrawEffects: true });
|
|
||||||
for (const key of ['bar1', 'bar2']) {
|
|
||||||
const name = `${this.object?.objectId}.animate${key.capitalize()}`;
|
|
||||||
const easing = foundry.canvas.animation.CanvasAnimation.easeInOutCosine;
|
|
||||||
this.object?.animate({ [key]: this[key] }, { name, easing });
|
|
||||||
}
|
|
||||||
for (const app of foundry.applications.sheets.TokenConfig.instances()) {
|
|
||||||
app._preview?.updateSource({ delta: this.toObject().delta }, { diff: false, recursive: false });
|
|
||||||
app._preview?.object?.renderFlags.set({ refreshBars: true, redrawEffects: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* V14 TEMP until foundry fixes: https://discord.com/channels/170995199584108546/1421197211194228907/1467296028700049566 */
|
|
||||||
#refreshOverrides(operation) {
|
|
||||||
if (!this.actor) return;
|
|
||||||
|
|
||||||
const { deepClone, mergeObject, equals, isEmpty } = foundry.utils;
|
|
||||||
const oldOverrides = deepClone(this._overrides) ?? {};
|
|
||||||
const newOverrides = deepClone(this.actor?.tokenOverrides ?? {}, { prune: true });
|
|
||||||
if (!equals(oldOverrides, newOverrides)) {
|
|
||||||
this._overrides = newOverrides;
|
|
||||||
this.reset();
|
|
||||||
|
|
||||||
// Send emulated update data to the PlaceableObject
|
|
||||||
if (!canvas.ready || canvas.scene !== this.scene) return;
|
|
||||||
const { width, height, depth, ...changes } = mergeObject(
|
|
||||||
mergeObject(oldOverrides, this, { insertKeys: false, insertValues: false }),
|
|
||||||
this._overrides
|
|
||||||
);
|
|
||||||
this.object?._onUpdate(changes, {}, game.user.id);
|
|
||||||
|
|
||||||
// Hand off size changes to a secondary handler requiring downstream implementation.
|
|
||||||
const sizeChanges = deepClone({ width, height, depth }, { prune: true });
|
|
||||||
if (!isEmpty(sizeChanges)) this._onOverrideSize(sizeChanges, operation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,39 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
this.#bordered = true;
|
this.#bordered = true;
|
||||||
let effect = {};
|
let effect = {};
|
||||||
if (element.dataset.uuid) {
|
if (element.dataset.uuid) {
|
||||||
const effectData = (await foundry.utils.fromUuid(element.dataset.uuid)).toObject();
|
const effectItem = await foundry.utils.fromUuid(element.dataset.uuid);
|
||||||
|
const effectData = effectItem.toObject();
|
||||||
|
|
||||||
effect = {
|
effect = {
|
||||||
...effectData,
|
...effectData,
|
||||||
name: game.i18n.localize(effectData.name),
|
name: game.i18n.localize(effectData.name)
|
||||||
description: game.i18n.localize(effectData.description ?? effectData.parent.system.description)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
const conditions = CONFIG.DH.GENERAL.conditions();
|
const conditions = CONFIG.DH.GENERAL.conditions();
|
||||||
const condition = conditions[element.dataset.condition];
|
const condition = conditions[element.dataset.condition];
|
||||||
|
|
|
||||||
1
module/macros/_modules.mjs
Normal file
1
module/macros/_modules.mjs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as spotlightCombatant } from './spotlightCombatant.mjs';
|
||||||
50
module/macros/spotlightCombatant.mjs
Normal file
50
module/macros/spotlightCombatant.mjs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* Spotlight a token on the canvas. If it is a combatant, run it through combatTracker's spotlight logic.
|
||||||
|
* @param {TokenDocument} token - The token to spotlight
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const spotlightCombatantMacro = async token => {
|
||||||
|
if (!token)
|
||||||
|
return ui.notifications.error(game.i18n.localize('DAGGERHEART.MACROS.Spotlight.errors.noTokenSelected'));
|
||||||
|
|
||||||
|
const combatantCombat = token.combatant
|
||||||
|
? game.combat
|
||||||
|
: game.combats.find(combat => combat.combatants.some(x => x.token && x.token.id === token.document.id));
|
||||||
|
if (combatantCombat) {
|
||||||
|
const combatant = combatantCombat.combatants.find(x => x.token.id === token.document.id);
|
||||||
|
if (!combatantCombat.active) {
|
||||||
|
await combatantCombat.activate();
|
||||||
|
if (combatantCombat.combatant?.id !== combatant.id) ui.combat.setCombatantSpotlight(combatant.id);
|
||||||
|
} else {
|
||||||
|
ui.combat.setCombatantSpotlight(combatant.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (game.combat) await ui.combat.clearTurn();
|
||||||
|
|
||||||
|
const spotlightTracker = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker);
|
||||||
|
const isSpotlighted = spotlightTracker.spotlightedTokens.has(token.document.uuid);
|
||||||
|
if (!isSpotlighted) await clearPreviousSpotlight();
|
||||||
|
|
||||||
|
spotlightTracker.updateSource({
|
||||||
|
spotlightedTokens: isSpotlighted ? [] : [token.document.uuid]
|
||||||
|
});
|
||||||
|
|
||||||
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker, spotlightTracker);
|
||||||
|
token.renderFlags.set({ refreshTurnMarker: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearPreviousSpotlight = async () => {
|
||||||
|
const spotlightTracker = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker);
|
||||||
|
const previouslySpotlightedUuid =
|
||||||
|
spotlightTracker.spotlightedTokens.size > 0 ? spotlightTracker.spotlightedTokens.first() : null;
|
||||||
|
if (!previouslySpotlightedUuid) return;
|
||||||
|
|
||||||
|
spotlightTracker.updateSource({ spotlightedTokens: [] });
|
||||||
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker, spotlightTracker);
|
||||||
|
|
||||||
|
const previousToken = await foundry.utils.fromUuid(previouslySpotlightedUuid);
|
||||||
|
previousToken.object.renderFlags.set({ refreshTurnMarker: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
export default spotlightCombatantMacro;
|
||||||
|
|
@ -36,6 +36,7 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/actionTypes/summon.hbs',
|
'systems/daggerheart/templates/actionTypes/summon.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/transform.hbs',
|
'systems/daggerheart/templates/actionTypes/transform.hbs',
|
||||||
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
||||||
|
'systems/daggerheart/templates/ui/tooltip/parts/beastformData.hbs',
|
||||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs',
|
'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs',
|
||||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
|
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
|
||||||
'systems/daggerheart/templates/dialogs/downtime/activities.hbs',
|
'systems/daggerheart/templates/dialogs/downtime/activities.hbs',
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,10 @@ import {
|
||||||
DhVariantRuleSettings
|
DhVariantRuleSettings
|
||||||
} from '../applications/settings/_module.mjs';
|
} from '../applications/settings/_module.mjs';
|
||||||
import { CompendiumBrowserSettings } from '../data/_module.mjs';
|
import { CompendiumBrowserSettings } from '../data/_module.mjs';
|
||||||
|
import SpotlightTracker from '../data/spotlightTracker.mjs';
|
||||||
|
|
||||||
export const registerDHSettings = () => {
|
export const registerDHSettings = () => {
|
||||||
|
registerKeyBindings();
|
||||||
registerMenuSettings();
|
registerMenuSettings();
|
||||||
registerMenus();
|
registerMenus();
|
||||||
registerNonConfigSettings();
|
registerNonConfigSettings();
|
||||||
|
|
@ -33,6 +35,25 @@ export const registerDHSettings = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const registerKeyBindings = () => {
|
||||||
|
game.keybindings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.keybindings.spotlight, {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.SETTINGS.Keybindings.spotlight.name'),
|
||||||
|
hint: game.i18n.localize('DAGGERHEART.SETTINGS.Keybindings.spotlight.hint'),
|
||||||
|
uneditable: [],
|
||||||
|
editable: [],
|
||||||
|
onDown: () => {
|
||||||
|
const selectedTokens = canvas.tokens.controlled.length > 0 ? canvas.tokens.controlled[0] : null;
|
||||||
|
const hoveredTokens = game.canvas.tokens.hover ? game.canvas.tokens.hover : null;
|
||||||
|
const tokens = selectedTokens ?? hoveredTokens;
|
||||||
|
game.system.api.macros.spotlightCombatant(tokens);
|
||||||
|
},
|
||||||
|
onUp: () => {},
|
||||||
|
restricted: true,
|
||||||
|
reservedModifiers: [],
|
||||||
|
precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const registerMenuSettings = () => {
|
const registerMenuSettings = () => {
|
||||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules, {
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules, {
|
||||||
scope: 'world',
|
scope: 'world',
|
||||||
|
|
@ -162,4 +183,10 @@ const registerNonConfigSettings = () => {
|
||||||
config: false,
|
config: false,
|
||||||
type: CompendiumBrowserSettings
|
type: CompendiumBrowserSettings
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker, {
|
||||||
|
scope: 'world',
|
||||||
|
config: false,
|
||||||
|
type: SpotlightTracker
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"_id": "6rlxhrRwFaVgq9fe",
|
"_id": "6rlxhrRwFaVgq9fe",
|
||||||
"img": "icons/magic/nature/wolf-paw-glow-large-orange.webp",
|
"img": "icons/magic/nature/wolf-paw-glow-large-orange.webp",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Spend 3 Hope</strong> to transform into a Beastform without marking a Stress. When you do, choose one trait to raise by +1 until you drop out of that Beastform.<br /><br /><strong>Note: Toggle one of the Evolution Traits in the effects tab to raise a trait by 1, e.g. Evolution: Agility</strong></p>",
|
"description": "<p><strong>Spend 3 Hope</strong> to transform into a Beastform without marking a Stress. When you do, choose one trait to raise by +1 until you drop out of that Beastform.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"bj4m9E8ObFT0xDQ4": {
|
"bj4m9E8ObFT0xDQ4": {
|
||||||
|
|
@ -31,6 +31,13 @@
|
||||||
"beastform": {
|
"beastform": {
|
||||||
"tierAccess": {
|
"tierAccess": {
|
||||||
"exact": null
|
"exact": null
|
||||||
|
},
|
||||||
|
"modifications": {
|
||||||
|
"traitBonuses": [
|
||||||
|
{
|
||||||
|
"bonus": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": "Beastform",
|
"name": "Beastform",
|
||||||
|
|
@ -46,266 +53,7 @@
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"effects": [
|
"effects": [],
|
||||||
{
|
|
||||||
"name": "Evolution: Agility",
|
|
||||||
"type": "base",
|
|
||||||
"system": {
|
|
||||||
"rangeDependence": {
|
|
||||||
"enabled": false,
|
|
||||||
"type": "withinRange",
|
|
||||||
"target": "hostile",
|
|
||||||
"range": "melee"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_id": "vQOqLZAxOltAzsVv",
|
|
||||||
"img": "icons/magic/nature/wolf-paw-glow-large-orange.webp",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"key": "system.traits.agility.value",
|
|
||||||
"mode": 2,
|
|
||||||
"value": "1",
|
|
||||||
"priority": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"disabled": true,
|
|
||||||
"duration": {
|
|
||||||
"startTime": null,
|
|
||||||
"combat": null,
|
|
||||||
"seconds": null,
|
|
||||||
"rounds": null,
|
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
|
||||||
"description": "<p>Toggle this for +1 to Agility when using Evolution. Turn it off when you leave Beastform.</p>",
|
|
||||||
"origin": null,
|
|
||||||
"tint": "#ffffff",
|
|
||||||
"transfer": true,
|
|
||||||
"statuses": [],
|
|
||||||
"sort": 0,
|
|
||||||
"flags": {},
|
|
||||||
"_stats": {
|
|
||||||
"compendiumSource": null
|
|
||||||
},
|
|
||||||
"_key": "!items.effects!6rlxhrRwFaVgq9fe.vQOqLZAxOltAzsVv"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Evolution: Strength",
|
|
||||||
"type": "base",
|
|
||||||
"system": {
|
|
||||||
"rangeDependence": {
|
|
||||||
"enabled": false,
|
|
||||||
"type": "withinRange",
|
|
||||||
"target": "hostile",
|
|
||||||
"range": "melee"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_id": "cwEsO1NZpkQHuoTT",
|
|
||||||
"img": "icons/magic/nature/wolf-paw-glow-large-orange.webp",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"key": "system.traits.strength.value",
|
|
||||||
"mode": 2,
|
|
||||||
"value": "1",
|
|
||||||
"priority": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"disabled": true,
|
|
||||||
"duration": {
|
|
||||||
"startTime": null,
|
|
||||||
"combat": null,
|
|
||||||
"seconds": null,
|
|
||||||
"rounds": null,
|
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
|
||||||
"description": "<p>Toggle this for +1 to Strength when using Evolution. Turn it off when you leave Beastform.</p>",
|
|
||||||
"origin": null,
|
|
||||||
"tint": "#ffffff",
|
|
||||||
"transfer": true,
|
|
||||||
"statuses": [],
|
|
||||||
"sort": 0,
|
|
||||||
"flags": {},
|
|
||||||
"_stats": {
|
|
||||||
"compendiumSource": null
|
|
||||||
},
|
|
||||||
"_key": "!items.effects!6rlxhrRwFaVgq9fe.cwEsO1NZpkQHuoTT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Evolution: Finesse",
|
|
||||||
"type": "base",
|
|
||||||
"system": {
|
|
||||||
"rangeDependence": {
|
|
||||||
"enabled": false,
|
|
||||||
"type": "withinRange",
|
|
||||||
"target": "hostile",
|
|
||||||
"range": "melee"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_id": "8P0nwRHNsVnHVPjq",
|
|
||||||
"img": "icons/magic/nature/wolf-paw-glow-large-orange.webp",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"key": "system.traits.finesse.value",
|
|
||||||
"mode": 2,
|
|
||||||
"value": "1",
|
|
||||||
"priority": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"disabled": true,
|
|
||||||
"duration": {
|
|
||||||
"startTime": null,
|
|
||||||
"combat": null,
|
|
||||||
"seconds": null,
|
|
||||||
"rounds": null,
|
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
|
||||||
"description": "<p>Toggle this for +1 to Finesse when using Evolution. Turn it off when you leave Beastform.</p>",
|
|
||||||
"origin": null,
|
|
||||||
"tint": "#ffffff",
|
|
||||||
"transfer": true,
|
|
||||||
"statuses": [],
|
|
||||||
"sort": 0,
|
|
||||||
"flags": {},
|
|
||||||
"_stats": {
|
|
||||||
"compendiumSource": null
|
|
||||||
},
|
|
||||||
"_key": "!items.effects!6rlxhrRwFaVgq9fe.8P0nwRHNsVnHVPjq"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Evolution: Instinct",
|
|
||||||
"type": "base",
|
|
||||||
"system": {
|
|
||||||
"rangeDependence": {
|
|
||||||
"enabled": false,
|
|
||||||
"type": "withinRange",
|
|
||||||
"target": "hostile",
|
|
||||||
"range": "melee"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_id": "i2GhNGo5TnGtLuA0",
|
|
||||||
"img": "icons/magic/nature/wolf-paw-glow-large-orange.webp",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"key": "system.traits.instinct.value",
|
|
||||||
"mode": 2,
|
|
||||||
"value": "1",
|
|
||||||
"priority": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"disabled": true,
|
|
||||||
"duration": {
|
|
||||||
"startTime": null,
|
|
||||||
"combat": null,
|
|
||||||
"seconds": null,
|
|
||||||
"rounds": null,
|
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
|
||||||
"description": "<p>Toggle this for +1 to Instinct when using Evolution. Turn it off when you leave Beastform.</p>",
|
|
||||||
"origin": null,
|
|
||||||
"tint": "#ffffff",
|
|
||||||
"transfer": true,
|
|
||||||
"statuses": [],
|
|
||||||
"sort": 0,
|
|
||||||
"flags": {},
|
|
||||||
"_stats": {
|
|
||||||
"compendiumSource": null
|
|
||||||
},
|
|
||||||
"_key": "!items.effects!6rlxhrRwFaVgq9fe.i2GhNGo5TnGtLuA0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Evolution: Presence",
|
|
||||||
"type": "base",
|
|
||||||
"system": {
|
|
||||||
"rangeDependence": {
|
|
||||||
"enabled": false,
|
|
||||||
"type": "withinRange",
|
|
||||||
"target": "hostile",
|
|
||||||
"range": "melee"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_id": "APQF1in1LXjBZh9n",
|
|
||||||
"img": "icons/magic/nature/wolf-paw-glow-large-orange.webp",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"key": "system.traits.presence.value",
|
|
||||||
"mode": 2,
|
|
||||||
"value": "1",
|
|
||||||
"priority": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"disabled": true,
|
|
||||||
"duration": {
|
|
||||||
"startTime": null,
|
|
||||||
"combat": null,
|
|
||||||
"seconds": null,
|
|
||||||
"rounds": null,
|
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
|
||||||
"description": "<p>Toggle this for +1 to Presence when using Evolution. Turn it off when you leave Beastform.</p>",
|
|
||||||
"origin": null,
|
|
||||||
"tint": "#ffffff",
|
|
||||||
"transfer": true,
|
|
||||||
"statuses": [],
|
|
||||||
"sort": 0,
|
|
||||||
"flags": {},
|
|
||||||
"_stats": {
|
|
||||||
"compendiumSource": null
|
|
||||||
},
|
|
||||||
"_key": "!items.effects!6rlxhrRwFaVgq9fe.APQF1in1LXjBZh9n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Evolution: Knowledge",
|
|
||||||
"type": "base",
|
|
||||||
"system": {
|
|
||||||
"rangeDependence": {
|
|
||||||
"enabled": false,
|
|
||||||
"type": "withinRange",
|
|
||||||
"target": "hostile",
|
|
||||||
"range": "melee"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_id": "WwOvGJYJb4d37cOy",
|
|
||||||
"img": "icons/magic/nature/wolf-paw-glow-large-orange.webp",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"key": "system.traits.knowledge.value",
|
|
||||||
"mode": 2,
|
|
||||||
"value": "1",
|
|
||||||
"priority": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"disabled": true,
|
|
||||||
"duration": {
|
|
||||||
"startTime": null,
|
|
||||||
"combat": null,
|
|
||||||
"seconds": null,
|
|
||||||
"rounds": null,
|
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
|
||||||
"description": "<p>Toggle this for +1 to Knowledge when using Evolution. Turn it off when you leave Beastform.</p>",
|
|
||||||
"origin": null,
|
|
||||||
"tint": "#ffffff",
|
|
||||||
"transfer": true,
|
|
||||||
"statuses": [],
|
|
||||||
"sort": 0,
|
|
||||||
"flags": {},
|
|
||||||
"_stats": {
|
|
||||||
"compendiumSource": null
|
|
||||||
},
|
|
||||||
"_key": "!items.effects!6rlxhrRwFaVgq9fe.WwOvGJYJb4d37cOy"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"sort": 100000,
|
"sort": 100000,
|
||||||
"ownership": {
|
"ownership": {
|
||||||
"default": 0,
|
"default": 0,
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,44 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modifications-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.trait-bonuses-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.bonus-separator {
|
||||||
|
background: light-dark(@dark-blue, @golden);
|
||||||
|
mask-image: linear-gradient(270deg, transparent 0%, black 50%, transparent 100%);
|
||||||
|
height: 2px;
|
||||||
|
width: calc(100% - 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trait-bonus-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.trait-card {
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2px;
|
||||||
|
opacity: 0.4;
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -240,12 +240,16 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
gap: 8px;
|
gap: 2px;
|
||||||
|
|
||||||
.trait-container {
|
.trait-container {
|
||||||
width: 60px;
|
span {
|
||||||
height: 60px;
|
font-size: var(--font-size-10);
|
||||||
|
}
|
||||||
|
width: 65px;
|
||||||
|
height: 65px;
|
||||||
background: url(../assets/svg/trait-shield.svg) no-repeat;
|
background: url(../assets/svg/trait-shield.svg) no-repeat;
|
||||||
|
background-size: 100%;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
filter: drop-shadow(0 0 3px black);
|
filter: drop-shadow(0 0 3px black);
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,22 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
gap: 8px;
|
gap: 2px;
|
||||||
|
|
||||||
.trait-container {
|
.trait-container {
|
||||||
width: 60px;
|
width: 65px;
|
||||||
height: 60px;
|
height: 65px;
|
||||||
background: url(../assets/svg/trait-shield.svg) no-repeat;
|
background: url(../assets/svg/trait-shield.svg) no-repeat;
|
||||||
|
background-size: 100%;
|
||||||
|
padding-top: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: var(--font-size-10);
|
||||||
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
filter: drop-shadow(0 0 3px black);
|
filter: drop-shadow(0 0 3px black);
|
||||||
text-shadow: 0 0 3px black;
|
text-shadow: 0 0 3px black;
|
||||||
|
|
|
||||||
|
|
@ -133,4 +133,18 @@
|
||||||
height: 300px;
|
height: 300px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.deletable-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
padding-bottom: 7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -384,6 +384,15 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 15px;
|
width: 15px;
|
||||||
}
|
}
|
||||||
|
&.has-minus:before {
|
||||||
|
content: '-';
|
||||||
|
font-size: var(--font-size-20);
|
||||||
|
grid-area: c;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 15px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,17 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-refresh-container {
|
.menu-options-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
.menu-refresh-inner-container {
|
.menu-options-inner-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
.experience-chip {
|
.option-chip {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
@import './tooltip/sheet.less';
|
||||||
@import './tooltip/tooltip.less';
|
@import './tooltip/tooltip.less';
|
||||||
@import './tooltip/armorManagement.less';
|
@import './tooltip/armorManagement.less';
|
||||||
@import './tooltip/battlepoints.less';
|
@import './tooltip/battlepoints.less';
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: start;
|
text-align: center;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
gap: 0px;
|
gap: 0px;
|
||||||
|
|
||||||
|
|
|
||||||
129
styles/less/ux/tooltip/sheet.less
Normal file
129
styles/less/ux/tooltip/sheet.less
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
#tooltip:has(div.daggerheart.dh-style.tooltip.card-style),
|
||||||
|
aside[role='tooltip']:has(div.daggerheart.dh-style.tooltip),
|
||||||
|
#tooltip.bordered-tooltip {
|
||||||
|
.tooltip-title {
|
||||||
|
font-size: var(--font-size-20);
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-description {
|
||||||
|
font-style: inherit;
|
||||||
|
text-align: inherit;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 10px;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 5px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
background: @golden;
|
||||||
|
mask-image: linear-gradient(270deg, transparent 0%, black 50%, transparent 100%);
|
||||||
|
height: 2px;
|
||||||
|
width: calc(100% - 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: -5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-separator {
|
||||||
|
background: @golden;
|
||||||
|
mask-image: linear-gradient(270deg, transparent 0%, black 50%, transparent 100%);
|
||||||
|
height: 2px;
|
||||||
|
width: calc(100% - 10px);
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 10px;
|
||||||
|
position: relative;
|
||||||
|
max-height: 150px;
|
||||||
|
overflow-y: auto;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
|
|
||||||
|
.tooltip-tag {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.tooltip-tag-label-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px 10px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&.advantages {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 10px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 5px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
background: @golden;
|
||||||
|
mask-image: linear-gradient(270deg, transparent 0%, black 50%, transparent 100%);
|
||||||
|
height: 2px;
|
||||||
|
width: calc(100% - 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
background: @green-10;
|
||||||
|
color: @green;
|
||||||
|
border-color: @green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px 5px;
|
||||||
|
font-size: var(--font-size-12);
|
||||||
|
font: @font-body;
|
||||||
|
|
||||||
|
background: light-dark(@dark-15, @beige-15);
|
||||||
|
border: 1px solid light-dark(@dark, @beige);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: var(--font-size-12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,13 +13,6 @@ aside[role='tooltip']:has(div.daggerheart.dh-style.tooltip.card-style) {
|
||||||
outline: 1px solid light-dark(@dark-80, @beige-80);
|
outline: 1px solid light-dark(@dark-80, @beige-80);
|
||||||
box-shadow: 0 0 25px rgba(0, 0, 0, 0.8);
|
box-shadow: 0 0 25px rgba(0, 0, 0, 0.8);
|
||||||
|
|
||||||
.tooltip-title {
|
|
||||||
font-size: var(--font-size-20);
|
|
||||||
color: light-dark(@dark-blue, @golden);
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-subtitle {
|
.tooltip-subtitle {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -80,110 +73,6 @@ aside[role='tooltip']:has(div.daggerheart.dh-style.tooltip.card-style) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-tags {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px 10px;
|
|
||||||
position: relative;
|
|
||||||
padding-top: 10px;
|
|
||||||
max-height: 150px;
|
|
||||||
overflow-y: auto;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
background: @golden;
|
|
||||||
mask-image: linear-gradient(270deg, transparent 0%, black 50%, transparent 100%);
|
|
||||||
height: 2px;
|
|
||||||
width: calc(100% - 10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-tag {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.tooltip-tag-label-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tags {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px 10px;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
&.advantages {
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px 10px;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
position: relative;
|
|
||||||
margin-top: 5px;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
background: @golden;
|
|
||||||
mask-image: linear-gradient(270deg, transparent 0%, black 50%, transparent 100%);
|
|
||||||
height: 2px;
|
|
||||||
width: calc(100% - 10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
top: -5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
background: @green-10;
|
|
||||||
color: @green;
|
|
||||||
border-color: @green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 3px 5px;
|
|
||||||
font-size: var(--font-size-12);
|
|
||||||
font: @font-body;
|
|
||||||
|
|
||||||
background: light-dark(@dark-15, @beige-15);
|
|
||||||
border: 1px solid light-dark(@dark, @beige);
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-size: var(--font-size-12);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-icons-list {
|
.item-icons-list {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "14.355",
|
"minimum": "14.355",
|
||||||
"verified": "14.357",
|
"verified": "14.358",
|
||||||
"maximum": "14"
|
"maximum": "14"
|
||||||
},
|
},
|
||||||
"authors": [
|
"authors": [
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,16 @@
|
||||||
|
{{formGroup fields.tierAccess.fields.exact value=source.tierAccess.exact name="beastform.tierAccess.exact" labelAttr="label" valueAttr="key" localize=true blank=""}}
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{{localize "DAGGERHEART.ACTIONS.Config.beastform.label"}}</legend>
|
<legend>{{localize "DAGGERHEART.ACTIONS.Config.beastform.modifications.traitBonuses.label.plural"}} <a data-action="addBeastformTraitBonus"><i class="fa-solid fa-plus"></i></a></legend>
|
||||||
{{formGroup fields.tierAccess.fields.exact value=source.tierAccess.exact name="beastform.tierAccess.exact" labelAttr="label" valueAttr="key" localize=true blank=""}}
|
|
||||||
|
{{#if source.modifications.traitBonuses.length}}
|
||||||
|
{{#each source.modifications.traitBonuses as |traitBonus index|}}
|
||||||
|
<div class="deletable-row">
|
||||||
|
{{formGroup ../fields.modifications.fields.traitBonuses.element.fields.bonus value=traitBonus.bonus name=(concat "beastform.modifications.traitBonuses." index ".bonus") localize=true}}
|
||||||
|
<a data-action="removeBeastformTraitBonus" data-index="{{index}}"><i class="fa-solid fa-trash"></i></a>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{else}}
|
||||||
|
<span class="hint">{{localize "DAGGERHEART.ACTIONS.Config.beastform.modifications.traitBonuses.hint"}}</span>
|
||||||
|
{{/if}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
28
templates/dialogs/beastform/modifications.hbs
Normal file
28
templates/dialogs/beastform/modifications.hbs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<div class="modifications-container">
|
||||||
|
{{#if modifications.traitBonuses.length}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
{{#if (gt modifications.traitBonuses.length 1)}}
|
||||||
|
{{localize "DAGGERHEART.ACTIONS.Config.beastform.modifications.traitBonuses.label.plural"}}
|
||||||
|
{{else}}
|
||||||
|
{{localize "DAGGERHEART.ACTIONS.Config.beastform.modifications.traitBonuses.label.single"}}
|
||||||
|
{{/if}}
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<div class="trait-bonuses-container">
|
||||||
|
{{#each modifications.traitBonuses as |traitBonus index|}}
|
||||||
|
<div class="trait-bonus-container">
|
||||||
|
{{#each @root.traits as |trait|}}
|
||||||
|
<a class="trait-card {{#if (eq trait.id traitBonus.trait)}}selected{{/if}}"
|
||||||
|
data-action="toggleTraitBonus" data-index="{{index}}" data-trait="{{trait.id}}"
|
||||||
|
>
|
||||||
|
{{localize trait.label}} +{{traitBonus.bonus}}
|
||||||
|
</a>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{#unless @last}}<div class="bonus-separator"></div>{{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
@ -53,14 +53,14 @@
|
||||||
{{#if @root.advantage}}
|
{{#if @root.advantage}}
|
||||||
{{#if (eq @root.advantage 1)}}
|
{{#if (eq @root.advantage 1)}}
|
||||||
<div class="dice-option">
|
<div class="dice-option">
|
||||||
<img class="dice-icon" src="{{concat 'systems/daggerheart/assets/icons/dice/adv/' @root.roll.dAdvantage.denomination '.svg'}}" alt="">
|
<img class="dice-icon" src="{{concat 'systems/daggerheart/assets/icons/dice/adv/d' @root.roll.advantageFaces '.svg'}}" alt="">
|
||||||
<div class="dice-select">
|
<div class="dice-select">
|
||||||
<span class="label">{{localize "DAGGERHEART.GENERAL.Advantage.full"}}</span>
|
<span class="label">{{localize "DAGGERHEART.GENERAL.Advantage.full"}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else if (eq @root.advantage -1)}}
|
{{else if (eq @root.advantage -1)}}
|
||||||
<div class="dice-option">
|
<div class="dice-option">
|
||||||
<img class="dice-icon" src="{{concat 'systems/daggerheart/assets/icons/dice/disadv/' @root.roll.dAdvantage.denomination '.svg'}}" alt="">
|
<img class="dice-icon" src="{{concat 'systems/daggerheart/assets/icons/dice/disadv/d' @root.roll.advantageFaces '.svg'}}" alt="">
|
||||||
<div class="dice-select">
|
<div class="dice-select">
|
||||||
<span class="label">{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}</span>
|
<span class="label">{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -158,7 +158,7 @@
|
||||||
{{/times}}
|
{{/times}}
|
||||||
</select>
|
</select>
|
||||||
<select name="roll.dice.advantageFaces"{{#unless advantage}} disabled{{/unless}}>
|
<select name="roll.dice.advantageFaces"{{#unless advantage}} disabled{{/unless}}>
|
||||||
{{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}}
|
{{selectOptions diceOptions selected=(concat 'd' @root.roll.advantageFaces)}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{{#if abilities}}
|
{{#if abilities}}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
{{#each damage.parts as |part|}}
|
{{#each damage.parts as |part|}}
|
||||||
<div class="roll-dice-container">
|
<div class="roll-dice-container">
|
||||||
{{#each part.dice as |dice index|}}
|
{{#each part.dice as |dice index|}}
|
||||||
<a class="roll-dice" data-action="rerollDamageDice" data-member-key="{{@../../../key}}" data-damage-key="{{@../../key}}" data-part="{{@../index}}" data-dice="{{index}}">
|
<a class="roll-dice" data-action="rerollDamageDice" data-member-key="{{../../../key}}" data-damage-key="{{@../../key}}" data-part="{{@../index}}" data-dice="{{index}}">
|
||||||
<span class="dice-label">{{dice.total}}</span>
|
<span class="dice-label">{{dice.total}}</span>
|
||||||
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/" dice.dice ".svg"}}" />
|
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/" dice.dice ".svg"}}" />
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -62,32 +62,30 @@
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{{#if rollData}}
|
{{#if roll}}
|
||||||
{{#with rollData.options.roll}}
|
<div class="roll-data {{#if roll.withHope}}hope{{else if roll.withFear}}fear{{else}}critical{{/if}}">
|
||||||
<div class="roll-data {{#if this.isCritical}}critical{{else}}{{#if (eq this.result.duality 1)}}hope{{else}}fear{{/if}}{{/if}}">
|
<div class="duality-label">{{roll.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}</div>
|
||||||
<div class="duality-label">{{this.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=this.result.label}}</div>
|
|
||||||
<div class="roll-dice-container">
|
<div class="roll-dice-container">
|
||||||
<a class="roll-dice" data-action="rerollDice" data-member="{{@root.partId}}" data-dice-type="hope">
|
<a class="roll-dice" data-action="rerollDice" data-member="{{@root.partId}}" data-dice-type="hope">
|
||||||
<span class="dice-label">{{this.hope.value}}</span>
|
<span class="dice-label">{{roll.dHope.total}}</span>
|
||||||
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/" this.hope.dice ".svg"}}" />
|
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/" roll.dHope.denomination ".svg"}}" />
|
||||||
</a>
|
</a>
|
||||||
<span class="roll-operator">+</span>
|
<span class="roll-operator">+</span>
|
||||||
<a class="roll-dice" data-action="rerollDice" data-member="{{@root.partId}}" data-dice-type="fear">
|
<a class="roll-dice" data-action="rerollDice" data-member="{{@root.partId}}" data-dice-type="fear">
|
||||||
<span class="dice-label">{{this.fear.value}}</span>
|
<span class="dice-label">{{roll.dFear.total}}</span>
|
||||||
<img src="{{concat "systems/daggerheart/assets/icons/dice/fear/" this.fear.dice ".svg"}}" />
|
<img src="{{concat "systems/daggerheart/assets/icons/dice/fear/" roll.dFear.denomination ".svg"}}" />
|
||||||
</a>
|
</a>
|
||||||
{{#if this.advantage.type}}
|
{{#if roll.advantage.type}}
|
||||||
<span class="roll-operator">{{#if (eq this.advantage.type 1)}}+{{else}}-{{/if}}</span>
|
<span class="roll-operator">{{#if (eq roll.advantage.type 1)}}+{{else}}-{{/if}}</span>
|
||||||
<span class="roll-dice">
|
<span class="roll-dice">
|
||||||
<span class="dice-label">{{this.advantage.value}}</span>
|
<span class="dice-label">{{roll.advantage.value}}</span>
|
||||||
<img src="{{concat "systems/daggerheart/assets/icons/dice/" (ifThen (eq this.advantage.type 1) "adv/" "disadv/") this.advantage.dice ".svg"}}" />
|
<img src="{{concat "systems/daggerheart/assets/icons/dice/" (ifThen (eq roll.advantage.type 1) "adv/" "disadv/") roll.advantage.dice ".svg"}}" />
|
||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<span class="roll-operator">{{#if (gte this.modifierTotal 0)}}+{{else}}-{{/if}}</span>
|
<span class="roll-operator">{{#if (gte roll.modifierTotal 0)}}+{{else}}-{{/if}}</span>
|
||||||
<span class="roll-value">{{positive this.modifierTotal}}</span>
|
<span class="roll-value">{{positive roll.modifierTotal}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/with}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="hint">{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}}</span>
|
<span class="hint">{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,16 @@
|
||||||
{{#if hintText}}
|
{{#if hintText}}
|
||||||
<div class="hint">{{localize hintText}}</div>
|
<div class="hint">{{localize hintText}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if joinedRoll.rollData}}
|
{{#if joinedRoll.roll}}
|
||||||
<div class="result-container">
|
<div class="result-container">
|
||||||
<span class="result-section-label">{{localize "DAGGERHEART.GENERAL.dualityRoll"}}</span>
|
<span class="result-section-label">{{localize "DAGGERHEART.GENERAL.dualityRoll"}}</span>
|
||||||
<div class="result-info">
|
<div class="result-info">
|
||||||
<div class="damage-info">{{joinedRoll.rollData.options.roll.total}}</div>
|
<div class="damage-info">{{joinedRoll.roll.total}}</div>
|
||||||
<div>{{localize "DAGGERHEART.GENERAL.withThing" thing=joinedRoll.rollData.options.roll.result.label}}</div>
|
<div>{{localize "DAGGERHEART.GENERAL.withThing" thing=joinedRoll.roll.totalLabel}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if hasDamage}}
|
{{#if joinedRoll.rollData.options.hasDamage}}
|
||||||
<div class="result-container">
|
<div class="result-container">
|
||||||
<span class="result-section-label">{{localize "DAGGERHEART.GENERAL.damage"}}</span>
|
<span class="result-section-label">{{localize "DAGGERHEART.GENERAL.damage"}}</span>
|
||||||
{{#each joinedRoll.rollData.options.damage as |damage key|}}
|
{{#each joinedRoll.rollData.options.damage as |damage key|}}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{{localize "DAGGERHEART.APPLICATIONS.DaggerheartMenu.refreshFeatures"}}</legend>
|
<legend>{{localize "DAGGERHEART.APPLICATIONS.DaggerheartMenu.refreshFeatures"}}</legend>
|
||||||
|
|
||||||
<div class="menu-refresh-container">
|
<div class="menu-options-container">
|
||||||
<div class="menu-refresh-inner-container">
|
<div class="menu-options-inner-container">
|
||||||
{{#each refreshables as |type key|}}
|
{{#each refreshables as |type key|}}
|
||||||
<div class="experience-chip {{#if type.selected}}selected{{/if}}" data-action="selectRefreshable" data-type="{{key}}">
|
<div class="option-chip {{#if type.selected}}selected{{/if}}" data-action="selectRefreshable" data-type="{{key}}">
|
||||||
{{#if type.selected}}
|
{{#if type.selected}}
|
||||||
<span><i class="fa-solid fa-circle"></i></span>
|
<span><i class="fa-solid fa-circle"></i></span>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
@ -21,4 +21,18 @@
|
||||||
<button data-action="refreshActors" {{disabled disableRefresh}}>{{localize "DAGGERHEART.GENERAL.refresh"}}</button>
|
<button data-action="refreshActors" {{disabled disableRefresh}}>{{localize "DAGGERHEART.GENERAL.refresh"}}</button>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "DAGGERHEART.APPLICATIONS.DaggerheartMenu.fallingAndCollision"}} <i class="fa-solid fa-explosion fa-fw"></i></legend>
|
||||||
|
|
||||||
|
<div class="menu-options-container">
|
||||||
|
<div class="menu-options-inner-container">
|
||||||
|
{{#each fallAndCollision as |data key|}}
|
||||||
|
<button data-action="createFallCollisionDamage" data-key="{{key}}">
|
||||||
|
{{localize data.label}}
|
||||||
|
</button>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
<div class="message-header-main">
|
<div class="message-header-main">
|
||||||
<img class="actor-img" src="{{actor.img}}" />
|
<img class="actor-img" src="{{actor.img}}" />
|
||||||
<div class="message-sub-header-container">
|
<div class="message-sub-header-container">
|
||||||
|
{{#if message.title}}
|
||||||
|
<h4>{{message.title}}</h4>
|
||||||
|
<div>{{actor.name}} {{#if author.isGM}}(GM){{/if}}</div>
|
||||||
|
{{else}}
|
||||||
{{#unless actor.name}}
|
{{#unless actor.name}}
|
||||||
<h4>{{author.name}}</h4>
|
<h4>{{author.name}}</h4>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
@ -11,10 +15,11 @@
|
||||||
<h4>{{actor.name}}</h4>
|
<h4>{{actor.name}}</h4>
|
||||||
<div>{{author.name}}</div>
|
<div>{{author.name}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<h4>{{ifThen message.title message.title alias}}</h4>
|
<h4>{{alias}}</h4>
|
||||||
<div>{{actor.name}} {{#if author.isGM}}(GM){{/if}}</div>
|
<div>{{actor.name}} {{#if author.isGM}}(GM){{/if}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="message-header-metadata">
|
<div class="message-header-metadata">
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.criticalShort"}}</span>
|
<span>{{localize "DAGGERHEART.GENERAL.criticalShort"}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if (and roll.result (not (eq roll.type "reaction")))}}
|
{{#if (and roll.result (not (eq roll.type "reaction")))}}
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.withThing" thing=roll.result.label}}</span>
|
<span>{{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -29,48 +29,48 @@
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="roll-dice">
|
<div class="roll-dice">
|
||||||
{{#if roll.fate}}
|
{{#if roll.fateDie}}
|
||||||
{{#if (eq roll.fate.fateDie "Hope")}}
|
{{#if (eq roll.fateDie "Hope")}}
|
||||||
<div class="roll-die">
|
<div class="roll-die">
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.hope"}}</label>
|
<label>{{localize "DAGGERHEART.GENERAL.hope"}}</label>
|
||||||
<div class="dice {{roll.fate.dice}} color-hope" data-die-index="0" data-type="hope">
|
<div class="dice {{roll.dHope.denomination}} color-hope" data-die-index="0" data-type="hope">
|
||||||
{{roll.fate.value}}
|
{{roll.dHope.total}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq roll.fate.fateDie "Fear")}}
|
{{#if (eq roll.fateDie "Fear")}}
|
||||||
<div class="roll-die">
|
<div class="roll-die">
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.fear"}}</label>
|
<label>{{localize "DAGGERHEART.GENERAL.fear"}}</label>
|
||||||
<div class="dice {{roll.fate.dice}} color-fear" data-die-index="0" data-type="fear">
|
<div class="dice {{roll.dFear.denomination}} color-fear" data-die-index="0" data-type="fear">
|
||||||
{{roll.fate.value}}
|
{{roll.dFear.total}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if roll.hope}}
|
{{#if roll.dHope}}
|
||||||
<div class="roll-die">
|
<div class="roll-die">
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.hope"}}</label>
|
<label>{{localize "DAGGERHEART.GENERAL.hope"}}</label>
|
||||||
<div class="dice {{roll.hope.dice}} color-hope reroll-button" data-die-index="0" data-type="hope" data-tooltip="{{localize "DAGGERHEART.GENERAL.rerollThing" thing=(localize "DAGGERHEART.GENERAL.hope")}}">
|
<div class="dice {{roll.dHope.denomination}} color-hope reroll-button" data-die-index="0" data-type="hope" data-tooltip="{{localize "DAGGERHEART.GENERAL.rerollThing" thing=(localize "DAGGERHEART.GENERAL.hope")}}">
|
||||||
{{#if roll.hope.rerolled.any}}<i class="fa-solid fa-dice dice-rerolled" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.diceIsRerolled" times=roll.hope.rerolled.rerolls.length}}"></i>{{/if}}
|
{{#if roll.dHopehope.rerolled.any}}<i class="fa-solid fa-dice dice-rerolled" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.diceIsRerolled" times=roll.dHope.rerolled.rerolls.length}}"></i>{{/if}}
|
||||||
{{roll.hope.value}}
|
{{roll.dHope.total}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="roll-die has-plus">
|
<div class="roll-die has-plus">
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.fear"}}</label>
|
<label>{{localize "DAGGERHEART.GENERAL.fear"}}</label>
|
||||||
<div class="dice {{roll.fear.dice}} color-fear reroll-button" data-die-index="2" data-type="fear" style="--svg-folder: 'fear';" data-tooltip="{{localize "DAGGERHEART.GENERAL.rerollThing" thing=(localize "DAGGERHEART.GENERAL.fear")}}">
|
<div class="dice {{roll.dFear.denomination}} color-fear reroll-button" data-die-index="1" data-type="fear" style="--svg-folder: 'fear';" data-tooltip="{{localize "DAGGERHEART.GENERAL.rerollThing" thing=(localize "DAGGERHEART.GENERAL.fear")}}">
|
||||||
{{#if roll.fear.rerolled.any}}<i class="fa-solid fa-dice dice-rerolled" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.diceIsRerolled" times=roll.fear.rerolled.rerolls.length}}"></i>{{/if}}
|
{{#if roll.dFear.rerolled.any}}<i class="fa-solid fa-dice dice-rerolled" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.diceIsRerolled" times=roll.dFear.rerolled.rerolls.length}}"></i>{{/if}}
|
||||||
{{roll.fear.value}}
|
{{roll.dFear.total}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if roll.advantage.type}}
|
{{#if roll.dAdvantage}}
|
||||||
<div class="roll-die has-plus">
|
<div class="roll-die has-plus">
|
||||||
{{#if (eq roll.advantage.type 1)}}
|
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.Advantage.short"}}</label>
|
<label>{{localize "DAGGERHEART.GENERAL.Advantage.short"}}</label>
|
||||||
<div class="dice {{roll.advantage.dice}} color-adv">{{roll.advantage.value}}</div>
|
<div class="dice {{roll.dAdvantage.denomination}} color-adv">{{roll.dAdvantage.total}}</div>
|
||||||
{{else}}
|
</div>
|
||||||
|
{{else if roll.dDisadvantage}}
|
||||||
|
<div class="roll-die has-minus">
|
||||||
<label>{{localize "DAGGERHEART.GENERAL.Disadvantage.short"}}</label>
|
<label>{{localize "DAGGERHEART.GENERAL.Disadvantage.short"}}</label>
|
||||||
<div class="dice {{roll.advantage.dice}} color-dis">{{roll.advantage.value}}</div>
|
<div class="dice {{roll.dDisadvantage.denomination}} color-dis">{{roll.dDisadvantage.total}}</div>
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if roll.rally.dice}}
|
{{#if roll.rally.dice}}
|
||||||
|
|
@ -79,15 +79,11 @@
|
||||||
<div class="dice {{roll.rally.dice}}">{{roll.rally.value}}</div>
|
<div class="dice {{roll.rally.dice}}">{{roll.rally.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#each roll.extra}}
|
{{#each roll.extraDice}}
|
||||||
{{#each results}}
|
|
||||||
{{#unless discarded}}
|
|
||||||
<div class="roll-die has-plus">
|
<div class="roll-die has-plus">
|
||||||
<label></label>
|
<label></label>
|
||||||
<div class="dice {{../dice}}">{{result}}</div>
|
<div class="dice {{this.denomination}}">{{this.total}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{/unless}}
|
|
||||||
{{/each}}
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#each roll.dice}}
|
{{#each roll.dice}}
|
||||||
|
|
|
||||||
|
|
@ -3,37 +3,7 @@
|
||||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||||
<p class="tooltip-subtitle"><i>{{item.system.examples}}</i></p>
|
<p class="tooltip-subtitle"><i>{{item.system.examples}}</i></p>
|
||||||
|
|
||||||
{{#if description}}
|
{{> "systems/daggerheart/templates/ui/tooltip/parts/beastformData.hbs" }}
|
||||||
<div class="tooltip-description">{{{description}}}</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="tags">
|
|
||||||
{{#with item.system.beastformAttackData}}
|
|
||||||
<div class="tag">
|
|
||||||
<span>{{localize "DAGGERHEART.ITEMS.Beastform.mainTrait"}} {{this.trait}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="tag">
|
|
||||||
<span>{{localize "DAGGERHEART.ITEMS.Beastform.traitBonus"}} {{this.traitBonus}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="tag">
|
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.evasion"}} {{this.evasionBonus}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="tag">
|
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.damage"}} {{concat this.damageDice ' ' this.damageBonus}} <i class="fa-solid fa-hand-fist"></i></span>
|
|
||||||
</div>
|
|
||||||
{{/with}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="tooltip-title">{{localize "DAGGERHEART.ITEMS.Beastform.FIELDS.advantageOn.label"}}</h2>
|
|
||||||
<div class="tags advantages">
|
|
||||||
{{#each item.system.advantageOn as | chip |}}
|
|
||||||
<div class="tag">
|
|
||||||
<span>{{ifThen chip.value chip.value chip}}</span>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.features label=(localize "DAGGERHEART.GENERAL.features")}}
|
|
||||||
|
|
||||||
<p class="tooltip-hint">
|
<p class="tooltip-hint">
|
||||||
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.Tooltip.middleClick"}}
|
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.Tooltip.middleClick"}}
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,14 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#unless effect.isLockedCondition}}
|
|
||||||
<div class="close-hints">
|
<div class="close-hints">
|
||||||
|
{{#if (eq effect.type 'beastform')}}
|
||||||
|
<p class="close-hint">
|
||||||
|
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.Tooltip.middleClick"}}
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
{{#unless effect.isLockedCondition}}
|
||||||
{{#if effect.system.stacking}}
|
{{#if effect.system.stacking}}
|
||||||
<p class="close-hint">
|
<p class="close-hint">
|
||||||
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.increaseStacks"}}
|
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.increaseStacks"}}
|
||||||
|
|
@ -61,6 +67,6 @@
|
||||||
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}}
|
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}}
|
||||||
</p>
|
</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
31
templates/ui/tooltip/parts/beastformData.hbs
Normal file
31
templates/ui/tooltip/parts/beastformData.hbs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{{#if description}}
|
||||||
|
<div class="tooltip-description">{{{description}}}</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="tags">
|
||||||
|
{{#with item.system.beastformAttackData}}
|
||||||
|
<div class="tag">
|
||||||
|
<span>{{localize "DAGGERHEART.ITEMS.Beastform.mainTrait"}} {{this.trait}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
<span>{{localize "DAGGERHEART.ITEMS.Beastform.traitBonus"}} {{this.traitBonus}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
<span>{{localize "DAGGERHEART.GENERAL.evasion"}} {{this.evasionBonus}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="tag">
|
||||||
|
<span>{{localize "DAGGERHEART.GENERAL.damage"}} {{concat this.damageDice ' ' this.damageBonus}} <i class="fa-solid fa-hand-fist"></i></span>
|
||||||
|
</div>
|
||||||
|
{{/with}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="tooltip-title">{{localize "DAGGERHEART.ITEMS.Beastform.FIELDS.advantageOn.label"}}</h2>
|
||||||
|
<div class="tags advantages">
|
||||||
|
{{#each item.system.advantageOn as | chip |}}
|
||||||
|
<div class="tag">
|
||||||
|
<span>{{ifThen chip.value chip.value chip}}</span>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.features label=(localize "DAGGERHEART.GENERAL.features")}}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{{#if (gt features.length 0)}}<h2 class="tooltip-title">{{label}}</h2>{{/if}}
|
{{#if (gt features.length 0)}}<h2 class="tooltip-title">{{label}}</h2>{{/if}}
|
||||||
|
<div class="tooltip-separator"></div>
|
||||||
<div class="tooltip-tags">
|
<div class="tooltip-tags">
|
||||||
{{#each features as | feature |}}
|
{{#each features as | feature |}}
|
||||||
{{#with (ifThen ../isAction feature (ifThen feature.item feature.item feature))}}
|
{{#with (ifThen ../isAction feature (ifThen feature.item feature.item feature))}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue