diff --git a/lang/en.json b/lang/en.json index bb611d84..7ebe4262 100755 --- a/lang/en.json +++ b/lang/en.json @@ -236,8 +236,6 @@ }, "defaultHopeDice": "Default Hope Dice", "defaultFearDice": "Default Fear Dice", - "defaultAdvantageDice": "Default Advantage Dice", - "defaultDisadvantageDice": "Default Disadvantage Dice", "disadvantageSources": { "label": "Disadvantage Sources", "hint": "Add single words or short text as reminders and hints of what a character has disadvantage on." @@ -3212,6 +3210,8 @@ "companion": "Level {level} - {partner}", "companionNoPartner": "No Partner", "duplicateToNewTier": "Duplicate to New Tier", + "activateParty": "Make Activate Party", + "partyIsActive": "Active", "createAdversary": "Create Adversary", "pickTierTitle": "Pick a new tier for this adversary" }, diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 987cfeb1..64fa168a 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -123,10 +123,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.advantage = this.config.roll?.advantage; context.disadvantage = this.config.roll?.disadvantage; context.diceOptions = CONFIG.DH.GENERAL.diceTypes; - context.diceFaces = CONFIG.DH.GENERAL.dieFaces.reduce((acc, face) => { - acc[face] = `d${face}`; - return acc; - }, {}); context.isLite = this.config.roll?.lite; context.extraFormula = this.config.extraFormula; context.formula = this.roll.constructFormula(this.config); @@ -157,10 +153,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses); if (rest.roll?.dice) { Object.entries(rest.roll.dice).forEach(([key, value]) => { - if(key === 'advantageFaces') - this.roll[key] = Number.parseInt(value); - else - this.roll[key] = value; + this.roll[key] = value; }); } if (rest.hasOwnProperty('trait')) { @@ -180,16 +173,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.disadvantage = advantage === -1; this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage; - - if(this.config.roll.advantage === 1 && this.config.data.rules.roll.defaultAdvantageDice) { - const faces = Number.parseInt(this.config.data.rules.roll.defaultAdvantageDice); - this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces; - } - else if(this.config.roll.advantage === -1 && this.config.data.rules.roll.defaultDisadvantageDice) { - const faces = Number.parseInt(this.config.data.rules.roll.defaultDisadvantageDice); - this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces; - } - this.render(); } diff --git a/module/applications/sidebar/tabs/actorDirectory.mjs b/module/applications/sidebar/tabs/actorDirectory.mjs index e9484553..a3c37acc 100644 --- a/module/applications/sidebar/tabs/actorDirectory.mjs +++ b/module/applications/sidebar/tabs/actorDirectory.mjs @@ -46,50 +46,71 @@ export default class DhActorDirectory extends foundry.applications.sidebar.tabs. _getEntryContextOptions() { const options = super._getEntryContextOptions(); - options.push({ - name: 'DAGGERHEART.UI.Sidebar.actorDirectory.duplicateToNewTier', - icon: ``, - condition: li => { - const actor = game.actors.get(li.dataset.entryId); - return actor?.type === 'adversary' && actor.system.type !== 'social'; - }, - callback: async li => { - const actor = game.actors.get(li.dataset.entryId); - if (!actor) throw new Error('Unexpected missing actor'); + options.push( + { + name: 'DAGGERHEART.UI.Sidebar.actorDirectory.duplicateToNewTier', + icon: ``, + condition: li => { + const actor = game.actors.get(li.dataset.entryId); + return actor?.type === 'adversary' && actor.system.type !== 'social'; + }, + callback: async li => { + const actor = game.actors.get(li.dataset.entryId); + if (!actor) throw new Error('Unexpected missing actor'); - const tiers = [1, 2, 3, 4].filter(t => t !== actor.system.tier); - const content = document.createElement('div'); - const select = document.createElement('select'); - select.name = 'tier'; - select.append( - ...tiers.map(t => { - const option = document.createElement('option'); - option.value = t; - option.textContent = game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${t}`); - return option; - }) - ); - content.append(select); + const tiers = [1, 2, 3, 4].filter(t => t !== actor.system.tier); + const content = document.createElement('div'); + const select = document.createElement('select'); + select.name = 'tier'; + select.append( + ...tiers.map(t => { + const option = document.createElement('option'); + option.value = t; + option.textContent = game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${t}`); + return option; + }) + ); + content.append(select); - const tier = await foundry.applications.api.Dialog.input({ - classes: ['dh-style', 'dialog'], - window: { title: 'DAGGERHEART.UI.Sidebar.actorDirectory.pickTierTitle' }, - content, - ok: { - label: 'DAGGERHEART.UI.Sidebar.actorDirectory.createAdversary', - callback: (event, button, dialog) => Number(button.form.elements.tier.value) + const tier = await foundry.applications.api.Dialog.input({ + classes: ['dh-style', 'dialog'], + window: { title: 'DAGGERHEART.UI.Sidebar.actorDirectory.pickTierTitle' }, + content, + ok: { + label: 'DAGGERHEART.UI.Sidebar.actorDirectory.createAdversary', + callback: (event, button, dialog) => Number(button.form.elements.tier.value) + } + }); + + if (tier === actor.system.tier) { + ui.notifications.warn('This actor is already at this tier'); + } else if (tier) { + const source = actor.system.adjustForTier(tier); + await Actor.create(source); + ui.notifications.info(`Tier ${tier} ${actor.name} created`); } - }); + } + }, + { + name: 'DAGGERHEART.UI.Sidebar.actorDirectory.activateParty', + icon: ``, + condition: li => { + const actor = game.actors.get(li.dataset.entryId); + return actor && actor.type === 'party' && !actor.system.active; + }, + callback: async li => { + const actor = game.actors.get(li.dataset.entryId); + if (!actor) throw new Error('Unexpected missing actor'); - if (tier === actor.system.tier) { - ui.notifications.warn('This actor is already at this tier'); - } else if (tier) { - const source = actor.system.adjustForTier(tier); - await Actor.create(source); - ui.notifications.info(`Tier ${tier} ${actor.name} created`); + const currentActiveParty = game.actors.find(x => x.type === 'party' && x.system.active); + if (currentActiveParty) + await currentActiveParty.update({ 'system.active': false }); + + await actor.update({ 'system.active': true }); + ui.actors.render(); } } - }); + ); return options; } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index e7f1bf8c..61338344 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -289,23 +289,7 @@ export default class DhCharacter extends DhCreature { guaranteedCritical: new fields.BooleanField({ label: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.label', hint: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.hint' - }), - defaultAdvantageDice: new fields.NumberField({ - nullable: true, - required: true, - integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: null, - label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice' - }), - defaultDisadvantageDice: new fields.NumberField({ - nullable: true, - required: true, - integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: null, - label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice' - }), + }) }) }) }; diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index 6e87708a..81d0aa8a 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -61,24 +61,6 @@ export default class DhCompanion extends DhCreature { initial: false, label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.vulnerable' }) - }), - roll: new fields.SchemaField({ - defaultAdvantageDice: new fields.NumberField({ - nullable: true, - required: true, - integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: null, - label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice' - }), - defaultDisadvantageDice: new fields.NumberField({ - nullable: true, - required: true, - integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: null, - label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice' - }), }) }), attack: new ActionField({ diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs index ec1beb99..d35261fc 100644 --- a/module/data/actor/party.mjs +++ b/module/data/actor/party.mjs @@ -9,6 +9,7 @@ export default class DhParty extends BaseDataActor { const fields = foundry.data.fields; return { ...super.defineSchema(), + active: new fields.BooleanField(), partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }, { prune: true }), notes: new fields.HTMLField(), gold: new fields.SchemaField({ @@ -44,6 +45,15 @@ export default class DhParty extends BaseDataActor { } } + /**@inheritdoc */ + async _preCreate(data, options, user) { + const allowed = await super._preCreate(data, options, user); + if (allowed === false) return; + + if (!game.actors.some(x => x.type === 'party' && x.active)) + await this.updateSource({ active: true }); + } + _onDelete(options, userId) { super._onDelete(options, userId); diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs index 36ea1010..914a1d69 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -1,3 +1,4 @@ +import { itemAbleRollParse } from '../../../helpers/utils.mjs'; import FormulaField from '../formulaField.mjs'; const fields = foundry.data.fields; @@ -36,13 +37,12 @@ export default class DHSummonField extends fields.ArrayField { 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 roll = new Roll(itemAbleRollParse(summon.count, this.actor, this.item)); + await roll.evaluate(); + const count = roll.total; + if (!roll.isDeterministic && game.modules.get('dice-so-nice')?.active) + rolls.push(roll); + const actor = await 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. */ diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 2448a16d..f9a06d37 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -3,6 +3,7 @@ import D20Roll from './d20Roll.mjs'; import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs'; export default class DualityRoll extends D20Roll { + _advantageFaces = 6; _advantageNumber = 1; _rallyIndex; @@ -10,9 +11,6 @@ export default class DualityRoll extends D20Roll { super(formula, data, options); this.rallyChoices = this.setRallyChoices(); this.guaranteedCritical = options.guaranteedCritical; - - const advantageFaces = data.rules?.roll?.defaultAdvantageDice ? Number.parseInt(data.rules.roll.defaultAdvantageDice) : 6 - this.advantageFaces = Number.isNaN(advantageFaces) ? 6 : advantageFaces; } static messageType = 'dualityRoll'; @@ -53,6 +51,14 @@ export default class DualityRoll extends D20Roll { return this.dice[2] instanceof game.system.api.dice.diceTypes.DisadvantageDie ? this.dice[2] : null; } + get advantageFaces() { + return this._advantageFaces; + } + + set advantageFaces(faces) { + this._advantageFaces = this.getFaces(faces); + } + get advantageNumber() { return this._advantageNumber; } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 3e3dfde4..48f2f171 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -600,6 +600,7 @@ export default class DhpActor extends Actor { rollData.system = this.system.getRollData(); rollData.prof = this.system.proficiency ?? 1; rollData.cast = this.system.spellcastModifier ?? 1; + return rollData; } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 131f94b7..96b2b130 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -189,7 +189,15 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue // Fix on Foundry native formula replacement for DH const nativeReplaceFormulaData = Roll.replaceFormulaData; -Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false } = {}) { +Roll.replaceFormulaData = function (formula, baseData = {}, { missing, warn = false } = {}) { + /* Inserting global data */ + const data = { + ...baseData, + partySize: + !game.actors ? 0 : + game.actors.find(x => x.type === 'party' && x.system.active)?.system.partyMembers.length ?? 0, + }; + const terms = Object.keys(CONFIG.DH.GENERAL.multiplierTypes).map(type => { return { term: type, default: 1 }; }); diff --git a/src/packs/adversaries/adversary_Fallen_Warlord__Undefeated_Champion_RXkZTwBRi4dJ3JE5.json b/src/packs/adversaries/adversary_Fallen_Warlord__Undefeated_Champion_RXkZTwBRi4dJ3JE5.json index 5ad77ab0..63d9ca2c 100644 --- a/src/packs/adversaries/adversary_Fallen_Warlord__Undefeated_Champion_RXkZTwBRi4dJ3JE5.json +++ b/src/packs/adversaries/adversary_Fallen_Warlord__Undefeated_Champion_RXkZTwBRi4dJ3JE5.json @@ -174,12 +174,9 @@ "src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg", "anchorX": 0.5, "anchorY": 0.5, - "offsetX": 0, - "offsetY": 0, "fit": "contain", "scaleX": 1, "scaleY": 1, - "rotation": 0, "tint": "#ffffff", "alphaThreshold": 0.75 }, @@ -230,7 +227,7 @@ "saturation": 0, "contrast": 0 }, - "detectionModes": [], + "detectionModes": {}, "occludable": { "radius": 0 }, @@ -256,7 +253,8 @@ "flags": {}, "randomImg": false, "appendNumber": false, - "prependAdjective": false + "prependAdjective": false, + "depth": 1 }, "items": [ { @@ -496,34 +494,42 @@ "description": "
Spend a Fear to summon a number of @UUID[Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9]{Fallen Shock Troops} equal to twice the number of PCs. The Shock Troops appear at Far range.
", "resource": null, "actions": { - "hGMzqw00JTlYfHYy": { - "type": "effect", - "_id": "hGMzqw00JTlYfHYy", + "SrU7qbh8LcOgfozT": { + "type": "summon", + "_id": "SrU7qbh8LcOgfozT", "systemPath": "actions", + "baseAction": false, "description": "", "chatDisplay": true, + "originItem": { + "type": "itemCollection" + }, "actionType": "action", + "triggers": [], "cost": [ { "scalable": false, "key": "fear", "value": 1, - "step": null + "itemId": null, + "step": null, + "consumeOnSuccess": false } ], "uses": { "value": null, "max": "", - "recovery": null - }, - "effects": [], - "target": { - "type": "self", - "amount": null + "recovery": null, + "consumeOnSuccess": false }, + "summon": [ + { + "actorUUID": "Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9", + "count": "@partySize*2" + } + ], "name": "Spend Fear", - "img": "icons/magic/death/undead-skeleton-worn-blue.webp", - "range": "" + "range": "far" } }, "originItemType": null, diff --git a/src/packs/environments/environment_Abandoned_Grove_pGEdzdLkqYtBhxnG.json b/src/packs/environments/environment_Abandoned_Grove_pGEdzdLkqYtBhxnG.json index 75fc932f..0cab16a2 100644 --- a/src/packs/environments/environment_Abandoned_Grove_pGEdzdLkqYtBhxnG.json +++ b/src/packs/environments/environment_Abandoned_Grove_pGEdzdLkqYtBhxnG.json @@ -52,12 +52,9 @@ "src": "systems/daggerheart/assets/icons/documents/actors/forest.svg", "anchorX": 0.5, "anchorY": 0.5, - "offsetX": 0, - "offsetY": 0, "fit": "contain", "scaleX": 1, "scaleY": 1, - "rotation": 0, "tint": "#ffffff", "alphaThreshold": 0.75 }, @@ -108,7 +105,7 @@ "saturation": 0, "contrast": 0 }, - "detectionModes": [], + "detectionModes": {}, "occludable": { "radius": 0 }, @@ -134,7 +131,8 @@ "flags": {}, "randomImg": false, "appendNumber": false, - "prependAdjective": false + "prependAdjective": false, + "depth": 1 }, "items": [ { @@ -323,7 +321,42 @@ "system": { "description": "A @UUID[Compendium.daggerheart.adversaries.Actor.8yUj2Mzvnifhxegm]{Young Dryad}, two @UUID[Compendium.daggerheart.adversaries.Actor.VtFBt9XBE0WrGGxP]{Sylvan Soldiers}, and a number of @UUID[Compendium.daggerheart.adversaries.Actor.G62k4oSkhkoXEs2D]{Minor Treants} equal to the number of PCs appear to confront the party for their intrusion.
What are the grove guardians concealing? What threat to the forest could the PCs confront to appease the Dryad?