mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-16 13:41:07 +01:00
Compare commits
18 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7aed6825a | ||
|
|
9cb5112b62 | ||
|
|
81b6f7fc51 | ||
|
|
828fffd552 | ||
|
|
fc5626ac47 | ||
|
|
2e62545aa7 | ||
|
|
b09c712dd5 | ||
|
|
ca4336bd39 | ||
|
|
77ac11c522 | ||
|
|
50311679a5 | ||
|
|
3a7bcd1b0a | ||
|
|
511e4bd644 | ||
|
|
395820513b | ||
|
|
3566ea3fd3 | ||
|
|
29d502fb97 | ||
|
|
685a25d25a | ||
|
|
dd045b3df7 | ||
|
|
0aabcec340 |
420 changed files with 2731 additions and 10434 deletions
225
daggerheart.mjs
225
daggerheart.mjs
|
|
@ -16,10 +16,10 @@ import {
|
||||||
settingsRegistration,
|
settingsRegistration,
|
||||||
socketRegistration
|
socketRegistration
|
||||||
} from './module/systemRegistration/_module.mjs';
|
} from './module/systemRegistration/_module.mjs';
|
||||||
import { placeables, DhTokenLayer } from './module/canvas/_module.mjs';
|
import { placeables } from './module/canvas/_module.mjs';
|
||||||
|
import { registerRollDiceHooks } from './module/dice/dhRoll.mjs';
|
||||||
import './node_modules/@yaireo/tagify/dist/tagify.css';
|
import './node_modules/@yaireo/tagify/dist/tagify.css';
|
||||||
import TemplateManager from './module/documents/templateManager.mjs';
|
import TemplateManager from './module/documents/templateManager.mjs';
|
||||||
import TokenManager from './module/documents/tokenManager.mjs';
|
|
||||||
|
|
||||||
CONFIG.DH = SYSTEM;
|
CONFIG.DH = SYSTEM;
|
||||||
CONFIG.TextEditor.enrichers.push(...enricherConfig);
|
CONFIG.TextEditor.enrichers.push(...enricherConfig);
|
||||||
|
|
@ -52,12 +52,8 @@ CONFIG.ChatMessage.template = 'systems/daggerheart/templates/ui/chat/chat-messag
|
||||||
|
|
||||||
CONFIG.Canvas.rulerClass = placeables.DhRuler;
|
CONFIG.Canvas.rulerClass = placeables.DhRuler;
|
||||||
CONFIG.Canvas.layers.templates.layerClass = placeables.DhTemplateLayer;
|
CONFIG.Canvas.layers.templates.layerClass = placeables.DhTemplateLayer;
|
||||||
CONFIG.Canvas.layers.tokens.layerClass = DhTokenLayer;
|
|
||||||
|
|
||||||
CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate;
|
CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate;
|
||||||
|
|
||||||
CONFIG.Scene.documentClass = documents.DhScene;
|
|
||||||
|
|
||||||
CONFIG.Token.documentClass = documents.DhToken;
|
CONFIG.Token.documentClass = documents.DhToken;
|
||||||
CONFIG.Token.prototypeSheetClass = applications.sheetConfigs.DhPrototypeTokenConfig;
|
CONFIG.Token.prototypeSheetClass = applications.sheetConfigs.DhPrototypeTokenConfig;
|
||||||
CONFIG.Token.objectClass = placeables.DhTokenPlaceable;
|
CONFIG.Token.objectClass = placeables.DhTokenPlaceable;
|
||||||
|
|
@ -65,7 +61,6 @@ CONFIG.Token.rulerClass = placeables.DhTokenRuler;
|
||||||
CONFIG.Token.hudClass = applications.hud.DHTokenHUD;
|
CONFIG.Token.hudClass = applications.hud.DHTokenHUD;
|
||||||
|
|
||||||
CONFIG.ui.combat = applications.ui.DhCombatTracker;
|
CONFIG.ui.combat = applications.ui.DhCombatTracker;
|
||||||
CONFIG.ui.nav = applications.ui.DhSceneNavigation;
|
|
||||||
CONFIG.ui.chat = applications.ui.DhChatLog;
|
CONFIG.ui.chat = applications.ui.DhChatLog;
|
||||||
CONFIG.ui.effectsDisplay = applications.ui.DhEffectsDisplay;
|
CONFIG.ui.effectsDisplay = applications.ui.DhEffectsDisplay;
|
||||||
CONFIG.ui.hotbar = applications.ui.DhHotbar;
|
CONFIG.ui.hotbar = applications.ui.DhHotbar;
|
||||||
|
|
@ -77,8 +72,6 @@ CONFIG.ui.countdowns = applications.ui.DhCountdowns;
|
||||||
CONFIG.ux.ContextMenu = applications.ux.DHContextMenu;
|
CONFIG.ux.ContextMenu = applications.ux.DHContextMenu;
|
||||||
CONFIG.ux.TooltipManager = documents.DhTooltipManager;
|
CONFIG.ux.TooltipManager = documents.DhTooltipManager;
|
||||||
CONFIG.ux.TemplateManager = new TemplateManager();
|
CONFIG.ux.TemplateManager = new TemplateManager();
|
||||||
CONFIG.ux.TokenManager = new TokenManager();
|
|
||||||
CONFIG.debug.triggers = false;
|
|
||||||
|
|
||||||
Hooks.once('init', () => {
|
Hooks.once('init', () => {
|
||||||
game.system.api = {
|
game.system.api = {
|
||||||
|
|
@ -90,102 +83,40 @@ Hooks.once('init', () => {
|
||||||
fields
|
fields
|
||||||
};
|
};
|
||||||
|
|
||||||
game.system.registeredTriggers = new game.system.api.data.RegisteredTriggers();
|
|
||||||
|
|
||||||
const { DocumentSheetConfig } = foundry.applications.apps;
|
const { DocumentSheetConfig } = foundry.applications.apps;
|
||||||
DocumentSheetConfig.unregisterSheet(TokenDocument, 'core', foundry.applications.sheets.TokenConfig);
|
DocumentSheetConfig.unregisterSheet(TokenDocument, 'core', foundry.applications.sheets.TokenConfig);
|
||||||
DocumentSheetConfig.registerSheet(TokenDocument, SYSTEM.id, applications.sheetConfigs.DhTokenConfig, {
|
DocumentSheetConfig.registerSheet(TokenDocument, SYSTEM.id, applications.sheetConfigs.DhTokenConfig, {
|
||||||
makeDefault: true
|
makeDefault: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const sheetLabel = typePath => () =>
|
|
||||||
game.i18n.format('DAGGERHEART.GENERAL.typeSheet', {
|
|
||||||
type: game.i18n.localize(typePath)
|
|
||||||
});
|
|
||||||
|
|
||||||
const { Items, Actors } = foundry.documents.collections;
|
const { Items, Actors } = foundry.documents.collections;
|
||||||
Items.unregisterSheet('core', foundry.applications.sheets.ItemSheetV2);
|
Items.unregisterSheet('core', foundry.applications.sheets.ItemSheetV2);
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Ancestry, {
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Ancestry, { types: ['ancestry'], makeDefault: true });
|
||||||
types: ['ancestry'],
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Community, { types: ['community'], makeDefault: true });
|
||||||
makeDefault: true,
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Class, { types: ['class'], makeDefault: true });
|
||||||
label: sheetLabel('TYPES.Item.ancestry')
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Subclass, { types: ['subclass'], makeDefault: true });
|
||||||
});
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Feature, { types: ['feature'], makeDefault: true });
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Community, {
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.DomainCard, { types: ['domainCard'], makeDefault: true });
|
||||||
types: ['community'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Item.community')
|
|
||||||
});
|
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Class, {
|
|
||||||
types: ['class'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Item.class')
|
|
||||||
});
|
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Subclass, {
|
|
||||||
types: ['subclass'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Item.subclass')
|
|
||||||
});
|
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Feature, {
|
|
||||||
types: ['feature'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Item.feature')
|
|
||||||
});
|
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.DomainCard, {
|
|
||||||
types: ['domainCard'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Item.domainCard')
|
|
||||||
});
|
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Loot, {
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Loot, {
|
||||||
types: ['loot'],
|
types: ['loot'],
|
||||||
makeDefault: true,
|
makeDefault: true
|
||||||
label: sheetLabel('TYPES.Item.loot')
|
|
||||||
});
|
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Consumable, {
|
|
||||||
types: ['consumable'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Item.consumable')
|
|
||||||
});
|
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Weapon, {
|
|
||||||
types: ['weapon'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Item.weapon')
|
|
||||||
});
|
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Armor, {
|
|
||||||
types: ['armor'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Item.armor')
|
|
||||||
});
|
|
||||||
Items.registerSheet(SYSTEM.id, applications.sheets.items.Beastform, {
|
|
||||||
types: ['beastform'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Item.beastform')
|
|
||||||
});
|
});
|
||||||
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Consumable, { types: ['consumable'], makeDefault: true });
|
||||||
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Weapon, { types: ['weapon'], makeDefault: true });
|
||||||
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Armor, { types: ['armor'], makeDefault: true });
|
||||||
|
Items.registerSheet(SYSTEM.id, applications.sheets.items.Beastform, { types: ['beastform'], makeDefault: true });
|
||||||
|
|
||||||
Actors.unregisterSheet('core', foundry.applications.sheets.ActorSheetV2);
|
Actors.unregisterSheet('core', foundry.applications.sheets.ActorSheetV2);
|
||||||
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Character, {
|
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Character, { types: ['character'], makeDefault: true });
|
||||||
types: ['character'],
|
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Companion, { types: ['companion'], makeDefault: true });
|
||||||
makeDefault: true,
|
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Adversary, { types: ['adversary'], makeDefault: true });
|
||||||
label: sheetLabel('TYPES.Actor.character')
|
|
||||||
});
|
|
||||||
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Companion, {
|
|
||||||
types: ['companion'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Actor.companion')
|
|
||||||
});
|
|
||||||
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Adversary, {
|
|
||||||
types: ['adversary'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: sheetLabel('TYPES.Actor.adversary')
|
|
||||||
});
|
|
||||||
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Environment, {
|
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Environment, {
|
||||||
types: ['environment'],
|
types: ['environment'],
|
||||||
makeDefault: true,
|
makeDefault: true
|
||||||
label: sheetLabel('TYPES.Actor.environment')
|
|
||||||
});
|
});
|
||||||
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Party, {
|
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Party, {
|
||||||
types: ['party'],
|
types: ['party'],
|
||||||
makeDefault: true,
|
makeDefault: true
|
||||||
label: sheetLabel('TYPES.Actor.party')
|
|
||||||
});
|
});
|
||||||
|
|
||||||
DocumentSheetConfig.unregisterSheet(
|
DocumentSheetConfig.unregisterSheet(
|
||||||
|
|
@ -198,8 +129,7 @@ Hooks.once('init', () => {
|
||||||
SYSTEM.id,
|
SYSTEM.id,
|
||||||
applications.sheetConfigs.ActiveEffectConfig,
|
applications.sheetConfigs.ActiveEffectConfig,
|
||||||
{
|
{
|
||||||
makeDefault: true,
|
makeDefault: true
|
||||||
label: sheetLabel('DOCUMENT.ActiveEffect')
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -208,10 +138,9 @@ Hooks.once('init', () => {
|
||||||
// Make Compendium Dialog resizable
|
// Make Compendium Dialog resizable
|
||||||
foundry.applications.sidebar.apps.Compendium.DEFAULT_OPTIONS.window.resizable = true;
|
foundry.applications.sidebar.apps.Compendium.DEFAULT_OPTIONS.window.resizable = true;
|
||||||
|
|
||||||
DocumentSheetConfig.unregisterSheet(foundry.documents.Scene, 'core', foundry.applications.sheets.SceneConfig);
|
|
||||||
DocumentSheetConfig.registerSheet(foundry.documents.Scene, SYSTEM.id, applications.scene.DhSceneConfigSettings, {
|
DocumentSheetConfig.registerSheet(foundry.documents.Scene, SYSTEM.id, applications.scene.DhSceneConfigSettings, {
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: sheetLabel('DOCUMENT.Scene')
|
label: 'Daggerheart'
|
||||||
});
|
});
|
||||||
|
|
||||||
settingsRegistration.registerDHSettings();
|
settingsRegistration.registerDHSettings();
|
||||||
|
|
@ -248,6 +177,7 @@ Hooks.on('ready', async () => {
|
||||||
ui.compendiumBrowser = new applications.ui.ItemBrowser();
|
ui.compendiumBrowser = new applications.ui.ItemBrowser();
|
||||||
|
|
||||||
socketRegistration.registerSocketHooks();
|
socketRegistration.registerSocketHooks();
|
||||||
|
registerRollDiceHooks();
|
||||||
socketRegistration.registerUserQueries();
|
socketRegistration.registerUserQueries();
|
||||||
|
|
||||||
if (!game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.welcomeMessage)) {
|
if (!game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.welcomeMessage)) {
|
||||||
|
|
@ -263,9 +193,9 @@ Hooks.on('ready', async () => {
|
||||||
|
|
||||||
Hooks.once('dicesoniceready', () => {});
|
Hooks.once('dicesoniceready', () => {});
|
||||||
|
|
||||||
Hooks.on('renderChatMessageHTML', (document, element) => {
|
Hooks.on('renderChatMessageHTML', (_, element, message) => {
|
||||||
enricherRenderSetup(element);
|
enricherRenderSetup(element);
|
||||||
const cssClass = document.flags?.daggerheart?.cssClass;
|
const cssClass = message.message.flags?.daggerheart?.cssClass;
|
||||||
if (cssClass) cssClass.split(' ').forEach(cls => element.classList.add(cls));
|
if (cssClass) cssClass.split(' ').forEach(cls => element.classList.add(cls));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -310,7 +240,7 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
target,
|
target,
|
||||||
difficulty,
|
difficulty,
|
||||||
title,
|
title,
|
||||||
label: game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll'),
|
label: 'test',
|
||||||
actionType: null,
|
actionType: null,
|
||||||
advantage
|
advantage
|
||||||
});
|
});
|
||||||
|
|
@ -318,84 +248,53 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateActorsRangeDependentEffects = async token => {
|
Hooks.on('moveToken', async (movedToken, data) => {
|
||||||
const rangeMeasurement = game.settings.get(
|
|
||||||
CONFIG.DH.id,
|
|
||||||
CONFIG.DH.SETTINGS.gameSettings.variantRules
|
|
||||||
).rangeMeasurement;
|
|
||||||
|
|
||||||
for (let effect of token.actor?.allApplicableEffects() ?? []) {
|
|
||||||
if (!effect.system.rangeDependence?.enabled) continue;
|
|
||||||
const { target, range, type } = effect.system.rangeDependence;
|
|
||||||
|
|
||||||
// If there are no targets, assume false. Otherwise, start with the effect enabled.
|
|
||||||
let enabledEffect = game.user.targets.size !== 0;
|
|
||||||
// Expect all targets to meet the rangeDependence requirements
|
|
||||||
for (let userTarget of game.user.targets) {
|
|
||||||
const disposition = userTarget.document.disposition;
|
|
||||||
if ((target === 'friendly' && disposition !== 1) || (target === 'hostile' && disposition !== -1)) {
|
|
||||||
enabledEffect = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get required distance and special case 5 feet to test adjacency
|
|
||||||
const required = rangeMeasurement[range];
|
|
||||||
const reverse = type === CONFIG.DH.GENERAL.rangeInclusion.outsideRange.id;
|
|
||||||
const inRange =
|
|
||||||
required === 5
|
|
||||||
? userTarget.isAdjacentWith(token.object)
|
|
||||||
: userTarget.distanceTo(token.object) <= required;
|
|
||||||
if (reverse ? inRange : !inRange) {
|
|
||||||
enabledEffect = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await effect.update({ disabled: !enabledEffect });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateAllRangeDependentEffects = async () => {
|
|
||||||
const effectsAutomation = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).effects;
|
const effectsAutomation = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).effects;
|
||||||
if (!effectsAutomation.rangeDependent) return;
|
if (!effectsAutomation.rangeDependent) return;
|
||||||
|
|
||||||
const tokens = canvas.scene?.tokens;
|
const rangeDependantEffects = movedToken.actor.effects.filter(effect => effect.system.rangeDependence?.enabled);
|
||||||
if (!tokens) return;
|
|
||||||
|
|
||||||
if (game.user.character) {
|
const updateEffects = async (disposition, token, effects, effectUpdates) => {
|
||||||
// The character updates their character's token. There can be only one token.
|
const rangeMeasurement = game.settings.get(
|
||||||
const characterToken = tokens.find(x => x.actor === game.user.character);
|
CONFIG.DH.id,
|
||||||
updateActorsRangeDependentEffects(characterToken);
|
CONFIG.DH.SETTINGS.gameSettings.variantRules
|
||||||
} else if (game.user.isActiveGM) {
|
).rangeMeasurement;
|
||||||
// The GM is responsible for all other tokens.
|
|
||||||
const playerCharacters = game.users.players.filter(x => x.active).map(x => x.character);
|
for (let effect of effects.filter(x => x.system.rangeDependence?.enabled)) {
|
||||||
for (const token of tokens.filter(x => !playerCharacters.includes(x.actor))) {
|
const { target, range, type } = effect.system.rangeDependence;
|
||||||
updateActorsRangeDependentEffects(token);
|
if ((target === 'friendly' && disposition !== 1) || (target === 'hostile' && disposition !== -1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const distanceBetween = canvas.grid.measurePath([
|
||||||
|
{ ...movedToken.toObject(), x: data.destination.x, y: data.destination.y },
|
||||||
|
token
|
||||||
|
]).distance;
|
||||||
|
const distance = rangeMeasurement[range];
|
||||||
|
|
||||||
|
const reverse = type === CONFIG.DH.GENERAL.rangeInclusion.outsideRange.id;
|
||||||
|
const newDisabled = reverse ? distanceBetween <= distance : distanceBetween > distance;
|
||||||
|
const oldDisabled = effectUpdates[effect.uuid] ? effectUpdates[effect.uuid].disabled : newDisabled;
|
||||||
|
effectUpdates[effect.uuid] = {
|
||||||
|
disabled: oldDisabled || newDisabled,
|
||||||
|
value: effect
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const effectUpdates = {};
|
||||||
|
for (let token of game.scenes.find(x => x.active).tokens) {
|
||||||
|
if (token.id !== movedToken.id) {
|
||||||
|
await updateEffects(token.disposition, token, rangeDependantEffects, effectUpdates);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.actor) await updateEffects(movedToken.disposition, token, token.actor.effects, effectUpdates);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const debouncedRangeEffectCall = foundry.utils.debounce(updateAllRangeDependentEffects, 50);
|
for (let key in effectUpdates) {
|
||||||
|
const effect = effectUpdates[key];
|
||||||
Hooks.on('targetToken', () => {
|
await effect.value.update({ disabled: effect.disabled });
|
||||||
debouncedRangeEffectCall();
|
|
||||||
});
|
|
||||||
|
|
||||||
Hooks.on('refreshToken', (_, options) => {
|
|
||||||
if (options.refreshPosition) {
|
|
||||||
debouncedRangeEffectCall();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.on('renderCompendiumDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
|
Hooks.on('renderCompendiumDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
|
||||||
Hooks.on('renderDocumentDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
|
Hooks.on('renderDocumentDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
|
||||||
|
|
||||||
/* Non actor-linked Actors should unregister the triggers of their tokens if a scene's token layer is torn down */
|
|
||||||
Hooks.on('canvasTearDown', canvas => {
|
|
||||||
game.system.registeredTriggers.unregisterSceneTriggers(canvas.scene);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Non actor-linked Actors should register the triggers of their tokens on a readied scene */
|
|
||||||
Hooks.on('canvasReady', canas => {
|
|
||||||
game.system.registeredTriggers.registerSceneTriggers(canvas.scene);
|
|
||||||
});
|
|
||||||
|
|
|
||||||
124
lang/en.json
124
lang/en.json
|
|
@ -36,7 +36,6 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"DAGGERHEART": {
|
"DAGGERHEART": {
|
||||||
"CharacterSheet": "Character Sheet",
|
|
||||||
"ACTIONS": {
|
"ACTIONS": {
|
||||||
"TYPES": {
|
"TYPES": {
|
||||||
"attack": {
|
"attack": {
|
||||||
|
|
@ -69,11 +68,7 @@
|
||||||
},
|
},
|
||||||
"summon": {
|
"summon": {
|
||||||
"name": "Summon",
|
"name": "Summon",
|
||||||
"tooltip": "Create tokens in the scene.",
|
"tooltip": "Create tokens in the scene."
|
||||||
"error": "You do not have permission to summon tokens or there is no active scene.",
|
|
||||||
"invalidDrop": "You can only drop Actor entities to summon.",
|
|
||||||
"chatMessageTitle": "Test2",
|
|
||||||
"chatMessageHeaderTitle": "Summoning"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Config": {
|
"Config": {
|
||||||
|
|
@ -94,9 +89,7 @@
|
||||||
"customFormula": "Custom Formula",
|
"customFormula": "Custom Formula",
|
||||||
"formula": "Formula"
|
"formula": "Formula"
|
||||||
},
|
},
|
||||||
"displayInChat": "Display in chat",
|
"displayInChat": "Display in chat"
|
||||||
"deleteTriggerTitle": "Delete Trigger",
|
|
||||||
"deleteTriggerContent": "Are you sure you want to delete the {trigger} trigger?"
|
|
||||||
},
|
},
|
||||||
"RollField": {
|
"RollField": {
|
||||||
"diceRolling": {
|
"diceRolling": {
|
||||||
|
|
@ -126,9 +119,6 @@
|
||||||
},
|
},
|
||||||
"cost": {
|
"cost": {
|
||||||
"stepTooltip": "+{step} per step"
|
"stepTooltip": "+{step} per step"
|
||||||
},
|
|
||||||
"summon": {
|
|
||||||
"dropSummonsHere": "Drop Summons Here"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -203,8 +193,6 @@
|
||||||
"unequip": "Unequip",
|
"unequip": "Unequip",
|
||||||
"useItem": "Use Item"
|
"useItem": "Use Item"
|
||||||
},
|
},
|
||||||
"defaultHopeDice": "Default Hope Dice",
|
|
||||||
"defaultFearDice": "Default Fear Dice",
|
|
||||||
"disadvantageSources": {
|
"disadvantageSources": {
|
||||||
"label": "Disadvantage Sources",
|
"label": "Disadvantage Sources",
|
||||||
"hint": "Add single words or short text as reminders and hints of what a character has disadvantage on."
|
"hint": "Add single words or short text as reminders and hints of what a character has disadvantage on."
|
||||||
|
|
@ -237,7 +225,6 @@
|
||||||
"confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)"
|
"confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)"
|
||||||
},
|
},
|
||||||
"viewLevelups": "View Levelups",
|
"viewLevelups": "View Levelups",
|
||||||
"viewParty": "View Party",
|
|
||||||
"InvalidOldCharacterImportTitle": "Old Character Import",
|
"InvalidOldCharacterImportTitle": "Old Character Import",
|
||||||
"InvalidOldCharacterImportText": "Character data exported prior to system version 1.1 will not generate a complete character. Do you wish to continue?",
|
"InvalidOldCharacterImportText": "Character data exported prior to system version 1.1 will not generate a complete character. Do you wish to continue?",
|
||||||
"cancelBeastform": "Cancel Beastform"
|
"cancelBeastform": "Cancel Beastform"
|
||||||
|
|
@ -338,7 +325,6 @@
|
||||||
"equip": "Equip",
|
"equip": "Equip",
|
||||||
"sendToChat": "Send To Chat",
|
"sendToChat": "Send To Chat",
|
||||||
"toLoadout": "Send to Loadout",
|
"toLoadout": "Send to Loadout",
|
||||||
"recall": "Recall",
|
|
||||||
"toVault": "Send to Vault",
|
"toVault": "Send to Vault",
|
||||||
"unequip": "Unequip",
|
"unequip": "Unequip",
|
||||||
"useItem": "Use Item"
|
"useItem": "Use Item"
|
||||||
|
|
@ -599,7 +585,6 @@
|
||||||
},
|
},
|
||||||
"OwnershipSelection": {
|
"OwnershipSelection": {
|
||||||
"title": "Ownership Selection - {name}",
|
"title": "Ownership Selection - {name}",
|
||||||
"noPlayers": "No players to assign ownership to",
|
|
||||||
"default": "Default Ownership"
|
"default": "Default Ownership"
|
||||||
},
|
},
|
||||||
"ReactionRoll": {
|
"ReactionRoll": {
|
||||||
|
|
@ -626,9 +611,6 @@
|
||||||
"insufficientHope": "The initiating character doesn't have enough hope",
|
"insufficientHope": "The initiating character doesn't have enough hope",
|
||||||
"createTagTeam": "Create TagTeam Roll",
|
"createTagTeam": "Create TagTeam Roll",
|
||||||
"chatMessageRollTitle": "Roll"
|
"chatMessageRollTitle": "Roll"
|
||||||
},
|
|
||||||
"TokenConfig": {
|
|
||||||
"actorSizeUsed": "Actor size is set, determining the dimensions"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CLASS": {
|
"CLASS": {
|
||||||
|
|
@ -638,6 +620,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CONFIG": {
|
"CONFIG": {
|
||||||
|
"ActionType": {
|
||||||
|
"passive": "Passive",
|
||||||
|
"action": "Action",
|
||||||
|
"reaction": "Reaction"
|
||||||
|
},
|
||||||
"AdversaryTrait": {
|
"AdversaryTrait": {
|
||||||
"relentless": {
|
"relentless": {
|
||||||
"name": "Relentless",
|
"name": "Relentless",
|
||||||
|
|
@ -1046,12 +1033,6 @@
|
||||||
"description": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"FeatureForm": {
|
|
||||||
"label": "Feature Form",
|
|
||||||
"passive": "Passive",
|
|
||||||
"action": "Action",
|
|
||||||
"reaction": "Reaction"
|
|
||||||
},
|
|
||||||
"Gold": {
|
"Gold": {
|
||||||
"title": "Gold",
|
"title": "Gold",
|
||||||
"coins": "Coins",
|
"coins": "Coins",
|
||||||
|
|
@ -1155,8 +1136,7 @@
|
||||||
"any": "Any",
|
"any": "Any",
|
||||||
"friendly": "Friendly",
|
"friendly": "Friendly",
|
||||||
"hostile": "Hostile",
|
"hostile": "Hostile",
|
||||||
"self": "Self",
|
"self": "Self"
|
||||||
"other": "Other"
|
|
||||||
},
|
},
|
||||||
"TemplateTypes": {
|
"TemplateTypes": {
|
||||||
"circle": "Circle",
|
"circle": "Circle",
|
||||||
|
|
@ -1166,14 +1146,6 @@
|
||||||
"rect": "Rectangle",
|
"rect": "Rectangle",
|
||||||
"ray": "Ray"
|
"ray": "Ray"
|
||||||
},
|
},
|
||||||
"TokenSize": {
|
|
||||||
"tiny": "Tiny",
|
|
||||||
"small": "Small",
|
|
||||||
"medium": "Medium",
|
|
||||||
"large": "Large",
|
|
||||||
"huge": "Huge",
|
|
||||||
"gargantuan": "Gargantuan"
|
|
||||||
},
|
|
||||||
"Traits": {
|
"Traits": {
|
||||||
"agility": {
|
"agility": {
|
||||||
"name": "Agility",
|
"name": "Agility",
|
||||||
|
|
@ -1230,29 +1202,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Triggers": {
|
|
||||||
"postDamageReduction": {
|
|
||||||
"label": "After Damage Reduction"
|
|
||||||
},
|
|
||||||
"preDamageReduction": {
|
|
||||||
"label": "Before Damage Reduction"
|
|
||||||
},
|
|
||||||
"dualityRoll": {
|
|
||||||
"label": "Duality Roll"
|
|
||||||
},
|
|
||||||
"fearRoll": {
|
|
||||||
"label": "Fear Roll"
|
|
||||||
},
|
|
||||||
"triggerTexts": {
|
|
||||||
"strangePatternsContentTitle": "Matched {nr} times.",
|
|
||||||
"strangePatternsContentSubTitle": "Increase hope and stress to a total of {nr}.",
|
|
||||||
"ferocityContent": "Spend 2 Hope to gain {bonus} bonus Evasion until after the next attack against you?",
|
|
||||||
"ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you."
|
|
||||||
},
|
|
||||||
"triggerType": "Trigger Type",
|
|
||||||
"triggeringActorType": "Triggering Actor Type",
|
|
||||||
"triggerError": "{trigger} trigger failed for {actor}. It's probably configured wrong."
|
|
||||||
},
|
|
||||||
"WeaponFeature": {
|
"WeaponFeature": {
|
||||||
"barrier": {
|
"barrier": {
|
||||||
"name": "Barrier",
|
"name": "Barrier",
|
||||||
|
|
@ -1837,9 +1786,7 @@
|
||||||
"label": "Long Rest: Bonus Long Rest Moves",
|
"label": "Long Rest: Bonus Long Rest Moves",
|
||||||
"hint": "The number of extra Long Rest Moves the character can take during a Long Rest."
|
"hint": "The number of extra Long Rest Moves the character can take during a Long Rest."
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"target": "Target",
|
|
||||||
"targetSelf": "Self"
|
|
||||||
},
|
},
|
||||||
"maxLoadout": {
|
"maxLoadout": {
|
||||||
"label": "Max Loadout Cards Bonus"
|
"label": "Max Loadout Cards Bonus"
|
||||||
|
|
@ -2085,16 +2032,15 @@
|
||||||
"tier4": "tier 4",
|
"tier4": "tier 4",
|
||||||
"domains": "Domains",
|
"domains": "Domains",
|
||||||
"downtime": "Downtime",
|
"downtime": "Downtime",
|
||||||
"itemFeatures": "Item Features",
|
|
||||||
"roll": "Roll",
|
"roll": "Roll",
|
||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"partyMembers": "Party Members",
|
"partyMembers": "Party Members",
|
||||||
"projects": "Projects",
|
"projects": "Projects",
|
||||||
"types": "Types",
|
"types": "Types",
|
||||||
|
"itemFeatures": "Item Features",
|
||||||
"questions": "Questions",
|
"questions": "Questions",
|
||||||
"configuration": "Configuration",
|
"configuration": "Configuration",
|
||||||
"base": "Base",
|
"base": "Base"
|
||||||
"triggers": "Triggers"
|
|
||||||
},
|
},
|
||||||
"Tiers": {
|
"Tiers": {
|
||||||
"singular": "Tier",
|
"singular": "Tier",
|
||||||
|
|
@ -2148,7 +2094,6 @@
|
||||||
"fear": "Fear",
|
"fear": "Fear",
|
||||||
"features": "Features",
|
"features": "Features",
|
||||||
"formula": "Formula",
|
"formula": "Formula",
|
||||||
"general": "General",
|
|
||||||
"gm": "GM",
|
"gm": "GM",
|
||||||
"healing": "Healing",
|
"healing": "Healing",
|
||||||
"healingRoll": "Healing Roll",
|
"healingRoll": "Healing Roll",
|
||||||
|
|
@ -2215,22 +2160,16 @@
|
||||||
"stress": "Stress",
|
"stress": "Stress",
|
||||||
"subclasses": "Subclasses",
|
"subclasses": "Subclasses",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"summon": {
|
|
||||||
"single": "Summon",
|
|
||||||
"plural": "Summons"
|
|
||||||
},
|
|
||||||
"take": "Take",
|
"take": "Take",
|
||||||
"Target": {
|
"Target": {
|
||||||
"single": "Target",
|
"single": "Target",
|
||||||
"plural": "Targets"
|
"plural": "Targets"
|
||||||
},
|
},
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
"tokenSize": "Token Size",
|
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"traitModifier": "Trait Modifier",
|
"traitModifier": "Trait Modifier",
|
||||||
"true": "True",
|
"true": "True",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"typeSheet": "System {type} Sheet",
|
|
||||||
"unarmed": "Unarmed",
|
"unarmed": "Unarmed",
|
||||||
"unarmedAttack": "Unarmed Attack",
|
"unarmedAttack": "Unarmed Attack",
|
||||||
"unarmored": "Unarmored",
|
"unarmored": "Unarmored",
|
||||||
|
|
@ -2283,10 +2222,8 @@
|
||||||
"tokenRingImg": { "label": "Subject Texture" },
|
"tokenRingImg": { "label": "Subject Texture" },
|
||||||
"tokenSize": {
|
"tokenSize": {
|
||||||
"placeholder": "Using character dimensions",
|
"placeholder": "Using character dimensions",
|
||||||
"disabledPlaceholder": "Set by character size",
|
|
||||||
"height": { "label": "Height" },
|
"height": { "label": "Height" },
|
||||||
"width": { "label": "Width" },
|
"width": { "label": "Width" }
|
||||||
"scale": { "label": "Token Scale" }
|
|
||||||
},
|
},
|
||||||
"evolved": {
|
"evolved": {
|
||||||
"maximumTier": { "label": "Maximum Tier" },
|
"maximumTier": { "label": "Maximum Tier" },
|
||||||
|
|
@ -2310,9 +2247,7 @@
|
||||||
"hybridizeFeatureTitle": "Hybrid Features",
|
"hybridizeFeatureTitle": "Hybrid Features",
|
||||||
"hybridizeDrag": "Drag a form here to hybridize it.",
|
"hybridizeDrag": "Drag a form here to hybridize it.",
|
||||||
"mainTrait": "Main Trait",
|
"mainTrait": "Main Trait",
|
||||||
"traitBonus": "Trait Bonus",
|
"traitBonus": "Trait Bonus"
|
||||||
"evolvedTokenHint": "An evolved beastform's token is based on that of the form you evolve",
|
|
||||||
"evolvedImagePlaceholder": "The image for the form selected for evolution will be used"
|
|
||||||
},
|
},
|
||||||
"Class": {
|
"Class": {
|
||||||
"hopeFeatures": "Hope Features",
|
"hopeFeatures": "Hope Features",
|
||||||
|
|
@ -2335,9 +2270,6 @@
|
||||||
"DomainCard": {
|
"DomainCard": {
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"recallCost": "Recall Cost",
|
"recallCost": "Recall Cost",
|
||||||
"vaultActive": "Active In Vault",
|
|
||||||
"loadoutIgnore": "Ignores Loadout Limits",
|
|
||||||
"domainTouched": "Domain Touched",
|
|
||||||
"foundationTitle": "Foundation",
|
"foundationTitle": "Foundation",
|
||||||
"specializationTitle": "Specialization",
|
"specializationTitle": "Specialization",
|
||||||
"masteryTitle": "Mastery"
|
"masteryTitle": "Mastery"
|
||||||
|
|
@ -2476,12 +2408,6 @@
|
||||||
"hint": "Automatically apply effects. Targets must be selected before the action is made and Reaction Roll Automation must be different than Never. Bypass users permissions."
|
"hint": "Automatically apply effects. Targets must be selected before the action is made and Reaction Roll Automation must be different than Never. Bypass users permissions."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"triggers": {
|
|
||||||
"enabled": {
|
|
||||||
"label": "Enabled",
|
|
||||||
"hint": "Advanced automation such as triggering a popup for a wizard's Strange Patterns"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"summaryMessages": {
|
"summaryMessages": {
|
||||||
"label": "Summary Messages"
|
"label": "Summary Messages"
|
||||||
}
|
}
|
||||||
|
|
@ -2491,9 +2417,6 @@
|
||||||
},
|
},
|
||||||
"roll": {
|
"roll": {
|
||||||
"title": "Actions"
|
"title": "Actions"
|
||||||
},
|
|
||||||
"trigger": {
|
|
||||||
"title": "Triggers"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Homebrew": {
|
"Homebrew": {
|
||||||
|
|
@ -2604,8 +2527,8 @@
|
||||||
"enabled": { "label": "Enabled" },
|
"enabled": { "label": "Enabled" },
|
||||||
"tokens": { "label": "Tokens" }
|
"tokens": { "label": "Tokens" }
|
||||||
},
|
},
|
||||||
"massiveDamage": {
|
"massiveDamage":{
|
||||||
"title": "Massive Damage",
|
"title":"Massive Damage",
|
||||||
"enabled": { "label": "Enabled" }
|
"enabled": { "label": "Enabled" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2621,9 +2544,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"disabledText": "Daggerheart Measurements are disabled in System Settings - Variant Rules",
|
"disabledText": "Daggerheart Measurements are disabled in System Settings - Variant Rules",
|
||||||
"rangeMeasurement": "Range Measurement",
|
"rangeMeasurement": "Range Measurement"
|
||||||
"sceneEnvironments": "Scene Environments",
|
|
||||||
"dragEnvironmentHere": "Drag environments here"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"UI": {
|
"UI": {
|
||||||
|
|
@ -2710,9 +2631,6 @@
|
||||||
"rerollDamage": "Reroll Damage",
|
"rerollDamage": "Reroll Damage",
|
||||||
"assignTagRoll": "Assign as Tag Roll"
|
"assignTagRoll": "Assign as Tag Roll"
|
||||||
},
|
},
|
||||||
"ConsoleLogs": {
|
|
||||||
"triggerRun": "DH TRIGGER | Item '{item}' on actor '{actor}' ran a '{trigger}' trigger."
|
|
||||||
},
|
|
||||||
"Countdowns": {
|
"Countdowns": {
|
||||||
"title": "Countdowns",
|
"title": "Countdowns",
|
||||||
"toggleIconMode": "Toggle Icon Only",
|
"toggleIconMode": "Toggle Icon Only",
|
||||||
|
|
@ -2839,10 +2757,7 @@
|
||||||
"gmRequired": "This action requires an online GM",
|
"gmRequired": "This action requires an online GM",
|
||||||
"gmOnly": "This can only be accessed by the GM",
|
"gmOnly": "This can only be accessed by the GM",
|
||||||
"noActorOwnership": "You do not have permissions for this character",
|
"noActorOwnership": "You do not have permissions for this character",
|
||||||
"documentIsMissing": "The {documentType} is missing from the world.",
|
"documentIsMissing": "The {documentType} is missing from the world."
|
||||||
"tokenActorMissing": "{name} is missing an Actor",
|
|
||||||
"tokenActorsMissing": "[{names}] missing Actors",
|
|
||||||
"domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used"
|
|
||||||
},
|
},
|
||||||
"Sidebar": {
|
"Sidebar": {
|
||||||
"actorDirectory": {
|
"actorDirectory": {
|
||||||
|
|
@ -2885,10 +2800,7 @@
|
||||||
"companionPartnerLevelBlock": "The companion needs an assigned partner to level up.",
|
"companionPartnerLevelBlock": "The companion needs an assigned partner to level up.",
|
||||||
"configureAttribution": "Configure Attribution",
|
"configureAttribution": "Configure Attribution",
|
||||||
"deleteItem": "Delete Item",
|
"deleteItem": "Delete Item",
|
||||||
"immune": "Immune",
|
"immune": "Immune"
|
||||||
"middleClick": "[Middle Click] Keep tooltip view",
|
|
||||||
"tokenSize": "The token size used on the canvas",
|
|
||||||
"previewTokenHelp": "Left-click to place, right-click to cancel"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -278,26 +278,19 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
'close',
|
'close',
|
||||||
async () => {
|
async () => {
|
||||||
const selected = app.selected.toObject();
|
const selected = app.selected.toObject();
|
||||||
const evolved = app.evolved.form ? app.evolved.form.toObject() : null;
|
|
||||||
const data = await game.system.api.data.items.DHBeastform.getWildcardImage(
|
const data = await game.system.api.data.items.DHBeastform.getWildcardImage(
|
||||||
app.configData.data.parent,
|
app.configData.data.parent,
|
||||||
evolved ?? app.selected
|
app.selected
|
||||||
);
|
);
|
||||||
if (data) {
|
if (data) {
|
||||||
if (!data.selectedImage) selected = null;
|
if (!data.selectedImage) selected = null;
|
||||||
else {
|
else {
|
||||||
const imageSource = evolved ?? selected;
|
if (data.usesDynamicToken) selected.system.tokenRingImg = data.selectedImage;
|
||||||
if (imageSource.usesDynamicToken) imageSource.system.tokenRingImg = data.selectedImage;
|
else selected.system.tokenImg = data.selectedImage;
|
||||||
else imageSource.system.tokenImg = data.selectedImage;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve({
|
resolve({ selected: selected, evolved: app.evolved, hybrid: app.hybrid, item: featureItem });
|
||||||
selected: selected,
|
|
||||||
evolved: { ...app.evolved, form: evolved },
|
|
||||||
hybrid: app.hybrid,
|
|
||||||
item: featureItem
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
{ once: true }
|
{ once: true }
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.config.experiences = [];
|
this.config.experiences = [];
|
||||||
this.reactionOverride = config.actionType === 'reaction';
|
this.reactionOverride = config.actionType === 'reaction';
|
||||||
this.selectedEffects = this.config.bonusEffects;
|
|
||||||
|
|
||||||
if (config.source?.action) {
|
if (config.source?.action) {
|
||||||
this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent;
|
this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent;
|
||||||
|
|
@ -36,7 +35,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
selectExperience: this.selectExperience,
|
selectExperience: this.selectExperience,
|
||||||
toggleReaction: this.toggleReaction,
|
toggleReaction: this.toggleReaction,
|
||||||
toggleTagTeamRoll: this.toggleTagTeamRoll,
|
toggleTagTeamRoll: this.toggleTagTeamRoll,
|
||||||
toggleSelectedEffect: this.toggleSelectedEffect,
|
|
||||||
submitRoll: this.submitRoll
|
submitRoll: this.submitRoll
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
|
|
@ -78,9 +76,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
icon
|
icon
|
||||||
}));
|
}));
|
||||||
|
|
||||||
context.hasSelectedEffects = Boolean(this.selectedEffects && Object.keys(this.selectedEffects).length);
|
|
||||||
context.selectedEffects = this.selectedEffects;
|
|
||||||
|
|
||||||
this.config.costs ??= [];
|
this.config.costs ??= [];
|
||||||
if (this.config.costs?.length) {
|
if (this.config.costs?.length) {
|
||||||
const updatedCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call(
|
const updatedCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call(
|
||||||
|
|
@ -109,7 +104,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
context.roll = this.roll;
|
context.roll = this.roll;
|
||||||
context.rollType = this.roll?.constructor.name;
|
context.rollType = this.roll?.constructor.name;
|
||||||
context.rallyDie = this.roll.rallyChoices;
|
context.rallyDie = this.roll.rallyChoices;
|
||||||
const experiences = this.config.data?.system?.experiences || {};
|
const experiences = this.config.data?.experiences || {};
|
||||||
context.experiences = Object.keys(experiences).map(id => ({
|
context.experiences = Object.keys(experiences).map(id => ({
|
||||||
id,
|
id,
|
||||||
...experiences[id]
|
...experiences[id]
|
||||||
|
|
@ -123,7 +118,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
context.formula = this.roll.constructFormula(this.config);
|
context.formula = this.roll.constructFormula(this.config);
|
||||||
if (this.actor?.system?.traits) context.abilities = this.getTraitModifiers();
|
if (this.actor?.system?.traits) context.abilities = this.getTraitModifiers();
|
||||||
|
|
||||||
context.showReaction = !this.config.roll?.type || context.rollType === 'DualityRoll';
|
context.showReaction = !this.config.roll?.type && context.rollType === 'DualityRoll';
|
||||||
context.reactionOverride = this.reactionOverride;
|
context.reactionOverride = this.reactionOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,7 +185,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
extKey: button.dataset.key,
|
extKey: button.dataset.key,
|
||||||
key: this.config?.data?.parent?.isNPC ? 'fear' : 'hope',
|
key: this.config?.data?.parent?.isNPC ? 'fear' : 'hope',
|
||||||
value: 1,
|
value: 1,
|
||||||
name: this.config.data?.system.experiences?.[button.dataset.key]?.name
|
name: this.config.data?.experiences?.[button.dataset.key]?.name
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
this.render();
|
this.render();
|
||||||
|
|
@ -200,9 +195,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
if (this.config.roll) {
|
if (this.config.roll) {
|
||||||
this.reactionOverride = !this.reactionOverride;
|
this.reactionOverride = !this.reactionOverride;
|
||||||
this.config.actionType = this.reactionOverride
|
this.config.actionType = this.reactionOverride
|
||||||
? 'reaction'
|
? CONFIG.DH.ITEM.actionTypes.reaction.id
|
||||||
: this.config.actionType === 'reaction'
|
: this.config.actionType === CONFIG.DH.ITEM.actionTypes.reaction.id
|
||||||
? 'action'
|
? CONFIG.DH.ITEM.actionTypes.action.id
|
||||||
: this.config.actionType;
|
: this.config.actionType;
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
@ -213,11 +208,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static toggleSelectedEffect(_event, button) {
|
|
||||||
this.selectedEffects[button.dataset.key].selected = !this.selectedEffects[button.dataset.key].selected;
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async submitRoll() {
|
static async submitRoll() {
|
||||||
await this.close({ submitted: true });
|
await this.close({ submitted: true });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
|
|
||||||
this.roll = roll;
|
this.roll = roll;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.selectedEffects = this.config.bonusEffects;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
|
|
@ -21,7 +20,6 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
icon: 'fa-solid fa-dice'
|
icon: 'fa-solid fa-dice'
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
toggleSelectedEffect: this.toggleSelectedEffect,
|
|
||||||
submitRoll: this.submitRoll
|
submitRoll: this.submitRoll
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
|
|
@ -59,9 +57,6 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
icon
|
icon
|
||||||
}));
|
}));
|
||||||
context.modifiers = this.config.modifiers;
|
context.modifiers = this.config.modifiers;
|
||||||
context.hasSelectedEffects = Boolean(Object.keys(this.selectedEffects).length);
|
|
||||||
context.selectedEffects = this.selectedEffects;
|
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,11 +69,6 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static toggleSelectedEffect(_event, button) {
|
|
||||||
this.selectedEffects[button.dataset.key].selected = !this.selectedEffects[button.dataset.key].selected;
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async submitRoll() {
|
static async submitRoll() {
|
||||||
await this.close({ submitted: true });
|
await this.close({ submitted: true });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,29 +93,27 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
}
|
}
|
||||||
|
|
||||||
getRefreshables() {
|
getRefreshables() {
|
||||||
const actionItems = this.actor.items
|
const actionItems = this.actor.items.reduce((acc, x) => {
|
||||||
.filter(x => this.actor.system.isItemAvailable(x))
|
if (x.system.actions) {
|
||||||
.reduce((acc, x) => {
|
const recoverable = x.system.actions.reduce((acc, action) => {
|
||||||
if (x.system.actions) {
|
if (refreshIsAllowed([this.shortrest ? 'shortRest' : 'longRest'], action.uses.recovery)) {
|
||||||
const recoverable = x.system.actions.reduce((acc, action) => {
|
acc.push({
|
||||||
if (refreshIsAllowed([this.shortrest ? 'shortRest' : 'longRest'], action.uses.recovery)) {
|
title: x.name,
|
||||||
acc.push({
|
name: action.name,
|
||||||
title: x.name,
|
uuid: action.uuid
|
||||||
name: action.name,
|
});
|
||||||
uuid: action.uuid
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (recoverable) {
|
|
||||||
acc.push(...recoverable);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
if (recoverable) {
|
||||||
|
acc.push(...recoverable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
const resourceItems = this.actor.items.reduce((acc, x) => {
|
const resourceItems = this.actor.items.reduce((acc, x) => {
|
||||||
if (
|
if (
|
||||||
x.system.resource &&
|
x.system.resource &&
|
||||||
|
|
@ -183,18 +181,12 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
.filter(x => category.moves[x].selected)
|
.filter(x => category.moves[x].selected)
|
||||||
.flatMap(key => {
|
.flatMap(key => {
|
||||||
const move = category.moves[key];
|
const move = category.moves[key];
|
||||||
const needsTarget = move.actions.filter(x => x.target?.type && x.target.type !== 'self').length > 0;
|
|
||||||
return [...Array(move.selected).keys()].map(_ => ({
|
return [...Array(move.selected).keys()].map(_ => ({
|
||||||
...move,
|
...move,
|
||||||
movePath: `${categoryKey}.moves.${key}`,
|
movePath: `${categoryKey}.moves.${key}`
|
||||||
needsTarget: needsTarget
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const characters = game.actors
|
|
||||||
.filter(x => x.type === 'character')
|
|
||||||
.filter(x => x.testUserPermission(game.user, 'LIMITED'))
|
|
||||||
.filter(x => x.uuid !== this.actor.uuid);
|
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
const msg = {
|
const msg = {
|
||||||
|
|
@ -214,9 +206,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
`DAGGERHEART.APPLICATIONS.Downtime.${this.shortrest ? 'shortRest' : 'longRest'}.title`
|
`DAGGERHEART.APPLICATIONS.Downtime.${this.shortrest ? 'shortRest' : 'longRest'}.title`
|
||||||
),
|
),
|
||||||
actor: { name: this.actor.name, img: this.actor.img },
|
actor: { name: this.actor.name, img: this.actor.img },
|
||||||
moves: moves,
|
moves: moves
|
||||||
characters: characters,
|
|
||||||
selfId: this.actor.uuid
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
flags: {
|
flags: {
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,69 @@
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
export default class ItemTransferDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class ItemTransferDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
constructor(data) {
|
constructor(item) {
|
||||||
super({});
|
super({});
|
||||||
this.data = data;
|
|
||||||
|
this.item = item;
|
||||||
|
this.quantity = item.system.quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
return this.data.title;
|
return this.item.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
classes: ['daggerheart', 'dh-style', 'dialog', 'item-transfer'],
|
classes: ['daggerheart', 'dh-style', 'dialog', 'item-transfer'],
|
||||||
position: { width: 400, height: 'auto' },
|
position: { width: 300, height: 'auto' },
|
||||||
window: { icon: 'fa-solid fa-hand-holding-hand' },
|
window: { icon: 'fa-solid fa-hand-holding-hand' },
|
||||||
actions: {
|
actions: {
|
||||||
finish: ItemTransferDialog.#finish
|
finish: ItemTransferDialog.#finish
|
||||||
}
|
},
|
||||||
|
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
main: { template: 'systems/daggerheart/templates/dialogs/item-transfer.hbs', root: true }
|
main: { template: 'systems/daggerheart/templates/dialogs/item-transfer.hbs' }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
|
htmlElement.querySelector('.number-display').addEventListener('change', event => {
|
||||||
|
this.quantity = isNaN(event.target.value) ? this.quantity : Number(event.target.value);
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
return foundry.utils.mergeObject(context, this.data);
|
context.item = this.item;
|
||||||
|
context.quantity = this.quantity;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateData(_event, _element, formData) {
|
||||||
|
const { quantity } = foundry.utils.expandObject(formData.object);
|
||||||
|
this.quantity = quantity;
|
||||||
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #finish() {
|
static async #finish() {
|
||||||
this.selected = this.form.elements.quantity.valueAsNumber || null;
|
this.close({ submitted: true });
|
||||||
this.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static #determineTransferOptions({ originActor, targetActor, item, currency }) {
|
close(options = {}) {
|
||||||
originActor ??= item?.actor;
|
if (!options.submitted) this.quantity = null;
|
||||||
const homebrewKey = CONFIG.DH.SETTINGS.gameSettings.Homebrew;
|
|
||||||
const currencySetting = game.settings.get(CONFIG.DH.id, homebrewKey).currency?.[currency] ?? null;
|
|
||||||
|
|
||||||
return {
|
super.close();
|
||||||
originActor,
|
|
||||||
targetActor,
|
|
||||||
itemImage: item?.img,
|
|
||||||
currencyIcon: currencySetting?.icon,
|
|
||||||
max: item?.system.quantity ?? originActor.system.gold[currency] ?? 0,
|
|
||||||
title: item?.name ?? currencySetting?.label
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async configure(options) {
|
static async configure(item) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const data = this.#determineTransferOptions(options);
|
const app = new this(item);
|
||||||
if (data.max <= 1) return resolve(data.max);
|
app.addEventListener('close', () => resolve(app.quantity), { once: true });
|
||||||
|
|
||||||
const app = new this(data);
|
|
||||||
app.addEventListener('close', () => resolve(app.selected), { once: true });
|
|
||||||
app.render({ force: true });
|
app.render({ force: true });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ export default class OwnershipSelection extends HandlebarsApplicationMixin(Appli
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
|
context.ownershipDefaultOptions = CONFIG.DH.GENERAL.basicOwnershiplevels;
|
||||||
context.ownershipOptions = CONFIG.DH.GENERAL.simpleOwnershiplevels;
|
context.ownershipOptions = CONFIG.DH.GENERAL.simpleOwnershiplevels;
|
||||||
context.defaultOwnership = this.defaultOwnership;
|
context.defaultOwnership = this.defaultOwnership;
|
||||||
context.ownership = game.users.reduce((acc, user) => {
|
context.ownership = game.users.reduce((acc, user) => {
|
||||||
|
|
@ -51,7 +52,6 @@ export default class OwnershipSelection extends HandlebarsApplicationMixin(Appli
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
context.showOwnership = Boolean(Object.keys(context.ownership).length);
|
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
cost: this.data.initiator.cost
|
cost: this.data.initiator.cost
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectedMember = Object.values(context.members).find(x => x.selected && x.roll);
|
const selectedMember = Object.values(context.members).find(x => x.selected);
|
||||||
const selectedIsCritical = selectedMember?.roll?.system?.isCritical;
|
const selectedIsCritical = selectedMember?.roll?.system?.isCritical;
|
||||||
context.selectedData = {
|
context.selectedData = {
|
||||||
result: selectedMember
|
result: selectedMember
|
||||||
|
|
@ -220,8 +220,8 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
!roll.system.isCritical && criticalRoll
|
!roll.system.isCritical && criticalRoll
|
||||||
? (await getCritDamageBonus(damage.formula)) + damage.total
|
? (await getCritDamageBonus(damage.formula)) + damage.total
|
||||||
: damage.total;
|
: damage.total;
|
||||||
const updatedDamageParts = damage.parts;
|
|
||||||
if (systemData.damage[key]) {
|
if (systemData.damage[key]) {
|
||||||
|
const updatedDamageParts = damage.parts;
|
||||||
if (!roll.system.isCritical && criticalRoll) {
|
if (!roll.system.isCritical && criticalRoll) {
|
||||||
for (let part of updatedDamageParts) {
|
for (let part of updatedDamageParts) {
|
||||||
const criticalDamage = await getCritDamageBonus(part.formula);
|
const criticalDamage = await getCritDamageBonus(part.formula);
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,6 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
|
||||||
|
|
||||||
async _prepareContext(options) {
|
async _prepareContext(options) {
|
||||||
const context = await super._prepareContext(options);
|
const context = await super._prepareContext(options);
|
||||||
if (!this.actor) return context;
|
|
||||||
|
|
||||||
context.partyOnCanvas =
|
context.partyOnCanvas =
|
||||||
this.actor.type === 'party' &&
|
this.actor.type === 'party' &&
|
||||||
this.actor.system.partyMembers.some(member => member.getActiveTokens().length > 0);
|
this.actor.system.partyMembers.some(member => member.getActiveTokens().length > 0);
|
||||||
|
|
@ -60,33 +58,14 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onToggleCombat() {
|
static async #onToggleCombat() {
|
||||||
const tokensWithoutActors = canvas.tokens.controlled.filter(t => !t.actor);
|
|
||||||
const warning =
|
|
||||||
tokensWithoutActors.length === 1
|
|
||||||
? game.i18n.format('DAGGERHEART.UI.Notifications.tokenActorMissing', {
|
|
||||||
name: tokensWithoutActors[0].name
|
|
||||||
})
|
|
||||||
: game.i18n.format('DAGGERHEART.UI.Notifications.tokenActorsMissing', {
|
|
||||||
names: tokensWithoutActors.map(x => x.name).join(', ')
|
|
||||||
});
|
|
||||||
|
|
||||||
const tokens = canvas.tokens.controlled
|
const tokens = canvas.tokens.controlled
|
||||||
.filter(t => t.actor && !DHTokenHUD.#nonCombatTypes.includes(t.actor.type))
|
.filter(t => !t.actor || !DHTokenHUD.#nonCombatTypes.includes(t.actor.type))
|
||||||
.map(t => t.document);
|
.map(t => t.document);
|
||||||
if (!this.object.controlled && this.document.actor) tokens.push(this.document);
|
if (!this.object.controlled) tokens.push(this.document);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.document.inCombat) {
|
if (this.document.inCombat) await TokenDocument.implementation.deleteCombatants(tokens);
|
||||||
const tokensInCombat = tokens.filter(t => t.inCombat);
|
else await TokenDocument.implementation.createCombatants(tokens);
|
||||||
await TokenDocument.implementation.deleteCombatants([...tokensInCombat, ...tokensWithoutActors]);
|
|
||||||
} else {
|
|
||||||
if (tokensWithoutActors.length) {
|
|
||||||
ui.notifications.warn(warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokensOutOfCombat = tokens.filter(t => !t.inCombat);
|
|
||||||
await TokenDocument.implementation.createCombatants(tokensOutOfCombat);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ui.notifications.warn(err.message);
|
ui.notifications.warn(err.message);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -280,19 +280,11 @@ export default class DhCharacterLevelUp extends LevelUpBase {
|
||||||
break;
|
break;
|
||||||
case 'experience':
|
case 'experience':
|
||||||
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||||
const allExperiences = {
|
|
||||||
...this.actor.system.experiences,
|
|
||||||
...Object.values(this.levelup.levels).reduce((acc, level) => {
|
|
||||||
for (const key of Object.keys(level.achievements.experiences)) {
|
|
||||||
acc[key] = level.achievements.experiences[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, {})
|
|
||||||
};
|
|
||||||
const data = checkbox.data.map(data => {
|
const data = checkbox.data.map(data => {
|
||||||
const experience = Object.keys(allExperiences).find(x => x === data);
|
const experience = Object.keys(this.actor.system.experiences).find(
|
||||||
return allExperiences[experience]?.name ?? '';
|
x => x === data
|
||||||
|
);
|
||||||
|
return this.actor.system.experiences[experience]?.name ?? '';
|
||||||
});
|
});
|
||||||
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -357,23 +357,11 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
|
|
||||||
const experienceIncreaseTagify = htmlElement.querySelector('.levelup-experience-increases');
|
const experienceIncreaseTagify = htmlElement.querySelector('.levelup-experience-increases');
|
||||||
if (experienceIncreaseTagify) {
|
if (experienceIncreaseTagify) {
|
||||||
const allExperiences = {
|
|
||||||
...this.actor.system.experiences,
|
|
||||||
...Object.values(this.levelup.levels).reduce((acc, level) => {
|
|
||||||
for (const key of Object.keys(level.achievements.experiences)) {
|
|
||||||
acc[key] = level.achievements.experiences[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, {})
|
|
||||||
};
|
|
||||||
tagifyElement(
|
tagifyElement(
|
||||||
experienceIncreaseTagify,
|
experienceIncreaseTagify,
|
||||||
Object.keys(allExperiences).reduce((acc, id) => {
|
Object.keys(this.actor.system.experiences).reduce((acc, id) => {
|
||||||
const experience = allExperiences[id];
|
const experience = this.actor.system.experiences[id];
|
||||||
if (experience.name) {
|
acc.push({ id: id, label: experience.name });
|
||||||
acc.push({ id: id, label: experience.name });
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, []),
|
}, []),
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,16 @@
|
||||||
import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
|
||||||
|
|
||||||
export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig {
|
export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig {
|
||||||
constructor(options) {
|
// static DEFAULT_OPTIONS = {
|
||||||
super(options);
|
// ...super.DEFAULT_OPTIONS,
|
||||||
|
// form: {
|
||||||
Hooks.on(socketEvent.Refresh, ({ refreshType }) => {
|
// handler: this.updateData,
|
||||||
if (refreshType === RefreshType.Scene) this.render();
|
// closeOnSubmit: true
|
||||||
});
|
// }
|
||||||
}
|
// };
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
|
||||||
...super.DEFAULT_OPTIONS,
|
|
||||||
actions: {
|
|
||||||
...super.DEFAULT_OPTIONS.actions,
|
|
||||||
removeSceneEnvironment: DhSceneConfigSettings.#removeSceneEnvironment
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static buildParts() {
|
static buildParts() {
|
||||||
const { footer, tabs, ...parts } = super.PARTS;
|
const { footer, tabs, ...parts } = super.PARTS;
|
||||||
const tmpParts = {
|
const tmpParts = {
|
||||||
|
// tabs,
|
||||||
tabs: { template: 'systems/daggerheart/templates/scene/tabs.hbs' },
|
tabs: { template: 'systems/daggerheart/templates/scene/tabs.hbs' },
|
||||||
...parts,
|
...parts,
|
||||||
dh: { template: 'systems/daggerheart/templates/scene/dh-config.hbs' },
|
dh: { template: 'systems/daggerheart/templates/scene/dh-config.hbs' },
|
||||||
|
|
@ -37,47 +28,27 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
|
||||||
|
|
||||||
static TABS = DhSceneConfigSettings.buildTabs();
|
static TABS = DhSceneConfigSettings.buildTabs();
|
||||||
|
|
||||||
async _preRender(context, options) {
|
|
||||||
await super._preFirstRender(context, options);
|
|
||||||
|
|
||||||
if (!options.internalRefresh)
|
|
||||||
this.daggerheartFlag = new game.system.api.data.scenes.DHScene(this.document.flags.daggerheart);
|
|
||||||
}
|
|
||||||
|
|
||||||
_attachPartListeners(partId, htmlElement, options) {
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
case 'dh':
|
case 'dh':
|
||||||
htmlElement.querySelector('#rangeMeasurementSetting')?.addEventListener('change', async event => {
|
htmlElement.querySelector('#rangeMeasurementSetting')?.addEventListener('change', async event => {
|
||||||
this.daggerheartFlag.updateSource({ rangeMeasurement: { setting: event.target.value } });
|
const flagData = foundry.utils.mergeObject(this.document.flags.daggerheart, {
|
||||||
this.render({ internalRefresh: true });
|
rangeMeasurement: { setting: event.target.value }
|
||||||
|
});
|
||||||
|
this.document.flags.daggerheart = flagData;
|
||||||
|
this.render();
|
||||||
});
|
});
|
||||||
|
|
||||||
const dragArea = htmlElement.querySelector('.scene-environments');
|
|
||||||
if (dragArea) dragArea.ondrop = this._onDrop.bind(this);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onDrop(event) {
|
|
||||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
|
||||||
const item = await foundry.utils.fromUuid(data.uuid);
|
|
||||||
if (item instanceof game.system.api.documents.DhpActor && item.type === 'environment') {
|
|
||||||
await this.daggerheartFlag.updateSource({
|
|
||||||
sceneEnvironments: [...this.daggerheartFlag.sceneEnvironments, data.uuid]
|
|
||||||
});
|
|
||||||
this.render({ internalRefresh: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
async _preparePartContext(partId, context, options) {
|
async _preparePartContext(partId, context, options) {
|
||||||
context = await super._preparePartContext(partId, context, options);
|
context = await super._preparePartContext(partId, context, options);
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
case 'dh':
|
case 'dh':
|
||||||
context.data = this.daggerheartFlag;
|
context.data = new game.system.api.data.scenes.DHScene(canvas.scene.flags.daggerheart);
|
||||||
context.variantRules = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules);
|
context.variantRules = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -85,24 +56,8 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #removeSceneEnvironment(_event, button) {
|
// static async updateData(event, _, formData) {
|
||||||
await this.daggerheartFlag.updateSource({
|
// const data = foundry.utils.expandObject(formData.object);
|
||||||
sceneEnvironments: this.daggerheartFlag.sceneEnvironments.filter(
|
// this.close(data);
|
||||||
(_, index) => index !== Number.parseInt(button.dataset.index)
|
// }
|
||||||
)
|
|
||||||
});
|
|
||||||
this.render({ internalRefresh: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
async _processSubmitData(event, form, submitData, options) {
|
|
||||||
submitData.flags.daggerheart = this.daggerheartFlag.toObject();
|
|
||||||
for (const key of Object.keys(this.document._source.flags.daggerheart?.sceneEnvironments ?? {})) {
|
|
||||||
if (!submitData.flags.daggerheart.sceneEnvironments[key]) {
|
|
||||||
submitData.flags.daggerheart.sceneEnvironments[`-=${key}`] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super._processSubmitData(event, form, submitData, options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
deleteAdversaryType: this.deleteAdversaryType,
|
deleteAdversaryType: this.deleteAdversaryType,
|
||||||
selectAdversaryType: this.selectAdversaryType,
|
selectAdversaryType: this.selectAdversaryType,
|
||||||
save: this.save,
|
save: this.save,
|
||||||
resetTokenSizes: this.resetTokenSizes,
|
|
||||||
reset: this.reset
|
reset: this.reset
|
||||||
},
|
},
|
||||||
form: { handler: this.updateData, submitOnChange: true }
|
form: { handler: this.updateData, submitOnChange: true }
|
||||||
|
|
@ -425,14 +424,6 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async resetTokenSizes() {
|
|
||||||
await this.settings.updateSource({
|
|
||||||
tokenSizes: this.settings.schema.fields.tokenSizes.initial
|
|
||||||
});
|
|
||||||
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async reset() {
|
static async reset() {
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
window: {
|
window: {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
|
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.openSection = null;
|
this.openSection = null;
|
||||||
this.openTrigger = this.action.triggers.length > 0 ? 0 : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
|
|
@ -16,7 +15,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
classes: ['daggerheart', 'dh-style', 'action-config', 'dialog', 'max-800'],
|
classes: ['daggerheart', 'dh-style', 'dialog', 'max-800'],
|
||||||
window: {
|
window: {
|
||||||
icon: 'fa-solid fa-wrench',
|
icon: 'fa-solid fa-wrench',
|
||||||
resizable: false
|
resizable: false
|
||||||
|
|
@ -30,18 +29,13 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
removeElement: this.removeElement,
|
removeElement: this.removeElement,
|
||||||
editEffect: this.editEffect,
|
editEffect: this.editEffect,
|
||||||
addDamage: this.addDamage,
|
addDamage: this.addDamage,
|
||||||
removeDamage: this.removeDamage,
|
removeDamage: this.removeDamage
|
||||||
editDoc: this.editDoc,
|
|
||||||
addTrigger: this.addTrigger,
|
|
||||||
removeTrigger: this.removeTrigger,
|
|
||||||
expandTrigger: this.expandTrigger
|
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
handler: this.updateForm,
|
handler: this.updateForm,
|
||||||
submitOnChange: true,
|
submitOnChange: true,
|
||||||
closeOnSubmit: false
|
closeOnSubmit: false
|
||||||
},
|
}
|
||||||
dragDrop: [{ dragSelector: null, dropSelector: '#summon-drop-zone', handlers: ['_onDrop'] }]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
|
|
@ -61,10 +55,6 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
effect: {
|
effect: {
|
||||||
id: 'effect',
|
id: 'effect',
|
||||||
template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs'
|
template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs'
|
||||||
},
|
|
||||||
trigger: {
|
|
||||||
id: 'trigger',
|
|
||||||
template: 'systems/daggerheart/templates/sheets-settings/action-settings/trigger.hbs'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -92,18 +82,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
id: 'effect',
|
id: 'effect',
|
||||||
icon: null,
|
icon: null,
|
||||||
label: 'DAGGERHEART.GENERAL.Tabs.effects'
|
label: 'DAGGERHEART.GENERAL.Tabs.effects'
|
||||||
},
|
|
||||||
trigger: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'trigger',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.GENERAL.Tabs.triggers'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects', 'summon'];
|
static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects'];
|
||||||
|
|
||||||
_getTabs(tabs) {
|
_getTabs(tabs) {
|
||||||
for (const v of Object.values(tabs)) {
|
for (const v of Object.values(tabs)) {
|
||||||
|
|
@ -114,24 +96,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
return tabs;
|
return tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
_attachPartListeners(partId, htmlElement, options) {
|
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
|
||||||
|
|
||||||
htmlElement.querySelectorAll('.summon-count-wrapper input').forEach(element => {
|
|
||||||
element.addEventListener('change', this.updateSummonCount.bind(this));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options, 'action');
|
const context = await super._prepareContext(_options, 'action');
|
||||||
context.source = this.action.toObject(true);
|
context.source = this.action.toObject(false);
|
||||||
|
|
||||||
context.summons = [];
|
|
||||||
for (const summon of context.source.summon ?? []) {
|
|
||||||
const actor = await foundry.utils.fromUuid(summon.actorUUID);
|
|
||||||
context.summons.push({ actor, count: summon.count });
|
|
||||||
}
|
|
||||||
|
|
||||||
context.openSection = this.openSection;
|
context.openSection = this.openSection;
|
||||||
context.tabs = this._getTabs(this.constructor.TABS);
|
context.tabs = this._getTabs(this.constructor.TABS);
|
||||||
context.config = CONFIG.DH;
|
context.config = CONFIG.DH;
|
||||||
|
|
@ -144,16 +111,6 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty;
|
context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty;
|
||||||
context.baseAttackBonus = this.action.actor?.system.attack?.roll.bonus;
|
context.baseAttackBonus = this.action.actor?.system.attack?.roll.bonus;
|
||||||
context.hasRoll = this.action.hasRoll;
|
context.hasRoll = this.action.hasRoll;
|
||||||
context.triggers = context.source.triggers.map((trigger, index) => {
|
|
||||||
const { hint, returns, usesActor } = CONFIG.DH.TRIGGER.triggers[trigger.trigger];
|
|
||||||
return {
|
|
||||||
...trigger,
|
|
||||||
hint,
|
|
||||||
returns,
|
|
||||||
usesActor,
|
|
||||||
revealed: this.openTrigger === index
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
|
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
|
||||||
context.tierOptions = [
|
context.tierOptions = [
|
||||||
|
|
@ -224,9 +181,8 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
static async updateForm(event, _, formData) {
|
||||||
const submitData = this._prepareSubmitData(event, formData);
|
const submitData = this._prepareSubmitData(event, formData),
|
||||||
|
data = foundry.utils.mergeObject(this.action.toObject(), submitData);
|
||||||
const data = foundry.utils.mergeObject(this.action.toObject(), submitData);
|
|
||||||
this.action = await this.action.update(data);
|
this.action = await this.action.update(data);
|
||||||
|
|
||||||
this.sheetUpdate?.(this.action);
|
this.sheetUpdate?.(this.action);
|
||||||
|
|
@ -245,26 +201,12 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
static removeElement(event, button) {
|
static removeElement(event, button) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const data = this.action.toObject(),
|
const data = this.action.toObject(),
|
||||||
key = event.target.closest('[data-key]').dataset.key;
|
key = event.target.closest('[data-key]').dataset.key,
|
||||||
|
index = button.dataset.index;
|
||||||
// Prefer explicit index, otherwise find by uuid
|
|
||||||
let index = button?.dataset.index;
|
|
||||||
if (index === undefined || index === null || index === '') {
|
|
||||||
const uuid = button?.dataset.uuid ?? button?.dataset.itemUuid;
|
|
||||||
index = data[key].findIndex(e => (e?.actorUUID ?? e?.uuid) === uuid);
|
|
||||||
if (index === -1) return;
|
|
||||||
} else index = Number(index);
|
|
||||||
|
|
||||||
data[key].splice(index, 1);
|
data[key].splice(index, 1);
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
static async editDoc(_event, target) {
|
|
||||||
const element = target.closest('[data-item-uuid]');
|
|
||||||
const doc = (await foundry.utils.fromUuid(element.dataset.itemUuid)) ?? null;
|
|
||||||
if (doc) return doc.sheet.render({ force: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
static addDamage(_event) {
|
static addDamage(_event) {
|
||||||
if (!this.action.damage.parts) return;
|
if (!this.action.damage.parts) return;
|
||||||
const data = this.action.toObject(),
|
const data = this.action.toObject(),
|
||||||
|
|
@ -282,69 +224,6 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
static addTrigger() {
|
|
||||||
const data = this.action.toObject();
|
|
||||||
data.triggers.push({
|
|
||||||
trigger: CONFIG.DH.TRIGGER.triggers.dualityRoll.id,
|
|
||||||
triggeringActor: CONFIG.DH.TRIGGER.triggerActorTargetType.any.id
|
|
||||||
});
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
static async removeTrigger(_event, button) {
|
|
||||||
const trigger = CONFIG.DH.TRIGGER.triggers[this.action.triggers[button.dataset.index].trigger];
|
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
|
||||||
window: {
|
|
||||||
title: game.i18n.localize('DAGGERHEART.ACTIONS.Config.deleteTriggerTitle')
|
|
||||||
},
|
|
||||||
content: game.i18n.format('DAGGERHEART.ACTIONS.Config.deleteTriggerContent', {
|
|
||||||
trigger: game.i18n.localize(trigger.label)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!confirmed) return;
|
|
||||||
|
|
||||||
const data = this.action.toObject();
|
|
||||||
data.triggers = data.triggers.filter((_, index) => index !== Number.parseInt(button.dataset.index));
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
static async expandTrigger(_event, button) {
|
|
||||||
const index = Number.parseInt(button.dataset.index);
|
|
||||||
const toggle = (element, codeMirror) => {
|
|
||||||
codeMirror.classList.toggle('revealed');
|
|
||||||
const button = element.querySelector('a > i');
|
|
||||||
button.classList.toggle('fa-angle-up');
|
|
||||||
button.classList.toggle('fa-angle-down');
|
|
||||||
};
|
|
||||||
|
|
||||||
const fieldset = button.closest('fieldset');
|
|
||||||
const codeMirror = fieldset.querySelector('.code-mirror-wrapper');
|
|
||||||
toggle(fieldset, codeMirror);
|
|
||||||
|
|
||||||
if (this.openTrigger !== null && this.openTrigger !== index) {
|
|
||||||
const previouslyExpanded = fieldset
|
|
||||||
.closest(`section`)
|
|
||||||
.querySelector(`fieldset[data-index="${this.openTrigger}"]`);
|
|
||||||
const codeMirror = previouslyExpanded.querySelector('.code-mirror-wrapper');
|
|
||||||
toggle(previouslyExpanded, codeMirror);
|
|
||||||
this.openTrigger = index;
|
|
||||||
} else if (this.openTrigger === index) {
|
|
||||||
this.openTrigger = null;
|
|
||||||
} else {
|
|
||||||
this.openTrigger = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSummonCount(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
const wrapper = event.target.closest('.summon-count-wrapper');
|
|
||||||
const index = wrapper.dataset.index;
|
|
||||||
const data = this.action.toObject();
|
|
||||||
data.summon[index].count = event.target.value;
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Specific implementation in extending classes **/
|
/** Specific implementation in extending classes **/
|
||||||
static async addEffect(_event) {}
|
static async addEffect(_event) {}
|
||||||
static removeEffect(_event, _button) {}
|
static removeEffect(_event, _button) {}
|
||||||
|
|
@ -354,29 +233,4 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
this.tabGroups.primary = 'base';
|
this.tabGroups.primary = 'base';
|
||||||
await super.close(options);
|
await super.close(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onDrop(event) {
|
|
||||||
const data = foundry.applications.ux.TextEditor.getDragEventData(event);
|
|
||||||
const item = await foundry.utils.fromUuid(data.uuid);
|
|
||||||
if (!(item instanceof game.system.api.documents.DhpActor)) {
|
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.invalidDrop'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const actionData = this.action.toObject();
|
|
||||||
let countvalue = 1;
|
|
||||||
for (const entry of actionData.summon) {
|
|
||||||
if (entry.actorUUID === data.uuid) {
|
|
||||||
entry.count += 1;
|
|
||||||
countvalue = entry.count;
|
|
||||||
await this.constructor.updateForm.bind(this)(null, null, {
|
|
||||||
object: foundry.utils.flattenObject(actionData)
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actionData.summon.push({ actorUUID: data.uuid, count: countvalue });
|
|
||||||
await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,6 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
if (!ignoredActorKeys.includes(key)) {
|
if (!ignoredActorKeys.includes(key)) {
|
||||||
const model = game.system.api.models.actors[key];
|
const model = game.system.api.models.actors[key];
|
||||||
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
|
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
|
||||||
// As per DHToken._getTrackedAttributesFromSchema, attributes.bar have a max version as well.
|
|
||||||
const maxAttributes = attributes.bar.map(x => [...x, 'max']);
|
|
||||||
attributes.value.push(...maxAttributes);
|
|
||||||
const group = game.i18n.localize(model.metadata.label);
|
const group = game.i18n.localize(model.metadata.label);
|
||||||
const choices = CONFIG.Token.documentClass
|
const choices = CONFIG.Token.documentClass
|
||||||
.getTrackedAttributeChoices(attributes, model)
|
.getTrackedAttributeChoices(attributes, model)
|
||||||
|
|
|
||||||
|
|
@ -51,19 +51,6 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async _prepareContext(options) {
|
|
||||||
const context = await super._prepareContext(options);
|
|
||||||
|
|
||||||
const featureForms = ['passive', 'action', 'reaction'];
|
|
||||||
context.features = context.document.system.features.sort((a, b) =>
|
|
||||||
a.system.featureForm !== b.system.featureForm
|
|
||||||
? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm)
|
|
||||||
: a.sort - b.sort
|
|
||||||
);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -49,19 +49,6 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async _prepareContext(options) {
|
|
||||||
const context = await super._prepareContext(options);
|
|
||||||
|
|
||||||
const featureForms = ['passive', 'action', 'reaction'];
|
|
||||||
context.features = context.document.system.features.sort((a, b) =>
|
|
||||||
a.system.featureForm !== b.system.featureForm
|
|
||||||
? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm)
|
|
||||||
: a.sort - b.sort
|
|
||||||
);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new category entry to the actor.
|
* Adds a new category entry to the actor.
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,20 @@
|
||||||
import DHTokenConfigMixin from './token-config-mixin.mjs';
|
export default class DhPrototypeTokenConfig extends foundry.applications.sheets.PrototypeTokenConfig {
|
||||||
import { getActorSizeFromForm } from './token-config-mixin.mjs';
|
|
||||||
|
|
||||||
export default class DhPrototypeTokenConfig extends DHTokenConfigMixin(
|
|
||||||
foundry.applications.sheets.PrototypeTokenConfig
|
|
||||||
) {
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static DEFAULT_OPTIONS = {
|
async _prepareResourcesTab() {
|
||||||
...super.DEFAULT_OPTIONS,
|
const token = this.token;
|
||||||
form: { handler: DhPrototypeTokenConfig.#onSubmit }
|
const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes);
|
||||||
};
|
const attributeSource =
|
||||||
|
this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes
|
||||||
/**
|
? this.actor?.type
|
||||||
* Process form submission for the sheet
|
: this.actor?.system;
|
||||||
* @this {PrototypeTokenConfig}
|
const TokenDocument = foundry.utils.getDocumentClass('Token');
|
||||||
* @type {ApplicationFormSubmission}
|
const attributes = TokenDocument.getTrackedAttributes(attributeSource);
|
||||||
*/
|
return {
|
||||||
static async #onSubmit(event, form, formData) {
|
barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource),
|
||||||
const submitData = this._processFormData(event, form, formData);
|
bar1: token.getBarAttribute?.('bar1'),
|
||||||
submitData.detectionModes ??= []; // Clear detection modes array
|
bar2: token.getBarAttribute?.('bar2'),
|
||||||
this._processChanges(submitData);
|
turnMarkerModes: DhPrototypeTokenConfig.TURN_MARKER_MODES,
|
||||||
const changes = { prototypeToken: submitData };
|
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
|
||||||
|
};
|
||||||
const changedTokenSizeValue = getActorSizeFromForm(this.element, this.actor);
|
|
||||||
if (changedTokenSizeValue) changes.system = { size: changedTokenSizeValue };
|
|
||||||
|
|
||||||
this.actor.validate({ changes, clean: true, fallback: false });
|
|
||||||
await this.actor.update(changes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
export default function DHTokenConfigMixin(Base) {
|
|
||||||
class DHTokenConfigBase extends Base {
|
|
||||||
/** @override */
|
|
||||||
static PARTS = {
|
|
||||||
tabs: super.PARTS.tabs,
|
|
||||||
identity: super.PARTS.identity,
|
|
||||||
appearance: {
|
|
||||||
template: 'systems/daggerheart/templates/sheets-settings/token-config/appearance.hbs',
|
|
||||||
scrollable: ['']
|
|
||||||
},
|
|
||||||
vision: super.PARTS.vision,
|
|
||||||
light: super.PARTS.light,
|
|
||||||
resources: super.PARTS.resources,
|
|
||||||
footer: super.PARTS.footer
|
|
||||||
};
|
|
||||||
|
|
||||||
_attachPartListeners(partId, htmlElement, options) {
|
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
|
||||||
|
|
||||||
switch (partId) {
|
|
||||||
case 'appearance':
|
|
||||||
htmlElement
|
|
||||||
.querySelector('#dhTokenSize')
|
|
||||||
?.addEventListener('change', this.onTokenSizeChange.bind(this));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
async _prepareResourcesTab() {
|
|
||||||
const token = this.token;
|
|
||||||
const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes);
|
|
||||||
const attributeSource =
|
|
||||||
this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes
|
|
||||||
? this.actor?.type
|
|
||||||
: this.actor?.system;
|
|
||||||
const TokenDocument = foundry.utils.getDocumentClass('Token');
|
|
||||||
const attributes = TokenDocument.getTrackedAttributes(attributeSource);
|
|
||||||
return {
|
|
||||||
barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource),
|
|
||||||
bar1: token.getBarAttribute?.('bar1'),
|
|
||||||
bar2: token.getBarAttribute?.('bar2'),
|
|
||||||
turnMarkerModes: DHTokenConfigBase.TURN_MARKER_MODES,
|
|
||||||
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async _prepareAppearanceTab() {
|
|
||||||
const context = await super._prepareAppearanceTab();
|
|
||||||
context.tokenSizes = CONFIG.DH.ACTOR.tokenSize;
|
|
||||||
context.tokenSize = this.actor?.system?.size;
|
|
||||||
context.usesActorSize = this.actor?.system?.metadata?.usesSize;
|
|
||||||
context.actorSizeDisable = context.usesActorSize && this.actor.system.size !== 'custom';
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
_previewChanges(changes) {
|
|
||||||
if (!changes || !this._preview) return;
|
|
||||||
|
|
||||||
const tokenSizeSelect = this.element?.querySelector('#dhTokenSize');
|
|
||||||
if (this.actor && tokenSizeSelect && tokenSizeSelect.value !== 'custom') {
|
|
||||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
|
||||||
const tokenSize = tokenSizes[tokenSizeSelect.value];
|
|
||||||
changes.width = tokenSize;
|
|
||||||
changes.height = tokenSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
const deletions = { '-=actorId': null, '-=actorLink': null };
|
|
||||||
const mergeOptions = { inplace: false, performDeletions: true };
|
|
||||||
this._preview.updateSource(mergeObject(changes, deletions, mergeOptions));
|
|
||||||
|
|
||||||
if (this._preview?.object?.destroyed === false) {
|
|
||||||
this._preview.object.initializeSources();
|
|
||||||
this._preview.object.renderFlags.set({ refresh: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async onTokenSizeChange(event) {
|
|
||||||
const value = event.target.value;
|
|
||||||
const tokenSizeDimensions = this.element.querySelector('#tokenSizeDimensions');
|
|
||||||
if (tokenSizeDimensions) {
|
|
||||||
const disabled = value !== 'custom';
|
|
||||||
|
|
||||||
tokenSizeDimensions.dataset.tooltip = disabled
|
|
||||||
? game.i18n.localize('DAGGERHEART.APPLICATIONS.TokenConfig.actorSizeUsed')
|
|
||||||
: '';
|
|
||||||
|
|
||||||
const disabledIcon = tokenSizeDimensions.querySelector('i');
|
|
||||||
if (disabledIcon) {
|
|
||||||
disabledIcon.style.opacity = disabled ? '' : '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
const dimensionsInputs = tokenSizeDimensions.querySelectorAll('.form-fields input');
|
|
||||||
for (const input of dimensionsInputs) {
|
|
||||||
input.disabled = disabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DHTokenConfigBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getActorSizeFromForm(element, actor) {
|
|
||||||
const tokenSizeSelect = element.querySelector('#dhTokenSize');
|
|
||||||
const isSizeDifferent = tokenSizeSelect?.value !== actor?.system?.size;
|
|
||||||
if (tokenSizeSelect && actor && isSizeDifferent) {
|
|
||||||
return tokenSizeSelect.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,20 @@
|
||||||
import DHTokenConfigMixin from './token-config-mixin.mjs';
|
export default class DhTokenConfig extends foundry.applications.sheets.TokenConfig {
|
||||||
import { getActorSizeFromForm } from './token-config-mixin.mjs';
|
/** @inheritDoc */
|
||||||
|
async _prepareResourcesTab() {
|
||||||
export default class DhTokenConfig extends DHTokenConfigMixin(foundry.applications.sheets.TokenConfig) {
|
const token = this.token;
|
||||||
async _processSubmitData(event, form, submitData, options) {
|
const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes);
|
||||||
const changedTokenSizeValue = getActorSizeFromForm(this.element, this.actor);
|
const attributeSource =
|
||||||
if (changedTokenSizeValue) this.token.actor.update({ 'system.size': changedTokenSizeValue });
|
this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes
|
||||||
|
? this.actor?.type
|
||||||
super._processSubmitData(event, form, submitData, options);
|
: this.actor?.system;
|
||||||
|
const TokenDocument = foundry.utils.getDocumentClass('Token');
|
||||||
|
const attributes = TokenDocument.getTrackedAttributes(attributeSource);
|
||||||
|
return {
|
||||||
|
barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource),
|
||||||
|
bar1: token.getBarAttribute?.('bar1'),
|
||||||
|
bar2: token.getBarAttribute?.('bar2'),
|
||||||
|
turnMarkerModes: DhTokenConfig.TURN_MARKER_MODES,
|
||||||
|
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
dragDrop: [
|
dragDrop: [{ dragSelector: '[data-item-id][draggable="true"]', dropSelector: null }]
|
||||||
{
|
|
||||||
dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]',
|
|
||||||
dropSelector: null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
|
|
@ -93,13 +88,6 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
||||||
context.resources.stress.emptyPips =
|
context.resources.stress.emptyPips =
|
||||||
context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0;
|
context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0;
|
||||||
|
|
||||||
const featureForms = ['passive', 'action', 'reaction'];
|
|
||||||
context.features = this.document.system.features.sort((a, b) =>
|
|
||||||
a.system.featureForm !== b.system.featureForm
|
|
||||||
? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm)
|
|
||||||
: a.sort - b.sort
|
|
||||||
);
|
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,15 +164,6 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
async _onDragStart(event) {
|
|
||||||
const inventoryItem = event.currentTarget.closest('.inventory-item');
|
|
||||||
if (inventoryItem) {
|
|
||||||
event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0);
|
|
||||||
}
|
|
||||||
super._onDragStart(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Application Clicks Actions */
|
/* Application Clicks Actions */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
handleResourceDice: CharacterSheet.#handleResourceDice,
|
handleResourceDice: CharacterSheet.#handleResourceDice,
|
||||||
advanceResourceDie: CharacterSheet.#advanceResourceDie,
|
advanceResourceDie: CharacterSheet.#advanceResourceDie,
|
||||||
cancelBeastform: CharacterSheet.#cancelBeastform,
|
cancelBeastform: CharacterSheet.#cancelBeastform,
|
||||||
useDowntime: this.useDowntime,
|
useDowntime: this.useDowntime
|
||||||
viewParty: CharacterSheet.#viewParty
|
|
||||||
},
|
},
|
||||||
window: {
|
window: {
|
||||||
resizable: true,
|
resizable: true,
|
||||||
|
|
@ -47,7 +46,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
},
|
},
|
||||||
dragDrop: [
|
dragDrop: [
|
||||||
{
|
{
|
||||||
dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]',
|
dragSelector: '[data-item-id][draggable="true"]',
|
||||||
dropSelector: null
|
dropSelector: null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -319,45 +318,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached'));
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'recall',
|
|
||||||
icon: 'fa-solid fa-bolt-lightning',
|
|
||||||
condition: target => {
|
|
||||||
const doc = getDocFromElementSync(target);
|
|
||||||
return doc && doc.system.inVault;
|
|
||||||
},
|
|
||||||
callback: async (target, event) => {
|
|
||||||
const doc = await getDocFromElement(target);
|
|
||||||
const actorLoadout = doc.actor.system.loadoutSlot;
|
|
||||||
if (!actorLoadout.available) {
|
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (doc.system.recallCost == 0) {
|
|
||||||
return doc.update({ 'system.inVault': false });
|
|
||||||
}
|
|
||||||
const type = 'effect';
|
|
||||||
const cls = game.system.api.models.actions.actionsTypes[type];
|
|
||||||
const action = new cls(
|
|
||||||
{
|
|
||||||
...cls.getSourceConfig(doc.system),
|
|
||||||
type: type,
|
|
||||||
chatDisplay: false,
|
|
||||||
cost: [
|
|
||||||
{
|
|
||||||
key: 'stress',
|
|
||||||
value: doc.system.recallCost
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ parent: doc.system }
|
|
||||||
);
|
|
||||||
const config = await action.use(event);
|
|
||||||
if (config) {
|
|
||||||
return doc.update({ 'system.inVault': false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'toVault',
|
name: 'toVault',
|
||||||
icon: 'fa-solid fa-arrow-down',
|
icon: 'fa-solid fa-arrow-down',
|
||||||
|
|
@ -712,27 +672,19 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||||
ability: abilityLabel
|
ability: abilityLabel
|
||||||
}),
|
}),
|
||||||
effects: await game.system.api.data.actions.actionsTypes.base.getEffects(this.document),
|
|
||||||
roll: {
|
roll: {
|
||||||
trait: button.dataset.attribute,
|
trait: button.dataset.attribute
|
||||||
type: 'trait'
|
|
||||||
},
|
},
|
||||||
hasRoll: true,
|
hasRoll: true
|
||||||
|
};
|
||||||
|
const result = await this.document.diceRoll({
|
||||||
|
...config,
|
||||||
actionType: 'action',
|
actionType: 'action',
|
||||||
headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${this.actor.name}`,
|
headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${this.actor.name}`,
|
||||||
title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||||
ability: abilityLabel
|
ability: abilityLabel
|
||||||
})
|
})
|
||||||
};
|
});
|
||||||
const result = await this.document.diceRoll(config);
|
|
||||||
if (!result) return;
|
|
||||||
|
|
||||||
/* This could be avoided by baking config.costs into config.resourceUpdates. Didn't feel like messing with it at the time */
|
|
||||||
const costResources = result.costs
|
|
||||||
.filter(x => x.enabled)
|
|
||||||
.map(cost => ({ ...cost, value: -cost.value, total: -cost.total }));
|
|
||||||
config.resourceUpdates.addResources(costResources);
|
|
||||||
await config.resourceUpdates.updateResources();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: redo toggleEquipItem method
|
//TODO: redo toggleEquipItem method
|
||||||
|
|
@ -830,7 +782,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
static async #toggleVault(_event, button) {
|
static async #toggleVault(_event, button) {
|
||||||
const doc = await getDocFromElement(button);
|
const doc = await getDocFromElement(button);
|
||||||
const { available } = this.document.system.loadoutSlot;
|
const { available } = this.document.system.loadoutSlot;
|
||||||
if (doc.system.inVault && !available && !doc.system.loadoutIgnore) {
|
if (doc.system.inVault && !available) {
|
||||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached'));
|
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -901,41 +853,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(item);
|
game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #viewParty(_, target) {
|
|
||||||
const parties = this.document.parties;
|
|
||||||
if (parties.size <= 1) {
|
|
||||||
parties.first()?.sheet.render({ force: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttons = parties.map(p => {
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.type = 'button';
|
|
||||||
button.classList.add('plain');
|
|
||||||
const img = document.createElement('img');
|
|
||||||
img.src = p.img;
|
|
||||||
button.append(img);
|
|
||||||
const name = document.createElement('span');
|
|
||||||
name.textContent = p.name;
|
|
||||||
button.append(name);
|
|
||||||
button.addEventListener('click', () => {
|
|
||||||
p.sheet?.render({ force: true });
|
|
||||||
game.tooltip.dismissLockedTooltips();
|
|
||||||
});
|
|
||||||
return button;
|
|
||||||
});
|
|
||||||
|
|
||||||
const html = document.createElement('div');
|
|
||||||
html.classList.add('party-list');
|
|
||||||
html.append(...buttons);
|
|
||||||
|
|
||||||
game.tooltip.dismissLockedTooltips();
|
|
||||||
game.tooltip.activate(target, {
|
|
||||||
html,
|
|
||||||
locked: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the downtime application.
|
* Open the downtime application.
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
|
|
@ -946,15 +863,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
async _onDragStart(event) {
|
|
||||||
const inventoryItem = event.currentTarget.closest('.inventory-item');
|
|
||||||
if (inventoryItem) {
|
|
||||||
event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0);
|
|
||||||
}
|
|
||||||
super._onDragStart(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _onDropItem(event, item) {
|
async _onDropItem(event, item) {
|
||||||
if (this.document.uuid === item.parent?.uuid) {
|
if (this.document.uuid === item.parent?.uuid) {
|
||||||
return super._onDropItem(event, item);
|
return super._onDropItem(event, item);
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,7 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
||||||
toggleResourceDice: DhpEnvironment.#toggleResourceDice,
|
toggleResourceDice: DhpEnvironment.#toggleResourceDice,
|
||||||
handleResourceDice: DhpEnvironment.#handleResourceDice
|
handleResourceDice: DhpEnvironment.#handleResourceDice
|
||||||
},
|
},
|
||||||
dragDrop: [
|
dragDrop: [{ dragSelector: '.inventory-item', dropSelector: null }]
|
||||||
{
|
|
||||||
dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]',
|
|
||||||
dropSelector: null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**@override */
|
/**@override */
|
||||||
|
|
@ -79,9 +74,6 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
||||||
case 'header':
|
case 'header':
|
||||||
await this._prepareHeaderContext(context, options);
|
await this._prepareHeaderContext(context, options);
|
||||||
|
|
||||||
break;
|
|
||||||
case 'features':
|
|
||||||
await this._prepareFeaturesContext(context, options);
|
|
||||||
break;
|
break;
|
||||||
case 'notes':
|
case 'notes':
|
||||||
await this._prepareNotesContext(context, options);
|
await this._prepareNotesContext(context, options);
|
||||||
|
|
@ -118,22 +110,6 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare render context for the features part.
|
|
||||||
* @param {ApplicationRenderContext} context
|
|
||||||
* @param {ApplicationRenderOptions} options
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
async _prepareFeaturesContext(context, _options) {
|
|
||||||
const featureForms = ['passive', 'action', 'reaction'];
|
|
||||||
context.features = this.document.system.features.sort((a, b) =>
|
|
||||||
a.system.featureForm !== b.system.featureForm
|
|
||||||
? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm)
|
|
||||||
: a.sort - b.sort
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare render context for the Header part.
|
* Prepare render context for the Header part.
|
||||||
* @param {ApplicationRenderContext} context
|
* @param {ApplicationRenderContext} context
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ export default class Party extends DHBaseActorSheet {
|
||||||
selectRefreshable: DaggerheartMenu.selectRefreshable,
|
selectRefreshable: DaggerheartMenu.selectRefreshable,
|
||||||
refreshActors: DaggerheartMenu.refreshActors
|
refreshActors: DaggerheartMenu.refreshActors
|
||||||
},
|
},
|
||||||
dragDrop: [{ dragSelector: '[data-item-id]', dropSelector: null }]
|
dragDrop: [{ dragSelector: '[data-item-id][draggable="true"]', dropSelector: null }]
|
||||||
};
|
};
|
||||||
|
|
||||||
/**@override */
|
/**@override */
|
||||||
|
|
|
||||||
|
|
@ -179,10 +179,10 @@ export default function DHApplicationMixin(Base) {
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
this._dragDrop.forEach(d => d.bind(htmlElement));
|
this._dragDrop.forEach(d => d.bind(htmlElement));
|
||||||
|
|
||||||
// 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;
|
||||||
deltaInput.inputMode = 'numeric';
|
deltaInput.inputMode = 'numeric';
|
||||||
|
deltaInput.pattern = '^[+=\\-]?\d*';
|
||||||
|
|
||||||
const handleUpdate = (delta = 0) => {
|
const handleUpdate = (delta = 0) => {
|
||||||
const min = Number(deltaInput.min) || 0;
|
const min = Number(deltaInput.min) || 0;
|
||||||
|
|
@ -211,7 +211,7 @@ export default function DHApplicationMixin(Base) {
|
||||||
const step = event.key === 'ArrowUp' ? 1 : event.key === 'ArrowDown' ? -1 : 0;
|
const step = event.key === 'ArrowUp' ? 1 : event.key === 'ArrowDown' ? -1 : 0;
|
||||||
if (step !== 0) {
|
if (step !== 0) {
|
||||||
handleUpdate(step);
|
handleUpdate(step);
|
||||||
deltaInput.dispatchEvent(new Event('change', { bubbles: true }));
|
deltaInput.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -222,7 +222,7 @@ export default function DHApplicationMixin(Base) {
|
||||||
if (deltaInput === document.activeElement) {
|
if (deltaInput === document.activeElement) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
handleUpdate(Math.sign(-1 * event.deltaY));
|
handleUpdate(Math.sign(-1 * event.deltaY));
|
||||||
deltaInput.dispatchEvent(new Event('change', { bubbles: true }));
|
deltaInput.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ passive: false }
|
{ passive: false }
|
||||||
|
|
@ -232,25 +232,6 @@ export default function DHApplicationMixin(Base) {
|
||||||
handleUpdate();
|
handleUpdate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle contenteditable
|
|
||||||
for (const input of htmlElement.querySelectorAll('[contenteditable][data-property]')) {
|
|
||||||
const property = input.dataset.property;
|
|
||||||
input.addEventListener('blur', () => {
|
|
||||||
const selection = document.getSelection();
|
|
||||||
if (input.contains(selection.anchorNode)) {
|
|
||||||
selection.empty();
|
|
||||||
}
|
|
||||||
this.document.update({ [property]: input.textContent });
|
|
||||||
});
|
|
||||||
|
|
||||||
input.addEventListener('keydown', event => {
|
|
||||||
if (event.key === 'Enter') input.blur();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Chrome sometimes add <br>, which aren't a problem for the value but are for the placeholder
|
|
||||||
input.addEventListener('input', () => input.querySelectorAll('br').forEach(i => i.remove()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
|
|
@ -505,10 +486,6 @@ export default function DHApplicationMixin(Base) {
|
||||||
const doc = await getDocFromElement(target),
|
const doc = await getDocFromElement(target),
|
||||||
action = doc?.system?.attack ?? doc;
|
action = doc?.system?.attack ?? doc;
|
||||||
const config = action.prepareConfig(event);
|
const config = action.prepareConfig(event);
|
||||||
config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects(
|
|
||||||
this.document,
|
|
||||||
doc
|
|
||||||
);
|
|
||||||
config.hasRoll = false;
|
config.hasRoll = false;
|
||||||
return action && action.workflow.get('damage').execute(config, null, true);
|
return action && action.workflow.get('damage').execute(config, null, true);
|
||||||
}
|
}
|
||||||
|
|
@ -589,9 +566,7 @@ export default function DHApplicationMixin(Base) {
|
||||||
if (!doc || !descriptionElement) continue;
|
if (!doc || !descriptionElement) continue;
|
||||||
|
|
||||||
// localize the description (idk if it's still necessary)
|
// localize the description (idk if it's still necessary)
|
||||||
const description = doc.system?.getEnrichedDescription
|
const description = game.i18n.localize(doc.system?.description ?? doc.description);
|
||||||
? await doc.system.getEnrichedDescription()
|
|
||||||
: game.i18n.localize(doc.system?.description ?? doc.description);
|
|
||||||
|
|
||||||
// Enrich the description and attach it;
|
// Enrich the description and attach it;
|
||||||
const isAction = doc.documentName === 'Action';
|
const isAction = doc.documentName === 'Action';
|
||||||
|
|
@ -742,7 +717,7 @@ export default function DHApplicationMixin(Base) {
|
||||||
};
|
};
|
||||||
if (inVault) data['system.inVault'] = true;
|
if (inVault) data['system.inVault'] = true;
|
||||||
if (disabled) data.disabled = true;
|
if (disabled) data.disabled = true;
|
||||||
if (type === 'domainCard' && parent?.system.domains?.length) {
|
if (type === "domainCard" && parent?.system.domains?.length) {
|
||||||
data.system.domain = parent.system.domains[0];
|
data.system.domain = parent.system.domains[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
dragDrop: [
|
dragDrop: [{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null }]
|
||||||
{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null },
|
|
||||||
{ dragSelector: ".currency[data-currency] .drag-handle", dropSelector: null }
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -257,35 +254,14 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
/* Application Drag/Drop */
|
/* Application Drag/Drop */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
async _onDrop(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
|
||||||
if (data.type === 'Currency' && ['character', 'party'].includes(this.document.type)) {
|
|
||||||
const originActor = await foundry.utils.fromUuid(data.originActor);
|
|
||||||
if (!originActor || originActor.uuid === this.document.uuid) return;
|
|
||||||
const currency = data.currency;
|
|
||||||
const quantity = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
|
|
||||||
originActor,
|
|
||||||
targetActor: this.document,
|
|
||||||
currency
|
|
||||||
});
|
|
||||||
if (quantity) {
|
|
||||||
originActor.update({ [`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity) });
|
|
||||||
this.document.update({ [`system.gold.${currency}`]: this.document.system.gold[currency] + quantity });
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super._onDrop(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _onDropItem(event, item) {
|
async _onDropItem(event, item) {
|
||||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||||
|
const physicalActorTypes = ['character', 'party'];
|
||||||
const originActor = item.actor;
|
const originActor = item.actor;
|
||||||
if (
|
if (
|
||||||
item.actor?.uuid === this.document.uuid ||
|
item.actor?.uuid === this.document.uuid ||
|
||||||
!originActor ||
|
!originActor ||
|
||||||
!['character', 'party'].includes(this.document.type)
|
!physicalActorTypes.includes(this.document.type)
|
||||||
) {
|
) {
|
||||||
return super._onDropItem(event, item);
|
return super._onDropItem(event, item);
|
||||||
}
|
}
|
||||||
|
|
@ -294,10 +270,10 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
if (item.system.metadata.isInventoryItem) {
|
if (item.system.metadata.isInventoryItem) {
|
||||||
if (item.system.metadata.isQuantifiable) {
|
if (item.system.metadata.isQuantifiable) {
|
||||||
const actorItem = originActor.items.get(data.originId);
|
const actorItem = originActor.items.get(data.originId);
|
||||||
const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
|
const quantityTransfered =
|
||||||
item,
|
actorItem.system.quantity === 1
|
||||||
targetActor: this.document
|
? 1
|
||||||
});
|
: await game.system.api.applications.dialogs.ItemTransferDialog.configure(item);
|
||||||
|
|
||||||
if (quantityTransfered) {
|
if (quantityTransfered) {
|
||||||
if (quantityTransfered === actorItem.system.quantity) {
|
if (quantityTransfered === actorItem.system.quantity) {
|
||||||
|
|
@ -338,16 +314,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
* @param {DragEvent} event - The drag event
|
* @param {DragEvent} event - The drag event
|
||||||
*/
|
*/
|
||||||
async _onDragStart(event) {
|
async _onDragStart(event) {
|
||||||
// Handle drag/dropping currencies
|
|
||||||
const currencyEl = event.currentTarget.closest(".currency[data-currency]");
|
|
||||||
if (currencyEl) {
|
|
||||||
const currency = currencyEl.dataset.currency;
|
|
||||||
const data = { type: 'Currency', currency, originActor: this.document.uuid };
|
|
||||||
event.dataTransfer.setData('text/plain', JSON.stringify(data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle drag/dropping attacks
|
|
||||||
const attackItem = event.currentTarget.closest('.inventory-item[data-type="attack"]');
|
const attackItem = event.currentTarget.closest('.inventory-item[data-type="attack"]');
|
||||||
if (attackItem) {
|
if (attackItem) {
|
||||||
const attackData = {
|
const attackData = {
|
||||||
|
|
|
||||||
|
|
@ -76,10 +76,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _preparePartContext(partId, context, options) {
|
async _preparePartContext(partId, context, options) {
|
||||||
await super._preparePartContext(partId, context, options);
|
await super._preparePartContext(partId, context, options);
|
||||||
|
const { TextEditor } = foundry.applications.ux;
|
||||||
|
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
case 'description':
|
case 'description':
|
||||||
context.enrichedDescription = await this.document.system.getEnrichedDescription();
|
const value = foundry.utils.getProperty(this.document, 'system.description') ?? '';
|
||||||
|
context.enrichedDescription = await TextEditor.enrichHTML(value, {
|
||||||
|
relativeTo: this.item,
|
||||||
|
rollData: this.item.getRollData(),
|
||||||
|
secrets: this.item.isOwner
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case 'effects':
|
case 'effects':
|
||||||
await this._prepareEffectsContext(context, options);
|
await this._prepareEffectsContext(context, options);
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,6 @@ export default class BeastformSheet extends DHBaseItemSheet {
|
||||||
name: context.document.system.advantageOn[key].value
|
name: context.document.system.advantageOn[key].value
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
context.dimensionsDisabled = context.document.system.tokenSize.size !== 'custom';
|
|
||||||
break;
|
break;
|
||||||
case 'effects':
|
case 'effects':
|
||||||
context.effects.actives = context.effects.actives.map(effect => {
|
context.effects.actives = context.effects.actives.map(effect => {
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,4 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
||||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//Might be wrong location but testing out if here is okay.
|
|
||||||
/**@override */
|
|
||||||
async _prepareContext(options) {
|
|
||||||
const context = await super._prepareContext(options);
|
|
||||||
context.featureFormChoices = CONFIG.DH.ITEM.featureForm;
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,30 +17,4 @@ export default class DhActorDirectory extends foundry.applications.sidebar.tabs.
|
||||||
: null;
|
: null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
_onDragStart(event) {
|
|
||||||
let actor;
|
|
||||||
const { entryId } = event.currentTarget.dataset;
|
|
||||||
if (entryId) {
|
|
||||||
actor = this.collection.get(entryId);
|
|
||||||
if (!actor?.visible) return false;
|
|
||||||
}
|
|
||||||
super._onDragStart(event);
|
|
||||||
|
|
||||||
// Create the drag preview.
|
|
||||||
if (actor && canvas.ready) {
|
|
||||||
const img = event.currentTarget.querySelector('img');
|
|
||||||
const pt = actor.prototypeToken;
|
|
||||||
const usesSize = actor.system.metadata.usesSize;
|
|
||||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
|
||||||
const width = usesSize ? tokenSizes[actor.system.size] : pt.width;
|
|
||||||
const height = usesSize ? tokenSizes[actor.system.size] : pt.height;
|
|
||||||
|
|
||||||
const w = width * canvas.dimensions.size * Math.abs(pt.texture.scaleX) * canvas.stage.scale.x;
|
|
||||||
const h = height * canvas.dimensions.size * Math.abs(pt.texture.scaleY) * canvas.stage.scale.y;
|
|
||||||
const preview = foundry.applications.ux.DragDrop.implementation.createDragImage(img, w, h);
|
|
||||||
event.dataTransfer.setDragImage(preview, w / 2, h / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
classes: ['dh-style', 'directory'],
|
classes: ['dh-style'],
|
||||||
window: {
|
window: {
|
||||||
title: 'SIDEBAR.TabSettings'
|
title: 'SIDEBAR.TabSettings'
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,4 @@ export { default as DhCombatTracker } from './combatTracker.mjs';
|
||||||
export { default as DhEffectsDisplay } from './effectsDisplay.mjs';
|
export { default as DhEffectsDisplay } from './effectsDisplay.mjs';
|
||||||
export { default as DhFearTracker } from './fearTracker.mjs';
|
export { default as DhFearTracker } from './fearTracker.mjs';
|
||||||
export { default as DhHotbar } from './hotbar.mjs';
|
export { default as DhHotbar } from './hotbar.mjs';
|
||||||
export { default as DhSceneNavigation } from './sceneNavigation.mjs';
|
|
||||||
export { ItemBrowser } from './itemBrowser.mjs';
|
export { ItemBrowser } from './itemBrowser.mjs';
|
||||||
|
|
|
||||||
|
|
@ -55,28 +55,27 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
addChatListeners = async (document, html, data) => {
|
addChatListeners = async (app, html, data) => {
|
||||||
const message = data?.message ?? document.toObject(false);
|
|
||||||
html.querySelectorAll('.simple-roll-button').forEach(element =>
|
html.querySelectorAll('.simple-roll-button').forEach(element =>
|
||||||
element.addEventListener('click', event => this.onRollSimple(event, message))
|
element.addEventListener('click', event => this.onRollSimple(event, data.message))
|
||||||
);
|
);
|
||||||
html.querySelectorAll('.ability-use-button').forEach(element =>
|
html.querySelectorAll('.ability-use-button').forEach(element =>
|
||||||
element.addEventListener('click', event => this.abilityUseButton(event, message))
|
element.addEventListener('click', event => this.abilityUseButton(event, data.message))
|
||||||
);
|
);
|
||||||
html.querySelectorAll('.action-use-button').forEach(element =>
|
html.querySelectorAll('.action-use-button').forEach(element =>
|
||||||
element.addEventListener('click', event => this.actionUseButton(event, message))
|
element.addEventListener('click', event => this.actionUseButton(event, data.message))
|
||||||
);
|
);
|
||||||
html.querySelectorAll('.reroll-button').forEach(element =>
|
html.querySelectorAll('.reroll-button').forEach(element =>
|
||||||
element.addEventListener('click', event => this.rerollEvent(event, message))
|
element.addEventListener('click', event => this.rerollEvent(event, data.message))
|
||||||
);
|
);
|
||||||
html.querySelectorAll('.group-roll-button').forEach(element =>
|
html.querySelectorAll('.group-roll-button').forEach(element =>
|
||||||
element.addEventListener('click', event => this.groupRollButton(event, message))
|
element.addEventListener('click', event => this.groupRollButton(event, data.message))
|
||||||
);
|
);
|
||||||
html.querySelectorAll('.group-roll-reroll').forEach(element =>
|
html.querySelectorAll('.group-roll-reroll').forEach(element =>
|
||||||
element.addEventListener('click', event => this.groupRollReroll(event, message))
|
element.addEventListener('click', event => this.groupRollReroll(event, data.message))
|
||||||
);
|
);
|
||||||
html.querySelectorAll('.group-roll-success').forEach(element =>
|
html.querySelectorAll('.group-roll-success').forEach(element =>
|
||||||
element.addEventListener('click', event => this.groupRollSuccessEvent(event, message))
|
element.addEventListener('click', event => this.groupRollSuccessEvent(event, data.message))
|
||||||
);
|
);
|
||||||
html.querySelectorAll('.group-roll-header-expand-section').forEach(element =>
|
html.querySelectorAll('.group-roll-header-expand-section').forEach(element =>
|
||||||
element.addEventListener('click', this.groupRollExpandSection)
|
element.addEventListener('click', this.groupRollExpandSection)
|
||||||
|
|
@ -92,19 +91,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
super.close(options);
|
super.close(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Ensure the chat theme inherits the interface theme */
|
|
||||||
_replaceHTML(result, content, options) {
|
|
||||||
const themedElement = result.log?.querySelector(".chat-log");
|
|
||||||
themedElement?.classList.remove("themed", "theme-light", "theme-dark");
|
|
||||||
super._replaceHTML(result, content, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Remove chat log theme from notifications area */
|
|
||||||
async _onFirstRender(result, content) {
|
|
||||||
await super._onFirstRender(result, content);
|
|
||||||
document.querySelector("#chat-notifications .chat-log")?.classList.remove("themed", "theme-light", "theme-dark")
|
|
||||||
}
|
|
||||||
|
|
||||||
async onRollSimple(event, message) {
|
async onRollSimple(event, message) {
|
||||||
const buttonType = event.target.dataset.type ?? 'damage',
|
const buttonType = event.target.dataset.type ?? 'damage',
|
||||||
total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0),
|
total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0),
|
||||||
|
|
@ -147,9 +133,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
|
|
||||||
async actionUseButton(event, message) {
|
async actionUseButton(event, message) {
|
||||||
const { moveIndex, actionIndex, movePath } = event.currentTarget.dataset;
|
const { moveIndex, actionIndex, movePath } = event.currentTarget.dataset;
|
||||||
const targetUuid = event.currentTarget.closest('.action-use-button-parent').querySelector('select')?.value;
|
const parent = await foundry.utils.fromUuid(message.system.actor);
|
||||||
const parent = await foundry.utils.fromUuid(targetUuid || message.system.actor);
|
|
||||||
|
|
||||||
const actionType = message.system.moves[moveIndex].actions[actionIndex];
|
const actionType = message.system.moves[moveIndex].actions[actionIndex];
|
||||||
const cls = game.system.api.models.actions.actionsTypes[actionType.type];
|
const cls = game.system.api.models.actions.actionsTypes[actionType.type];
|
||||||
const action = new cls(
|
const action = new cls(
|
||||||
|
|
@ -161,8 +145,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
type: CONFIG.DH.ITEM.originItemType.restMove,
|
type: CONFIG.DH.ITEM.originItemType.restMove,
|
||||||
itemPath: movePath,
|
itemPath: movePath,
|
||||||
actionIndex: actionIndex
|
actionIndex: actionIndex
|
||||||
},
|
}
|
||||||
targetUuid: targetUuid
|
|
||||||
},
|
},
|
||||||
{ parent: parent.system }
|
{ parent: parent.system }
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
||||||
actions: {
|
actions: {
|
||||||
requestSpotlight: this.requestSpotlight,
|
requestSpotlight: this.requestSpotlight,
|
||||||
toggleSpotlight: this.toggleSpotlight,
|
toggleSpotlight: this.toggleSpotlight,
|
||||||
setActionTokens: this.setActionTokens
|
setActionTokens: this.setActionTokens,
|
||||||
|
openCountdowns: this.openCountdowns
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -56,10 +57,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
||||||
|
|
||||||
const adversaries = context.turns?.filter(x => x.isNPC) ?? [];
|
const adversaries = context.turns?.filter(x => x.isNPC) ?? [];
|
||||||
const characters = context.turns?.filter(x => !x.isNPC) ?? [];
|
const characters = context.turns?.filter(x => !x.isNPC) ?? [];
|
||||||
const spotlightQueueEnabled = game.settings.get(
|
const spotlightQueueEnabled = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightRequestQueue);
|
||||||
CONFIG.DH.id,
|
|
||||||
CONFIG.DH.SETTINGS.gameSettings.SpotlightRequestQueue
|
|
||||||
);
|
|
||||||
|
|
||||||
const spotlightRequests = characters
|
const spotlightRequests = characters
|
||||||
?.filter(x => !x.isNPC && spotlightQueueEnabled)
|
?.filter(x => !x.isNPC && spotlightQueueEnabled)
|
||||||
|
|
@ -73,9 +71,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
||||||
Object.assign(context, {
|
Object.assign(context, {
|
||||||
actionTokens: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).actionTokens,
|
actionTokens: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).actionTokens,
|
||||||
adversaries,
|
adversaries,
|
||||||
characters: characters
|
characters: characters?.filter(x => !x.isNPC).filter(x => !spotlightQueueEnabled || x.system.spotlight.requestOrderIndex == 0),
|
||||||
?.filter(x => !x.isNPC)
|
|
||||||
.filter(x => !spotlightQueueEnabled || x.system.spotlight.requestOrderIndex == 0),
|
|
||||||
spotlightRequests
|
spotlightRequests
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +123,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
||||||
resource,
|
resource,
|
||||||
active: index === combat.turn,
|
active: index === combat.turn,
|
||||||
canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'),
|
canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'),
|
||||||
type: combatant.actor?.system?.type,
|
type: combatant.actor.system.type,
|
||||||
img: await this._getCombatantThumbnail(combatant)
|
img: await this._getCombatantThumbnail(combatant)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -165,11 +161,9 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
||||||
|
|
||||||
if (this.viewed.turn !== toggleTurn) {
|
if (this.viewed.turn !== toggleTurn) {
|
||||||
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
||||||
if (combatant.actor?.type === 'character') {
|
if (combatant.actor.type === 'character') {
|
||||||
await updateCountdowns(
|
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id,
|
||||||
CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id,
|
CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id);
|
||||||
CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id);
|
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -230,14 +230,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
result.flatMap(r => r),
|
result.flatMap(r => r),
|
||||||
'name'
|
'name'
|
||||||
);
|
);
|
||||||
|
|
||||||
/* If any noticeable slowdown occurs, consider replacing with enriching description on clicking to expand descriptions */
|
|
||||||
for (const item of this.items) {
|
|
||||||
item.system.enrichedDescription =
|
|
||||||
(await item.system.getEnrichedDescription?.()) ??
|
|
||||||
(await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.description));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fieldFilter = this._createFieldFilter();
|
this.fieldFilter = this._createFieldFilter();
|
||||||
|
|
||||||
if (this.presets?.filter) {
|
if (this.presets?.filter) {
|
||||||
|
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs';
|
|
||||||
|
|
||||||
export default class DhSceneNavigation extends foundry.applications.ui.SceneNavigation {
|
|
||||||
/** @inheritdoc */
|
|
||||||
static DEFAULT_OPTIONS = {
|
|
||||||
...super.DEFAULT_OPTIONS,
|
|
||||||
classes: ['faded-ui', 'flexcol', 'scene-navigation'],
|
|
||||||
actions: {
|
|
||||||
openSceneEnvironment: DhSceneNavigation.#openSceneEnvironment
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
static PARTS = {
|
|
||||||
scenes: {
|
|
||||||
root: true,
|
|
||||||
template: 'systems/daggerheart/templates/ui/sceneNavigation/scene-navigation.hbs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
async _prepareContext(options) {
|
|
||||||
const context = await super._prepareContext(options);
|
|
||||||
|
|
||||||
const extendScenes = scenes =>
|
|
||||||
scenes.map(x => {
|
|
||||||
const scene = game.scenes.get(x.id);
|
|
||||||
if (!scene.flags.daggerheart) return x;
|
|
||||||
|
|
||||||
const daggerheartInfo = new game.system.api.data.scenes.DHScene(scene.flags.daggerheart);
|
|
||||||
const environments = daggerheartInfo.sceneEnvironments.filter(
|
|
||||||
x => x && x.testUserPermission(game.user, 'LIMITED')
|
|
||||||
);
|
|
||||||
const hasEnvironments = environments.length > 0 && x.isView;
|
|
||||||
return {
|
|
||||||
...x,
|
|
||||||
hasEnvironments,
|
|
||||||
environmentImage: hasEnvironments ? environments[0].img : null,
|
|
||||||
environments: environments
|
|
||||||
};
|
|
||||||
});
|
|
||||||
context.scenes.active = extendScenes(context.scenes.active);
|
|
||||||
context.scenes.inactive = extendScenes(context.scenes.inactive);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async #openSceneEnvironment(event, button) {
|
|
||||||
const scene = game.scenes.get(button.dataset.sceneId);
|
|
||||||
const sceneEnvironments = new game.system.api.data.scenes.DHScene(
|
|
||||||
scene.flags.daggerheart
|
|
||||||
).sceneEnvironments.filter(x => x.testUserPermission(game.user, 'LIMITED'));
|
|
||||||
|
|
||||||
if (sceneEnvironments.length === 1 || event.shiftKey) {
|
|
||||||
sceneEnvironments[0].sheet.render(true);
|
|
||||||
} else {
|
|
||||||
new foundry.applications.ux.ContextMenu.implementation(
|
|
||||||
button,
|
|
||||||
'.scene-environment',
|
|
||||||
sceneEnvironments.map(environment => ({
|
|
||||||
name: environment.name,
|
|
||||||
callback: () => {
|
|
||||||
if (scene.flags.daggerheart.sceneEnvironments[0] !== environment.uuid) {
|
|
||||||
const newEnvironments = scene.flags.daggerheart.sceneEnvironments;
|
|
||||||
const newFirst = newEnvironments.splice(
|
|
||||||
newEnvironments.findIndex(x => x === environment.uuid)
|
|
||||||
)[0];
|
|
||||||
newEnvironments.unshift(newFirst);
|
|
||||||
emitAsGM(
|
|
||||||
GMUpdateEvent.UpdateDocument,
|
|
||||||
scene.update.bind(scene),
|
|
||||||
{ 'flags.daggerheart.sceneEnvironments': newEnvironments },
|
|
||||||
scene.uuid
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
environment.sheet.render({ force: true });
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
{
|
|
||||||
jQuery: false,
|
|
||||||
fixed: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
CONFIG.ux.ContextMenu.triggerContextMenu(event, '.scene-environment');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -96,11 +96,11 @@ export default class DHContextMenu extends foundry.applications.ux.ContextMenu {
|
||||||
* Trigger a context menu event in response to a normal click on a additional options button.
|
* Trigger a context menu event in response to a normal click on a additional options button.
|
||||||
* @param {PointerEvent} event
|
* @param {PointerEvent} event
|
||||||
*/
|
*/
|
||||||
static triggerContextMenu(event, altSelector) {
|
static triggerContextMenu(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const { clientX, clientY } = event;
|
const { clientX, clientY } = event;
|
||||||
const selector = altSelector ?? '[data-item-uuid]';
|
const selector = '[data-item-uuid]';
|
||||||
const target = event.target.closest(selector) ?? event.currentTarget.closest(selector);
|
const target = event.target.closest(selector) ?? event.currentTarget.closest(selector);
|
||||||
target?.dispatchEvent(
|
target?.dispatchEvent(
|
||||||
new PointerEvent('contextmenu', {
|
new PointerEvent('contextmenu', {
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
export * as placeables from './placeables/_module.mjs';
|
export * as placeables from './placeables/_module.mjs';
|
||||||
export { default as DhTokenLayer } from './tokens.mjs';
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,4 @@
|
||||||
export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
/** @inheritdoc */
|
|
||||||
async _draw(options) {
|
|
||||||
await super._draw(options);
|
|
||||||
|
|
||||||
if (this.document.flags.daggerheart?.createPlacement)
|
|
||||||
this.previewHelp ||= this.addChild(this.#drawPreviewHelp());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
async _drawEffects() {
|
async _drawEffects() {
|
||||||
this.effects.renderable = false;
|
this.effects.renderable = false;
|
||||||
|
|
@ -42,69 +34,6 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
this.renderFlags.set({ refreshEffects: true });
|
this.renderFlags.set({ refreshEffects: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the distance from this token to another token object.
|
|
||||||
* This value is corrected to handle alternate token sizes and other grid types
|
|
||||||
* according to the diagonal rules.
|
|
||||||
*/
|
|
||||||
distanceTo(target) {
|
|
||||||
if (!canvas.ready) return NaN;
|
|
||||||
if (this === target) return 0;
|
|
||||||
|
|
||||||
const originPoint = this.center;
|
|
||||||
const destinationPoint = target.center;
|
|
||||||
|
|
||||||
// Compute for gridless. This version returns circular edge to edge + grid distance,
|
|
||||||
// so that tokens that are touching return 5.
|
|
||||||
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
|
||||||
const boundsCorrection = canvas.grid.distance / canvas.grid.size;
|
|
||||||
const originRadius = (this.bounds.width * boundsCorrection) / 2;
|
|
||||||
const targetRadius = (target.bounds.width * boundsCorrection) / 2;
|
|
||||||
const distance = canvas.grid.measurePath([originPoint, destinationPoint]).distance;
|
|
||||||
return distance - originRadius - targetRadius + canvas.grid.distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute what the closest grid space of each token is, then compute that distance
|
|
||||||
const originEdge = this.#getEdgeBoundary(this.bounds, originPoint, destinationPoint);
|
|
||||||
const targetEdge = this.#getEdgeBoundary(target.bounds, originPoint, destinationPoint);
|
|
||||||
const adjustedOriginPoint = canvas.grid.getTopLeftPoint({
|
|
||||||
x: originEdge.x + Math.sign(originPoint.x - originEdge.x),
|
|
||||||
y: originEdge.y + Math.sign(originPoint.y - originEdge.y)
|
|
||||||
});
|
|
||||||
const adjustDestinationPoint = canvas.grid.getTopLeftPoint({
|
|
||||||
x: targetEdge.x + Math.sign(destinationPoint.x - targetEdge.x),
|
|
||||||
y: targetEdge.y + Math.sign(destinationPoint.y - targetEdge.y)
|
|
||||||
});
|
|
||||||
return canvas.grid.measurePath([adjustedOriginPoint, adjustDestinationPoint]).distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the point at which a line starting at origin and ending at destination intersects the edge of the bounds */
|
|
||||||
#getEdgeBoundary(bounds, originPoint, destinationPoint) {
|
|
||||||
const points = [
|
|
||||||
{ x: bounds.x, y: bounds.y },
|
|
||||||
{ x: bounds.x + bounds.width, y: bounds.y },
|
|
||||||
{ x: bounds.x + bounds.width, y: bounds.y + bounds.height },
|
|
||||||
{ x: bounds.x, y: bounds.y + bounds.height }
|
|
||||||
];
|
|
||||||
const pairsToTest = [
|
|
||||||
[points[0], points[1]],
|
|
||||||
[points[1], points[2]],
|
|
||||||
[points[2], points[3]],
|
|
||||||
[points[3], points[0]]
|
|
||||||
];
|
|
||||||
for (const pair of pairsToTest) {
|
|
||||||
const result = foundry.utils.lineSegmentIntersection(originPoint, destinationPoint, pair[0], pair[1]);
|
|
||||||
if (result) return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Tests if the token is at least adjacent with another, with some leeway for diagonals */
|
|
||||||
isAdjacentWith(token) {
|
|
||||||
return this.distanceTo(token) <= canvas.grid.distance * 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
_drawBar(number, bar, data) {
|
_drawBar(number, bar, data) {
|
||||||
const val = Number(data.value);
|
const val = Number(data.value);
|
||||||
|
|
@ -140,25 +69,4 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
bar.position.set(0, posY);
|
bar.position.set(0, posY);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw a helptext for previews as a text object
|
|
||||||
* @returns {PreciseText} The Text object for the preview helper
|
|
||||||
*/
|
|
||||||
#drawPreviewHelp() {
|
|
||||||
const { uiScale } = canvas.dimensions;
|
|
||||||
|
|
||||||
const textStyle = CONFIG.canvasTextStyle.clone();
|
|
||||||
textStyle.fontSize = 18;
|
|
||||||
textStyle.wordWrapWidth = this.w * 2.5;
|
|
||||||
textStyle.fontStyle = 'italic';
|
|
||||||
|
|
||||||
const helpText = new foundry.canvas.containers.PreciseText(
|
|
||||||
`(${game.i18n.localize('DAGGERHEART.UI.Tooltip.previewTokenHelp')})`,
|
|
||||||
textStyle
|
|
||||||
);
|
|
||||||
helpText.anchor.set(helpText.width / 900, 1);
|
|
||||||
helpText.scale.set(uiScale, uiScale);
|
|
||||||
return helpText;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
export default class DhTokenLayer extends foundry.canvas.layers.TokenLayer {
|
|
||||||
async _createPreview(createData, options) {
|
|
||||||
if (options.actor) {
|
|
||||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
|
||||||
if (options.actor?.system.metadata.usesSize) {
|
|
||||||
const tokenSize = tokenSizes[options.actor.system.size];
|
|
||||||
if (tokenSize && options.actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
|
||||||
createData.width = tokenSize;
|
|
||||||
createData.height = tokenSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return super._createPreview(createData, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -10,4 +10,3 @@ export * as itemConfig from './itemConfig.mjs';
|
||||||
export * as settingsConfig from './settingsConfig.mjs';
|
export * as settingsConfig from './settingsConfig.mjs';
|
||||||
export * as systemConfig from './system.mjs';
|
export * as systemConfig from './system.mjs';
|
||||||
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
|
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
|
||||||
export * as triggerConfig from './triggerConfig.mjs';
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ export const actionTypes = {
|
||||||
attack: {
|
attack: {
|
||||||
id: 'attack',
|
id: 'attack',
|
||||||
name: 'DAGGERHEART.ACTIONS.TYPES.attack.name',
|
name: 'DAGGERHEART.ACTIONS.TYPES.attack.name',
|
||||||
icon: 'fa-hand-fist',
|
icon: 'fa-khanda',
|
||||||
tooltip: 'DAGGERHEART.ACTIONS.TYPES.attack.tooltip'
|
tooltip: 'DAGGERHEART.ACTIONS.TYPES.attack.tooltip'
|
||||||
},
|
},
|
||||||
countdown: {
|
countdown: {
|
||||||
|
|
|
||||||
|
|
@ -211,44 +211,6 @@ export const adversaryTraits = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const tokenSize = {
|
|
||||||
custom: {
|
|
||||||
id: 'custom',
|
|
||||||
value: 0,
|
|
||||||
label: 'DAGGERHEART.GENERAL.custom'
|
|
||||||
},
|
|
||||||
tiny: {
|
|
||||||
id: 'tiny',
|
|
||||||
value: 1,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.tiny'
|
|
||||||
},
|
|
||||||
small: {
|
|
||||||
id: 'small',
|
|
||||||
value: 2,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.small'
|
|
||||||
},
|
|
||||||
medium: {
|
|
||||||
id: 'medium',
|
|
||||||
value: 3,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.medium'
|
|
||||||
},
|
|
||||||
large: {
|
|
||||||
id: 'large',
|
|
||||||
value: 4,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.large'
|
|
||||||
},
|
|
||||||
huge: {
|
|
||||||
id: 'huge',
|
|
||||||
value: 5,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.huge'
|
|
||||||
},
|
|
||||||
gargantuan: {
|
|
||||||
id: 'gargantuan',
|
|
||||||
value: 6,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.gargantuan'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const levelChoices = {
|
export const levelChoices = {
|
||||||
attributes: {
|
attributes: {
|
||||||
name: 'attributes',
|
name: 'attributes',
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export const AdversaryBPPerEncounter = (adversaries, characters) => {
|
||||||
);
|
);
|
||||||
if (existingEntry) {
|
if (existingEntry) {
|
||||||
existingEntry.nr += 1;
|
existingEntry.nr += 1;
|
||||||
} else if (adversary.type) {
|
} else {
|
||||||
acc.push({ adversary, nr: 1 });
|
acc.push({ adversary, nr: 1 });
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
|
|
@ -84,7 +84,6 @@ export const BPModifiers = {
|
||||||
increaseDamage: {
|
increaseDamage: {
|
||||||
sort: 2,
|
sort: 2,
|
||||||
description: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.description',
|
description: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.description',
|
||||||
effectTargetTypes: ['adversary'],
|
|
||||||
effects: [
|
effects: [
|
||||||
{
|
{
|
||||||
name: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.name',
|
name: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.name',
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ export const defaultRestOptions = {
|
||||||
actionType: 'action',
|
actionType: 'action',
|
||||||
chatDisplay: false,
|
chatDisplay: false,
|
||||||
target: {
|
target: {
|
||||||
type: 'friendly'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: [
|
||||||
|
|
@ -298,7 +298,7 @@ export const defaultRestOptions = {
|
||||||
actionType: 'action',
|
actionType: 'action',
|
||||||
chatDisplay: false,
|
chatDisplay: false,
|
||||||
target: {
|
target: {
|
||||||
type: 'friendly'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: [
|
||||||
|
|
@ -341,7 +341,7 @@ export const defaultRestOptions = {
|
||||||
actionType: 'action',
|
actionType: 'action',
|
||||||
chatDisplay: false,
|
chatDisplay: false,
|
||||||
target: {
|
target: {
|
||||||
type: 'friendly'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: [
|
||||||
|
|
@ -407,7 +407,7 @@ export const defaultRestOptions = {
|
||||||
actionType: 'action',
|
actionType: 'action',
|
||||||
chatDisplay: false,
|
chatDisplay: false,
|
||||||
target: {
|
target: {
|
||||||
type: 'friendly'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: [
|
||||||
|
|
@ -496,8 +496,6 @@ export const diceTypes = {
|
||||||
d20: 'd20'
|
d20: 'd20'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dieFaces = [4, 6, 8, 10, 12, 20];
|
|
||||||
|
|
||||||
export const multiplierTypes = {
|
export const multiplierTypes = {
|
||||||
prof: 'Proficiency',
|
prof: 'Proficiency',
|
||||||
cast: 'Spellcast',
|
cast: 'Spellcast',
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
export const hooksConfig = {
|
const hooksConfig = {
|
||||||
effectDisplayToggle: 'DHEffectDisplayToggle'
|
effectDisplayToggle: 'DHEffectDisplayToggle'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default hooksConfig;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export const armorFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'damage',
|
type: 'damage',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.burning.actions.burn.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.burning.actions.burn.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.burning.actions.burn.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.burning.actions.burn.description',
|
||||||
|
|
@ -173,6 +174,7 @@ export const armorFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.hopeful.actions.hope.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.hopeful.actions.hope.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.hopeful.actions.hope.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.hopeful.actions.hope.description',
|
||||||
|
|
@ -186,6 +188,7 @@ export const armorFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.impenetrable.actions.impenetrable.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.impenetrable.actions.impenetrable.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.impenetrable.actions.impenetrable.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.impenetrable.actions.impenetrable.description',
|
||||||
|
|
@ -228,6 +231,7 @@ export const armorFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.painful.actions.pain.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.painful.actions.pain.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.painful.actions.pain.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.painful.actions.pain.description',
|
||||||
|
|
@ -265,6 +269,7 @@ export const armorFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.quiet.actions.quiet.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.quiet.actions.quiet.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.quiet.actions.quiet.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.quiet.actions.quiet.description',
|
||||||
|
|
@ -301,6 +306,7 @@ export const armorFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'attack',
|
type: 'attack',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.resilient.actions.resilient.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.resilient.actions.resilient.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.resilient.actions.resilient.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.resilient.actions.resilient.description',
|
||||||
|
|
@ -347,6 +353,7 @@ export const armorFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.shifting.actions.shift.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.shifting.actions.shift.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.shifting.actions.shift.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.shifting.actions.shift.description',
|
||||||
|
|
@ -366,6 +373,7 @@ export const armorFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'attack',
|
type: 'attack',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.timeslowing.actions.slowTime.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.timeslowing.actions.slowTime.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.timeslowing.actions.slowTime.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.timeslowing.actions.slowTime.description',
|
||||||
|
|
@ -393,6 +401,7 @@ export const armorFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.truthseeking.actions.truthseeking.name',
|
name: 'DAGGERHEART.CONFIG.ArmorFeature.truthseeking.actions.truthseeking.name',
|
||||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.truthseeking.actions.truthseeking.description',
|
description: 'DAGGERHEART.CONFIG.ArmorFeature.truthseeking.actions.truthseeking.description',
|
||||||
|
|
@ -435,8 +444,7 @@ export const armorFeatures = {
|
||||||
{
|
{
|
||||||
key: 'system.resistance.magical.reduction',
|
key: 'system.resistance.magical.reduction',
|
||||||
mode: 2,
|
mode: 2,
|
||||||
value: '@system.armorScore',
|
value: '@system.armorScore'
|
||||||
priority: 21
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -529,6 +537,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.bouncing.actions.bounce.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.bouncing.actions.bounce.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.bouncing.actions.bounce.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.bouncing.actions.bounce.description',
|
||||||
|
|
@ -573,6 +582,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.brutal.actions.addDamage.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.brutal.actions.addDamage.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.brutal.actions.addDamage.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.brutal.actions.addDamage.description',
|
||||||
|
|
@ -586,6 +596,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.burning.actions.burn.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.burning.actions.burn.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.burning.actions.burn.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.burning.actions.burn.description',
|
||||||
|
|
@ -599,6 +610,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.charged.actions.markStress.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.charged.actions.markStress.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.charged.actions.markStress.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.charged.actions.markStress.description',
|
||||||
|
|
@ -635,6 +647,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.concussive.actions.attack.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.concussive.actions.attack.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.concussive.actions.attack.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.concussive.actions.attack.description',
|
||||||
|
|
@ -675,6 +688,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.deadly.actions.extraDamage.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.deadly.actions.extraDamage.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.deadly.actions.extraDamage.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.deadly.actions.extraDamage.description',
|
||||||
|
|
@ -688,6 +702,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.actions.deflect.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.actions.deflect.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.actions.deflect.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.actions.deflect.description',
|
||||||
|
|
@ -710,8 +725,7 @@ export const weaponFeatures = {
|
||||||
{
|
{
|
||||||
key: 'system.evasion',
|
key: 'system.evasion',
|
||||||
mode: 2,
|
mode: 2,
|
||||||
value: '@system.armorScore',
|
value: '@system.armorScore'
|
||||||
priority: 21
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -725,6 +739,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'damage',
|
type: 'damage',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.actions.attack.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.actions.attack.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.actions.attack.descriptive',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.actions.attack.descriptive',
|
||||||
|
|
@ -769,6 +784,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.devastating.actions.devastate.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.devastating.actions.devastate.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.devastating.actions.devastate.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.devastating.actions.devastate.description',
|
||||||
|
|
@ -819,6 +835,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.doubledUp.actions.doubleUp.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.doubledUp.actions.doubleUp.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.doubledUp.actions.doubleUp.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.doubledUp.actions.doubleUp.description',
|
||||||
|
|
@ -832,6 +849,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.dueling.actions.duel.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.dueling.actions.duel.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.dueling.actions.duel.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.dueling.actions.duel.description',
|
||||||
|
|
@ -845,6 +863,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect', // Should prompt a dc 14 reaction save on adversaries
|
type: 'effect', // Should prompt a dc 14 reaction save on adversaries
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.eruptive.actions.erupt.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.eruptive.actions.erupt.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.eruptive.actions.erupt.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.eruptive.actions.erupt.description',
|
||||||
|
|
@ -858,6 +877,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.grappling.actions.grapple.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.grappling.actions.grapple.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.grappling.actions.grapple.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.grappling.actions.grapple.description',
|
||||||
|
|
@ -877,6 +897,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.description',
|
||||||
|
|
@ -908,6 +929,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'healing',
|
type: 'healing',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.healing.actions.heal.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.healing.actions.heal.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.healing.actions.heal.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.healing.actions.heal.description',
|
||||||
|
|
@ -955,6 +977,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.hooked.actions.hook.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.hooked.actions.hook.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.hooked.actions.hook.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.hooked.actions.hook.description',
|
||||||
|
|
@ -968,6 +991,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.hot.actions.hot.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.hot.actions.hot.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.hot.actions.hot.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.hot.actions.hot.description',
|
||||||
|
|
@ -981,6 +1005,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.invigorating.actions.invigorate.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.invigorating.actions.invigorate.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.invigorating.actions.invigorate.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.invigorating.actions.invigorate.description',
|
||||||
|
|
@ -994,6 +1019,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.lifestealing.actions.lifesteal.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.lifestealing.actions.lifesteal.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.lifestealing.actions.lifesteal.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.lifestealing.actions.lifesteal.description',
|
||||||
|
|
@ -1007,6 +1033,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.lockedOn.actions.lockOn.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.lockedOn.actions.lockOn.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.lockedOn.actions.lockOn.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.lockedOn.actions.lockOn.description',
|
||||||
|
|
@ -1020,6 +1047,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.long.actions.long.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.long.actions.long.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.long.actions.long.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.long.actions.long.description',
|
||||||
|
|
@ -1033,6 +1061,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.lucky.actions.luck.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.lucky.actions.luck.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.lucky.actions.luck.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.lucky.actions.luck.description',
|
||||||
|
|
@ -1070,6 +1099,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.painful.actions.pain.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.painful.actions.pain.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.painful.actions.pain.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.painful.actions.pain.description',
|
||||||
|
|
@ -1115,6 +1145,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.parry.actions.parry.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.parry.actions.parry.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.parry.actions.parry.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.parry.actions.parry.description',
|
||||||
|
|
@ -1128,6 +1159,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.actions.persuade.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.actions.persuade.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.actions.persuade.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.actions.persuade.description',
|
||||||
|
|
@ -1164,6 +1196,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.pompous.actions.pompous.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.pompous.actions.pompous.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.pompous.actions.pompous.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.pompous.actions.pompous.description',
|
||||||
|
|
@ -1207,6 +1240,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.quick.actions.quick.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.quick.actions.quick.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.quick.actions.quick.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.quick.actions.quick.description',
|
||||||
|
|
@ -1244,6 +1278,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.reloading.actions.reload.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.reloading.actions.reload.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.reloading.actions.reload.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.reloading.actions.reload.description',
|
||||||
|
|
@ -1257,6 +1292,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.retractable.actions.retract.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.retractable.actions.retract.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.retractable.actions.retract.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.retractable.actions.retract.description',
|
||||||
|
|
@ -1270,6 +1306,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.returning.actions.return.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.returning.actions.return.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.returning.actions.return.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.returning.actions.return.description',
|
||||||
|
|
@ -1283,6 +1320,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.scary.actions.scare.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.scary.actions.scare.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.scary.actions.scare.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.scary.actions.scare.description',
|
||||||
|
|
@ -1326,8 +1364,7 @@ export const weaponFeatures = {
|
||||||
{
|
{
|
||||||
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
||||||
mode: 2,
|
mode: 2,
|
||||||
value: '@system.traits.agility.value',
|
value: '@system.traits.agility.value'
|
||||||
priority: 21
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1339,6 +1376,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.sheltering.actions.shelter.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.sheltering.actions.shelter.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.sheltering.actions.shelter.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.sheltering.actions.shelter.description',
|
||||||
|
|
@ -1352,6 +1390,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.startling.actions.startle.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.startling.actions.startle.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.startling.actions.startle.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.startling.actions.startle.description',
|
||||||
|
|
@ -1371,6 +1410,7 @@ export const weaponFeatures = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'effect',
|
type: 'effect',
|
||||||
|
actionType: 'action',
|
||||||
chatDisplay: true,
|
chatDisplay: true,
|
||||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.timebending.actions.bendTime.name',
|
name: 'DAGGERHEART.CONFIG.WeaponFeature.timebending.actions.bendTime.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.timebending.actions.bendTime.description',
|
description: 'DAGGERHEART.CONFIG.WeaponFeature.timebending.actions.bendTime.description',
|
||||||
|
|
@ -1418,12 +1458,6 @@ export const orderedWeaponFeatures = () => {
|
||||||
return Object.values(all).sort((a, b) => game.i18n.localize(a.label).localeCompare(game.i18n.localize(b.label)));
|
return Object.values(all).sort((a, b) => game.i18n.localize(a.label).localeCompare(game.i18n.localize(b.label)));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const featureForm = {
|
|
||||||
passive: 'DAGGERHEART.CONFIG.FeatureForm.passive',
|
|
||||||
action: 'DAGGERHEART.CONFIG.FeatureForm.action',
|
|
||||||
reaction: 'DAGGERHEART.CONFIG.FeatureForm.reaction'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const featureTypes = {
|
export const featureTypes = {
|
||||||
ancestry: {
|
ancestry: {
|
||||||
id: 'ancestry',
|
id: 'ancestry',
|
||||||
|
|
@ -1481,6 +1515,21 @@ export const featureSubTypes = {
|
||||||
mastery: 'mastery'
|
mastery: 'mastery'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const actionTypes = {
|
||||||
|
passive: {
|
||||||
|
id: 'passive',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActionType.passive'
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
id: 'action',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActionType.action'
|
||||||
|
},
|
||||||
|
reaction: {
|
||||||
|
id: 'reaction',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActionType.reaction'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const itemResourceTypes = {
|
export const itemResourceTypes = {
|
||||||
simple: {
|
simple: {
|
||||||
id: 'simple',
|
id: 'simple',
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ import * as SETTINGS from './settingsConfig.mjs';
|
||||||
import * as EFFECTS from './effectConfig.mjs';
|
import * as EFFECTS from './effectConfig.mjs';
|
||||||
import * as ACTIONS from './actionConfig.mjs';
|
import * as ACTIONS from './actionConfig.mjs';
|
||||||
import * as FLAGS from './flagsConfig.mjs';
|
import * as FLAGS from './flagsConfig.mjs';
|
||||||
import * as HOOKS from './hooksConfig.mjs';
|
import HOOKS from './hooksConfig.mjs';
|
||||||
import * as TRIGGER from './triggerConfig.mjs';
|
|
||||||
import * as ITEMBROWSER from './itemBrowserConfig.mjs';
|
import * as ITEMBROWSER from './itemBrowserConfig.mjs';
|
||||||
|
|
||||||
export const SYSTEM_ID = 'daggerheart';
|
export const SYSTEM_ID = 'daggerheart';
|
||||||
|
|
@ -25,6 +24,5 @@ export const SYSTEM = {
|
||||||
ACTIONS,
|
ACTIONS,
|
||||||
FLAGS,
|
FLAGS,
|
||||||
HOOKS,
|
HOOKS,
|
||||||
TRIGGER,
|
|
||||||
ITEMBROWSER
|
ITEMBROWSER
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
/* hints and returns are intentionally not translated. They are programatical terms and best understood in english */
|
|
||||||
export const triggers = {
|
|
||||||
dualityRoll: {
|
|
||||||
id: 'dualityRoll',
|
|
||||||
usesActor: true,
|
|
||||||
args: ['roll', 'actor'],
|
|
||||||
label: 'DAGGERHEART.CONFIG.Triggers.dualityRoll.label',
|
|
||||||
hint: 'this: Action, roll: DhRoll, actor: DhActor',
|
|
||||||
returns: '{ updates: [{ key, value, total }] }'
|
|
||||||
},
|
|
||||||
fearRoll: {
|
|
||||||
id: 'fearRoll',
|
|
||||||
usesActor: true,
|
|
||||||
args: ['roll', 'actor'],
|
|
||||||
label: 'DAGGERHEART.CONFIG.Triggers.fearRoll.label',
|
|
||||||
hint: 'this: Action, roll: DhRoll, actor: DhActor',
|
|
||||||
returns: '{ updates: [{ key, value, total }] }'
|
|
||||||
},
|
|
||||||
postDamageReduction: {
|
|
||||||
id: 'postDamageReduction',
|
|
||||||
usesActor: true,
|
|
||||||
args: ['damageUpdates', 'actor'],
|
|
||||||
label: 'DAGGERHEART.CONFIG.Triggers.postDamageReduction.label',
|
|
||||||
hint: 'damageUpdates: ResourceUpdates, actor: DhActor',
|
|
||||||
returns: '{ updates: [{ originActor: this.actor, updates: [{ key, value, total }] }] }'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const triggerActorTargetType = {
|
|
||||||
any: {
|
|
||||||
id: 'any',
|
|
||||||
label: 'DAGGERHEART.CONFIG.TargetTypes.any'
|
|
||||||
},
|
|
||||||
self: {
|
|
||||||
id: 'self',
|
|
||||||
label: 'DAGGERHEART.CONFIG.TargetTypes.self'
|
|
||||||
},
|
|
||||||
other: {
|
|
||||||
id: 'other',
|
|
||||||
label: 'DAGGERHEART.CONFIG.TargetTypes.other'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
export { default as DhCombat } from './combat.mjs';
|
export { default as DhCombat } from './combat.mjs';
|
||||||
export { default as DhCombatant } from './combatant.mjs';
|
export { default as DhCombatant } from './combatant.mjs';
|
||||||
export { default as DhTagTeamRoll } from './tagTeamRoll.mjs';
|
export { default as DhTagTeamRoll } from './tagTeamRoll.mjs';
|
||||||
export { default as RegisteredTriggers } from './registeredTriggers.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';
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
|
|
||||||
async use(event, options) {
|
async use(event, options) {
|
||||||
const result = await super.use(event, options);
|
const result = await super.use(event, options);
|
||||||
if (!result.message) return;
|
|
||||||
|
|
||||||
if (result.message.system.action.roll?.type === 'attack') {
|
if (result.message.system.action.roll?.type === 'attack') {
|
||||||
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import DhpActor from '../../documents/actor.mjs';
|
||||||
import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
|
import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
|
||||||
import { ActionMixin } from '../fields/actionField.mjs';
|
import { ActionMixin } from '../fields/actionField.mjs';
|
||||||
import { originItemField } from '../chat-message/actorRoll.mjs';
|
import { originItemField } from '../chat-message/actorRoll.mjs';
|
||||||
import TriggerField from '../fields/triggerField.mjs';
|
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
|
@ -34,9 +33,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
initial: 'action',
|
initial: 'action',
|
||||||
nullable: false,
|
nullable: false,
|
||||||
required: true
|
required: true
|
||||||
}),
|
})
|
||||||
targetUuid: new fields.StringField({ initial: undefined }),
|
|
||||||
triggers: new fields.ArrayField(new TriggerField())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.extraSchemas.forEach(s => {
|
this.extraSchemas.forEach(s => {
|
||||||
|
|
@ -98,9 +95,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
prepareData() {
|
prepareData() {
|
||||||
this.name = this.name || game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[this.type].name);
|
this.name = this.name || game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[this.type].name);
|
||||||
this.img = this.img ?? this.parent?.parent?.img;
|
this.img = this.img ?? this.parent?.parent?.img;
|
||||||
|
|
||||||
/* Fallback to feature description */
|
|
||||||
this.description = this.description || this.parent?.description;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -165,8 +159,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
getRollData(data = {}) {
|
getRollData(data = {}) {
|
||||||
const actorData = this.actor ? this.actor.getRollData(false) : {};
|
if (!this.actor) return null;
|
||||||
|
const actorData = this.actor.getRollData(false);
|
||||||
|
|
||||||
|
// Add Roll results to RollDatas
|
||||||
actorData.result = data.roll?.total ?? 1;
|
actorData.result = data.roll?.total ?? 1;
|
||||||
|
|
||||||
actorData.scale = data.costs?.length // Right now only return the first scalable cost.
|
actorData.scale = data.costs?.length // Right now only return the first scalable cost.
|
||||||
? (data.costs.find(c => c.scalable)?.total ?? 1)
|
? (data.costs.find(c => c.scalable)?.total ?? 1)
|
||||||
: 1;
|
: 1;
|
||||||
|
|
@ -195,11 +193,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
async use(event) {
|
async use(event) {
|
||||||
if (!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
|
if (!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
|
||||||
|
|
||||||
|
if (this.chatDisplay) await this.toChat();
|
||||||
|
|
||||||
let config = this.prepareConfig(event);
|
let config = this.prepareConfig(event);
|
||||||
if (!config) return;
|
if (!config) return;
|
||||||
|
|
||||||
config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects(this.actor, this.item);
|
|
||||||
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return;
|
if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return;
|
||||||
|
|
||||||
// Display configuration window if necessary
|
// Display configuration window if necessary
|
||||||
|
|
@ -210,12 +208,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
|
|
||||||
// Execute the Action Worflow in order based of schema fields
|
// Execute the Action Worflow in order based of schema fields
|
||||||
await this.executeWorkflow(config);
|
await this.executeWorkflow(config);
|
||||||
await config.resourceUpdates.updateResources();
|
|
||||||
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
||||||
|
|
||||||
if (this.chatDisplay) await this.toChat();
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,11 +239,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
isDirect: !!this.damage?.direct,
|
isDirect: !!this.damage?.direct,
|
||||||
selectedRollMode: game.settings.get('core', 'rollMode'),
|
selectedRollMode: game.settings.get('core', 'rollMode'),
|
||||||
data: this.getRollData(),
|
data: this.getRollData(),
|
||||||
evaluate: this.hasRoll,
|
evaluate: this.hasRoll
|
||||||
resourceUpdates: new ResourceUpdateMap(this.actor),
|
|
||||||
targetUuid: this.targetUuid
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DHBaseAction.applyKeybindings(config);
|
DHBaseAction.applyKeybindings(config);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
@ -266,28 +258,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the all potentially applicable effects on the actor
|
|
||||||
* @param {DHActor} actor The actor performing the action
|
|
||||||
* @param {DHItem|DhActor} effectParent The parent of the effect
|
|
||||||
* @returns {DhActiveEffect[]}
|
|
||||||
*/
|
|
||||||
static async getEffects(actor, effectParent) {
|
|
||||||
if (!actor) return [];
|
|
||||||
|
|
||||||
return Array.from(await actor.allApplicableEffects()).filter(effect => {
|
|
||||||
/* Effects on weapons only ever apply for the weapon itself */
|
|
||||||
if (effect.parent.type === 'weapon') {
|
|
||||||
/* Unless they're secondary - then they apply only to other primary weapons */
|
|
||||||
if (effect.parent.system.secondary) {
|
|
||||||
if (effectParent?.type !== 'weapon' || effectParent?.system.secondary) return false;
|
|
||||||
} else if (effectParent?.id !== effect.parent.id) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !effect.isSuppressed;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method used to know if a configuration dialog must be shown or not when there is no roll.
|
* Method used to know if a configuration dialog must be shown or not when there is no roll.
|
||||||
* @param {*} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
* @param {*} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
||||||
|
|
@ -352,50 +322,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
* @returns {string[]} An array of localized tag strings.
|
* @returns {string[]} An array of localized tag strings.
|
||||||
*/
|
*/
|
||||||
_getTags() {
|
_getTags() {
|
||||||
const tags = [game.i18n.localize(`DAGGERHEART.ACTIONS.TYPES.${this.type}.name`)];
|
const tags = [
|
||||||
|
game.i18n.localize(`DAGGERHEART.ACTIONS.TYPES.${this.type}.name`),
|
||||||
|
game.i18n.localize(`DAGGERHEART.CONFIG.ActionType.${this.actionType}`)
|
||||||
|
];
|
||||||
|
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResourceUpdateMap extends Map {
|
|
||||||
#actor;
|
|
||||||
|
|
||||||
constructor(actor) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.#actor = actor;
|
|
||||||
}
|
|
||||||
|
|
||||||
addResources(resources) {
|
|
||||||
if (!resources?.length) return;
|
|
||||||
const invalidResources = resources.some(resource => !resource.key);
|
|
||||||
if (invalidResources) return;
|
|
||||||
|
|
||||||
for (const resource of resources) {
|
|
||||||
if (!resource.key) continue;
|
|
||||||
|
|
||||||
const existing = this.get(resource.key);
|
|
||||||
if (existing) {
|
|
||||||
this.set(resource.key, {
|
|
||||||
...existing,
|
|
||||||
value: existing.value + (resource.value ?? 0),
|
|
||||||
total: existing.total + (resource.total ?? 0)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.set(resource.key, resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#getResources() {
|
|
||||||
return Array.from(this.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateResources() {
|
|
||||||
if (this.#actor) {
|
|
||||||
const target = this.#actor.system.partner ?? this.#actor;
|
|
||||||
await target.modifyResource(this.#getResources());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,19 @@
|
||||||
import DHBaseAction from './baseAction.mjs';
|
import DHBaseAction from './baseAction.mjs';
|
||||||
|
|
||||||
export default class DHSummonAction extends DHBaseAction {
|
export default class DHSummonAction extends DHBaseAction {
|
||||||
static extraSchemas = [...super.extraSchemas, 'summon'];
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
documentUUID: new fields.DocumentUUIDField({ type: 'Actor' })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async trigger(event, ...args) {
|
||||||
|
if (!this.canSummon || !canvas.scene) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
get canSummon() {
|
||||||
|
return game.user.can('TOKEN_CREATE');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,3 @@
|
||||||
/** -- Changes Type Priorities --
|
|
||||||
* - Base Number -
|
|
||||||
* Custom: 0
|
|
||||||
* Multiply: 10
|
|
||||||
* Add: 20
|
|
||||||
* Downgrade: 30
|
|
||||||
* Upgrade: 40
|
|
||||||
* Override: 50
|
|
||||||
*
|
|
||||||
* - Changes Value Priorities -
|
|
||||||
* Standard: +0
|
|
||||||
* "Anything that uses another data model value as its value": +1 - Effects that increase traits have to be calculated first at Base priority. (EX: Raise evasion by half your agility)
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default class BaseEffect extends foundry.abstract.TypeDataModel {
|
export default class BaseEffect extends foundry.abstract.TypeDataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ export default class BeastformEffect extends BaseEffect {
|
||||||
base64: false
|
base64: false
|
||||||
}),
|
}),
|
||||||
tokenSize: new fields.SchemaField({
|
tokenSize: new fields.SchemaField({
|
||||||
scale: new fields.NumberField({ nullable: false, initial: 1 }),
|
|
||||||
height: new fields.NumberField({ integer: false, nullable: true }),
|
height: new fields.NumberField({ integer: false, nullable: true }),
|
||||||
width: new fields.NumberField({ integer: false, nullable: true })
|
width: new fields.NumberField({ integer: false, nullable: true })
|
||||||
})
|
})
|
||||||
|
|
@ -56,9 +55,7 @@ export default class BeastformEffect extends BaseEffect {
|
||||||
const update = {
|
const update = {
|
||||||
...baseUpdate,
|
...baseUpdate,
|
||||||
texture: {
|
texture: {
|
||||||
src: this.characterTokenData.tokenImg,
|
src: this.characterTokenData.tokenImg
|
||||||
scaleX: this.characterTokenData.tokenSize.scale,
|
|
||||||
scaleY: this.characterTokenData.tokenSize.scale
|
|
||||||
},
|
},
|
||||||
ring: {
|
ring: {
|
||||||
enabled: this.characterTokenData.usesDynamicToken,
|
enabled: this.characterTokenData.usesDynamicToken,
|
||||||
|
|
@ -68,40 +65,20 @@ export default class BeastformEffect extends BaseEffect {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateToken = token => {
|
const updateToken = token => ({
|
||||||
let x = null,
|
...baseUpdate,
|
||||||
y = null;
|
'texture': {
|
||||||
if (token.object?.scene?.grid) {
|
enabled: this.characterTokenData.usesDynamicToken,
|
||||||
const positionData = game.system.api.documents.DhToken.getSnappedPositionInSquareGrid(
|
src: token.flags.daggerheart?.beastformTokenImg ?? this.characterTokenData.tokenImg
|
||||||
token.object.scene.grid,
|
},
|
||||||
{ x: token.x, y: token.y, elevation: token.elevation },
|
'ring': {
|
||||||
baseUpdate.width,
|
subject: {
|
||||||
baseUpdate.height
|
texture:
|
||||||
);
|
token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg
|
||||||
|
}
|
||||||
x = positionData.x;
|
},
|
||||||
y = positionData.y;
|
'flags.daggerheart': { '-=beastformTokenImg': null, '-=beastformSubjectTexture': null }
|
||||||
}
|
});
|
||||||
|
|
||||||
return {
|
|
||||||
...baseUpdate,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
'texture': {
|
|
||||||
enabled: this.characterTokenData.usesDynamicToken,
|
|
||||||
src: token.flags.daggerheart?.beastformTokenImg ?? this.characterTokenData.tokenImg,
|
|
||||||
scaleX: this.characterTokenData.tokenSize.scale,
|
|
||||||
scaleY: this.characterTokenData.tokenSize.scale
|
|
||||||
},
|
|
||||||
'ring': {
|
|
||||||
subject: {
|
|
||||||
texture:
|
|
||||||
token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'flags.daggerheart': { '-=beastformTokenImg': null, '-=beastformSubjectTexture': null }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
await updateActorTokens(this.parent.parent, update, updateToken);
|
await updateActorTokens(this.parent.parent, update, updateToken);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import DHAdversarySettings from '../../applications/sheets-configs/adversary-settings.mjs';
|
import DHAdversarySettings from '../../applications/sheets-configs/adversary-settings.mjs';
|
||||||
import { ActionField } from '../fields/actionField.mjs';
|
import { ActionField } from '../fields/actionField.mjs';
|
||||||
import BaseDataActor, { commonActorRules } from './base.mjs';
|
import BaseDataActor from './base.mjs';
|
||||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||||
|
|
||||||
export default class DhpAdversary extends BaseDataActor {
|
export default class DhpAdversary extends BaseDataActor {
|
||||||
|
|
@ -11,8 +11,7 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
label: 'TYPES.Actor.adversary',
|
label: 'TYPES.Actor.adversary',
|
||||||
type: 'adversary',
|
type: 'adversary',
|
||||||
settingSheet: DHAdversarySettings,
|
settingSheet: DHAdversarySettings,
|
||||||
hasAttribution: true,
|
hasAttribution: true
|
||||||
usesSize: true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,11 +55,27 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
resources: new fields.SchemaField({
|
resources: new fields.SchemaField({
|
||||||
hitPoints: resourceField(0, 0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
|
hitPoints: resourceField(
|
||||||
stress: resourceField(0, 0, 'DAGGERHEART.GENERAL.stress', true)
|
0,
|
||||||
|
0,
|
||||||
|
'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||||
|
true,
|
||||||
|
game.i18n.localize('DAGGERHEART.GENERAL.max')
|
||||||
|
),
|
||||||
|
stress: resourceField(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
'DAGGERHEART.GENERAL.stress',
|
||||||
|
true,
|
||||||
|
game.i18n.localize('DAGGERHEART.GENERAL.max')
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
rules: new fields.SchemaField({
|
rules: new fields.SchemaField({
|
||||||
...commonActorRules()
|
conditionImmunities: new fields.SchemaField({
|
||||||
|
hidden: new fields.BooleanField({ initial: false }),
|
||||||
|
restrained: new fields.BooleanField({ initial: false }),
|
||||||
|
vulnerable: new fields.BooleanField({ initial: false })
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
attack: new ActionField({
|
attack: new ActionField({
|
||||||
initial: {
|
initial: {
|
||||||
|
|
@ -127,7 +142,7 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
}
|
}
|
||||||
|
|
||||||
isItemValid(source) {
|
isItemValid(source) {
|
||||||
return source.type === 'feature';
|
return source.type === "feature";
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preUpdate(changes, options, user) {
|
async _preUpdate(changes, options, user) {
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,21 @@
|
||||||
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
|
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
|
||||||
import DHItem from '../../documents/item.mjs';
|
|
||||||
import { getScrollTextData } from '../../helpers/utils.mjs';
|
import { getScrollTextData } from '../../helpers/utils.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
|
||||||
|
|
||||||
const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) =>
|
const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) =>
|
||||||
new fields.SchemaField({
|
new foundry.data.fields.SchemaField({
|
||||||
resistance: new fields.BooleanField({
|
resistance: new foundry.data.fields.BooleanField({
|
||||||
initial: false,
|
initial: false,
|
||||||
label: `${resistanceLabel}.label`,
|
label: `${resistanceLabel}.label`,
|
||||||
hint: `${resistanceLabel}.hint`,
|
hint: `${resistanceLabel}.hint`,
|
||||||
isAttributeChoice: true
|
isAttributeChoice: true
|
||||||
}),
|
}),
|
||||||
immunity: new fields.BooleanField({
|
immunity: new foundry.data.fields.BooleanField({
|
||||||
initial: false,
|
initial: false,
|
||||||
label: `${immunityLabel}.label`,
|
label: `${immunityLabel}.label`,
|
||||||
hint: `${immunityLabel}.hint`,
|
hint: `${immunityLabel}.hint`,
|
||||||
isAttributeChoice: true
|
isAttributeChoice: true
|
||||||
}),
|
}),
|
||||||
reduction: new fields.NumberField({
|
reduction: new foundry.data.fields.NumberField({
|
||||||
integer: true,
|
integer: true,
|
||||||
initial: 0,
|
initial: 0,
|
||||||
label: `${reductionLabel}.label`,
|
label: `${reductionLabel}.label`,
|
||||||
|
|
@ -26,25 +23,6 @@ const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) =>
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Common rules applying to Characters and Adversaries */
|
|
||||||
export const commonActorRules = (extendedData = { damageReduction: {} }) => ({
|
|
||||||
conditionImmunities: new fields.SchemaField({
|
|
||||||
hidden: new fields.BooleanField({ initial: false }),
|
|
||||||
restrained: new fields.BooleanField({ initial: false }),
|
|
||||||
vulnerable: new fields.BooleanField({ initial: false })
|
|
||||||
}),
|
|
||||||
damageReduction: new fields.SchemaField({
|
|
||||||
thresholdImmunities: new fields.SchemaField({
|
|
||||||
minor: new fields.BooleanField({ initial: false })
|
|
||||||
}),
|
|
||||||
reduceSeverity: new fields.SchemaField({
|
|
||||||
magical: new fields.NumberField({ initial: 0, min: 0 }),
|
|
||||||
physical: new fields.NumberField({ initial: 0, min: 0 })
|
|
||||||
}),
|
|
||||||
...extendedData.damageReduction
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes metadata about the actor data model type
|
* Describes metadata about the actor data model type
|
||||||
* @typedef {Object} ActorDataModelMetadata
|
* @typedef {Object} ActorDataModelMetadata
|
||||||
|
|
@ -63,8 +41,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
settingSheet: null,
|
settingSheet: null,
|
||||||
hasResistances: true,
|
hasResistances: true,
|
||||||
hasAttribution: false,
|
hasAttribution: false,
|
||||||
hasLimitedView: true,
|
hasLimitedView: true
|
||||||
usesSize: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +52,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
const schema = {};
|
const schema = {};
|
||||||
|
|
||||||
if (this.metadata.hasAttribution) {
|
if (this.metadata.hasAttribution) {
|
||||||
|
|
@ -98,13 +76,6 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
'DAGGERHEART.GENERAL.DamageResistance.magicalReduction'
|
'DAGGERHEART.GENERAL.DamageResistance.magicalReduction'
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
if (this.metadata.usesSize)
|
|
||||||
schema.size = new fields.StringField({
|
|
||||||
required: true,
|
|
||||||
nullable: false,
|
|
||||||
choices: CONFIG.DH.ACTOR.tokenSize,
|
|
||||||
initial: CONFIG.DH.ACTOR.tokenSize.custom.id
|
|
||||||
});
|
|
||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,17 +106,6 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if an item is available for use, such as multiclass features being disabled
|
|
||||||
* on a character.
|
|
||||||
*
|
|
||||||
* @param {DHItem} item The item being checked for availability
|
|
||||||
* @return {boolean} whether the item is available
|
|
||||||
*/
|
|
||||||
isItemAvailable(item) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _preDelete() {
|
async _preDelete() {
|
||||||
/* Clear all partyMembers from tagTeam setting.*/
|
/* Clear all partyMembers from tagTeam setting.*/
|
||||||
/* Revisit this when tagTeam is improved for many parties */
|
/* Revisit this when tagTeam is improved for many parties */
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { burden } from '../../config/generalConfig.mjs';
|
import { burden } from '../../config/generalConfig.mjs';
|
||||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||||
import DhLevelData from '../levelData.mjs';
|
import DhLevelData from '../levelData.mjs';
|
||||||
import BaseDataActor, { commonActorRules } from './base.mjs';
|
import BaseDataActor from './base.mjs';
|
||||||
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||||
import { ActionField } from '../fields/actionField.mjs';
|
import { ActionField } from '../fields/actionField.mjs';
|
||||||
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
||||||
|
|
@ -217,41 +217,44 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
}),
|
}),
|
||||||
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
|
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
|
||||||
rules: new fields.SchemaField({
|
rules: new fields.SchemaField({
|
||||||
...commonActorRules({
|
damageReduction: new fields.SchemaField({
|
||||||
damageReduction: {
|
maxArmorMarked: new fields.SchemaField({
|
||||||
magical: new fields.BooleanField({ initial: false }),
|
value: new fields.NumberField({
|
||||||
physical: new fields.BooleanField({ initial: false }),
|
required: true,
|
||||||
maxArmorMarked: new fields.SchemaField({
|
|
||||||
value: new fields.NumberField({
|
|
||||||
required: true,
|
|
||||||
integer: true,
|
|
||||||
initial: 1,
|
|
||||||
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedBonus'
|
|
||||||
}),
|
|
||||||
stressExtra: new fields.NumberField({
|
|
||||||
required: true,
|
|
||||||
integer: true,
|
|
||||||
initial: 0,
|
|
||||||
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.label',
|
|
||||||
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.hint'
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
stressDamageReduction: new fields.SchemaField({
|
|
||||||
severe: stressDamageReductionRule(
|
|
||||||
'DAGGERHEART.GENERAL.Rules.damageReduction.stress.severe'
|
|
||||||
),
|
|
||||||
major: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.major'),
|
|
||||||
minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor'),
|
|
||||||
any: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.any')
|
|
||||||
}),
|
|
||||||
increasePerArmorMark: new fields.NumberField({
|
|
||||||
integer: true,
|
integer: true,
|
||||||
initial: 1,
|
initial: 1,
|
||||||
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.label',
|
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedBonus'
|
||||||
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint'
|
|
||||||
}),
|
}),
|
||||||
disabledArmor: new fields.BooleanField({ intial: false })
|
stressExtra: new fields.NumberField({
|
||||||
}
|
required: true,
|
||||||
|
integer: true,
|
||||||
|
initial: 0,
|
||||||
|
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.label',
|
||||||
|
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.hint'
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
stressDamageReduction: new fields.SchemaField({
|
||||||
|
severe: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.severe'),
|
||||||
|
major: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.major'),
|
||||||
|
minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor'),
|
||||||
|
any: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.any')
|
||||||
|
}),
|
||||||
|
increasePerArmorMark: new fields.NumberField({
|
||||||
|
integer: true,
|
||||||
|
initial: 1,
|
||||||
|
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.label',
|
||||||
|
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint'
|
||||||
|
}),
|
||||||
|
magical: new fields.BooleanField({ initial: false }),
|
||||||
|
physical: new fields.BooleanField({ initial: false }),
|
||||||
|
thresholdImmunities: new fields.SchemaField({
|
||||||
|
minor: new fields.BooleanField({ initial: false })
|
||||||
|
}),
|
||||||
|
reduceSeverity: new fields.SchemaField({
|
||||||
|
magical: new fields.NumberField({ initial: 0, min: 0 }),
|
||||||
|
physical: new fields.NumberField({ initial: 0, min: 0 })
|
||||||
|
}),
|
||||||
|
disabledArmor: new fields.BooleanField({ intial: false })
|
||||||
}),
|
}),
|
||||||
attack: new fields.SchemaField({
|
attack: new fields.SchemaField({
|
||||||
damage: new fields.SchemaField({
|
damage: new fields.SchemaField({
|
||||||
|
|
@ -280,23 +283,10 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
dualityRoll: new fields.SchemaField({
|
conditionImmunities: new fields.SchemaField({
|
||||||
defaultHopeDice: new fields.NumberField({
|
hidden: new fields.BooleanField({ initial: false }),
|
||||||
nullable: false,
|
restrained: new fields.BooleanField({ initial: false }),
|
||||||
required: true,
|
vulnerable: new fields.BooleanField({ initial: false })
|
||||||
integer: true,
|
|
||||||
choices: CONFIG.DH.GENERAL.dieFaces,
|
|
||||||
initial: 12,
|
|
||||||
label: 'DAGGERHEART.ACTORS.Character.defaultHopeDice'
|
|
||||||
}),
|
|
||||||
defaultFearDice: new fields.NumberField({
|
|
||||||
nullable: false,
|
|
||||||
required: true,
|
|
||||||
integer: true,
|
|
||||||
choices: CONFIG.DH.GENERAL.dieFaces,
|
|
||||||
initial: 12,
|
|
||||||
label: 'DAGGERHEART.ACTORS.Character.defaultFearDice'
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
runeWard: new fields.BooleanField({ initial: false }),
|
runeWard: new fields.BooleanField({ initial: false }),
|
||||||
burden: new fields.SchemaField({
|
burden: new fields.SchemaField({
|
||||||
|
|
@ -445,33 +435,6 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
return attack;
|
return attack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
isItemAvailable(item) {
|
|
||||||
if (!super.isItemAvailable(this)) return false;
|
|
||||||
/**
|
|
||||||
* Preventing subclass features from being available if the chacaracter does not
|
|
||||||
* have the right subclass advancement
|
|
||||||
*/
|
|
||||||
if (item.system.originItemType !== CONFIG.DH.ITEM.featureTypes.subclass.id) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!this.class.subclass) return false;
|
|
||||||
|
|
||||||
const prop = item.system.multiclassOrigin ? 'multiclass' : 'class';
|
|
||||||
const subclassState = this[prop].subclass?.system?.featureState;
|
|
||||||
if (!subclassState) return false;
|
|
||||||
|
|
||||||
if (
|
|
||||||
item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation ||
|
|
||||||
(item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) ||
|
|
||||||
(item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get sheetLists() {
|
get sheetLists() {
|
||||||
const ancestryFeatures = [],
|
const ancestryFeatures = [],
|
||||||
communityFeatures = [],
|
communityFeatures = [],
|
||||||
|
|
@ -480,7 +443,7 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
companionFeatures = [],
|
companionFeatures = [],
|
||||||
features = [];
|
features = [];
|
||||||
|
|
||||||
for (let item of this.parent.items.filter(x => this.isItemAvailable(x))) {
|
for (let item of this.parent.items) {
|
||||||
if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.ancestry.id) {
|
if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.ancestry.id) {
|
||||||
ancestryFeatures.push(item);
|
ancestryFeatures.push(item);
|
||||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.community.id) {
|
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.community.id) {
|
||||||
|
|
@ -488,7 +451,20 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.class.id) {
|
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.class.id) {
|
||||||
classFeatures.push(item);
|
classFeatures.push(item);
|
||||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
|
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
|
||||||
subclassFeatures.push(item);
|
if (this.class.subclass) {
|
||||||
|
const prop = item.system.multiclassOrigin ? 'multiclass' : 'class';
|
||||||
|
const subclassState = this[prop].subclass?.system?.featureState;
|
||||||
|
if (!subclassState) continue;
|
||||||
|
|
||||||
|
if (
|
||||||
|
item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation ||
|
||||||
|
(item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization &&
|
||||||
|
subclassState >= 2) ||
|
||||||
|
(item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
|
||||||
|
) {
|
||||||
|
subclassFeatures.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.companion.id) {
|
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.companion.id) {
|
||||||
companionFeatures.push(item);
|
companionFeatures.push(item);
|
||||||
} else if (item.type === 'feature' && !item.system.type) {
|
} else if (item.type === 'feature' && !item.system.type) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
import BaseDataActor from './base.mjs';
|
import BaseDataActor from './base.mjs';
|
||||||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||||
import DHEnvironmentSettings from '../../applications/sheets-configs/environment-settings.mjs';
|
import DHEnvironmentSettings from '../../applications/sheets-configs/environment-settings.mjs';
|
||||||
import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
|
||||||
|
|
||||||
export default class DhEnvironment extends BaseDataActor {
|
export default class DhEnvironment extends BaseDataActor {
|
||||||
scenes = new Set();
|
|
||||||
|
|
||||||
/**@override */
|
/**@override */
|
||||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Environment'];
|
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Environment'];
|
||||||
|
|
||||||
|
|
@ -56,31 +53,6 @@ export default class DhEnvironment extends BaseDataActor {
|
||||||
}
|
}
|
||||||
|
|
||||||
isItemValid(source) {
|
isItemValid(source) {
|
||||||
return source.type === 'feature';
|
return source.type === "feature";
|
||||||
}
|
|
||||||
|
|
||||||
_onUpdate(changes, options, userId) {
|
|
||||||
super._onUpdate(changes, options, userId);
|
|
||||||
for (const scene of this.scenes) {
|
|
||||||
scene.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDelete(options, userId) {
|
|
||||||
super._onDelete(options, userId);
|
|
||||||
for (const scene of this.scenes) {
|
|
||||||
if (game.user.isActiveGM) {
|
|
||||||
const newSceneEnvironments = scene.flags.daggerheart.sceneEnvironments.filter(
|
|
||||||
x => x !== this.parent.uuid
|
|
||||||
);
|
|
||||||
scene.update({ 'flags.daggerheart.sceneEnvironments': newSceneEnvironments }).then(() => {
|
|
||||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Scene });
|
|
||||||
game.socket.emit(`system.${CONFIG.DH.id}`, {
|
|
||||||
action: socketEvent.Refresh,
|
|
||||||
data: { refreshType: RefreshType.TagTeamRoll }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export default class DhParty extends BaseDataActor {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
isItemValid(source) {
|
isItemValid(source) {
|
||||||
return ['weapon', 'armor', 'consumable', 'loot'].includes(source.type);
|
return ["weapon", "armor", "consumable", "loot"].includes(source.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareBaseData() {
|
prepareBaseData() {
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,8 @@ export default class DhCombat extends foundry.abstract.TypeDataModel {
|
||||||
get extendedBattleToggles() {
|
get extendedBattleToggles() {
|
||||||
const modifiers = CONFIG.DH.ENCOUNTER.BPModifiers;
|
const modifiers = CONFIG.DH.ENCOUNTER.BPModifiers;
|
||||||
const adversaries =
|
const adversaries =
|
||||||
this.parent.turns?.filter(x => x.actor && x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ??
|
this.parent.turns?.filter(x => x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? [];
|
||||||
[];
|
const characters = this.parent.turns?.filter(x => !x.isNPC) ?? [];
|
||||||
const characters = this.parent.turns?.filter(x => x.actor && !x.isNPC) ?? [];
|
|
||||||
|
|
||||||
const activeAutomatic = Object.keys(modifiers).reduce((acc, categoryKey) => {
|
const activeAutomatic = Object.keys(modifiers).reduce((acc, categoryKey) => {
|
||||||
const category = modifiers[categoryKey];
|
const category = modifiers[categoryKey];
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,5 @@ export { ActionCollection } from './actionField.mjs';
|
||||||
export { default as FormulaField } from './formulaField.mjs';
|
export { default as FormulaField } from './formulaField.mjs';
|
||||||
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
||||||
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
|
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
|
||||||
export { default as TriggerField } from './triggerField.mjs';
|
|
||||||
export { default as MappingField } from './mappingField.mjs';
|
export { default as MappingField } from './mappingField.mjs';
|
||||||
export * as ActionFields from './action/_module.mjs';
|
export * as ActionFields from './action/_module.mjs';
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,3 @@ export { default as BeastformField } from './beastformField.mjs';
|
||||||
export { default as DamageField } from './damageField.mjs';
|
export { default as DamageField } from './damageField.mjs';
|
||||||
export { default as RollField } from './rollField.mjs';
|
export { default as RollField } from './rollField.mjs';
|
||||||
export { default as MacroField } from './macroField.mjs';
|
export { default as MacroField } from './macroField.mjs';
|
||||||
export { default as SummonField } from './summonField.mjs';
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ export default class BeastformField extends fields.SchemaField {
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
static async transform(selectedForm, evolvedData, hybridData) {
|
static async transform(selectedForm, evolvedData, hybridData) {
|
||||||
const formData = evolvedData?.form ?? selectedForm;
|
const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm;
|
||||||
const beastformEffect = formData.effects.find(x => x.type === 'beastform');
|
const beastformEffect = formData.effects.find(x => x.type === 'beastform');
|
||||||
if (!beastformEffect) {
|
if (!beastformEffect) {
|
||||||
ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect');
|
ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect');
|
||||||
|
|
@ -92,18 +92,6 @@ export default class BeastformField extends fields.SchemaField {
|
||||||
|
|
||||||
beastformEffect.changes = [...beastformEffect.changes, ...evolvedForm.changes];
|
beastformEffect.changes = [...beastformEffect.changes, ...evolvedForm.changes];
|
||||||
formData.system.features = [...formData.system.features, ...selectedForm.system.features.map(x => x.uuid)];
|
formData.system.features = [...formData.system.features, ...selectedForm.system.features.map(x => x.uuid)];
|
||||||
|
|
||||||
const baseSize = evolvedData.form.system.tokenSize.size;
|
|
||||||
const evolvedSize =
|
|
||||||
baseSize === 'custom'
|
|
||||||
? 'custom'
|
|
||||||
: (Object.keys(CONFIG.DH.ACTOR.tokenSize).find(
|
|
||||||
x => CONFIG.DH.ACTOR.tokenSize[x].value === CONFIG.DH.ACTOR.tokenSize[baseSize].value + 1
|
|
||||||
) ?? baseSize);
|
|
||||||
formData.system.tokenSize = {
|
|
||||||
...evolvedData.form.system.tokenSize,
|
|
||||||
size: evolvedSize
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedForm.system.beastformType === CONFIG.DH.ITEM.beastformTypes.hybrid.id) {
|
if (selectedForm.system.beastformType === CONFIG.DH.ITEM.beastformTypes.hybrid.id) {
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ export default class CostField extends fields.ArrayField {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
config.resourceUpdates.addResources(resources);
|
await actor.modifyResource(resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
||||||
if (this.type === CONFIG.DH.GENERAL.rollTypes.attack.id)
|
if (this.type === CONFIG.DH.GENERAL.rollTypes.attack.id)
|
||||||
modifiers.push({
|
modifiers.push({
|
||||||
label: 'Bonus to Hit',
|
label: 'Bonus to Hit',
|
||||||
value: this.bonus ?? this.parent.actor.system.attack.roll.bonus ?? 0
|
value: this.bonus ?? this.parent.actor.system.attack.roll.bonus
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -124,21 +124,29 @@ export default class SaveField extends fields.SchemaField {
|
||||||
*/
|
*/
|
||||||
static async updateSaveMessage(result, message, targetId) {
|
static async updateSaveMessage(result, message, targetId) {
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
|
const updateMsg = async function (message, targetId, result) {
|
||||||
const chatMessage = ui.chat.collection.get(message._id),
|
// setTimeout(async () => {
|
||||||
changes = {
|
const chatMessage = ui.chat.collection.get(message._id),
|
||||||
flags: {
|
changes = {
|
||||||
[game.system.id]: {
|
flags: {
|
||||||
reactionRolls: {
|
[game.system.id]: {
|
||||||
[targetId]: {
|
reactionRolls: {
|
||||||
result: result.roll.total,
|
[targetId]: {
|
||||||
success: result.roll.success
|
result: result.roll.total,
|
||||||
|
success: result.roll.success
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
await chatMessage.update(changes);
|
||||||
await chatMessage.update(changes);
|
// }, 100);
|
||||||
|
};
|
||||||
|
if (game.modules.get('dice-so-nice')?.active)
|
||||||
|
game.dice3d
|
||||||
|
.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id)
|
||||||
|
.then(async () => await updateMsg(message, targetId, result));
|
||||||
|
else await updateMsg(message, targetId, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
import FormulaField from '../formulaField.mjs';
|
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
|
||||||
|
|
||||||
export default class DHSummonField extends fields.ArrayField {
|
|
||||||
/**
|
|
||||||
* Action Workflow order
|
|
||||||
*/
|
|
||||||
static order = 120;
|
|
||||||
|
|
||||||
constructor(options = {}, context = {}) {
|
|
||||||
const summonFields = new fields.SchemaField({
|
|
||||||
actorUUID: new fields.DocumentUUIDField({
|
|
||||||
type: 'Actor',
|
|
||||||
required: true
|
|
||||||
}),
|
|
||||||
count: new FormulaField({
|
|
||||||
required: true,
|
|
||||||
default: '1'
|
|
||||||
})
|
|
||||||
});
|
|
||||||
super(summonFields, options, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async execute() {
|
|
||||||
if (!canvas.scene) {
|
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.error'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.summon.length === 0) {
|
|
||||||
ui.notifications.warn('No actors configured for this Summon action.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rolls = [];
|
|
||||||
const summonData = [];
|
|
||||||
for (const summon of this.summon) {
|
|
||||||
let count = summon.count;
|
|
||||||
const roll = new Roll(summon.count);
|
|
||||||
if (!roll.isDeterministic) {
|
|
||||||
await roll.evaluate();
|
|
||||||
if (game.modules.get('dice-so-nice')?.active) rolls.push(roll);
|
|
||||||
count = roll.total;
|
|
||||||
}
|
|
||||||
|
|
||||||
const actor = DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID));
|
|
||||||
/* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */
|
|
||||||
summon.rolledCount = count;
|
|
||||||
summon.actor = actor.toObject();
|
|
||||||
|
|
||||||
summonData.push({ actor, count: count });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true)));
|
|
||||||
|
|
||||||
this.actor.sheet?.minimize();
|
|
||||||
DHSummonField.handleSummon(summonData, this.actor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for any available instances of the actor present in the world if we're missing artwork in the compendium */
|
|
||||||
static getWorldActor(baseActor) {
|
|
||||||
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
|
|
||||||
if (baseActor.inCompendium && dataType && baseActor.img === dataType.DEFAULT_ICON) {
|
|
||||||
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
|
|
||||||
return worldActorCopy ?? baseActor;
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseActor;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async handleSummon(summonData, actionActor, summonIndex = 0) {
|
|
||||||
const summon = summonData[summonIndex];
|
|
||||||
const result = await CONFIG.ux.TokenManager.createPreviewAsync(summon.actor, {
|
|
||||||
name: `${summon.actor.prototypeToken.name}${summon.count > 1 ? ` (${summon.count}x)` : ''}`
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result) return actionActor.sheet?.maximize();
|
|
||||||
summon.actor = result.actor;
|
|
||||||
|
|
||||||
summon.count--;
|
|
||||||
if (summon.count <= 0) {
|
|
||||||
summonIndex++;
|
|
||||||
if (summonIndex === summonData.length) return actionActor.sheet?.maximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
DHSummonField.handleSummon(summonData, actionActor, summonIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -25,12 +25,9 @@ export default class TargetField extends fields.SchemaField {
|
||||||
config.hasTarget = true;
|
config.hasTarget = true;
|
||||||
let targets;
|
let targets;
|
||||||
// If the Action is configured as self-targeted, set targets as the owner. Probably better way than to fallback to getDependentTokens
|
// If the Action is configured as self-targeted, set targets as the owner. Probably better way than to fallback to getDependentTokens
|
||||||
if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id) {
|
if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id)
|
||||||
targets = [this.actor.token ?? this.actor.prototypeToken];
|
targets = [this.actor.token ?? this.actor.prototypeToken];
|
||||||
} else if (config.targetUuid) {
|
else {
|
||||||
const actor = fromUuidSync(config.targetUuid);
|
|
||||||
targets = [actor.token ?? actor.prototypeToken];
|
|
||||||
} else {
|
|
||||||
targets = Array.from(game.user.targets);
|
targets = Array.from(game.user.targets);
|
||||||
if (this.target.type !== CONFIG.DH.GENERAL.targetTypes.any.id) {
|
if (this.target.type !== CONFIG.DH.GENERAL.targetTypes.any.id) {
|
||||||
targets = targets.filter(target => TargetField.isTargetFriendly(this.actor, target, this.target.type));
|
targets = targets.filter(target => TargetField.isTargetFriendly(this.actor, target, this.target.type));
|
||||||
|
|
@ -82,7 +79,7 @@ export default class TargetField extends fields.SchemaField {
|
||||||
return {
|
return {
|
||||||
id: token.id,
|
id: token.id,
|
||||||
actorId: token.actor.uuid,
|
actorId: token.actor.uuid,
|
||||||
name: token.name,
|
name: token.actor.name,
|
||||||
img: token.actor.img,
|
img: token.actor.img,
|
||||||
difficulty: token.actor.system.difficulty,
|
difficulty: token.actor.system.difficulty,
|
||||||
evasion: token.actor.system.evasion,
|
evasion: token.actor.system.evasion,
|
||||||
|
|
|
||||||
|
|
@ -141,12 +141,6 @@ export function ActionMixin(Base) {
|
||||||
return this.documentName;
|
return this.documentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Getter for icons
|
|
||||||
get typeIcon() {
|
|
||||||
const config = CONFIG.DH.ACTIONS.actionTypes[this.type];
|
|
||||||
return config?.icon || 'fa-question'; // Fallback icon just in case
|
|
||||||
}
|
|
||||||
|
|
||||||
get relativeUUID() {
|
get relativeUUID() {
|
||||||
return `.Item.${this.item.id}.Action.${this.id}`;
|
return `.Item.${this.item.id}.Action.${this.id}`;
|
||||||
}
|
}
|
||||||
|
|
@ -262,28 +256,22 @@ export function ActionMixin(Base) {
|
||||||
async toChat(origin) {
|
async toChat(origin) {
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
const systemData = {
|
const systemData = {
|
||||||
title: game.i18n.localize('DAGGERHEART.CONFIG.FeatureForm.action'),
|
title: game.i18n.localize('DAGGERHEART.CONFIG.ActionType.action'),
|
||||||
origin: origin,
|
origin: origin,
|
||||||
action: {
|
action: {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
img: this.baseAction ? this.parent.parent.img : this.img,
|
img: this.baseAction ? this.parent.parent.img : this.img,
|
||||||
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'],
|
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10']
|
||||||
summon: this.summon
|
|
||||||
},
|
},
|
||||||
itemOrigin: this.item,
|
itemOrigin: this.item,
|
||||||
description: this.description || (this.item instanceof Item ? this.item.system.description : '')
|
description: this.description || (this.item instanceof Item ? this.item.system.description : '')
|
||||||
};
|
};
|
||||||
|
|
||||||
const speaker = cls.getSpeaker();
|
|
||||||
const msg = {
|
const msg = {
|
||||||
type: 'abilityUse',
|
type: 'abilityUse',
|
||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
actor: { name: this.actor.name, img: this.actor.img },
|
actor: { name: this.actor.name, img: this.actor.img },
|
||||||
author: this.author,
|
author: this.author,
|
||||||
speaker: {
|
speaker: cls.getSpeaker(),
|
||||||
speaker,
|
|
||||||
actor: speaker.actor ?? this.actor
|
|
||||||
},
|
|
||||||
title: game.i18n.localize('DAGGERHEART.UI.Chat.action.title'),
|
title: game.i18n.localize('DAGGERHEART.UI.Chat.action.title'),
|
||||||
system: systemData,
|
system: systemData,
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
export default class TriggerField extends foundry.data.fields.SchemaField {
|
|
||||||
constructor(context) {
|
|
||||||
super(
|
|
||||||
{
|
|
||||||
trigger: new foundry.data.fields.StringField({
|
|
||||||
nullable: false,
|
|
||||||
blank: false,
|
|
||||||
initial: CONFIG.DH.TRIGGER.triggers.dualityRoll.id,
|
|
||||||
choices: CONFIG.DH.TRIGGER.triggers,
|
|
||||||
label: 'DAGGERHEART.CONFIG.Triggers.triggerType'
|
|
||||||
}),
|
|
||||||
triggeringActorType: new foundry.data.fields.StringField({
|
|
||||||
nullable: false,
|
|
||||||
blank: false,
|
|
||||||
initial: CONFIG.DH.TRIGGER.triggerActorTargetType.any.id,
|
|
||||||
choices: CONFIG.DH.TRIGGER.triggerActorTargetType,
|
|
||||||
label: 'DAGGERHEART.CONFIG.Triggers.triggeringActorType'
|
|
||||||
}),
|
|
||||||
command: new foundry.data.fields.JavaScriptField({ async: true })
|
|
||||||
},
|
|
||||||
context
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -54,21 +54,6 @@ export default class DHArmor extends AttachableItem {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**@inheritdoc */
|
|
||||||
async getDescriptionData() {
|
|
||||||
const baseDescription = this.description;
|
|
||||||
const allFeatures = CONFIG.DH.ITEM.allArmorFeatures();
|
|
||||||
const features = this.armorFeatures.map(x => allFeatures[x.value]);
|
|
||||||
if (!features.length) return { prefix: null, value: baseDescription, suffix: null };
|
|
||||||
|
|
||||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/sheets/items/armor/description.hbs',
|
|
||||||
{ features }
|
|
||||||
);
|
|
||||||
|
|
||||||
return { prefix, value: baseDescription, suffix: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _preUpdate(changes, options, user) {
|
async _preUpdate(changes, options, user) {
|
||||||
const allowed = await super._preUpdate(changes, options, user);
|
const allowed = await super._preUpdate(changes, options, user);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
|
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addLinkedItemsDiff, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs';
|
import { addLinkedItemsDiff, createScrollText, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs';
|
||||||
import { ActionsField } from '../fields/actionField.mjs';
|
import { ActionsField } from '../fields/actionField.mjs';
|
||||||
import FormulaField from '../fields/formulaField.mjs';
|
import FormulaField from '../fields/formulaField.mjs';
|
||||||
|
|
||||||
|
|
@ -124,33 +124,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
return [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
|
return [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Augments the description for the item with type specific info to display. Implemented in applicable item subtypes.
|
|
||||||
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
async getDescriptionData(_options) {
|
|
||||||
return { prefix: null, value: this.description, suffix: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the enriched and augmented description for the item.
|
|
||||||
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
async getEnrichedDescription() {
|
|
||||||
if (!this.metadata.hasDescription) return '';
|
|
||||||
|
|
||||||
const { prefix, value, suffix } = await this.getDescriptionData();
|
|
||||||
const fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n<hr>\n');
|
|
||||||
|
|
||||||
return await foundry.applications.ux.TextEditor.implementation.enrichHTML(fullDescription, {
|
|
||||||
relativeTo: this,
|
|
||||||
rollData: this.getRollData(),
|
|
||||||
secrets: this.isOwner
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||||
* @param {object} [options] - Options which modify the getRollData method.
|
* @param {object} [options] - Options which modify the getRollData method.
|
||||||
|
|
@ -162,11 +135,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareBaseData() {
|
|
||||||
super.prepareBaseData();
|
|
||||||
game.system.registeredTriggers.registerItemTriggers(this.parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
// Skip if no initial action is required or actions already exist
|
// Skip if no initial action is required or actions already exist
|
||||||
if (this.metadata.hasInitialAction && foundry.utils.isEmpty(this.actions)) {
|
if (this.metadata.hasInitialAction && foundry.utils.isEmpty(this.actions)) {
|
||||||
|
|
@ -227,28 +195,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor');
|
const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor');
|
||||||
options.scrollingTextData = [armorData];
|
options.scrollingTextData = [armorData];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed.system?.actions) {
|
|
||||||
const triggersToRemove = Object.keys(changed.system.actions).reduce((acc, key) => {
|
|
||||||
if (!changed.system.actions[key]) {
|
|
||||||
const strippedKey = key.replace('-=', '');
|
|
||||||
acc.push(...this.actions.get(strippedKey).triggers.map(x => x.trigger));
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
game.system.registeredTriggers.unregisterTriggers(triggersToRemove, this.parent.uuid);
|
|
||||||
|
|
||||||
if (!(this.parent.parent.token instanceof game.system.api.documents.DhToken)) {
|
|
||||||
for (const token of this.parent.parent.getActiveTokens()) {
|
|
||||||
game.system.registeredTriggers.unregisterTriggers(
|
|
||||||
triggersToRemove,
|
|
||||||
`${token.document.uuid}.${this.parent.uuid}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onUpdate(changed, options, userId) {
|
_onUpdate(changed, options, userId) {
|
||||||
|
|
|
||||||
|
|
@ -43,13 +43,6 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
base64: false
|
base64: false
|
||||||
}),
|
}),
|
||||||
tokenSize: new fields.SchemaField({
|
tokenSize: new fields.SchemaField({
|
||||||
size: new fields.StringField({
|
|
||||||
required: true,
|
|
||||||
nullable: false,
|
|
||||||
choices: CONFIG.DH.ACTOR.tokenSize,
|
|
||||||
initial: CONFIG.DH.ACTOR.tokenSize.custom.id
|
|
||||||
}),
|
|
||||||
scale: new fields.NumberField({ nullable: false, min: 0.2, max: 3, step: 0.05, initial: 1 }),
|
|
||||||
height: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }),
|
height: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }),
|
||||||
width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true })
|
width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true })
|
||||||
}),
|
}),
|
||||||
|
|
@ -185,7 +178,6 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
tokenImg: this.parent.parent.prototypeToken.texture.src,
|
tokenImg: this.parent.parent.prototypeToken.texture.src,
|
||||||
tokenRingImg: this.parent.parent.prototypeToken.ring.subject.texture,
|
tokenRingImg: this.parent.parent.prototypeToken.ring.subject.texture,
|
||||||
tokenSize: {
|
tokenSize: {
|
||||||
scale: this.parent.parent.prototypeToken.texture.scaleX,
|
|
||||||
height: this.parent.parent.prototypeToken.height,
|
height: this.parent.parent.prototypeToken.height,
|
||||||
width: this.parent.parent.prototypeToken.width
|
width: this.parent.parent.prototypeToken.width
|
||||||
}
|
}
|
||||||
|
|
@ -198,22 +190,11 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
|
|
||||||
await this.parent.parent.createEmbeddedDocuments('ActiveEffect', [beastformEffect.toObject()]);
|
await this.parent.parent.createEmbeddedDocuments('ActiveEffect', [beastformEffect.toObject()]);
|
||||||
|
|
||||||
const autoTokenSize =
|
|
||||||
this.tokenSize.size !== 'custom'
|
|
||||||
? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes[
|
|
||||||
this.tokenSize.size
|
|
||||||
]
|
|
||||||
: null;
|
|
||||||
const width = autoTokenSize ?? this.tokenSize.width;
|
|
||||||
const height = autoTokenSize ?? this.tokenSize.height;
|
|
||||||
|
|
||||||
const prototypeTokenUpdate = {
|
const prototypeTokenUpdate = {
|
||||||
height,
|
height: this.tokenSize.height,
|
||||||
width,
|
width: this.tokenSize.width,
|
||||||
texture: {
|
texture: {
|
||||||
src: this.tokenImg,
|
src: this.tokenImg
|
||||||
scaleX: this.tokenSize.scale,
|
|
||||||
scaleY: this.tokenSize.scale
|
|
||||||
},
|
},
|
||||||
ring: {
|
ring: {
|
||||||
subject: {
|
subject: {
|
||||||
|
|
@ -221,33 +202,16 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const tokenUpdate = token => {
|
|
||||||
let x = null,
|
|
||||||
y = null;
|
|
||||||
if (token.object?.scene?.grid) {
|
|
||||||
const positionData = game.system.api.documents.DhToken.getSnappedPositionInSquareGrid(
|
|
||||||
token.object.scene.grid,
|
|
||||||
{ x: token.x, y: token.y, elevation: token.elevation },
|
|
||||||
width ?? token.width,
|
|
||||||
height ?? token.height
|
|
||||||
);
|
|
||||||
|
|
||||||
x = positionData.x;
|
const tokenUpdate = token => ({
|
||||||
y = positionData.y;
|
...prototypeTokenUpdate,
|
||||||
}
|
flags: {
|
||||||
|
daggerheart: {
|
||||||
return {
|
beastformTokenImg: token.texture.src,
|
||||||
...prototypeTokenUpdate,
|
beastformSubjectTexture: token.ring.subject.texture
|
||||||
x,
|
|
||||||
y,
|
|
||||||
flags: {
|
|
||||||
daggerheart: {
|
|
||||||
beastformTokenImg: token.texture.src,
|
|
||||||
beastformSubjectTexture: token.ring.subject.texture
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
await updateActorTokens(this.parent.parent, prototypeTokenUpdate, tokenUpdate);
|
await updateActorTokens(this.parent.parent, prototypeTokenUpdate, tokenUpdate);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,21 +29,7 @@ export default class DHDomainCard extends BaseDataItem {
|
||||||
required: true,
|
required: true,
|
||||||
initial: CONFIG.DH.DOMAIN.cardTypes.ability.id
|
initial: CONFIG.DH.DOMAIN.cardTypes.ability.id
|
||||||
}),
|
}),
|
||||||
inVault: new fields.BooleanField({ initial: false }),
|
inVault: new fields.BooleanField({ initial: false })
|
||||||
vaultActive: new fields.BooleanField({
|
|
||||||
required: true,
|
|
||||||
nullable: false,
|
|
||||||
initial: false
|
|
||||||
}),
|
|
||||||
loadoutIgnore: new fields.BooleanField({
|
|
||||||
required: true,
|
|
||||||
nullable: false,
|
|
||||||
initial: false
|
|
||||||
}),
|
|
||||||
domainTouched: new fields.NumberField({
|
|
||||||
nullable: true,
|
|
||||||
initial: null
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,19 +38,6 @@ export default class DHDomainCard extends BaseDataItem {
|
||||||
return game.i18n.localize(allDomainData[this.domain].label);
|
return game.i18n.localize(allDomainData[this.domain].label);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isVaultSupressed() {
|
|
||||||
return this.inVault && !this.vaultActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isDomainTouchedSuppressed() {
|
|
||||||
if (!this.parent.system.domainTouched || this.parent.parent?.type !== 'character') return false;
|
|
||||||
|
|
||||||
const matchingDomainCards = this.parent.parent.items.filter(
|
|
||||||
item => !item.system.inVault && item.system.domain === this.parent.system.domain
|
|
||||||
).length;
|
|
||||||
return matchingDomainCards < this.parent.system.domainTouched;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**@override */
|
/**@override */
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,7 @@ export default class DHFeature extends BaseDataItem {
|
||||||
initial: null
|
initial: null
|
||||||
}),
|
}),
|
||||||
multiclassOrigin: new fields.BooleanField({ initial: false }),
|
multiclassOrigin: new fields.BooleanField({ initial: false }),
|
||||||
identifier: new fields.StringField(),
|
identifier: new fields.StringField()
|
||||||
featureForm: new fields.StringField({
|
|
||||||
required: true,
|
|
||||||
initial: 'passive',
|
|
||||||
choices: CONFIG.DH.ITEM.featureForm,
|
|
||||||
label: 'DAGGERHEART.CONFIG.FeatureForm.label'
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,21 +110,6 @@ export default class DHWeapon extends AttachableItem {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**@inheritdoc */
|
|
||||||
async getDescriptionData() {
|
|
||||||
const baseDescription = this.description;
|
|
||||||
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
|
||||||
const features = this.weaponFeatures.map(x => allFeatures[x.value]);
|
|
||||||
if (!features.length) return { prefix: null, value: baseDescription, suffix: null };
|
|
||||||
|
|
||||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
|
|
||||||
{ features }
|
|
||||||
);
|
|
||||||
|
|
||||||
return { prefix, value: baseDescription, suffix: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,154 +0,0 @@
|
||||||
export default class RegisteredTriggers extends Map {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
registerTriggers(triggers, actor, uuid) {
|
|
||||||
for (const triggerKey of Object.keys(CONFIG.DH.TRIGGER.triggers)) {
|
|
||||||
const match = triggers[triggerKey];
|
|
||||||
const existingTrigger = this.get(triggerKey);
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
if (existingTrigger?.get(uuid)) this.get(triggerKey).delete(uuid);
|
|
||||||
} else {
|
|
||||||
const { trigger, triggeringActorType, commands } = match;
|
|
||||||
|
|
||||||
if (!existingTrigger) this.set(trigger, new Map());
|
|
||||||
this.get(trigger).set(uuid, { actor, triggeringActorType, commands });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerItemTriggers(item, registerOverride) {
|
|
||||||
for (const action of item.system.actions ?? []) {
|
|
||||||
if (!action.actor) continue;
|
|
||||||
|
|
||||||
/* Non actor-linked should only prep synthetic actors so they're not registering triggers unless they're on the canvas */
|
|
||||||
if (
|
|
||||||
!registerOverride &&
|
|
||||||
!action.actor.prototypeToken.actorLink &&
|
|
||||||
(!(action.actor.parent instanceof game.system.api.documents.DhToken) || !action.actor.parent?.uuid)
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const triggers = {};
|
|
||||||
for (const trigger of action.triggers) {
|
|
||||||
const { args } = CONFIG.DH.TRIGGER.triggers[trigger.trigger];
|
|
||||||
const fn = new foundry.utils.AsyncFunction(...args, `{${trigger.command}\n}`);
|
|
||||||
|
|
||||||
if (!triggers[trigger.trigger])
|
|
||||||
triggers[trigger.trigger] = {
|
|
||||||
trigger: trigger.trigger,
|
|
||||||
triggeringActorType: trigger.triggeringActorType,
|
|
||||||
commands: []
|
|
||||||
};
|
|
||||||
triggers[trigger.trigger].commands.push(fn.bind(action));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.registerTriggers(triggers, action.actor?.uuid, item.uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unregisterTriggers(triggerKeys, uuid) {
|
|
||||||
for (const triggerKey of triggerKeys) {
|
|
||||||
const existingTrigger = this.get(triggerKey);
|
|
||||||
if (!existingTrigger) return;
|
|
||||||
|
|
||||||
existingTrigger.delete(uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unregisterItemTriggers(items) {
|
|
||||||
for (const item of items) {
|
|
||||||
if (!item.system.actions.size) continue;
|
|
||||||
|
|
||||||
const triggers = (item.system.actions ?? []).reduce((acc, action) => {
|
|
||||||
acc.push(...action.triggers.map(x => x.trigger));
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
this.unregisterTriggers(triggers, item.uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unregisterSceneTriggers(scene) {
|
|
||||||
for (const triggerKey of Object.keys(CONFIG.DH.TRIGGER.triggers)) {
|
|
||||||
const existingTrigger = this.get(triggerKey);
|
|
||||||
if (!existingTrigger) continue;
|
|
||||||
const filtered = new Map();
|
|
||||||
for (const [uuid, data] of existingTrigger.entries()) {
|
|
||||||
if (!uuid.startsWith(scene.uuid)) filtered.set(uuid, data);
|
|
||||||
}
|
|
||||||
this.set(triggerKey, filtered);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerSceneTriggers(scene) {
|
|
||||||
/* TODO: Finish sceneEnvironment registration and unreg */
|
|
||||||
// const systemData = new game.system.api.data.scenes.DHScene(scene.flags.daggerheart);
|
|
||||||
// for (const environment of systemData.sceneEnvironments) {
|
|
||||||
// for (const feature of environment.system.features) {
|
|
||||||
// if(feature) this.registerItemTriggers(feature, true);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
for (const actor of scene.tokens.filter(x => x.actor).map(x => x.actor)) {
|
|
||||||
if (actor.prototypeToken.actorLink) continue;
|
|
||||||
|
|
||||||
for (const item of actor.items) {
|
|
||||||
this.registerItemTriggers(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async runTrigger(trigger, currentActor, ...args) {
|
|
||||||
const updates = [];
|
|
||||||
const triggerSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).triggers;
|
|
||||||
if (!triggerSettings.enabled) return updates;
|
|
||||||
|
|
||||||
const dualityTrigger = this.get(trigger);
|
|
||||||
if (dualityTrigger) {
|
|
||||||
const tokenBoundActors = ['adversary', 'environment'];
|
|
||||||
const triggerActors = ['character', ...tokenBoundActors];
|
|
||||||
for (let [itemUuid, { actor: actorUuid, triggeringActorType, commands }] of dualityTrigger.entries()) {
|
|
||||||
const actor = await foundry.utils.fromUuid(actorUuid);
|
|
||||||
if (!actor || !triggerActors.includes(actor.type)) continue;
|
|
||||||
if (tokenBoundActors.includes(actor.type) && !actor.getActiveTokens().length) continue;
|
|
||||||
|
|
||||||
const triggerData = CONFIG.DH.TRIGGER.triggers[trigger];
|
|
||||||
if (triggerData.usesActor && triggeringActorType !== 'any') {
|
|
||||||
if (triggeringActorType === 'self' && currentActor?.uuid !== actorUuid) continue;
|
|
||||||
else if (triggeringActorType === 'other' && currentActor?.uuid === actorUuid) continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const command of commands) {
|
|
||||||
try {
|
|
||||||
if (CONFIG.debug.triggers) {
|
|
||||||
const item = await foundry.utils.fromUuid(itemUuid);
|
|
||||||
console.log(
|
|
||||||
game.i18n.format('DAGGERHEART.UI.ConsoleLogs.triggerRun', {
|
|
||||||
actor: actor.name ?? '<Missing Actor>',
|
|
||||||
item: item?.name ?? '<Missing Item>',
|
|
||||||
trigger: game.i18n.localize(triggerData.label)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await command(...args);
|
|
||||||
if (result?.updates?.length) updates.push(...result.updates);
|
|
||||||
} catch (_) {
|
|
||||||
const triggerName = game.i18n.localize(triggerData.label);
|
|
||||||
ui.notifications.error(
|
|
||||||
game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerError', {
|
|
||||||
trigger: triggerName,
|
|
||||||
actor: currentActor?.name
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return updates;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
|
||||||
|
|
||||||
/* Foundry does not add any system data for subtyped Scenes. The data model is therefore used by instantiating a new instance of it for sceneConfigSettings.mjs.
|
|
||||||
Needed dataprep and lifetime hooks are handled in documents/scene.
|
|
||||||
*/
|
|
||||||
export default class DHScene extends foundry.abstract.DataModel {
|
export default class DHScene extends foundry.abstract.DataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
@ -18,8 +13,7 @@ export default class DHScene extends foundry.abstract.DataModel {
|
||||||
veryClose: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.veryClose.name' }),
|
veryClose: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.veryClose.name' }),
|
||||||
close: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.close.name' }),
|
close: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.close.name' }),
|
||||||
far: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.far.name' })
|
far: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.far.name' })
|
||||||
}),
|
})
|
||||||
sceneEnvironments: new ForeignDocumentUUIDArrayField({ type: 'Actor', prune: true })
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,13 +173,6 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
||||||
label: 'DAGGERHEART.GENERAL.player.plurial'
|
label: 'DAGGERHEART.GENERAL.player.plurial'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}),
|
|
||||||
triggers: new fields.SchemaField({
|
|
||||||
enabled: new fields.BooleanField({
|
|
||||||
nullable: false,
|
|
||||||
initial: true,
|
|
||||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.triggers.enabled.label'
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,38 +40,6 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
traitArray: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }), {
|
traitArray: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }), {
|
||||||
initial: () => [2, 1, 1, 0, 0, -1]
|
initial: () => [2, 1, 1, 0, 0, -1]
|
||||||
}),
|
}),
|
||||||
tokenSizes: new fields.SchemaField({
|
|
||||||
tiny: new fields.NumberField({
|
|
||||||
integer: false,
|
|
||||||
initial: 0.5,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.tiny'
|
|
||||||
}),
|
|
||||||
small: new fields.NumberField({
|
|
||||||
integer: false,
|
|
||||||
initial: 0.8,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.small'
|
|
||||||
}),
|
|
||||||
medium: new fields.NumberField({
|
|
||||||
integer: false,
|
|
||||||
initial: 1,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.medium'
|
|
||||||
}),
|
|
||||||
large: new fields.NumberField({
|
|
||||||
integer: false,
|
|
||||||
initial: 2,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.large'
|
|
||||||
}),
|
|
||||||
huge: new fields.NumberField({
|
|
||||||
integer: false,
|
|
||||||
initial: 3,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.huge'
|
|
||||||
}),
|
|
||||||
gargantuan: new fields.NumberField({
|
|
||||||
integer: false,
|
|
||||||
initial: 4,
|
|
||||||
label: 'DAGGERHEART.CONFIG.TokenSize.gargantuan'
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
currency: new fields.SchemaField({
|
currency: new fields.SchemaField({
|
||||||
title: new fields.StringField({
|
title: new fields.StringField({
|
||||||
required: true,
|
required: true,
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,7 @@ export default class D20Roll extends DHRoll {
|
||||||
|
|
||||||
get isCritical() {
|
get isCritical() {
|
||||||
if (!this.d20._evaluated) return;
|
if (!this.d20._evaluated) return;
|
||||||
|
return this.d20.total >= this.data.system.criticalThreshold;
|
||||||
const criticalThreshold = this.options.actionType === 'reaction' ? 20 : this.data.system.criticalThreshold;
|
|
||||||
return this.d20.total >= criticalThreshold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasAdvantage() {
|
get hasAdvantage() {
|
||||||
|
|
@ -100,10 +98,10 @@ export default class D20Roll extends DHRoll {
|
||||||
this.options.roll.modifiers = this.applyBaseBonus();
|
this.options.roll.modifiers = this.applyBaseBonus();
|
||||||
|
|
||||||
this.options.experiences?.forEach(m => {
|
this.options.experiences?.forEach(m => {
|
||||||
if (this.options.data.system?.experiences?.[m])
|
if (this.options.data.experiences?.[m])
|
||||||
this.options.roll.modifiers.push({
|
this.options.roll.modifiers.push({
|
||||||
label: this.options.data.system.experiences[m].name,
|
label: this.options.data.experiences[m].name,
|
||||||
value: this.options.data.system.experiences[m].value
|
value: this.options.data.experiences[m].value
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -129,55 +127,15 @@ export default class D20Roll extends DHRoll {
|
||||||
const modifiers = foundry.utils.deepClone(this.options.roll.baseModifiers) ?? [];
|
const modifiers = foundry.utils.deepClone(this.options.roll.baseModifiers) ?? [];
|
||||||
|
|
||||||
modifiers.push(
|
modifiers.push(
|
||||||
...this.getBonus(
|
...this.getBonus(`roll.${this.options.actionType}`, `${this.options.actionType?.capitalize()} Bonus`)
|
||||||
`system.bonuses.roll.${this.options.actionType}`,
|
);
|
||||||
`${this.options.actionType?.capitalize()} Bonus`
|
modifiers.push(
|
||||||
)
|
...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type?.capitalize()} Bonus`)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.attack.id) {
|
|
||||||
modifiers.push(
|
|
||||||
...this.getBonus(
|
|
||||||
`system.bonuses.roll.${this.options.roll.type}`,
|
|
||||||
`${this.options.roll.type?.capitalize()} Bonus`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.attack.id ||
|
|
||||||
(this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id && this.options.hasDamage)
|
|
||||||
) {
|
|
||||||
modifiers.push(
|
|
||||||
...this.getBonus(`system.bonuses.roll.attack`, `${this.options.roll.type?.capitalize()} Bonus`)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return modifiers;
|
return modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
getActionChangeKeys() {
|
|
||||||
const changeKeys = new Set([`system.bonuses.roll.${this.options.actionType}`]);
|
|
||||||
|
|
||||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.attack.id) {
|
|
||||||
changeKeys.add(`system.bonuses.roll.${this.options.roll.type}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.attack.id ||
|
|
||||||
(this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id && this.options.hasDamage)
|
|
||||||
) {
|
|
||||||
changeKeys.add(`system.bonuses.roll.attack`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait]) {
|
|
||||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.spellcast.id)
|
|
||||||
changeKeys.add('system.bonuses.roll.trait');
|
|
||||||
}
|
|
||||||
|
|
||||||
return changeKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
static postEvaluate(roll, config = {}) {
|
static postEvaluate(roll, config = {}) {
|
||||||
const data = super.postEvaluate(roll, config);
|
const data = super.postEvaluate(roll, config);
|
||||||
data.type = config.actionType;
|
data.type = config.actionType;
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@ export default class DamageRoll extends DHRoll {
|
||||||
type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage'),
|
type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage'),
|
||||||
options = part ?? this.options;
|
options = part ?? this.options;
|
||||||
|
|
||||||
|
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
|
||||||
if (!this.options.hasHealing) {
|
if (!this.options.hasHealing) {
|
||||||
options.damageTypes?.forEach(t => {
|
options.damageTypes?.forEach(t => {
|
||||||
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
|
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
|
||||||
|
|
@ -107,31 +108,6 @@ export default class DamageRoll extends DHRoll {
|
||||||
return modifiers;
|
return modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
getActionChangeKeys() {
|
|
||||||
const type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage');
|
|
||||||
const changeKeys = [];
|
|
||||||
|
|
||||||
for (const roll of this.options.roll) {
|
|
||||||
for (const damageType of roll.damageTypes?.values?.() ?? []) {
|
|
||||||
changeKeys.push(`system.bonuses.${type}.${damageType}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const item = this.data.parent?.items?.get(this.options.source.item);
|
|
||||||
if (item) {
|
|
||||||
switch (item.type) {
|
|
||||||
case 'weapon':
|
|
||||||
if (!this.options.hasHealing)
|
|
||||||
['primaryWeapon', 'secondaryWeapon'].forEach(w =>
|
|
||||||
changeKeys.push(`system.bonuses.damage.${w}`)
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changeKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructFormula(config) {
|
constructFormula(config) {
|
||||||
this.options.roll.forEach((part, index) => {
|
this.options.roll.forEach((part, index) => {
|
||||||
part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data));
|
part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data));
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ export default class DHRoll extends Roll {
|
||||||
baseTerms = [];
|
baseTerms = [];
|
||||||
constructor(formula, data = {}, options = {}) {
|
constructor(formula, data = {}, options = {}) {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
options.bonusEffects = this.bonusEffectBuilder();
|
|
||||||
if (!this.data || !Object.keys(this.data).length) this.data = options.data;
|
if (!this.data || !Object.keys(this.data).length) this.data = options.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,17 +164,11 @@ export default class DHRoll extends Roll {
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||||
...this.constructor.parse(modifier.join(' + '), this.options.data)
|
...this.constructor.parse(modifier.join(' + '), this.options.data)
|
||||||
];
|
];
|
||||||
} else if (Number.isNumeric(modifier)) {
|
|
||||||
const numTerm = modifier < 0 ? '-' : '+';
|
|
||||||
return [
|
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
|
||||||
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
|
|
||||||
];
|
|
||||||
} else {
|
} else {
|
||||||
const numTerm = modifier < 0 ? '-' : '+';
|
const numTerm = modifier < 0 ? '-' : '+';
|
||||||
return [
|
return [
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
||||||
...this.constructor.parse(modifier, this.options.data)
|
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -192,20 +185,18 @@ export default class DHRoll extends Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
getBonus(path, label) {
|
getBonus(path, label) {
|
||||||
const modifiers = [];
|
const bonus = foundry.utils.getProperty(this.data.bonuses, path),
|
||||||
for (const effect of Object.values(this.options.bonusEffects)) {
|
modifiers = [];
|
||||||
if (!effect.selected) continue;
|
if (bonus?.bonus)
|
||||||
for (const change of effect.changes) {
|
modifiers.push({
|
||||||
if (!change.key.includes(path)) continue;
|
label: label,
|
||||||
const changeValue = game.system.api.documents.DhActiveEffect.getChangeValue(
|
value: bonus?.bonus
|
||||||
this.data,
|
});
|
||||||
change,
|
if (bonus?.dice?.length)
|
||||||
effect.origEffect
|
modifiers.push({
|
||||||
);
|
label: label,
|
||||||
modifiers.push({ label: label, value: changeValue });
|
value: bonus?.dice
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return modifiers;
|
return modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,28 +235,82 @@ export default class DHRoll extends Roll {
|
||||||
static temporaryModifierBuilder(config) {
|
static temporaryModifierBuilder(config) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bonusEffectBuilder() {
|
async function automateHopeFear(config) {
|
||||||
const changeKeys = this.getActionChangeKeys();
|
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
||||||
return (
|
const hopeFearAutomation = automationSettings.hopeFear;
|
||||||
this.options.effects?.reduce((acc, effect) => {
|
if (!config.source?.actor ||
|
||||||
if (effect.changes.some(x => changeKeys.some(key => x.key.includes(key)))) {
|
(game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
|
||||||
acc[effect.id] = {
|
config.actionType === 'reaction' ||
|
||||||
id: effect.id,
|
config.tagTeamSelected ||
|
||||||
name: effect.name,
|
config.skips?.resources)
|
||||||
description: effect.description,
|
return;
|
||||||
changes: effect.changes,
|
const actor = await fromUuid(config.source.actor);
|
||||||
origEffect: effect,
|
let updates = [];
|
||||||
selected: !effect.disabled
|
if (!actor) return;
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
if (config.rerolledRoll) {
|
||||||
}, {}) ?? []
|
if (config.roll.result.duality != config.rerolledRoll.result.duality) {
|
||||||
);
|
const hope = (config.roll.isCritical || config.roll.result.duality === 1 ? 1 : 0)
|
||||||
|
- (config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1 ? 1 : 0);
|
||||||
|
const stress = (config.roll.isCritical ? 1 : 0) - (config.rerolledRoll.isCritical ? 1 : 0);
|
||||||
|
const fear = (config.roll.result.duality === -1 ? 1 : 0)
|
||||||
|
- (config.rerolledRoll.result.duality === -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 });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (config.roll.isCritical || config.roll.result.duality === 1)
|
||||||
|
updates.push({ key: 'hope', value: 1, total: -1, enabled: true });
|
||||||
|
if (config.roll.isCritical)
|
||||||
|
updates.push({ key: 'stress', value: -1, total: 1, enabled: true });
|
||||||
|
if (config.roll.result.duality === -1)
|
||||||
|
updates.push({ key: 'fear', value: 1, total: -1, enabled: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
getActionChangeKeys() {
|
if (updates.length) {
|
||||||
return [];
|
const target = actor.system.partner ?? actor;
|
||||||
|
if (!['dead', 'defeated', 'unconscious'].some(x => actor.statuses.has(x))) {
|
||||||
|
await target.modifyResource(updates);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const registerRollDiceHooks = () => {
|
||||||
|
Hooks.on(`${CONFIG.DH.id}.postRollDuality`, async (config, message) => {
|
||||||
|
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
||||||
|
if (
|
||||||
|
automationSettings.countdownAutomation &&
|
||||||
|
config.actionType !== CONFIG.DH.ITEM.actionTypes.reaction.id &&
|
||||||
|
!config.tagTeamSelected &&
|
||||||
|
!config.skips?.updateCountdowns
|
||||||
|
) {
|
||||||
|
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
||||||
|
|
||||||
|
if (config.roll.result.duality === -1) {
|
||||||
|
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id,
|
||||||
|
CONFIG.DH.GENERAL.countdownProgressionTypes.fear.id);
|
||||||
|
} else {
|
||||||
|
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await automateHopeFear(config);
|
||||||
|
|
||||||
|
if (!config.roll.hasOwnProperty('success') && !config.targets?.length) return;
|
||||||
|
|
||||||
|
const rollResult = config.roll.success || config.targets.some(t => t.hit),
|
||||||
|
looseSpotlight = !rollResult || config.roll.result.duality === -1;
|
||||||
|
|
||||||
|
if (looseSpotlight && game.combat?.active) {
|
||||||
|
const currentCombatant = game.combat.combatants.get(game.combat.current?.combatantId);
|
||||||
|
if (currentCombatant?.actorId == actor.id) ui.combat.setCombatantSpotlight(currentCombatant.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||||
import D20Roll from './d20Roll.mjs';
|
import D20Roll from './d20Roll.mjs';
|
||||||
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||||
import { getDiceSoNicePresets } from '../config/generalConfig.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;
|
||||||
|
|
@ -20,7 +19,7 @@ export default class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
return game.i18n.localize(
|
return game.i18n.localize(
|
||||||
`DAGGERHEART.GENERAL.${this.options?.actionType === 'reaction' ? 'reactionRoll' : 'dualityRoll'}`
|
`DAGGERHEART.GENERAL.${this.options?.actionType === CONFIG.DH.ITEM.actionTypes.reaction.id ? 'reactionRoll' : 'dualityRoll'}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,14 +129,9 @@ export default class DualityRoll extends D20Roll {
|
||||||
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({ faces: 12 });
|
||||||
this.terms[0] = new foundry.dice.terms.Die({
|
|
||||||
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 foundry.dice.terms.Die({ faces: 12 });
|
||||||
faces: this.data.rules.dualityRoll?.defaultFearDice ?? 12
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
applyAdvantage() {
|
applyAdvantage() {
|
||||||
|
|
@ -178,34 +172,6 @@ export default class DualityRoll extends D20Roll {
|
||||||
return modifiers;
|
return modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
getActionChangeKeys() {
|
|
||||||
const changeKeys = new Set([`system.bonuses.roll.${this.options.actionType}`]);
|
|
||||||
|
|
||||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.attack.id) {
|
|
||||||
changeKeys.add(`system.bonuses.roll.${this.options.roll.type}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.attack.id ||
|
|
||||||
(this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id && this.options.hasDamage)
|
|
||||||
) {
|
|
||||||
changeKeys.add(`system.bonuses.roll.attack`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait]) {
|
|
||||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.spellcast.id)
|
|
||||||
changeKeys.add('system.bonuses.roll.trait');
|
|
||||||
}
|
|
||||||
|
|
||||||
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
|
||||||
weapons.forEach(w => {
|
|
||||||
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
|
|
||||||
changeKeys.add(`system.bonuses.roll.${w}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
return changeKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||||
await super.buildEvaluate(roll, config, message);
|
await super.buildEvaluate(roll, config, message);
|
||||||
|
|
||||||
|
|
@ -253,114 +219,6 @@ export default class DualityRoll extends D20Roll {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async buildPost(roll, config, message) {
|
|
||||||
await super.buildPost(roll, config, message);
|
|
||||||
|
|
||||||
await DualityRoll.dualityUpdate(config);
|
|
||||||
await DualityRoll.handleTriggers(roll, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async handleTriggers(roll, config) {
|
|
||||||
if (!config.source?.actor) return;
|
|
||||||
|
|
||||||
const updates = [];
|
|
||||||
const dualityUpdates = await game.system.registeredTriggers.runTrigger(
|
|
||||||
CONFIG.DH.TRIGGER.triggers.dualityRoll.id,
|
|
||||||
roll.data?.parent,
|
|
||||||
roll,
|
|
||||||
roll.data?.parent
|
|
||||||
);
|
|
||||||
if (dualityUpdates?.length) updates.push(...dualityUpdates);
|
|
||||||
|
|
||||||
if (config.roll.result.duality === -1) {
|
|
||||||
const fearUpdates = await game.system.registeredTriggers.runTrigger(
|
|
||||||
CONFIG.DH.TRIGGER.triggers.fearRoll.id,
|
|
||||||
roll.data?.parent,
|
|
||||||
roll,
|
|
||||||
roll.data?.parent
|
|
||||||
);
|
|
||||||
if (fearUpdates?.length) updates.push(...fearUpdates);
|
|
||||||
}
|
|
||||||
|
|
||||||
config.resourceUpdates.addResources(updates);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addDualityResourceUpdates(config) {
|
|
||||||
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
|
||||||
const hopeFearAutomation = automationSettings.hopeFear;
|
|
||||||
if (
|
|
||||||
!config.source?.actor ||
|
|
||||||
(game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
|
|
||||||
config.actionType === 'reaction' ||
|
|
||||||
config.tagTeamSelected ||
|
|
||||||
config.skips?.resources
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
const actor = await fromUuid(config.source.actor);
|
|
||||||
let updates = [];
|
|
||||||
if (!actor) return;
|
|
||||||
|
|
||||||
if (config.rerolledRoll) {
|
|
||||||
if (config.roll.result.duality != config.rerolledRoll.result.duality) {
|
|
||||||
const hope =
|
|
||||||
(config.roll.isCritical || config.roll.result.duality === 1 ? 1 : 0) -
|
|
||||||
(config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1 ? 1 : 0);
|
|
||||||
const stress = (config.roll.isCritical ? 1 : 0) - (config.rerolledRoll.isCritical ? 1 : 0);
|
|
||||||
const fear =
|
|
||||||
(config.roll.result.duality === -1 ? 1 : 0) - (config.rerolledRoll.result.duality === -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 });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (config.roll.isCritical || config.roll.result.duality === 1)
|
|
||||||
updates.push({ key: 'hope', value: 1, total: -1, enabled: true });
|
|
||||||
if (config.roll.isCritical) updates.push({ key: 'stress', value: -1, total: 1, enabled: true });
|
|
||||||
if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1, total: -1, enabled: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updates.length) {
|
|
||||||
// const target = actor.system.partner ?? actor;
|
|
||||||
if (!['dead', 'defeated', 'unconscious'].some(x => actor.statuses.has(x))) {
|
|
||||||
config.resourceUpdates.addResources(updates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async dualityUpdate(config) {
|
|
||||||
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
|
||||||
if (
|
|
||||||
automationSettings.countdownAutomation &&
|
|
||||||
config.actionType !== 'reaction' &&
|
|
||||||
!config.tagTeamSelected &&
|
|
||||||
!config.skips?.updateCountdowns
|
|
||||||
) {
|
|
||||||
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
|
||||||
|
|
||||||
if (config.roll.result.duality === -1) {
|
|
||||||
await updateCountdowns(
|
|
||||||
CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id,
|
|
||||||
CONFIG.DH.GENERAL.countdownProgressionTypes.fear.id
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await DualityRoll.addDualityResourceUpdates(config);
|
|
||||||
|
|
||||||
if (!config.roll.hasOwnProperty('success') && !config.targets?.length) return;
|
|
||||||
|
|
||||||
const rollResult = config.roll.success || config.targets?.some(t => t.hit),
|
|
||||||
looseSpotlight = !rollResult || config.roll.result.duality === -1;
|
|
||||||
|
|
||||||
if (looseSpotlight && game.combat?.active) {
|
|
||||||
const currentCombatant = game.combat.combatants.get(game.combat.current?.combatantId);
|
|
||||||
if (currentCombatant?.actorId == config.data.id) ui.combat.setCombatantSpotlight(currentCombatant.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async reroll(rollString, target, message) {
|
static async reroll(rollString, target, message) {
|
||||||
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollString, evaluated: false });
|
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollString, evaluated: false });
|
||||||
const term = parsedRoll.terms[target.dataset.dieIndex];
|
const term = parsedRoll.terms[target.dataset.dieIndex];
|
||||||
|
|
@ -399,20 +257,13 @@ export default class DualityRoll extends D20Roll {
|
||||||
newRoll.extra = newRoll.extra.slice(2);
|
newRoll.extra = newRoll.extra.slice(2);
|
||||||
|
|
||||||
const tagTeamSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
const tagTeamSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||||
|
Hooks.call(`${CONFIG.DH.id}.postRollDuality`, {
|
||||||
const actor = message.system.source.actor ? await foundry.utils.fromUuid(message.system.source.actor) : null;
|
|
||||||
const config = {
|
|
||||||
source: { actor: message.system.source.actor ?? '' },
|
source: { actor: message.system.source.actor ?? '' },
|
||||||
targets: message.system.targets,
|
targets: message.system.targets,
|
||||||
tagTeamSelected: Object.values(tagTeamSettings.members).some(x => x.messageId === message._id),
|
tagTeamSelected: Object.values(tagTeamSettings.members).some(x => x.messageId === message._id),
|
||||||
roll: newRoll,
|
roll: newRoll,
|
||||||
rerolledRoll: message.system.roll,
|
rerolledRoll: message.system.roll
|
||||||
resourceUpdates: new ResourceUpdateMap(actor)
|
});
|
||||||
};
|
|
||||||
|
|
||||||
await DualityRoll.addDualityResourceUpdates(config);
|
|
||||||
await config.resourceUpdates.updateResources();
|
|
||||||
|
|
||||||
return { newRoll, parsedRoll };
|
return { newRoll, parsedRoll };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ export { default as DhpCombat } from './combat.mjs';
|
||||||
export { default as DHCombatant } from './combatant.mjs';
|
export { default as DHCombatant } from './combatant.mjs';
|
||||||
export { default as DhActiveEffect } from './activeEffect.mjs';
|
export { default as DhActiveEffect } from './activeEffect.mjs';
|
||||||
export { default as DhChatMessage } from './chatMessage.mjs';
|
export { default as DhChatMessage } from './chatMessage.mjs';
|
||||||
export { default as DhScene } from './scene.mjs';
|
|
||||||
export { default as DhToken } from './token.mjs';
|
export { default as DhToken } from './token.mjs';
|
||||||
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
||||||
export { default as DhTemplateManager } from './templateManager.mjs';
|
export { default as DhTemplateManager } from './templateManager.mjs';
|
||||||
export { default as DhTokenManager } from './tokenManager.mjs';
|
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.parent?.type === 'domainCard') {
|
if (this.parent?.type === 'domainCard') {
|
||||||
const isVaultSupressed = this.parent.system.isVaultSupressed;
|
return this.parent.system.inVault;
|
||||||
const domainTouchedSupressed = this.parent.system.isDomainTouchedSuppressed;
|
|
||||||
|
|
||||||
return isVaultSupressed || domainTouchedSupressed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.isSuppressed;
|
return super.isSuppressed;
|
||||||
|
|
@ -109,29 +106,23 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
|
|
||||||
/**@inheritdoc*/
|
/**@inheritdoc*/
|
||||||
static applyField(model, change, field) {
|
static applyField(model, change, field) {
|
||||||
change.value = DhActiveEffect.getChangeValue(model, change, change.effect);
|
const isOriginTarget = change.value.toLowerCase().includes('origin.@');
|
||||||
super.applyField(model, change, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** */
|
|
||||||
static getChangeValue(model, change, effect) {
|
|
||||||
let value = change.value;
|
|
||||||
const isOriginTarget = value.toLowerCase().includes('origin.@');
|
|
||||||
let parseModel = model;
|
let parseModel = model;
|
||||||
if (isOriginTarget && effect.origin) {
|
if (isOriginTarget && change.effect.origin) {
|
||||||
value = change.value.replaceAll(/origin\.@/gi, '@');
|
change.value = change.value.replaceAll(/origin\.@/gi, '@');
|
||||||
try {
|
try {
|
||||||
const originEffect = foundry.utils.fromUuidSync(effect.origin);
|
const effect = foundry.utils.fromUuidSync(change.effect.origin);
|
||||||
const doc =
|
const doc =
|
||||||
originEffect.parent?.parent instanceof game.system.api.documents.DhpActor
|
effect.parent?.parent instanceof game.system.api.documents.DhpActor
|
||||||
? originEffect.parent
|
? effect.parent
|
||||||
: originEffect.parent.parent;
|
: effect.parent.parent;
|
||||||
if (doc) parseModel = doc;
|
if (doc) parseModel = doc;
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const evalValue = this.effectSafeEval(itemAbleRollParse(value, parseModel, effect.parent));
|
const evalValue = this.effectSafeEval(itemAbleRollParse(change.value, parseModel, change.effect.parent));
|
||||||
return evalValue ?? value;
|
change.value = evalValue ?? change.value;
|
||||||
|
super.applyField(model, change, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -203,10 +194,27 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
/* Check for item availability such as in the case of subclass advancement. */
|
/* Preventing subclass features from transferring to actor if they do not have the right subclass advancement */
|
||||||
if (this.parent?.parent?.system?.isItemAvailable) {
|
if (this.parent?.type === 'feature') {
|
||||||
if (!this.parent.parent.system.isItemAvailable(this.parent)) {
|
const origSubclassParent = this.parent.system.originItemType === 'subclass';
|
||||||
this.transfer = false;
|
if (origSubclassParent) {
|
||||||
|
const subclass = this.parent.parent.items.find(
|
||||||
|
x =>
|
||||||
|
x.type === 'subclass' &&
|
||||||
|
x.system.isMulticlass === (this.parent.system.identifier === 'multiclass')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (subclass) {
|
||||||
|
const featureState = subclass.system.featureState;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(this.parent.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization &&
|
||||||
|
featureState < 2) ||
|
||||||
|
(this.parent.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && featureState < 3)
|
||||||
|
) {
|
||||||
|
this.transfer = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
|
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
|
||||||
import { LevelOptionType } from '../data/levelTier.mjs';
|
import { LevelOptionType } from '../data/levelTier.mjs';
|
||||||
import DHFeature from '../data/item/feature.mjs';
|
import DHFeature from '../data/item/feature.mjs';
|
||||||
import { createScrollText, damageKeyToNumber, getDamageKey } from '../helpers/utils.mjs';
|
import { createScrollText, damageKeyToNumber } from '../helpers/utils.mjs';
|
||||||
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
|
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
|
||||||
import { ResourceUpdateMap } from '../data/action/baseAction.mjs';
|
|
||||||
|
|
||||||
export default class DhpActor extends Actor {
|
export default class DhpActor extends Actor {
|
||||||
parties = new Set();
|
parties = new Set();
|
||||||
|
|
@ -74,27 +73,16 @@ export default class DhpActor extends Actor {
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
if ((await super._preCreate(data, options, user)) === false) return false;
|
if ((await super._preCreate(data, options, user)) === false) return false;
|
||||||
const update = {};
|
|
||||||
|
|
||||||
// Set default token size. Done here as we do not want to set a datamodel default, since that would apply the sizing to third party actor modules that aren't set up with the size system.
|
|
||||||
if (this.system.metadata.usesSize && !data.system?.size) {
|
|
||||||
Object.assign(update, {
|
|
||||||
system: {
|
|
||||||
size: CONFIG.DH.ACTOR.tokenSize.medium.id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure prototype token settings
|
// Configure prototype token settings
|
||||||
|
const prototypeToken = {};
|
||||||
if (['character', 'companion', 'party'].includes(this.type))
|
if (['character', 'companion', 'party'].includes(this.type))
|
||||||
Object.assign(update, {
|
Object.assign(prototypeToken, {
|
||||||
prototypeToken: {
|
sight: { enabled: true },
|
||||||
sight: { enabled: true },
|
actorLink: true,
|
||||||
actorLink: true,
|
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
||||||
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
this.updateSource(update);
|
this.updateSource({ prototypeToken });
|
||||||
}
|
}
|
||||||
|
|
||||||
_onUpdate(changes, options, userId) {
|
_onUpdate(changes, options, userId) {
|
||||||
|
|
@ -104,16 +92,6 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preDelete() {
|
|
||||||
if (this.prototypeToken.actorLink) {
|
|
||||||
game.system.registeredTriggers.unregisterItemTriggers(this.items);
|
|
||||||
} else {
|
|
||||||
for (const token of this.getActiveTokens()) {
|
|
||||||
game.system.registeredTriggers.unregisterItemTriggers(token.actor.items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDelete(options, userId) {
|
_onDelete(options, userId) {
|
||||||
super._onDelete(options, userId);
|
super._onDelete(options, userId);
|
||||||
for (const party of this.parties) {
|
for (const party of this.parties) {
|
||||||
|
|
@ -499,7 +477,6 @@ export default class DhpActor extends Actor {
|
||||||
async diceRoll(config) {
|
async diceRoll(config) {
|
||||||
config.source = { ...(config.source ?? {}), actor: this.uuid };
|
config.source = { ...(config.source ?? {}), actor: this.uuid };
|
||||||
config.data = this.getRollData();
|
config.data = this.getRollData();
|
||||||
config.resourceUpdates = new ResourceUpdateMap(this);
|
|
||||||
const rollClass = config.roll.lite ? CONFIG.Dice.daggerheart['DHRoll'] : this.rollClass;
|
const rollClass = config.roll.lite ? CONFIG.Dice.daggerheart['DHRoll'] : this.rollClass;
|
||||||
return await rollClass.build(config);
|
return await rollClass.build(config);
|
||||||
}
|
}
|
||||||
|
|
@ -549,11 +526,7 @@ export default class DhpActor extends Actor {
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
getRollData() {
|
getRollData() {
|
||||||
const rollData = foundry.utils.deepClone(super.getRollData());
|
const rollData = super.getRollData();
|
||||||
/* system gets repeated infinately which causes issues when trying to use the data for document creation */
|
|
||||||
delete rollData.system;
|
|
||||||
|
|
||||||
rollData.id = this.id;
|
|
||||||
rollData.name = this.name;
|
rollData.name = this.name;
|
||||||
rollData.system = this.system.getRollData();
|
rollData.system = this.system.getRollData();
|
||||||
rollData.prof = this.system.proficiency ?? 1;
|
rollData.prof = this.system.proficiency ?? 1;
|
||||||
|
|
@ -641,32 +614,6 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.type === 'adversary') {
|
|
||||||
const reducedSeverity = hpDamage.damageTypes.reduce((value, curr) => {
|
|
||||||
return Math.max(this.system.rules.damageReduction.reduceSeverity[curr], value);
|
|
||||||
}, 0);
|
|
||||||
hpDamage.value = Math.max(hpDamage.value - reducedSeverity, 0);
|
|
||||||
|
|
||||||
if (
|
|
||||||
hpDamage.value &&
|
|
||||||
this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)]
|
|
||||||
) {
|
|
||||||
hpDamage.value -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const results = await game.system.registeredTriggers.runTrigger(
|
|
||||||
CONFIG.DH.TRIGGER.triggers.postDamageReduction.id,
|
|
||||||
this,
|
|
||||||
updates,
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
if (results?.length) {
|
|
||||||
const resourceMap = new ResourceUpdateMap(results[0].originActor);
|
|
||||||
for (var result of results) resourceMap.addResources(result.updates);
|
|
||||||
resourceMap.updateResources();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updates.forEach(
|
updates.forEach(
|
||||||
|
|
@ -818,9 +765,8 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
convertDamageToThreshold(damage) {
|
convertDamageToThreshold(damage) {
|
||||||
const massiveDamageEnabled = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules)
|
const massiveDamageEnabled=game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).massiveDamage.enabled;
|
||||||
.massiveDamage.enabled;
|
if (massiveDamageEnabled && damage >= (this.system.damageThresholds.severe * 2)) {
|
||||||
if (massiveDamageEnabled && damage >= this.system.damageThresholds.severe * 2) {
|
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
return damage >= this.system.damageThresholds.severe ? 3 : damage >= this.system.damageThresholds.major ? 2 : 1;
|
return damage >= this.system.damageThresholds.severe ? 3 : damage >= this.system.damageThresholds.major ? 2 : 1;
|
||||||
|
|
|
||||||
|
|
@ -157,12 +157,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const config = foundry.utils.deepClone(this.system);
|
const config = foundry.utils.deepClone(this.system);
|
||||||
config.event = event;
|
config.event = event;
|
||||||
if (this.system.action) {
|
await this.system.action?.workflow.get('damage')?.execute(config, this._id, true);
|
||||||
const actor = await foundry.utils.fromUuid(config.source.actor);
|
|
||||||
const item = actor?.items.get(config.source.item) ?? null;
|
|
||||||
config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects(actor, item);
|
|
||||||
await this.system.action.workflow.get('damage')?.execute(config, this._id, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
||||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||||
|
|
@ -194,16 +189,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
||||||
|
|
||||||
this.consumeOnSuccess();
|
this.consumeOnSuccess();
|
||||||
if (this.system.action) this.system.action.workflow.get('applyDamage')?.execute(config, targets, true);
|
this.system.action?.workflow.get('applyDamage')?.execute(config, targets, true);
|
||||||
else {
|
|
||||||
for (const target of targets) {
|
|
||||||
const actor = await foundry.utils.fromUuid(target.actorId);
|
|
||||||
if (!actor) continue;
|
|
||||||
|
|
||||||
if (this.system.hasHealing) actor.takeHealing(this.system.damage);
|
|
||||||
else actor.takeDamage(this.system.damage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onRollSave(event) {
|
async onRollSave(event) {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ export default class DhpCombat extends Combat {
|
||||||
...effect,
|
...effect,
|
||||||
name: game.i18n.localize(effect.name),
|
name: game.i18n.localize(effect.name),
|
||||||
description: game.i18n.localize(effect.description),
|
description: game.i18n.localize(effect.description),
|
||||||
effectTargetTypes: grouping.effectTargetTypes ?? [],
|
|
||||||
flags: {
|
flags: {
|
||||||
[`${CONFIG.DH.id}.${CONFIG.DH.FLAGS.combatToggle}`]: {
|
[`${CONFIG.DH.id}.${CONFIG.DH.FLAGS.combatToggle}`]: {
|
||||||
category: toggle.category,
|
category: toggle.category,
|
||||||
|
|
@ -46,7 +45,11 @@ export default class DhpCombat extends Combat {
|
||||||
for (let actor of actors) {
|
for (let actor of actors) {
|
||||||
await actor.createEmbeddedDocuments(
|
await actor.createEmbeddedDocuments(
|
||||||
'ActiveEffect',
|
'ActiveEffect',
|
||||||
effects.filter(x => x.effectTargetTypes.includes(actor.type))
|
effects.map(effect => ({
|
||||||
|
...effect,
|
||||||
|
name: game.i18n.localize(effect.name),
|
||||||
|
description: game.i18n.localize(effect.description)
|
||||||
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
static async createDocuments(sources, operation) {
|
static async createDocuments(sources, operation) {
|
||||||
// Ensure that items being created are valid to the actor its being added to
|
// Ensure that items being created are valid to the actor its being added to
|
||||||
const actor = operation.parent;
|
const actor = operation.parent;
|
||||||
sources = actor?.system?.isItemValid ? sources.filter(s => actor.system.isItemValid(s)) : sources;
|
sources = actor?.system?.isItemValid ? sources.filter((s) => actor.system.isItemValid(s)) : sources;
|
||||||
return super.createDocuments(sources, operation);
|
return super.createDocuments(sources, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,16 +146,6 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
async use(event) {
|
async use(event) {
|
||||||
/* DomainCard check. Can be expanded or made neater */
|
|
||||||
if (this.system.isDomainTouchedSuppressed) {
|
|
||||||
return ui.notifications.warn(
|
|
||||||
game.i18n.format('DAGGERHEART.UI.Notifications.domainTouchRequirement', {
|
|
||||||
nr: this.domainTouched,
|
|
||||||
domain: game.i18n.localize(CONFIG.DH.DOMAIN.allDomains()[this.domain].label)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const actions = new Set(this.system.actionsList);
|
const actions = new Set(this.system.actionsList);
|
||||||
if (actions?.size) {
|
if (actions?.size) {
|
||||||
let action = actions.first();
|
let action = actions.first();
|
||||||
|
|
@ -208,23 +198,4 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
|
|
||||||
cls.create(msg);
|
cls.create(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteTriggers() {
|
|
||||||
const actions = Array.from(this.system.actions ?? []);
|
|
||||||
if (!actions.length) return;
|
|
||||||
|
|
||||||
const triggerKeys = actions.flatMap(action => action.triggers.map(x => x.trigger));
|
|
||||||
|
|
||||||
game.system.registeredTriggers.unregisterTriggers(triggerKeys, this.uuid);
|
|
||||||
|
|
||||||
if (!(this.actor.parent instanceof game.system.api.documents.DhToken)) {
|
|
||||||
for (const token of this.actor.getActiveTokens()) {
|
|
||||||
game.system.registeredTriggers.unregisterTriggers(triggerKeys, `${token.document.uuid}.${this.uuid}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _preDelete() {
|
|
||||||
this.deleteTriggers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
import DHToken from './token.mjs';
|
|
||||||
|
|
||||||
export default class DhScene extends Scene {
|
|
||||||
/** A map of `TokenDocument` IDs embedded in this scene long with new dimensions from actor size-category changes */
|
|
||||||
#sizeSyncBatch = new Map();
|
|
||||||
|
|
||||||
/** Synchronize a token's dimensions with its actor's size category. */
|
|
||||||
syncTokenDimensions(tokenDoc, tokenSize) {
|
|
||||||
if (!tokenDoc.parent?.tokens.has(tokenDoc.id)) return;
|
|
||||||
const prototype = tokenDoc.actor?.prototypeToken ?? tokenDoc;
|
|
||||||
this.#sizeSyncBatch.set(tokenDoc.id, {
|
|
||||||
size: tokenSize,
|
|
||||||
prototypeSize: { width: prototype.width, height: prototype.height },
|
|
||||||
position: { x: tokenDoc.x, y: tokenDoc.y, elevation: tokenDoc.elevation }
|
|
||||||
});
|
|
||||||
this.#processSyncBatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Retrieve size and clear size-sync batch, make updates. */
|
|
||||||
#processSyncBatch = foundry.utils.debounce(() => {
|
|
||||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
|
||||||
const entries = this.#sizeSyncBatch
|
|
||||||
.entries()
|
|
||||||
.toArray()
|
|
||||||
.map(([_id, { size, prototypeSize, position }]) => {
|
|
||||||
const tokenSize = tokenSizes[size];
|
|
||||||
const width = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.width;
|
|
||||||
const height = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.height;
|
|
||||||
const updatedPosition = DHToken.getSnappedPositionInSquareGrid(this.grid, position, width, height);
|
|
||||||
return {
|
|
||||||
_id,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
...updatedPosition
|
|
||||||
};
|
|
||||||
});
|
|
||||||
this.#sizeSyncBatch.clear();
|
|
||||||
this.updateEmbeddedDocuments('Token', entries, { animation: { movementSpeed: 1.5 } });
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
prepareBaseData() {
|
|
||||||
super.prepareBaseData();
|
|
||||||
|
|
||||||
if (this instanceof game.system.api.documents.DhScene) {
|
|
||||||
const system = new game.system.api.data.scenes.DHScene(this.flags.daggerheart);
|
|
||||||
|
|
||||||
// Register this scene to all environements
|
|
||||||
for (const environment of system.sceneEnvironments) {
|
|
||||||
environment.system.scenes?.add(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDelete(options, userId) {
|
|
||||||
super._onDelete(options, userId);
|
|
||||||
|
|
||||||
if (this instanceof game.system.api.documents.DhScene) {
|
|
||||||
const system = new game.system.api.data.scenes.DHScene(this.flags.daggerheart);
|
|
||||||
|
|
||||||
// Clear this scene from all environments that aren't deleted
|
|
||||||
for (const environment of system.sceneEnvironments) {
|
|
||||||
environment?.system?.scenes?.delete(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export default class DHToken extends CONFIG.Token.documentClass {
|
export default class DHToken extends TokenDocument {
|
||||||
/**
|
/**
|
||||||
* Inspect the Actor data model and identify the set of attributes which could be used for a Token Bar.
|
* Inspect the Actor data model and identify the set of attributes which could be used for a Token Bar.
|
||||||
* @param {object} attributes The tracked attributes which can be chosen from
|
* @param {object} attributes The tracked attributes which can be chosen from
|
||||||
|
|
@ -83,7 +83,7 @@ export default class DHToken extends CONFIG.Token.documentClass {
|
||||||
if (combat?.system?.battleToggles?.length) {
|
if (combat?.system?.battleToggles?.length) {
|
||||||
await combat.toggleModifierEffects(
|
await combat.toggleModifierEffects(
|
||||||
true,
|
true,
|
||||||
tokens.filter(x => x.actor).map(x => x.actor)
|
tokens.map(x => x.actor)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
super.createCombatants(tokens, combat ?? {});
|
super.createCombatants(tokens, combat ?? {});
|
||||||
|
|
@ -95,451 +95,9 @@ export default class DHToken extends CONFIG.Token.documentClass {
|
||||||
if (combat?.system?.battleToggles?.length) {
|
if (combat?.system?.battleToggles?.length) {
|
||||||
await combat.toggleModifierEffects(
|
await combat.toggleModifierEffects(
|
||||||
false,
|
false,
|
||||||
tokens.filter(x => x.actor).map(x => x.actor)
|
tokens.map(x => x.actor)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
super.deleteCombatants(tokens, combat ?? {});
|
super.deleteCombatants(tokens, combat ?? {});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**@inheritdoc */
|
|
||||||
static async _preCreateOperation(documents, operation, user) {
|
|
||||||
const allowed = await super._preCreateOperation(documents, operation, user);
|
|
||||||
if (allowed === false) return false;
|
|
||||||
|
|
||||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
|
||||||
for (const document of documents) {
|
|
||||||
const actor = document.actor;
|
|
||||||
if (actor?.system.metadata.usesSize) {
|
|
||||||
const tokenSize = tokenSizes[actor.system.size];
|
|
||||||
if (tokenSize && actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
|
||||||
document.updateSource({
|
|
||||||
width: tokenSize,
|
|
||||||
height: tokenSize
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**@inheritdoc */
|
|
||||||
_onRelatedUpdate(update = {}, operation = {}) {
|
|
||||||
super._onRelatedUpdate(update, operation);
|
|
||||||
|
|
||||||
if (!this.actor?.isOwner) return;
|
|
||||||
|
|
||||||
const updates = Array.isArray(update) ? update : [update];
|
|
||||||
const activeGM = game.users.activeGM; // Let the active GM take care of updates if available
|
|
||||||
for (let update of updates) {
|
|
||||||
if (
|
|
||||||
this.actor.system.metadata.usesSize &&
|
|
||||||
update.system?.size &&
|
|
||||||
activeGM &&
|
|
||||||
game.user.id === activeGM.id
|
|
||||||
) {
|
|
||||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
|
||||||
const tokenSize = tokenSizes[update.system.size];
|
|
||||||
if (tokenSize !== this.width || tokenSize !== this.height) {
|
|
||||||
this.parent?.syncTokenDimensions(this, update.system.size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**@inheritdoc */
|
|
||||||
getSnappedPosition(data = {}) {
|
|
||||||
const grid = this.parent?.grid ?? BaseScene.defaultGrid;
|
|
||||||
const x = data.x ?? this.x;
|
|
||||||
const y = data.y ?? this.y;
|
|
||||||
let elevation = data.elevation ?? this.elevation;
|
|
||||||
const unsnapped = { x, y, elevation };
|
|
||||||
|
|
||||||
// Gridless grid
|
|
||||||
if (grid.isGridless) return unsnapped;
|
|
||||||
|
|
||||||
// Get position and elevation
|
|
||||||
elevation = Math.round(elevation / grid.distance) * grid.distance;
|
|
||||||
|
|
||||||
let width = data.width ?? this.width;
|
|
||||||
let height = data.height ?? this.height;
|
|
||||||
|
|
||||||
if (this.actor?.system.metadata.usesSize) {
|
|
||||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
|
||||||
const tokenSize = tokenSizes[this.actor.system.size];
|
|
||||||
if (tokenSize && this.actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
|
||||||
width = tokenSize ?? width;
|
|
||||||
height = tokenSize ?? height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Round width and height to nearest multiple of 0.5 if not small
|
|
||||||
width = width < 1 ? width : Math.round(width * 2) / 2;
|
|
||||||
height = height < 1 ? height : Math.round(height * 2) / 2;
|
|
||||||
const shape = data.shape ?? this.shape;
|
|
||||||
|
|
||||||
// Square grid
|
|
||||||
let snapped;
|
|
||||||
if (grid.isSquare) snapped = DHToken.getSnappedPositionInSquareGrid(grid, unsnapped, width, height);
|
|
||||||
// Hexagonal grid
|
|
||||||
else snapped = DHToken.getSnappedPositionInHexagonalGrid(grid, unsnapped, width, height, shape);
|
|
||||||
return { x: snapped.x, y: snapped.y, elevation };
|
|
||||||
}
|
|
||||||
|
|
||||||
static getSnappedPositionInSquareGrid(grid, position, width, height) {
|
|
||||||
const M = CONST.GRID_SNAPPING_MODES;
|
|
||||||
// Small tokens snap to any vertex of the subgrid with resolution 4
|
|
||||||
// where the token is fully contained within the grid space
|
|
||||||
const isTiny = (width === 0.5 && height <= 1) || (width <= 1 && height === 0.5);
|
|
||||||
if (isTiny) {
|
|
||||||
let x = position.x / grid.size;
|
|
||||||
let y = position.y / grid.size;
|
|
||||||
if (width === 1) x = Math.round(x);
|
|
||||||
else {
|
|
||||||
x = Math.floor(x * 8);
|
|
||||||
const k = ((x % 8) + 8) % 8;
|
|
||||||
if (k >= 6) x = Math.ceil(x / 8);
|
|
||||||
else if (k === 5) x = Math.floor(x / 8) + 0.5;
|
|
||||||
else x = Math.round(x / 2) / 4;
|
|
||||||
}
|
|
||||||
if (height === 1) y = Math.round(y);
|
|
||||||
else {
|
|
||||||
y = Math.floor(y * 8);
|
|
||||||
const k = ((y % 8) + 8) % 8;
|
|
||||||
if (k >= 6) y = Math.ceil(y / 8);
|
|
||||||
else if (k === 5) y = Math.floor(y / 8) + 0.5;
|
|
||||||
else y = Math.round(y / 2) / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
x *= grid.size;
|
|
||||||
y *= grid.size;
|
|
||||||
|
|
||||||
return { x, y };
|
|
||||||
} else if (width < 1 && height < 1) {
|
|
||||||
// isSmall
|
|
||||||
let xGrid = Math.round(position.x / grid.size);
|
|
||||||
let yGrid = Math.round(position.y / grid.size);
|
|
||||||
|
|
||||||
const x = xGrid * grid.size + grid.size / 2 - (width * grid.size) / 2;
|
|
||||||
const y = yGrid * grid.size + grid.size / 2 - (height * grid.size) / 2;
|
|
||||||
|
|
||||||
return { x, y };
|
|
||||||
}
|
|
||||||
|
|
||||||
const modeX = Number.isInteger(width) ? M.VERTEX : M.VERTEX | M.EDGE_MIDPOINT | M.CENTER;
|
|
||||||
const modeY = Number.isInteger(height) ? M.VERTEX : M.VERTEX | M.EDGE_MIDPOINT | M.CENTER;
|
|
||||||
|
|
||||||
if (modeX === modeY) return grid.getSnappedPoint(position, { mode: modeX });
|
|
||||||
|
|
||||||
return {
|
|
||||||
x: grid.getSnappedPoint(position, { mode: modeX }).x,
|
|
||||||
y: grid.getSnappedPoint(position, { mode: modeY }).y
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//#region CopyPasta for mean private methods that have to be duplicated
|
|
||||||
static getSnappedPositionInHexagonalGrid(grid, position, width, height, shape) {
|
|
||||||
// Hexagonal shape
|
|
||||||
const hexagonalShape = DHToken.#getHexagonalShape(width, height, shape, grid.columns);
|
|
||||||
if (hexagonalShape) {
|
|
||||||
const offsetX = hexagonalShape.anchor.x * grid.sizeX;
|
|
||||||
const offsetY = hexagonalShape.anchor.y * grid.sizeY;
|
|
||||||
position = grid.getCenterPoint({ x: position.x + offsetX, y: position.y + offsetY });
|
|
||||||
position.x -= offsetX;
|
|
||||||
position.y -= offsetY;
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rectagular shape
|
|
||||||
const M = CONST.GRID_SNAPPING_MODES;
|
|
||||||
return grid.getSnappedPoint(position, { mode: M.CENTER | M.VERTEX | M.CORNER | M.SIDE_MIDPOINT });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The cache of hexagonal shapes.
|
|
||||||
* @type {Map<string, DeepReadonly<TokenHexagonalShapeData>>}
|
|
||||||
*/
|
|
||||||
static #hexagonalShapes = new Map();
|
|
||||||
|
|
||||||
static #getHexagonalShape(width, height, shape, columns) {
|
|
||||||
if (!Number.isInteger(width * 2) || !Number.isInteger(height * 2)) return null;
|
|
||||||
|
|
||||||
// TODO: can we set a max of 2^13 on width and height so that we may use an integer key?
|
|
||||||
const key = `${width},${height},${shape}${columns ? 'C' : 'R'}`;
|
|
||||||
let data = DHToken.#hexagonalShapes.get(key);
|
|
||||||
if (data) return data;
|
|
||||||
|
|
||||||
// Hexagon symmetry
|
|
||||||
if (columns) {
|
|
||||||
const rowData = BaseToken.#getHexagonalShape(height, width, shape, false);
|
|
||||||
if (!rowData) return null;
|
|
||||||
|
|
||||||
// Transpose the offsets/points of the shape in row orientation
|
|
||||||
const offsets = { even: [], odd: [] };
|
|
||||||
for (const { i, j } of rowData.offsets.even) offsets.even.push({ i: j, j: i });
|
|
||||||
for (const { i, j } of rowData.offsets.odd) offsets.odd.push({ i: j, j: i });
|
|
||||||
offsets.even.sort(({ i: i0, j: j0 }, { i: i1, j: j1 }) => j0 - j1 || i0 - i1);
|
|
||||||
offsets.odd.sort(({ i: i0, j: j0 }, { i: i1, j: j1 }) => j0 - j1 || i0 - i1);
|
|
||||||
const points = [];
|
|
||||||
for (let i = rowData.points.length; i > 0; i -= 2) {
|
|
||||||
points.push(rowData.points[i - 1], rowData.points[i - 2]);
|
|
||||||
}
|
|
||||||
data = {
|
|
||||||
offsets,
|
|
||||||
points,
|
|
||||||
center: { x: rowData.center.y, y: rowData.center.x },
|
|
||||||
anchor: { x: rowData.anchor.y, y: rowData.anchor.x }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Small hexagon
|
|
||||||
else if (width === 0.5 && height === 0.5) {
|
|
||||||
data = {
|
|
||||||
offsets: { even: [{ i: 0, j: 0 }], odd: [{ i: 0, j: 0 }] },
|
|
||||||
points: [0.25, 0.0, 0.5, 0.125, 0.5, 0.375, 0.25, 0.5, 0.0, 0.375, 0.0, 0.125],
|
|
||||||
center: { x: 0.25, y: 0.25 },
|
|
||||||
anchor: { x: 0.25, y: 0.25 }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normal hexagon
|
|
||||||
else if (width === 1 && height === 1) {
|
|
||||||
data = {
|
|
||||||
offsets: { even: [{ i: 0, j: 0 }], odd: [{ i: 0, j: 0 }] },
|
|
||||||
points: [0.5, 0.0, 1.0, 0.25, 1, 0.75, 0.5, 1.0, 0.0, 0.75, 0.0, 0.25],
|
|
||||||
center: { x: 0.5, y: 0.5 },
|
|
||||||
anchor: { x: 0.5, y: 0.5 }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hexagonal ellipse or trapezoid
|
|
||||||
else if (shape <= CONST.TOKEN_SHAPES.TRAPEZOID_2) {
|
|
||||||
data = DHToken.#createHexagonalEllipseOrTrapezoid(width, height, shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hexagonal rectangle
|
|
||||||
else if (shape <= CONST.TOKEN_SHAPES.RECTANGLE_2) {
|
|
||||||
data = DHToken.#createHexagonalRectangle(width, height, shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache the shape
|
|
||||||
if (data) {
|
|
||||||
foundry.utils.deepFreeze(data);
|
|
||||||
DHToken.#hexagonalShapes.set(key, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static #createHexagonalEllipseOrTrapezoid(width, height, shape) {
|
|
||||||
if (!Number.isInteger(width) || !Number.isInteger(height)) return null;
|
|
||||||
const points = [];
|
|
||||||
let top;
|
|
||||||
let bottom;
|
|
||||||
switch (shape) {
|
|
||||||
case CONST.TOKEN_SHAPES.ELLIPSE_1:
|
|
||||||
if (height >= 2 * width) return null;
|
|
||||||
top = Math.floor(height / 2);
|
|
||||||
bottom = Math.floor((height - 1) / 2);
|
|
||||||
break;
|
|
||||||
case CONST.TOKEN_SHAPES.ELLIPSE_2:
|
|
||||||
if (height >= 2 * width) return null;
|
|
||||||
top = Math.floor((height - 1) / 2);
|
|
||||||
bottom = Math.floor(height / 2);
|
|
||||||
break;
|
|
||||||
case CONST.TOKEN_SHAPES.TRAPEZOID_1:
|
|
||||||
if (height > width) return null;
|
|
||||||
top = height - 1;
|
|
||||||
bottom = 0;
|
|
||||||
break;
|
|
||||||
case CONST.TOKEN_SHAPES.TRAPEZOID_2:
|
|
||||||
if (height > width) return null;
|
|
||||||
top = 0;
|
|
||||||
bottom = height - 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const offsets = { even: [], odd: [] };
|
|
||||||
for (let i = bottom; i > 0; i--) {
|
|
||||||
for (let j = 0; j < width - i; j++) {
|
|
||||||
offsets.even.push({ i: bottom - i, j: j + (((bottom & 1) + i + 1) >> 1) });
|
|
||||||
offsets.odd.push({ i: bottom - i, j: j + (((bottom & 1) + i) >> 1) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i <= top; i++) {
|
|
||||||
for (let j = 0; j < width - i; j++) {
|
|
||||||
offsets.even.push({ i: bottom + i, j: j + (((bottom & 1) + i + 1) >> 1) });
|
|
||||||
offsets.odd.push({ i: bottom + i, j: j + (((bottom & 1) + i) >> 1) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let x = 0.5 * bottom;
|
|
||||||
let y = 0.25;
|
|
||||||
for (let k = width - bottom; k--; ) {
|
|
||||||
points.push(x, y);
|
|
||||||
x += 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
x += 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
}
|
|
||||||
points.push(x, y);
|
|
||||||
for (let k = bottom; k--; ) {
|
|
||||||
y += 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x += 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
y += 0.5;
|
|
||||||
for (let k = top; k--; ) {
|
|
||||||
points.push(x, y);
|
|
||||||
x -= 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
y += 0.5;
|
|
||||||
}
|
|
||||||
for (let k = width - top; k--; ) {
|
|
||||||
points.push(x, y);
|
|
||||||
x -= 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
x -= 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
}
|
|
||||||
points.push(x, y);
|
|
||||||
for (let k = top; k--; ) {
|
|
||||||
y -= 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x -= 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
y -= 0.5;
|
|
||||||
for (let k = bottom; k--; ) {
|
|
||||||
points.push(x, y);
|
|
||||||
x += 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
y -= 0.5;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
offsets,
|
|
||||||
points,
|
|
||||||
// We use the centroid of the polygon for ellipse and trapzoid shapes
|
|
||||||
center: foundry.utils.polygonCentroid(points),
|
|
||||||
anchor: bottom % 2 ? { x: 0.0, y: 0.5 } : { x: 0.5, y: 0.5 }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the row-based hexagonal rectangle given the type, width, and height.
|
|
||||||
* @param {number} width The width of the Token (positive)
|
|
||||||
* @param {number} height The height of the Token (positive)
|
|
||||||
* @param {TokenShapeType} shape The shape type (must be RECTANGLE_1 or RECTANGLE_2)
|
|
||||||
* @returns {TokenHexagonalShapeData|null} The hexagonal shape or null if there is no shape
|
|
||||||
* for the given combination of arguments
|
|
||||||
*/
|
|
||||||
static #createHexagonalRectangle(width, height, shape) {
|
|
||||||
if (width < 1 || !Number.isInteger(height)) return null;
|
|
||||||
if (width === 1 && height > 1) return null;
|
|
||||||
if (!Number.isInteger(width) && height === 1) return null;
|
|
||||||
|
|
||||||
const even = shape === CONST.TOKEN_SHAPES.RECTANGLE_1 || height === 1;
|
|
||||||
const offsets = { even: [], odd: [] };
|
|
||||||
for (let i = 0; i < height; i++) {
|
|
||||||
const j0 = even ? 0 : (i + 1) & 1;
|
|
||||||
const j1 = ((width + (i & 1) * 0.5) | 0) - (even ? i & 1 : 0);
|
|
||||||
for (let j = j0; j < j1; j++) {
|
|
||||||
offsets.even.push({ i, j: j + (i & 1) });
|
|
||||||
offsets.odd.push({ i, j });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let x = even ? 0.0 : 0.5;
|
|
||||||
let y = 0.25;
|
|
||||||
const points = [x, y];
|
|
||||||
while (x + 1 <= width) {
|
|
||||||
x += 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
x += 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
if (x !== width) {
|
|
||||||
y += 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x += 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
while (y + 1.5 <= 0.75 * height) {
|
|
||||||
y += 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x -= 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
y += 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x += 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
if (y + 0.75 < 0.75 * height) {
|
|
||||||
y += 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x -= 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
y += 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
while (x - 1 >= 0) {
|
|
||||||
x -= 0.5;
|
|
||||||
y += 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
x -= 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
if (x !== 0) {
|
|
||||||
y -= 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x -= 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
while (y - 1.5 > 0) {
|
|
||||||
y -= 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x += 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
y -= 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x -= 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
if (y - 0.75 > 0) {
|
|
||||||
y -= 0.5;
|
|
||||||
points.push(x, y);
|
|
||||||
x += 0.5;
|
|
||||||
y -= 0.25;
|
|
||||||
points.push(x, y);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
offsets,
|
|
||||||
points,
|
|
||||||
// We use center of the rectangle (and not the centroid of the polygon) for the rectangle shapes
|
|
||||||
center: {
|
|
||||||
x: width / 2,
|
|
||||||
y: (0.75 * Math.floor(height) + 0.5 * (height % 1) + 0.25) / 2
|
|
||||||
},
|
|
||||||
anchor: even ? { x: 0.5, y: 0.5 } : { x: 0.0, y: 0.5 }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
async _preDelete() {
|
|
||||||
if (this.actor && !this.actor.prototypeToken?.actorLink) {
|
|
||||||
game.system.registeredTriggers.unregisterItemTriggers(this.actor.items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
/**
|
|
||||||
* A singleton class that handles preview tokens.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default class DhTokenManager {
|
|
||||||
#activePreview;
|
|
||||||
#actor;
|
|
||||||
#resolve;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a template preview, deactivating any existing ones.
|
|
||||||
* @param {object} data
|
|
||||||
*/
|
|
||||||
async createPreview(actor, tokenData) {
|
|
||||||
this.#actor = actor;
|
|
||||||
const token = await canvas.tokens._createPreview(
|
|
||||||
{
|
|
||||||
...actor.prototypeToken,
|
|
||||||
displayName: 50,
|
|
||||||
...tokenData
|
|
||||||
},
|
|
||||||
{ renderSheet: false, actor }
|
|
||||||
);
|
|
||||||
|
|
||||||
this.#activePreview = {
|
|
||||||
document: token.document,
|
|
||||||
object: token,
|
|
||||||
origin: { x: token.document.x, y: token.document.y }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.#activePreview.events = {
|
|
||||||
contextmenu: this.#cancelTemplate.bind(this),
|
|
||||||
mousedown: this.#confirmTemplate.bind(this),
|
|
||||||
mousemove: this.#onDragMouseMove.bind(this)
|
|
||||||
};
|
|
||||||
|
|
||||||
canvas.stage.on('mousemove', this.#activePreview.events.mousemove);
|
|
||||||
canvas.stage.on('mousedown', this.#activePreview.events.mousedown);
|
|
||||||
canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Currently intended for using as a preview of where to create a token. (note the flag) */
|
|
||||||
async createPreviewAsync(actor, tokenData = {}) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
this.#resolve = resolve;
|
|
||||||
this.createPreview(actor, { ...tokenData, flags: { daggerheart: { createPlacement: true } } });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the movement of the token preview on mousedrag.
|
|
||||||
* @param {mousemove Event} event
|
|
||||||
*/
|
|
||||||
#onDragMouseMove(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
const { moveTime, object } = this.#activePreview;
|
|
||||||
const update = {};
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
if (now - (moveTime || 0) <= 16) return;
|
|
||||||
this.#activePreview.moveTime = now;
|
|
||||||
|
|
||||||
let cursor = event.getLocalPosition(canvas.templates);
|
|
||||||
|
|
||||||
Object.assign(update, canvas.grid.getTopLeftPoint(cursor));
|
|
||||||
|
|
||||||
object.document.updateSource(update);
|
|
||||||
object.renderFlags.set({ refresh: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the preview token on right-click.
|
|
||||||
* @param {contextmenu Event} event
|
|
||||||
*/
|
|
||||||
#cancelTemplate(_event, resolved) {
|
|
||||||
const { mousemove, mousedown, contextmenu } = this.#activePreview.events;
|
|
||||||
this.#activePreview.object.destroy();
|
|
||||||
|
|
||||||
canvas.stage.off('mousemove', mousemove);
|
|
||||||
canvas.stage.off('mousedown', mousedown);
|
|
||||||
canvas.app.view.removeEventListener('contextmenu', contextmenu);
|
|
||||||
if (this.#resolve && !resolved) this.#resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a real Actor and token at the preview location and cancels the preview.
|
|
||||||
* @param {click Event} event
|
|
||||||
*/
|
|
||||||
async #confirmTemplate(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
this.#cancelTemplate(event, true);
|
|
||||||
|
|
||||||
const actor = this.#actor.inCompendium
|
|
||||||
? await game.system.api.documents.DhpActor.create(this.#actor.toObject())
|
|
||||||
: this.#actor;
|
|
||||||
const tokenData = await actor.getTokenDocument();
|
|
||||||
const result = await canvas.scene.createEmbeddedDocuments('Token', [
|
|
||||||
{ ...tokenData, x: this.#activePreview.document.x, y: this.#activePreview.document.y }
|
|
||||||
]);
|
|
||||||
|
|
||||||
this.#activePreview = undefined;
|
|
||||||
if (this.#resolve && result.length) this.#resolve(result[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -67,7 +67,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
if (item) {
|
if (item) {
|
||||||
const isAction = item instanceof game.system.api.models.actions.actionsTypes.base;
|
const isAction = item instanceof game.system.api.models.actions.actionsTypes.base;
|
||||||
const isEffect = item instanceof ActiveEffect;
|
const isEffect = item instanceof ActiveEffect;
|
||||||
await this.enrichText(item);
|
await this.enrichText(item, isAction || isEffect);
|
||||||
|
|
||||||
const type = isAction ? 'action' : isEffect ? 'effect' : item.type;
|
const type = isAction ? 'action' : isEffect ? 'effect' : item.type;
|
||||||
html = await foundry.applications.handlebars.renderTemplate(
|
html = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
|
@ -202,20 +202,10 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async enrichText(item) {
|
async enrichText(item, flatStructure) {
|
||||||
const { TextEditor } = foundry.applications.ux;
|
const { TextEditor } = foundry.applications.ux;
|
||||||
|
|
||||||
if (item.system?.metadata?.hasDescription) {
|
|
||||||
const enrichedValue =
|
|
||||||
(await item.system?.getEnrichedDescription?.()) ??
|
|
||||||
(await TextEditor.enrichHTML(item.system.description));
|
|
||||||
foundry.utils.setProperty(item, 'system.enrichedDescription', enrichedValue);
|
|
||||||
} else if (item.description) {
|
|
||||||
const enrichedValue = await TextEditor.enrichHTML(item.description);
|
|
||||||
foundry.utils.setProperty(item, 'enrichedDescription', enrichedValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
const enrichPaths = [
|
const enrichPaths = [
|
||||||
|
{ path: flatStructure ? '' : 'system', name: 'description' },
|
||||||
{ path: 'system', name: 'features' },
|
{ path: 'system', name: 'features' },
|
||||||
{ path: 'system', name: 'actions' },
|
{ path: 'system', name: 'actions' },
|
||||||
{ path: 'system', name: 'customActions' }
|
{ path: 'system', name: 'customActions' }
|
||||||
|
|
@ -230,15 +220,12 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
for (const [index, itemValue] of pathValue.entries()) {
|
for (const [index, itemValue] of pathValue.entries()) {
|
||||||
const itemIsAction = itemValue instanceof game.system.api.models.actions.actionsTypes.base;
|
const itemIsAction = itemValue instanceof game.system.api.models.actions.actionsTypes.base;
|
||||||
const value = itemIsAction || !itemValue?.item ? itemValue : itemValue.item;
|
const value = itemIsAction || !itemValue?.item ? itemValue : itemValue.item;
|
||||||
const enrichedValue =
|
const enrichedValue = await TextEditor.enrichHTML(value.system?.description ?? value.description);
|
||||||
(await value.system?.getEnrichedDescription?.()) ??
|
|
||||||
(await TextEditor.enrichHTML(value.system?.description ?? value.description));
|
|
||||||
if (itemIsAction) value.enrichedDescription = enrichedValue;
|
if (itemIsAction) value.enrichedDescription = enrichedValue;
|
||||||
else foundry.utils.setProperty(item, `${basePath}.${index}.enrichedDescription`, enrichedValue);
|
else foundry.utils.setProperty(item, `${basePath}.${index}.enrichedDescription`, enrichedValue);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const enrichedValue =
|
const enrichedValue = await TextEditor.enrichHTML(pathValue);
|
||||||
(await item.system?.getEnrichedDescription?.()) ?? (await TextEditor.enrichHTML(pathValue));
|
|
||||||
foundry.utils.setProperty(
|
foundry.utils.setProperty(
|
||||||
item,
|
item,
|
||||||
`${data.path ? `${data.path}.` : ''}enriched${data.name.capitalize()}`,
|
`${data.path ? `${data.path}.` : ''}enriched${data.name.capitalize()}`,
|
||||||
|
|
@ -275,7 +262,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
const combat = game.combats.get(combatId);
|
const combat = game.combats.get(combatId);
|
||||||
const adversaries =
|
const adversaries =
|
||||||
combat.turns?.filter(x => x.actor?.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? [];
|
combat.turns?.filter(x => x.actor?.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? [];
|
||||||
const characters = combat.turns?.filter(x => !x.isNPC && x.actor) ?? [];
|
const characters = combat.turns?.filter(x => !x.isNPC) ?? [];
|
||||||
|
|
||||||
const nrCharacters = characters.length;
|
const nrCharacters = characters.length;
|
||||||
const currentBP = AdversaryBPPerEncounter(adversaries, characters);
|
const currentBP = AdversaryBPPerEncounter(adversaries, characters);
|
||||||
|
|
@ -285,7 +272,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
);
|
);
|
||||||
|
|
||||||
const categories = combat.combatants.reduce((acc, combatant) => {
|
const categories = combat.combatants.reduce((acc, combatant) => {
|
||||||
if (combatant.actor?.type === 'adversary') {
|
if (combatant.actor.type === 'adversary') {
|
||||||
const keyData = Object.keys(acc).reduce((identifiers, categoryKey) => {
|
const keyData = Object.keys(acc).reduce((identifiers, categoryKey) => {
|
||||||
if (identifiers) return identifiers;
|
if (identifiers) return identifiers;
|
||||||
const category = acc[categoryKey];
|
const category = acc[categoryKey];
|
||||||
|
|
@ -365,7 +352,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
|
|
||||||
await combat.toggleModifierEffects(
|
await combat.toggleModifierEffects(
|
||||||
event.target.checked,
|
event.target.checked,
|
||||||
combat.combatants.filter(x => x.actor?.type === 'adversary').map(x => x.actor),
|
combat.combatants.filter(x => x.actor.type === 'adversary').map(x => x.actor),
|
||||||
category,
|
category,
|
||||||
grouping
|
grouping
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -86,9 +86,9 @@ export const enrichedDualityRoll = async (
|
||||||
const config = {
|
const config = {
|
||||||
event: event ?? {},
|
event: event ?? {},
|
||||||
title: title,
|
title: title,
|
||||||
headerTitle: label,
|
|
||||||
roll: {
|
roll: {
|
||||||
trait: traitValue && target ? traitValue : null,
|
trait: traitValue && target ? traitValue : null,
|
||||||
|
label: label,
|
||||||
difficulty: difficulty,
|
difficulty: difficulty,
|
||||||
advantage,
|
advantage,
|
||||||
type: reaction ? 'reaction' : null
|
type: reaction ? 'reaction' : null
|
||||||
|
|
@ -101,7 +101,7 @@ export const enrichedDualityRoll = async (
|
||||||
await target.diceRoll(config);
|
await target.diceRoll(config);
|
||||||
} else {
|
} else {
|
||||||
// For no target, call DualityRoll directly with basic data
|
// For no target, call DualityRoll directly with basic data
|
||||||
config.data = { experiences: {}, traits: {}, rules: {} };
|
config.data = { experiences: {}, traits: {} };
|
||||||
config.source = { actor: null };
|
config.source = { actor: null };
|
||||||
await CONFIG.Dice.daggerheart.DualityRoll.build(config);
|
await CONFIG.Dice.daggerheart.DualityRoll.build(config);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,6 @@ import { parseInlineParams } from './parser.mjs';
|
||||||
export default function DhLookupEnricher(match, { rollData }) {
|
export default function DhLookupEnricher(match, { rollData }) {
|
||||||
const results = parseInlineParams(match[1], { first: 'formula' });
|
const results = parseInlineParams(match[1], { first: 'formula' });
|
||||||
const element = document.createElement('span');
|
const element = document.createElement('span');
|
||||||
|
element.textContent = Roll.replaceFormulaData(String(results.formula), rollData);
|
||||||
const lookupCommand = match[0];
|
|
||||||
const lookupParam = match[1];
|
|
||||||
const lookupText = Roll.replaceFormulaData(String(results.formula), rollData);
|
|
||||||
element.textContent = lookupText === lookupParam ? lookupCommand : lookupText;
|
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {}
|
||||||
spellcheck='false'
|
spellcheck='false'
|
||||||
tabIndex="${this.settings.a11y.focusableTags ? 0 : -1}"
|
tabIndex="${this.settings.a11y.focusableTags ? 0 : -1}"
|
||||||
class="${this.settings.classNames.tag} ${tagData.class ? tagData.class : ''}"
|
class="${this.settings.classNames.tag} ${tagData.class ? tagData.class : ''}"
|
||||||
data-tooltip="${tagData.description ? htmlToText(tagData.description) : tagData.name}"
|
data-tooltip="${tagData.description || tagData.name}"
|
||||||
${this.getAttributes(tagData)}>
|
${this.getAttributes(tagData)}>
|
||||||
<x class="${this.settings.classNames.tagX}" role='button' aria-label='remove tag'></x>
|
<x class="${this.settings.classNames.tagX}" role='button' aria-label='remove tag'></x>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -198,7 +198,7 @@ foundry.dice.terms.Die.prototype.selfCorrecting = function (modifier) {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDamageKey = damage => {
|
export const getDamageKey = damage => {
|
||||||
return ['none', 'minor', 'major', 'severe', 'massive', 'any'][damage];
|
return ['none', 'minor', 'major', 'severe', 'massive','any'][damage];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDamageLabel = damage => {
|
export const getDamageLabel = damage => {
|
||||||
|
|
@ -474,10 +474,3 @@ export async function getCritDamageBonus(formula) {
|
||||||
const critRoll = new Roll(formula);
|
const critRoll = new Roll(formula);
|
||||||
return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.number, 0);
|
return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.number, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function htmlToText(html) {
|
|
||||||
var tempDivElement = document.createElement('div');
|
|
||||||
tempDivElement.innerHTML = html;
|
|
||||||
|
|
||||||
return tempDivElement.textContent || tempDivElement.innerText || '';
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/actionTypes/effect.hbs',
|
'systems/daggerheart/templates/actionTypes/effect.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/beastform.hbs',
|
'systems/daggerheart/templates/actionTypes/beastform.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/countdown.hbs',
|
'systems/daggerheart/templates/actionTypes/countdown.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/summon.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/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',
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue