diff --git a/lang/en.json b/lang/en.json index a87c3ce9..544b8683 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1801,6 +1801,7 @@ "plural": "Costs" }, "Damage": { + "massive": "Massive", "severe": "Severe", "major": "Major", "minor": "Minor", @@ -2440,9 +2441,12 @@ }, "currency": { "title": "Currency Overrides", + "changeIcon": "Change Currency Icon", "currencyName": "Currency Name", "coinName": "Coin Name", "handfulName": "Handful Name", + "iconName": "Icon Name", + "iconNameHint": "Icons are from fontawesome", "bagName": "Bag Name", "chestName": "Chest Name" }, @@ -2504,6 +2508,11 @@ "hint": "Apply variant rules from the Daggerheart system", "name": "Variant Rules", "actionTokens": "Action Tokens" + }, + "SpotlightRequestQueue": { + "name": "Spotlight Request Queue", + "label": "Spotlight Request Queue", + "hint": "Adds more structure to spotlight requests by ordering them from oldest to newest" } }, "Resources": { @@ -2517,6 +2526,10 @@ "actionTokens": { "enabled": { "label": "Enabled" }, "tokens": { "label": "Tokens" } + }, + "massiveDamage":{ + "title":"Massive Damage", + "enabled": { "label": "Enabled" } } } }, diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index 8e566106..6d36a2b3 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -32,6 +32,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli icon: 'fa-solid fa-gears' }, actions: { + editCurrencyIcon: this.changeCurrencyIcon, addItem: this.addItem, editItem: this.editItem, removeItem: this.removeItem, @@ -115,6 +116,45 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli this.render(); } + static async changeCurrencyIcon(_, target) { + const type = target.dataset.currency; + const currentIcon = this.settings.currency[type].icon; + const icon = await foundry.applications.api.DialogV2.input({ + classes: ['daggerheart', 'dh-style', 'change-currency-icon'], + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/settings/homebrew-settings/change-currency-icon.hbs', + { currentIcon } + ), + window: { + title: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.currency.changeIcon'), + icon: 'fa-solid fa-coins' + }, + render: (_, dialog) => { + const icon = dialog.element.querySelector('.displayed-icon i'); + const input = dialog.element.querySelector('input'); + const reset = dialog.element.querySelector('button[data-action=reset]'); + input.addEventListener('input', () => { + icon.classList.value = input.value; + }); + reset.addEventListener('click', () => { + const currencyField = DhHomebrew.schema.fields.currency.fields[type]; + const initial = currencyField.fields.icon.getInitialValue(); + input.value = icon.classList.value = initial; + }); + }, + ok: { + callback: (_, button) => button.form.elements.icon.value + } + }); + + if (icon !== null) { + await this.settings.updateSource({ + [`currency.${type}.icon`]: icon + }); + this.render(); + } + } + static async addItem(_, target) { const { type } = target.dataset; if (['shortRest', 'longRest'].includes(type)) { diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 016cff13..953a0cf6 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -685,8 +685,6 @@ export default class CharacterSheet extends DHBaseActorSheet { ability: abilityLabel }) }); - - if (result) game.system.api.fields.ActionFields.CostField.execute.call(this, result); } //TODO: redo toggleEquipItem method diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 871ff173..47dfe500 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -245,7 +245,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo }); if (!result) return; - await game.system.api.fields.ActionFields.CostField.execute.call({ actor }, result); const newMessageData = foundry.utils.deepClone(message.system); foundry.utils.setProperty(newMessageData, `${path}.result`, result.roll); diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs index 23bede60..babc4a65 100644 --- a/module/applications/ui/combatTracker.mjs +++ b/module/applications/ui/combatTracker.mjs @@ -5,8 +5,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C actions: { requestSpotlight: this.requestSpotlight, toggleSpotlight: this.toggleSpotlight, - setActionTokens: this.setActionTokens, - openCountdowns: this.openCountdowns + setActionTokens: this.setActionTokens } }; @@ -57,21 +56,26 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C const adversaries = context.turns?.filter(x => x.isNPC) ?? []; const characters = context.turns?.filter(x => !x.isNPC) ?? []; + const spotlightQueueEnabled = game.settings.get( + CONFIG.DH.id, + CONFIG.DH.SETTINGS.gameSettings.SpotlightRequestQueue + ); const spotlightRequests = characters - ?.filter(x => !x.isNPC) + ?.filter(x => !x.isNPC && spotlightQueueEnabled) .filter(x => x.system.spotlight.requestOrderIndex > 0) .sort((a, b) => { const valueA = a.system.spotlight.requestOrderIndex; const valueB = b.system.spotlight.requestOrderIndex; - return valueA - valueB; }); Object.assign(context, { actionTokens: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).actionTokens, adversaries, - characters: characters?.filter(x => !x.isNPC).filter(x => x.system.spotlight.requestOrderIndex == 0), + characters: characters + ?.filter(x => !x.isNPC) + .filter(x => !spotlightQueueEnabled || x.system.spotlight.requestOrderIndex == 0), spotlightRequests }); } @@ -161,9 +165,13 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C if (this.viewed.turn !== toggleTurn) { const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns; - await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id); if (combatant.actor.type === 'character') { - await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id); + await updateCountdowns( + CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id, + CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id + ); + } else { + await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id); } const autoPoints = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).actionPoints; diff --git a/module/applications/ui/countdowns.mjs b/module/applications/ui/countdowns.mjs index 96315b17..42920a4a 100644 --- a/module/applications/ui/countdowns.mjs +++ b/module/applications/ui/countdowns.mjs @@ -245,14 +245,20 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return super.close(options); } - static async updateCountdowns(progressType) { + /** + * Sends updates of the countdowns to the GM player. Since this is asynchronous, be sure to + * update all the countdowns at the same time. + * + * @param {...any} progressTypes Countdowns to be updated + */ + static async updateCountdowns(...progressTypes) { const { countdownAutomation } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); if (!countdownAutomation) return; const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); const updatedCountdowns = Object.keys(countdownSetting.countdowns).reduce((acc, key) => { const countdown = countdownSetting.countdowns[key]; - if (countdown.progress.type === progressType && countdown.progress.current > 0) { + if (progressTypes.indexOf(countdown.progress.type) !== -1 && countdown.progress.current > 0) { acc.push(key); } @@ -260,7 +266,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application }, []); const countdownData = countdownSetting.toObject(); - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, { + const settings = { ...countdownData, countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => { const countdown = foundry.utils.deepClone(countdownData.countdowns[key]); @@ -271,14 +277,12 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application acc[key] = countdown; return acc; }, {}) + }; + await emitAsGM(GMUpdateEvent.UpdateCountdowns, + DhCountdowns.gmSetSetting.bind(settings), + settings, null, { + refreshType: RefreshType.Countdown }); - - const data = { refreshType: RefreshType.Countdown }; - await game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data - }); - Hooks.callAll(socketEvent.Refresh, data); } async _onRender(context, options) { diff --git a/module/config/encounterConfig.mjs b/module/config/encounterConfig.mjs index 0269b5c1..7565652f 100644 --- a/module/config/encounterConfig.mjs +++ b/module/config/encounterConfig.mjs @@ -84,6 +84,7 @@ export const BPModifiers = { increaseDamage: { sort: 2, description: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.description', + effectTargetTypes: ['adversary'], effects: [ { name: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.name', diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index aea9bc48..3d993949 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -28,7 +28,8 @@ export const gameSettings = { LevelTiers: 'LevelTiers', Countdowns: 'Countdowns', LastMigrationVersion: 'LastMigrationVersion', - TagTeamRoll: 'TagTeamRoll' + TagTeamRoll: 'TagTeamRoll', + SpotlightRequestQueue: 'SpotlightRequestQueue', }; export const actionAutomationChoices = { diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 93de0a2d..ae085064 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -193,8 +193,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel async use(event) { 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); if (!config) return; @@ -211,6 +209,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return; + if (this.chatDisplay) await this.toChat(); + return config; } diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index ead3c962..6f280cbd 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -1,14 +1,15 @@ import { defaultRestOptions } from '../../config/generalConfig.mjs'; import { ActionsField } from '../fields/actionField.mjs'; -const currencyField = (initial, label) => +const currencyField = (initial, label, icon) => new foundry.data.fields.SchemaField({ enabled: new foundry.data.fields.BooleanField({ required: true, initial: true }), label: new foundry.data.fields.StringField({ required: true, initial, label - }) + }), + icon: new foundry.data.fields.StringField({ required: true, nullable: false, blank: true, initial: icon }) }); export default class DhHomebrew extends foundry.abstract.DataModel { @@ -45,10 +46,22 @@ export default class DhHomebrew extends foundry.abstract.DataModel { initial: 'Gold', label: 'DAGGERHEART.SETTINGS.Homebrew.currency.currencyName' }), - coins: currencyField('Coins', 'DAGGERHEART.SETTINGS.Homebrew.currency.coinName'), - handfuls: currencyField('Handfuls', 'DAGGERHEART.SETTINGS.Homebrew.currency.handfulName'), - bags: currencyField('Bags', 'DAGGERHEART.SETTINGS.Homebrew.currency.bagName'), - chests: currencyField('Chests', 'DAGGERHEART.SETTINGS.Homebrew.currency.chestName') + coins: currencyField( + 'Coins', + 'DAGGERHEART.SETTINGS.Homebrew.currency.coinName', + 'fa-solid fa-coin-front' + ), + handfuls: currencyField( + 'Handfuls', + 'DAGGERHEART.SETTINGS.Homebrew.currency.handfulName', + 'fa-solid fa-coins' + ), + bags: currencyField('Bags', 'DAGGERHEART.SETTINGS.Homebrew.currency.bagName', 'fa-solid fa-sack'), + chests: currencyField( + 'Chests', + 'DAGGERHEART.SETTINGS.Homebrew.currency.chestName', + 'fa-solid fa-treasure-chest' + ) }), restMoves: new fields.SchemaField({ longRest: new fields.SchemaField({ @@ -139,22 +152,10 @@ export default class DhHomebrew extends foundry.abstract.DataModel { /** @inheritDoc */ _initializeSource(source, options = {}) { source = super._initializeSource(source, options); - source.currency.coins = { - enabled: source.currency.coins.enabled ?? true, - label: source.currency.coins.label || source.currency.coins - }; - source.currency.handfuls = { - enabled: source.currency.handfuls.enabled ?? true, - label: source.currency.handfuls.label || source.currency.handfuls - }; - source.currency.bags = { - enabled: source.currency.bags.enabled ?? true, - label: source.currency.bags.label || source.currency.bags - }; - source.currency.chests = { - enabled: source.currency.chests.enabled ?? true, - label: source.currency.chests.label || source.currency.chests - }; + for (const type of ['coins', 'handfuls', 'bags', 'chests']) { + const initial = this.schema.fields.currency.fields[type].getInitialValue(); + source.currency[type] = foundry.utils.mergeObject(initial, source.currency[type], { inplace: false }); + } return source; } } diff --git a/module/data/settings/VariantRules.mjs b/module/data/settings/VariantRules.mjs index ad7d707a..41c63be2 100644 --- a/module/data/settings/VariantRules.mjs +++ b/module/data/settings/VariantRules.mjs @@ -39,6 +39,13 @@ export default class DhVariantRules extends foundry.abstract.DataModel { label: 'DAGGERHEART.CONFIG.Range.close.name' }), far: new fields.NumberField({ required: true, initial: 60, label: 'DAGGERHEART.CONFIG.Range.far.name' }) + }), + massiveDamage: new fields.SchemaField({ + enabled: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.SETTINGS.VariantRules.FIELDS.massiveDamage.enabled.label' + }) }) }; } diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index e6856dab..ce39ed6a 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -237,6 +237,51 @@ export default class DHRoll extends Roll { } } +async function automateHopeFear(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))) { + 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); @@ -247,45 +292,16 @@ export const registerRollDiceHooks = () => { !config.skips?.updateCountdowns ) { const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns; - await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id); if (config.roll.result.duality === -1) { - await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.fear.id); + await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id, + CONFIG.DH.GENERAL.countdownProgressionTypes.fear.id); + } else { + await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id); } } - 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.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 (config.rerolledRoll) { - if (config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1) - updates.push({ key: 'hope', value: -1, total: 1, enabled: true }); - if (config.rerolledRoll.isCritical) updates.push({ key: 'stress', value: -1, total: 1, enabled: true }); - if (config.rerolledRoll.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))) { - if (config.rerolledRoll) target.modifyResource(updates); - else config.costs = [...(config.costs ?? []), ...updates]; - } - } + await automateHopeFear(config); if (!config.roll.hasOwnProperty('success') && !config.targets?.length) return; @@ -296,7 +312,5 @@ export const registerRollDiceHooks = () => { const currentCombatant = game.combat.combatants.get(game.combat.current?.combatantId); if (currentCombatant?.actorId == actor.id) ui.combat.setCombatantSpotlight(currentCombatant.id); } - - return; }); }; diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 813c913b..59cb6e02 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -262,8 +262,7 @@ export default class DualityRoll extends D20Roll { targets: message.system.targets, tagTeamSelected: Object.values(tagTeamSettings.members).some(x => x.messageId === message._id), roll: newRoll, - rerolledRoll: - newRoll.result.duality !== message.system.roll.result.duality ? message.system.roll : undefined + rerolledRoll: message.system.roll }); return { newRoll, parsedRoll }; } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 8999fdd8..35ab5cc6 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -526,7 +526,7 @@ export default class DhpActor extends Actor { /**@inheritdoc */ getRollData() { - const rollData = super.getRollData(); + const rollData = super.getRollData().clone(); rollData.name = this.name; rollData.system = this.system.getRollData(); rollData.prof = this.system.proficiency ?? 1; @@ -679,6 +679,10 @@ export default class DhpActor extends Actor { return updates; } + /** + * Resources are modified asynchronously, so be careful not to update the same resource in + * quick succession. + */ async modifyResource(resources) { if (!resources?.length) return; @@ -761,6 +765,10 @@ export default class DhpActor extends Actor { } convertDamageToThreshold(damage) { + const massiveDamageEnabled=game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).massiveDamage.enabled; + if (massiveDamageEnabled && damage >= (this.system.damageThresholds.severe * 2)) { + return 4; + } return damage >= this.system.damageThresholds.severe ? 3 : damage >= this.system.damageThresholds.major ? 2 : 1; } diff --git a/module/documents/combat.mjs b/module/documents/combat.mjs index 01cbee61..20996b77 100644 --- a/module/documents/combat.mjs +++ b/module/documents/combat.mjs @@ -28,6 +28,7 @@ export default class DhpCombat extends Combat { ...effect, name: game.i18n.localize(effect.name), description: game.i18n.localize(effect.description), + effectTargetTypes: grouping.effectTargetTypes ?? [], flags: { [`${CONFIG.DH.id}.${CONFIG.DH.FLAGS.combatToggle}`]: { category: toggle.category, @@ -45,11 +46,7 @@ export default class DhpCombat extends Combat { for (let actor of actors) { await actor.createEmbeddedDocuments( 'ActiveEffect', - effects.map(effect => ({ - ...effect, - name: game.i18n.localize(effect.name), - description: game.i18n.localize(effect.description) - })) + effects.filter(x => x.effectTargetTypes.includes(actor.type)) ); } } else { diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index 1784698a..b0a107b9 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -75,7 +75,8 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti { item: item, description: item.system?.enrichedDescription ?? item.enrichedDescription, - config: CONFIG.DH + config: CONFIG.DH, + allDomains: CONFIG.DH.DOMAIN.allDomains() } ); diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 39d5298d..396ed2fa 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -198,7 +198,7 @@ foundry.dice.terms.Die.prototype.selfCorrecting = function (modifier) { }; export const getDamageKey = damage => { - return ['none', 'minor', 'major', 'severe', 'any'][damage]; + return ['none', 'minor', 'major', 'severe', 'massive','any'][damage]; }; export const getDamageLabel = damage => { @@ -211,7 +211,8 @@ export const damageKeyToNumber = key => { minor: 1, major: 2, severe: 3, - any: 4 + massive: 4, + any: 5 }[key]; }; diff --git a/module/systemRegistration/settings.mjs b/module/systemRegistration/settings.mjs index 6954730f..d08d65d1 100644 --- a/module/systemRegistration/settings.mjs +++ b/module/systemRegistration/settings.mjs @@ -13,6 +13,16 @@ export const registerDHSettings = () => { registerMenuSettings(); registerMenus(); registerNonConfigSettings(); + + game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightRequestQueue, { + name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.SpotlightRequestQueue.name'), + label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.SpotlightRequestQueue.label'), + hint: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.SpotlightRequestQueue.hint'), + scope: 'world', + config: true, + type: Boolean, + onChange: () => ui.combat.render(), + }) }; const registerMenuSettings = () => { diff --git a/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json b/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json index 95e6c243..0e3a89c6 100644 --- a/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json +++ b/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json @@ -427,7 +427,8 @@ } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", diff --git a/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json b/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json index d77d3379..cf6583e4 100644 --- a/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json +++ b/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json @@ -307,7 +307,8 @@ } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", @@ -319,7 +320,7 @@ "trait": null, "difficulty": null, "bonus": null, - "advState": "neutral", + "advState": "advantage", "diceRolling": { "multiplier": "prof", "flatMultiplier": 1, diff --git a/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json b/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json index 3ccac5dd..fd73ee36 100644 --- a/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json +++ b/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json @@ -39,7 +39,8 @@ "experiences": { "7GpgCWSe6hNwnOO7": { "name": "Throw", - "value": 2 + "value": 2, + "description": "" } }, "bonuses": { @@ -105,7 +106,8 @@ }, "base": false } - ] + ], + "direct": true }, "name": "Club", "img": "icons/weapons/clubs/club-banded-barbed-black.webp", @@ -337,10 +339,11 @@ { "value": { "custom": { - "enabled": false + "enabled": false, + "formula": "" }, "flatMultiplier": 1, - "dice": "d12", + "dice": "d10", "bonus": 2, "multiplier": "flat" }, @@ -356,12 +359,14 @@ "dice": "d6", "bonus": null, "custom": { - "enabled": false + "enabled": false, + "formula": "" } } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", @@ -528,7 +533,8 @@ } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", diff --git a/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json b/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json index 08b22a0b..7ea12036 100644 --- a/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json +++ b/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json @@ -107,7 +107,8 @@ }, "base": false } - ] + ], + "direct": true }, "img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp", "type": "attack", diff --git a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json index 13b9d624..72ec986d 100644 --- a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json +++ b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json @@ -108,7 +108,8 @@ }, "base": false } - ] + ], + "direct": true }, "type": "attack", "chatDisplay": false @@ -358,7 +359,8 @@ } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", diff --git a/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json b/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json index e3b34aea..a900aa7b 100644 --- a/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json +++ b/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json @@ -377,7 +377,8 @@ } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", diff --git a/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json b/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json index 943559e1..557ef607 100644 --- a/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json +++ b/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json @@ -233,7 +233,89 @@ "system": { "description": "
The Assassin deals direct damage while they’re Hidden.
", "resource": null, - "actions": {}, + "actions": { + "xFBE0jLf96fbCY7K": { + "type": "attack", + "_id": "xFBE0jLf96fbCY7K", + "systemPath": "actions", + "baseAction": false, + "description": "The Assassin deals direct damage while they’re Hidden.
", + "chatDisplay": true, + "originItem": { + "type": "itemCollection" + }, + "actionType": "action", + "cost": [], + "uses": { + "value": null, + "max": "", + "recovery": null, + "consumeOnSuccess": false + }, + "damage": { + "parts": [ + { + "value": { + "custom": { + "enabled": false, + "formula": "" + }, + "flatMultiplier": 2, + "dice": "d10", + "bonus": 2, + "multiplier": "flat" + }, + "applyTo": "hitPoints", + "type": [ + "physical" + ], + "base": false, + "resultBased": false, + "valueAlt": { + "multiplier": "prof", + "flatMultiplier": 1, + "dice": "d6", + "bonus": null, + "custom": { + "enabled": false, + "formula": "" + } + } + } + ], + "includeBase": false, + "direct": true + }, + "target": { + "type": "any", + "amount": null + }, + "effects": [], + "roll": { + "type": "attack", + "trait": null, + "difficulty": null, + "bonus": null, + "advState": "neutral", + "diceRolling": { + "multiplier": "prof", + "flatMultiplier": 1, + "dice": "d6", + "compare": null, + "treshold": null + }, + "useDefault": false + }, + "save": { + "trait": null, + "difficulty": null, + "damageMod": "none" + }, + "name": "Hidden attack", + "img": "icons/magic/perception/silhouette-stealth-shadow.webp", + "range": "close" + } + }, "originItemType": null, "originId": null }, diff --git a/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json b/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json index 10f48d17..570db804 100644 --- a/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json +++ b/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json @@ -478,7 +478,8 @@ } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", diff --git a/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json b/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json index 74d7e2c3..721d8973 100644 --- a/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json +++ b/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json @@ -340,6 +340,87 @@ "name": "Curse", "img": "icons/magic/unholy/hand-marked-pink.webp", "range": "veryClose" + }, + "zLKfwa8a2YBRLKAF": { + "type": "attack", + "_id": "zLKfwa8a2YBRLKAF", + "systemPath": "actions", + "baseAction": false, + "description": "Attacks made by the Hunter against a Deathlocked target deal direct damage.
", + "chatDisplay": true, + "originItem": { + "type": "itemCollection" + }, + "actionType": "action", + "cost": [], + "uses": { + "value": null, + "max": "", + "recovery": null, + "consumeOnSuccess": false + }, + "damage": { + "parts": [ + { + "value": { + "custom": { + "enabled": false, + "formula": "" + }, + "flatMultiplier": 2, + "dice": "d12", + "bonus": 1, + "multiplier": "flat" + }, + "applyTo": "hitPoints", + "type": [ + "physical" + ], + "base": false, + "resultBased": false, + "valueAlt": { + "multiplier": "prof", + "flatMultiplier": 1, + "dice": "d6", + "bonus": null, + "custom": { + "enabled": false, + "formula": "" + } + } + } + ], + "includeBase": false, + "direct": true + }, + "target": { + "type": "any", + "amount": null + }, + "effects": [], + "roll": { + "type": "attack", + "trait": null, + "difficulty": null, + "bonus": null, + "advState": "neutral", + "diceRolling": { + "multiplier": "prof", + "flatMultiplier": 1, + "dice": "d6", + "compare": null, + "treshold": null + }, + "useDefault": false + }, + "save": { + "trait": null, + "difficulty": null, + "damageMod": "none" + }, + "name": "Deathlocked attack", + "img": "icons/magic/unholy/hand-marked-pink.webp", + "range": "veryClose" } }, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json b/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json index d5f30dda..2a753812 100644 --- a/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json +++ b/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json @@ -312,9 +312,10 @@ { "value": { "custom": { - "enabled": false + "enabled": false, + "formula": "" }, - "flatMultiplier": 1, + "flatMultiplier": 2, "dice": "d6", "bonus": 8, "multiplier": "flat" @@ -331,12 +332,14 @@ "dice": "d6", "bonus": null, "custom": { - "enabled": false + "enabled": false, + "formula": "" } } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", diff --git a/src/packs/adversaries/adversary_Volcanic_Dragon__Molten_Scourge_eArAPuB38CNR0ZIM.json b/src/packs/adversaries/adversary_Volcanic_Dragon__Molten_Scourge_eArAPuB38CNR0ZIM.json index 056bf848..d04dab50 100644 --- a/src/packs/adversaries/adversary_Volcanic_Dragon__Molten_Scourge_eArAPuB38CNR0ZIM.json +++ b/src/packs/adversaries/adversary_Volcanic_Dragon__Molten_Scourge_eArAPuB38CNR0ZIM.json @@ -804,7 +804,8 @@ } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", diff --git a/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json b/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json index 2fca6775..ededde93 100644 --- a/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json +++ b/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json @@ -208,13 +208,13 @@ }, "changes": [ { - "key": "system.resistance.magical.reduction", + "key": "system.rules.damageReduction.reduceSeverity.magical", "mode": 2, "value": "1", "priority": null }, { - "key": "system.resistance.magical.reduction", + "key": "system.rules.damageReduction.reduceSeverity.physical", "mode": 2, "value": "1", "priority": null diff --git a/src/packs/environments/environment_Burning_Heart_of_the_Woods_oY69NN4rYxoRE4hl.json b/src/packs/environments/environment_Burning_Heart_of_the_Woods_oY69NN4rYxoRE4hl.json index 81ce16f9..39070236 100644 --- a/src/packs/environments/environment_Burning_Heart_of_the_Woods_oY69NN4rYxoRE4hl.json +++ b/src/packs/environments/environment_Burning_Heart_of_the_Woods_oY69NN4rYxoRE4hl.json @@ -457,7 +457,8 @@ } } ], - "includeBase": false + "includeBase": false, + "direct": true }, "target": { "type": "any", diff --git a/src/packs/items/armors/armor_Elundrian_Chain_Mail_Q6LxmtFetDDkoZVZ.json b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json similarity index 97% rename from src/packs/items/armors/armor_Elundrian_Chain_Mail_Q6LxmtFetDDkoZVZ.json rename to src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json index d8b938fd..abf81dae 100644 --- a/src/packs/items/armors/armor_Elundrian_Chain_Mail_Q6LxmtFetDDkoZVZ.json +++ b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json @@ -1,6 +1,6 @@ { "folder": "hLn0v6ov6KuFgptu", - "name": "Elundrian Chain Mail", + "name": "Elundrian Chain Armor", "type": "armor", "_id": "Q6LxmtFetDDkoZVZ", "img": "icons/equipment/chest/breastplate-sculpted-green.webp", diff --git a/src/packs/subclasses/feature_Iron_Will_7AVRNyBcd1Nffjtn.json b/src/packs/subclasses/feature_Iron_Will_7AVRNyBcd1Nffjtn.json index eda72fd7..bea62e2b 100644 --- a/src/packs/subclasses/feature_Iron_Will_7AVRNyBcd1Nffjtn.json +++ b/src/packs/subclasses/feature_Iron_Will_7AVRNyBcd1Nffjtn.json @@ -32,7 +32,7 @@ "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "changes": [ { - "key": "system.rules.damageReduction.maxArmorMarked.stressExtra", + "key": "system.rules.damageReduction.maxArmorMarked.value", "mode": 2, "value": "1", "priority": null diff --git a/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json b/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json index f7157194..5b86e348 100644 --- a/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json +++ b/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json @@ -69,12 +69,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Spend Hope", "img": "icons/magic/light/projectile-beam-yellow.webp", - "range": "" + "range": "self" } }, "originItemType": null, diff --git a/styles/less/dialog/index.less b/styles/less/dialog/index.less index 91b2846b..b5ed8764 100644 --- a/styles/less/dialog/index.less +++ b/styles/less/dialog/index.less @@ -37,3 +37,5 @@ @import './image-select/sheet.less'; @import './item-transfer/sheet.less'; + +@import './settings/change-currency-icon.less'; \ No newline at end of file diff --git a/styles/less/dialog/settings/change-currency-icon.less b/styles/less/dialog/settings/change-currency-icon.less new file mode 100644 index 00000000..61870a4b --- /dev/null +++ b/styles/less/dialog/settings/change-currency-icon.less @@ -0,0 +1,13 @@ +.application.daggerheart.dialog.dh-style.change-currency-icon { + .displayed-icon { + height: 2.5rem; + text-align: center; + font-size: 2.5rem; + margin-bottom: 1.25rem; + } + .input-row { + display: flex; + gap: 4px; + align-items: center; + } +} \ No newline at end of file diff --git a/styles/less/hud/token-hud/token-hud.less b/styles/less/hud/token-hud/token-hud.less index ac269172..46003975 100644 --- a/styles/less/hud/token-hud/token-hud.less +++ b/styles/less/hud/token-hud/token-hud.less @@ -56,7 +56,7 @@ .effect-locked { position: absolute; bottom: 2px; - right: 2px; + left: 11.5px; font-size: 12px; color: @golden; filter: drop-shadow(0 0 3px black); diff --git a/styles/less/utils/fonts.less b/styles/less/utils/fonts.less index 7d84366a..5c1e597a 100755 --- a/styles/less/utils/fonts.less +++ b/styles/less/utils/fonts.less @@ -2,14 +2,14 @@ @import './mixin.less'; :root { - --font-title: 'Cinzel Decorative'; - --font-subtitle: 'Cinzel'; - --font-body: 'Montserrat'; + --dh-font-title: 'Cinzel Decorative'; + --dh-font-subtitle: 'Cinzel'; + --dh-font-body: 'Montserrat'; } -@font-title: ~"var(--font-title, 'Cinzel Decorative'), serif"; -@font-subtitle: ~"var(--font-subtitle, 'Cinzel'), serif"; -@font-body: ~"var(--font-body, 'Montserrat'), sans-serif"; +@font-title: ~"var(--dh-font-title, 'Cinzel Decorative'), serif"; +@font-subtitle: ~"var(--dh-font-subtitle, 'Cinzel'), serif"; +@font-body: ~"var(--dh-font-body, 'Montserrat'), sans-serif"; .dh-style { .dh-typography(); diff --git a/templates/hud/tokenHUD.hbs b/templates/hud/tokenHUD.hbs index 09259d4a..f079e5d9 100644 --- a/templates/hud/tokenHUD.hbs +++ b/templates/hud/tokenHUD.hbs @@ -46,8 +46,12 @@