diff --git a/daggerheart.mjs b/daggerheart.mjs index 2eb5109f..67d1c24f 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -43,7 +43,7 @@ CONFIG.Item.dataModels = models.items.config; CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect; CONFIG.ActiveEffect.dataModels = models.activeEffects.config; -CONFIG.ActiveEffect.changeTypes = { ...CONFIG.ActiveEffect.changeTypes, ...models.activeEffects.changeEffects }; +CONFIG.ActiveEffect.changeTypes = { ...CONFIG.ActiveEffect.changeTypes, ...models.activeEffects.changeTypes }; CONFIG.Combat.documentClass = documents.DhpCombat; CONFIG.Combat.dataModels = { base: models.DhCombat }; @@ -217,6 +217,17 @@ Hooks.once('init', () => { label: sheetLabel('DOCUMENT.ActiveEffect') } ); + DocumentSheetConfig.registerSheet( + CONFIG.ActiveEffect.documentClass, + SYSTEM.id, + applications.sheetConfigs.ArmorActiveEffectConfig, + { + types: ['armor'], + makeDefault: true, + label: () => + `${game.i18n.localize('TYPES.ActiveEffect.armor')} ${game.i18n.localize('DAGGERHEART.GENERAL.effect')}` + } + ); game.socket.on(`system.${SYSTEM.id}`, socketRegistration.handleSocketEvent); @@ -270,6 +281,7 @@ Hooks.on('setup', () => { ...damageThresholds, 'proficiency', 'evasion', + 'armorScore', 'scars', 'levelData.level.current' ] @@ -403,17 +415,6 @@ Hooks.on('chatMessage', (_, message) => { } }); -Hooks.on(CONFIG.DH.HOOKS.hooksConfig.tagTeamStart, async data => { - if (data.openForAllPlayers && data.partyId) { - const party = game.actors.get(data.partyId); - if (!party) return; - - const dialog = new game.system.api.applications.dialogs.TagTeamDialog(party); - dialog.tabGroups.application = 'tagTeamRoll'; - await dialog.render({ force: true }); - } -}); - const updateActorsRangeDependentEffects = async token => { const rangeMeasurement = game.settings.get( CONFIG.DH.id, diff --git a/lang/en.json b/lang/en.json index f7d49a04..fa3099df 100755 --- a/lang/en.json +++ b/lang/en.json @@ -16,7 +16,8 @@ "ActiveEffect": { "base": "Standard", "beastform": "Beastform", - "horde": "Horde" + "horde": "Horde", + "armor": "Armor" }, "Actor": { "character": "Character", @@ -677,35 +678,16 @@ }, "TagTeamSelect": { "title": "Tag Team Roll", - "FIELDS": { - "initiator": { - "memberId": { "label": "Initiating Character" }, - "cost": { "label": "Initiation Cost" } - } - }, "leaderTitle": "Initiating Character", "membersTitle": "Participants", "partyTeam": "Party Team", "hopeCost": "Hope Cost", "initiatingCharacter": "Initiating Character", - "selectParticipants": "Select the two participants", - "startTagTeamRoll": "Start Tag Team Roll", - "openDialogForAll": "Open Dialog For All", - "rollType": "Roll Type", - "makeYourRoll": "Make your roll", - "cancelTagTeamRoll": "Cancel Tag Team Roll", - "finishTagTeamRoll": "Finish Tag Team Roll", "linkMessageHint": "Make a roll from your character sheet to link it to the Tag Team Roll", "damageNotRolled": "Damage not rolled in chat message yet", "insufficientHope": "The initiating character doesn't have enough hope", - "createTagTeam": "Create Tag Team Roll", - "chatMessageRollTitle": "Roll", - "cancelConfirmTitle": "Cancel Tag Team Roll", - "cancelConfirmText": "Are you sure you want to cancel the Tag Team Roll? This will close it for all other players too.", - "hints": { - "completeRolls": "Set up and complete the rolls for the characters", - "selectRoll": "Select which roll value to be used for the Tag Team" - } + "createTagTeam": "Create TagTeam Roll", + "chatMessageRollTitle": "Roll" }, "TokenConfig": { "actorSizeUsed": "Actor size is set, determining the dimensions" @@ -796,8 +778,8 @@ }, "ArmorInteraction": { "none": { "label": "Ignores Armor" }, - "active": { "label": "Active w/ Armor" }, - "inactive": { "label": "Inactive w/ Armor" } + "active": { "label": "Only Active With Armor" }, + "inactive": { "label": "Only Active Without Armor" } }, "ArmorFeature": { "burning": { @@ -1254,11 +1236,6 @@ "selectType": "Select Action Type", "selectAction": "Action Selection" }, - "TagTeamRollTypes": { - "trait": "Trait", - "ability": "Ability", - "damageAbility": "Damage Ability" - }, "TargetTypes": { "any": "Any", "friendly": "Friendly", @@ -1887,17 +1864,6 @@ "name": "Healing Roll" } }, - "ChangeTypes": { - "armor": { - "newArmorEffect": "Armor Effect", - "FIELDS": { - "armorInteraction": { - "label": "Armor Interaction", - "hint": "Does the character wearing armor suppress this effect?" - } - } - } - }, "Duration": { "passive": "Passive", "temporary": "Temporary" @@ -1919,13 +1885,18 @@ "Attachments": { "attachHint": "Drop items here to attach them", "transferHint": "If checked, this effect will be applied to any actor that owns this Effect's parent Item. The effect is always applied if this Item is attached to another one." + }, + "Armor": { + "newArmorEffect": "Armor Effect", + "FIELDS": { + "armorInteraction": { + "label": "Armor Interaction", + "hint": "Does the character wearing armor suppress this effect?" + } + } } }, "GENERAL": { - "Ability": { - "single": "Ability", - "plural": "Abilities" - }, "Action": { "single": "Action", "plural": "Actions" @@ -2380,10 +2351,6 @@ "rerolled": "Rerolled", "rerollThing": "Reroll {thing}", "resource": "Resource", - "result": { - "single": "Result", - "plural": "Results" - }, "roll": "Roll", "rollAll": "Roll All", "rollDamage": "Roll Damage", @@ -3112,7 +3079,10 @@ "tokenActorsMissing": "[{names}] missing Actors", "domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used", "knowTheTide": "Know The Tide gained a token", - "lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}" + "lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}", + "cannotAlterArmorEffectChanges": "You cannot alter the changes length of an armor effect", + "cannotAlterArmorEffectType": "You cannot alter the type of armor effect changes", + "cannotAlterArmorEffectKey": "You cannot alter they key of armor effect changes" }, "Progress": { "migrationLabel": "Performing system migration. Please wait and do not close Foundry." diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 0fdb1896..8e79ba58 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -35,6 +35,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio updateIsAdvantage: this.updateIsAdvantage, selectExperience: this.selectExperience, toggleReaction: this.toggleReaction, + toggleTagTeamRoll: this.toggleTagTeamRoll, toggleSelectedEffect: this.toggleSelectedEffect, submitRoll: this.submitRoll }, @@ -132,6 +133,12 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.reactionOverride = this.reactionOverride; } + const tagTeamSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); + if (this.actor && tagTeamSetting.members[this.actor.id] && !this.config.skips?.createMessage) { + context.activeTagTeamRoll = true; + context.tagTeamSelected = this.config.tagTeamSelected; + } + return context; } @@ -208,6 +215,11 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio } } + static toggleTagTeamRoll() { + this.config.tagTeamSelected = !this.config.tagTeamSelected; + this.render(); + } + static toggleSelectedEffect(_event, button) { this.selectedEffects[button.dataset.key].selected = !this.selectedEffects[button.dataset.key].selected; this.render(); diff --git a/module/applications/dialogs/damageReductionDialog.mjs b/module/applications/dialogs/damageReductionDialog.mjs index 5e858e4b..6aeb2cf9 100644 --- a/module/applications/dialogs/damageReductionDialog.mjs +++ b/module/applications/dialogs/damageReductionDialog.mjs @@ -21,12 +21,13 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap this.rulesDefault ); - const allArmorEffects = Array.from(actor.allApplicableEffects()).filter(x => x.system.armorData); - const orderedArmorEffects = game.system.api.data.activeEffects.changeTypes.armor.orderEffectsForAutoChange( + const allArmorEffects = Array.from(actor.allApplicableEffects()).filter(x => x.type === 'armor'); + const orderedArmorEffects = game.system.api.data.activeEffects.ArmorEffect.orderEffectsForAutoChange( allArmorEffects, true ); const armor = orderedArmorEffects.reduce((acc, effect) => { + if (effect.type !== 'armor') return acc; const { value, max } = effect.system.armorData; acc.push({ effect: effect, diff --git a/module/applications/dialogs/rerollDamageDialog.mjs b/module/applications/dialogs/rerollDamageDialog.mjs index b821bd24..e1b75eb7 100644 --- a/module/applications/dialogs/rerollDamageDialog.mjs +++ b/module/applications/dialogs/rerollDamageDialog.mjs @@ -1,3 +1,5 @@ +import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; + const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; export default class RerollDamageDialog extends HandlebarsApplicationMixin(ApplicationV2) { @@ -121,8 +123,16 @@ export default class RerollDamageDialog extends HandlebarsApplicationMixin(Appli return acc; }, {}) }; - await this.message.update(update); + + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll }); + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { + refreshType: RefreshType.TagTeamRoll + } + }); + await this.close(); } diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index ddaabcb4..c28b773c 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -1,7 +1,5 @@ -import { MemberData } from '../../data/tagTeamData.mjs'; import { getCritDamageBonus } from '../../helpers/utils.mjs'; -import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; -import Party from '../sheets/actors/party.mjs'; +import { GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -9,23 +7,15 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio constructor(party) { super(); + this.data = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); this.party = party; - this.partyMembers = party.system.partyMembers - .filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type)) - .map(member => ({ - ...member.toObject(), - uuid: member.uuid, - id: member.id, - selected: false - })); - this.intiator = null; - this.openForAllPlayers = true; - this.tabGroups.application = Object.keys(party.system.tagTeam.members).length - ? 'tagTeamRoll' - : 'initialization'; - - Hooks.on(socketEvent.Refresh, this.tagTeamRefresh.bind()); + this.setupHooks = Hooks.on(socketEvent.Refresh, ({ refreshType }) => { + if (refreshType === RefreshType.TagTeamRoll) { + this.data = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); + this.render(); + } + }); } get title() { @@ -34,639 +24,324 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio static DEFAULT_OPTIONS = { tag: 'form', - id: 'TagTeamDialog', classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'tag-team-dialog'], position: { width: 550, height: 'auto' }, actions: { - toggleSelectMember: TagTeamDialog.#toggleSelectMember, - startTagTeamRoll: TagTeamDialog.#startTagTeamRoll, - makeRoll: TagTeamDialog.#makeRoll, - removeRoll: TagTeamDialog.#removeRoll, - rerollDice: TagTeamDialog.#rerollDice, - makeDamageRoll: TagTeamDialog.#makeDamageRoll, - removeDamageRoll: TagTeamDialog.#removeDamageRoll, - rerollDamageDice: TagTeamDialog.#rerollDamageDice, - selectRoll: TagTeamDialog.#selectRoll, - cancelRoll: TagTeamDialog.#onCancelRoll, - finishRoll: TagTeamDialog.#finishRoll + removeMember: TagTeamDialog.#removeMember, + unlinkMessage: TagTeamDialog.#unlinkMessage, + selectMessage: TagTeamDialog.#selectMessage, + createTagTeam: TagTeamDialog.#createTagTeam }, form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } }; static PARTS = { - initialization: { - id: 'initialization', - template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/initialization.hbs' - }, - tagTeamRoll: { - id: 'tagTeamRoll', - template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/tagTeamRoll.hbs' - } - }; - - /** @inheritdoc */ - static TABS = { application: { - tabs: [{ id: 'initialization' }, { id: 'tagTeamRoll' }] + id: 'tag-team-dialog', + template: 'systems/daggerheart/templates/dialogs/tagTeamDialog.hbs' } }; - _attachPartListeners(partId, htmlElement, options) { - super._attachPartListeners(partId, htmlElement, options); - - for (const element of htmlElement.querySelectorAll('.roll-type-select')) - element.addEventListener('change', this.updateRollType.bind(this)); - } - async _prepareContext(_options) { const context = await super._prepareContext(_options); - context.isEditable = this.getIsEditable(); + context.hopeCost = this.hopeCost; + context.data = this.data; + + context.memberOptions = this.party.filter(c => !this.data.members[c.id]); + context.selectedCharacterOptions = this.party.filter(c => this.data.members[c.id]); + + context.members = Object.keys(this.data.members).map(id => { + const roll = this.data.members[id].messageId ? game.messages.get(this.data.members[id].messageId) : null; + + context.usesDamage = + context.usesDamage === undefined + ? roll?.system.hasDamage + : context.usesDamage && roll?.system.hasDamage; + return { + character: this.party.find(x => x.id === id), + selected: this.data.members[id].selected, + roll: roll, + damageValues: roll + ? Object.keys(roll.system.damage).map(key => ({ + key: key, + name: game.i18n.localize(CONFIG.DH.GENERAL.healingTypes[key].label), + total: roll.system.damage[key].total + })) + : null + }; + }); + + const initiatorChar = this.party.find(x => x.id === this.data.initiator.id); + context.initiator = { + character: initiatorChar, + cost: this.data.initiator.cost + }; + + const selectedMember = Object.values(context.members).find(x => x.selected && x.roll); + const selectedIsCritical = selectedMember?.roll?.system?.isCritical; + context.selectedData = { + result: selectedMember + ? `${selectedMember.roll.system.roll.total} ${selectedMember.roll.system.roll.result.label}` + : null, + damageValues: null + }; + + for (const member of Object.values(context.members)) { + if (!member.roll) continue; + if (context.usesDamage) { + if (!context.selectedData.damageValues) context.selectedData.damageValues = {}; + for (let damage of member.damageValues) { + const damageTotal = member.roll.system.isCritical + ? damage.total + : selectedIsCritical + ? damage.total + (await getCritDamageBonus(member.roll.system.damage[damage.key].formula)) + : damage.total; + if (context.selectedData.damageValues[damage.key]) { + context.selectedData.damageValues[damage.key].total += damageTotal; + } else { + context.selectedData.damageValues[damage.key] = { + ...foundry.utils.deepClone(damage), + total: damageTotal + }; + } + } + } + } + + context.showResult = Object.values(context.members).reduce((enabled, member) => { + if (!member.roll) return enabled; + if (context.usesDamage) { + enabled = enabled === null ? member.damageValues.length > 0 : enabled && member.damageValues.length > 0; + } else { + enabled = enabled === null ? Boolean(member.roll) : enabled && Boolean(member.roll); + } + + return enabled; + }, null); + + context.createDisabled = + !context.selectedData.result || + !this.data.initiator.id || + Object.keys(this.data.members).length === 0 || + Object.values(context.members).some(x => + context.usesDamage ? !x.damageValues || x.damageValues.length === 0 : !x.roll + ); return context; } - async _preparePartContext(partId, context, options) { - const partContext = await super._preparePartContext(partId, context, options); - switch (partId) { - case 'initialization': - partContext.tagTeamFields = this.party.system.schema.fields.tagTeam.fields; - partContext.memberSelection = this.partyMembers; - const selectedMembers = partContext.memberSelection.filter(x => x.selected); + async updateSource(update) { + await this.data.updateSource(update); - partContext.allSelected = selectedMembers.length === 2; - partContext.canStartTagTeam = partContext.allSelected && this.initiator; - partContext.initiator = this.initiator; - partContext.initiatorOptions = selectedMembers.map(x => ({ value: x.id, label: x.name })); - partContext.initiatorDisabled = !selectedMembers.length; - partContext.openForAllPlayers = this.openForAllPlayers; - - break; - case 'tagTeamRoll': - partContext.fields = this.party.system.schema.fields.tagTeam.fields; - partContext.data = this.party.system.tagTeam; - partContext.rollTypes = CONFIG.DH.GENERAL.tagTeamRollTypes; - partContext.traitOptions = CONFIG.DH.ACTOR.abilities; - - const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected); - const critSelected = !selectedRoll - ? undefined - : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false); - - partContext.members = {}; - for (const actorId in this.party.system.tagTeam.members) { - const data = this.party.system.tagTeam.members[actorId]; - const actor = game.actors.get(actorId); - - const rollOptions = []; - const damageRollOptions = []; - for (const item of actor.items) { - if (item.system.metadata.hasActions) { - const actions = [ - ...item.system.actions, - ...(item.system.attack ? [item.system.attack] : []) - ]; - for (const action of actions) { - if (action.hasRoll) { - const actionItem = { - value: action.uuid, - label: action.name, - group: item.name, - baseAction: action.baseAction - }; - - if (action.hasDamage) damageRollOptions.push(actionItem); - else rollOptions.push(actionItem); - } - } - } - } - - const damage = data.rollData?.options?.damage; - partContext.hasDamage |= Boolean(damage); - const critHitPointsDamage = await this.getCriticalDamage(damage); - - partContext.members[actorId] = { - ...data, - isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER), - key: actorId, - readyToRoll: Boolean(data.rollChoice), - hasRolled: Boolean(data.rollData), - rollOptions, - damageRollOptions, - damage: damage, - critDamage: critHitPointsDamage, - useCritDamage: - critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical) - }; + if (game.user.isGM) { + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, this.data.toObject()); + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll }); + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { + refreshType: RefreshType.TagTeamRoll } + }); + } else { + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.GMUpdate, + data: { + action: GMUpdateEvent.UpdateSetting, + uuid: CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, + update: this.data.toObject(), + refresh: { refreshType: RefreshType.TagTeamRoll } + } + }); + } + } - partContext.hintText = await this.getInfoTexts(this.party.system.tagTeam.members); - partContext.joinedRoll = await this.getJoinedRoll({ - overrideIsCritical: critSelected, - displayVersion: true - }); - - break; + static async updateData(_event, _element, formData) { + const { selectedAddMember, initiator } = foundry.utils.expandObject(formData.object); + const update = { initiator: initiator }; + if (selectedAddMember) { + const member = await foundry.utils.fromUuid(selectedAddMember); + update[`members.${member.id}`] = { messageId: null }; } - return partContext; + await this.updateSource(update); + this.render(); } - static async updateData(_event, _, formData) { - const { initiator, openForAllPlayers, ...partyData } = foundry.utils.expandObject(formData.object); - this.initiator = initiator; - this.openForAllPlayers = openForAllPlayers !== undefined ? openForAllPlayers : this.openForAllPlayers; + static async #removeMember(_, button) { + const update = { [`members.${button.dataset.characterId}`]: _del }; + if (this.data.initiator.id === button.dataset.characterId) { + update.iniator = { id: null }; + } - this.updatePartyData(partyData); + await this.updateSource(update); } - async updatePartyData(update, options = { render: true }) { - const gmUpdate = async update => { - await this.party.update(update); - this.render(); - game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data: { refreshType: RefreshType.TagTeamRoll, action: 'refresh' } - }); - }; - - await emitAsGM( - GMUpdateEvent.UpdateDocument, - gmUpdate, - update, - this.party.uuid, - options.render ? { refreshType: RefreshType.TagTeamRoll, action: 'refresh' } : undefined - ); + static async #unlinkMessage(_, button) { + await this.updateSource({ [`members.${button.id}.messageId`]: null }); } - getIsEditable() { - return this.party.system.partyMembers.some(actor => { - const selected = Boolean(this.party.system.tagTeam.members[actor.id]); - return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER); + static async #selectMessage(_, button) { + const member = this.data.members[button.id]; + const currentSelected = Object.keys(this.data.members).find(key => this.data.members[key].selected); + const curretSelectedUpdate = + currentSelected && currentSelected !== button.id ? { [`${currentSelected}`]: { selected: false } } : {}; + await this.updateSource({ + members: { + [`${button.id}`]: { selected: !member.selected }, + ...curretSelectedUpdate + } }); } - tagTeamRefresh = ({ refreshType, action }) => { - if (refreshType !== RefreshType.TagTeamRoll) return; + static async #createTagTeam() { + const mainRollId = Object.keys(this.data.members).find(key => this.data.members[key].selected); + const mainRoll = game.messages.get(this.data.members[mainRollId].messageId); - switch (action) { - case 'startTagTeamRoll': - this.tabGroups.application = 'tagTeamRoll'; - break; - case 'refresh': - this.render(); - break; - case 'close': - this.close(); - break; - } - }; - - async close(options = {}) { - /* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */ - if (options.closeKey) return; - - Hooks.off(socketEvent.Refresh, this.tagTeamRefresh); - return super.close(options); - } - - checkInitiatorHopeError(initiator) { - if (initiator.cost && initiator.memberId) { - const actor = game.actors.get(initiator.memberId); - if (actor.system.resources.hope.value < initiator.cost) { + if (this.data.initiator.cost) { + const initiator = this.party.find(x => x.id === this.data.initiator.id); + if (initiator.system.resources.hope.value < this.data.initiator.cost) { return ui.notifications.warn( game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.insufficientHope') ); } } - } - //#region Initialization - static #toggleSelectMember(_, button) { - const member = this.partyMembers.find(x => x.id === button.dataset.id); - if (member.selected && this.initiator?.memberId === member.id) this.initiator = null; + const secondaryRolls = Object.keys(this.data.members) + .filter(key => key !== mainRollId) + .map(key => game.messages.get(this.data.members[key].messageId)); - member.selected = !member.selected; - this.render(); - } + const systemData = foundry.utils.deepClone(mainRoll).system.toObject(); + const criticalRoll = systemData.roll.isCritical; + for (let roll of secondaryRolls) { + if (roll.system.hasDamage) { + for (let key in roll.system.damage) { + var damage = roll.system.damage[key]; + const damageTotal = + !roll.system.isCritical && criticalRoll + ? (await getCritDamageBonus(damage.formula)) + damage.total + : damage.total; + const updatedDamageParts = damage.parts; + if (systemData.damage[key]) { + if (!roll.system.isCritical && criticalRoll) { + for (let part of updatedDamageParts) { + const criticalDamage = await getCritDamageBonus(part.formula); + if (criticalDamage) { + damage.formula = `${damage.formula} + ${criticalDamage}`; + part.formula = `${part.formula} + ${criticalDamage}`; + part.modifierTotal = part.modifierTotal + criticalDamage; + part.total += criticalDamage; + part.roll = new Roll(part.formula); + } + } + } - static async #startTagTeamRoll() { - const error = this.checkInitiatorHopeError(this.initiator); - if (error) return error; - - await this.party.update({ - 'system.tagTeam': _replace( - new game.system.api.data.TagTeamData({ - ...this.party.system.tagTeam.toObject(), - initiator: this.initiator, - members: this.partyMembers.reduce((acc, member) => { - if (member.selected) - acc[member.id] = { - name: member.name, - img: member.img, - rollType: CONFIG.DH.GENERAL.tagTeamRollTypes.trait.id - }; - return acc; - }, {}) - }) - ) - }); - - const hookData = { openForAllPlayers: this.openForAllPlayers, partyId: this.party.id }; - Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.tagTeamStart, hookData); - game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.TagTeamStart, - data: hookData - }); - - this.render(); - } - //#endregion - //#region Tag Team Roll - - async getInfoTexts(members) { - let rollsAreFinished = true; - let rollIsSelected = false; - for (const member of Object.values(members)) { - const rollFinished = Boolean(member.rollData); - const damageFinished = - member.rollData?.options?.hasDamage !== undefined ? member.rollData.options.damage : true; - - rollsAreFinished = rollsAreFinished && rollFinished && damageFinished; - rollIsSelected = rollIsSelected || member.selected; - } - - let hint = null; - if (!rollsAreFinished) hint = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.hints.completeRolls'); - else if (!rollIsSelected) hint = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.hints.selectRoll'); - - return hint; - } - - async updateRollType(event) { - this.updatePartyData({ - [`system.tagTeam.members.${event.target.dataset.member}`]: { - rollType: event.target.value, - rollChoice: null - } - }); - } - - static async #removeRoll(_, button) { - this.updatePartyData({ - [`system.tagTeam.members.${button.dataset.member}`]: { - rollData: null, - rollChoice: null, - selected: false - } - }); - } - - static async #makeRoll(event, button) { - const { member } = button.dataset; - - let result = null; - switch (this.party.system.tagTeam.members[member].rollType) { - case CONFIG.DH.GENERAL.tagTeamRollTypes.trait.id: - result = await this.makeTraitRoll(member); - break; - case CONFIG.DH.GENERAL.tagTeamRollTypes.ability.id: - case CONFIG.DH.GENERAL.tagTeamRollTypes.damageAbility.id: - result = await this.makeAbilityRoll(event, member); - break; - } - - if (!result) return; - - if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); - - const rollData = result.messageRoll.toJSON(); - delete rollData.options.messageRoll; - this.updatePartyData({ - [`system.tagTeam.members.${member}.rollData`]: rollData - }); - } - - async makeTraitRoll(memberKey) { - const actor = game.actors.find(x => x.id === memberKey); - if (!actor) return; - - const memberData = this.party.system.tagTeam.members[memberKey]; - return await actor.rollTrait(memberData.rollChoice, { - skips: { - createMessage: true, - resources: true, - triggers: true - } - }); - } - - async makeAbilityRoll(event, memberKey) { - const actor = game.actors.find(x => x.id === memberKey); - if (!actor) return; - - const memberData = this.party.system.tagTeam.members[memberKey]; - const action = await foundry.utils.fromUuid(memberData.rollChoice); - - return await action.use(event, { - skips: { - createMessage: true, - resources: true, - triggers: true - } - }); - } - - static async #rerollDice(_, button) { - const { member, diceType } = button.dataset; - const memberData = this.party.system.tagTeam.members[member]; - - const dieIndex = diceType === 'hope' ? 0 : diceType === 'fear' ? 2 : 4; - - const { parsedRoll, newRoll } = await game.system.api.dice.DualityRoll.reroll( - memberData.rollData, - dieIndex, - diceType - ); - const rollData = parsedRoll.toJSON(); - this.updatePartyData({ - [`system.tagTeam.members.${member}.rollData`]: { - ...rollData, - options: { - ...rollData.options, - roll: newRoll + systemData.damage[key].formula = `${systemData.damage[key].formula} + ${damage.formula}`; + systemData.damage[key].total += damageTotal; + systemData.damage[key].parts = [...systemData.damage[key].parts, ...updatedDamageParts]; + } else { + systemData.damage[key] = { ...damage, total: damageTotal, parts: updatedDamageParts }; + } } } - }); - } - - static async #makeDamageRoll(event, button) { - const { memberKey } = button.dataset; - const actor = game.actors.find(x => x.id === memberKey); - if (!actor) return; - - const memberData = this.party.system.tagTeam.members[memberKey]; - const action = await foundry.utils.fromUuid(memberData.rollChoice); - const config = { - ...memberData.rollData.options, - dialog: { - configure: !event.shiftKey - }, - skips: { - createMessage: true, - resources: true, - triggers: true - } - }; - - await action.workflow.get('damage').execute(config, null, true); - if (!config.damage) return; - - const current = this.party.system.tagTeam.members[memberKey].rollData; - await this.updatePartyData({ - [`system.tagTeam.members.${memberKey}.rollData`]: { - ...current, - options: { - ...current.options, - damage: config.damage - } - } - }); - } - - static async #removeDamageRoll(_, button) { - const { memberKey } = button.dataset; - const current = this.party.system.tagTeam.members[memberKey].rollData; - this.updatePartyData({ - [`system.tagTeam.members.${memberKey}.rollData`]: { - ...current, - options: { - ...current.options, - damage: null - } - } - }); - } - - static async #rerollDamageDice(_, button) { - const { memberKey, damageKey, part, dice } = button.dataset; - const memberData = this.party.system.tagTeam.members[memberKey]; - const partData = memberData.rollData.options.damage[damageKey].parts[part]; - const activeDiceResultKey = Object.keys(partData.dice[dice].results).find( - index => partData.dice[dice].results[index].active - ); - const { parsedRoll, rerolledDice } = await game.system.api.dice.DamageRoll.reroll( - partData, - dice, - activeDiceResultKey - ); - - const rollData = this.party.system.tagTeam.members[memberKey].rollData; - rollData.options.damage[damageKey].parts = rollData.options.damage[damageKey].parts.map((damagePart, index) => { - if (index !== Number.parseInt(part)) return damagePart; - - return { - ...damagePart, - total: parsedRoll.total, - dice: rerolledDice - }; - }); - rollData.options.damage[damageKey].total = rollData.options.damage[damageKey].parts.reduce((acc, part) => { - acc += part.total; - return acc; - }, 0); - - this.updatePartyData({ - [`system.tagTeam.members.${memberKey}.rollData`]: rollData - }); - } - - async getCriticalDamage(damage) { - const newDamage = foundry.utils.deepClone(damage); - for (let key in newDamage) { - var damage = newDamage[key]; - damage.formula = ''; - damage.total = 0; - - for (let part of damage.parts) { - const criticalDamage = await getCritDamageBonus(part.formula); - if (criticalDamage) { - part.modifierTotal += criticalDamage; - part.total += criticalDamage; - part.formula = `${part.dice.map(x => x.formula).join(' + ')} + ${part.modifierTotal}`; - part.roll = new Roll(part.formula); - } - - damage.formula = [damage.formula, part.formula].filter(x => x).join(' + '); - damage.total += part.total; - } } - return newDamage; - } - - async getNonCriticalDamage(config) { - const newDamage = foundry.utils.deepClone(config.damage); - for (let key in newDamage) { - var damage = newDamage[key]; - damage.formula = ''; - damage.total = 0; - - for (let part of damage.parts) { - const critDamageBonus = await getCritDamageBonus(part.formula); - part.modifierTotal -= critDamageBonus; - part.total -= critDamageBonus; - part.formula = `${part.dice.map(x => x.formula).join(' + ')} + ${part.modifierTotal}`; - part.roll = new Roll(part.formula); - - damage.formula = [damage.formula, part.formula].filter(x => x).join(' + '); - damage.total += part.total; - } - } - - return newDamage; - } - - static async #selectRoll(_, button) { - const { memberKey } = button.dataset; - this.updatePartyData({ - [`system.tagTeam.members`]: Object.entries(this.party.system.tagTeam.members).reduce( - (acc, [key, member]) => { - acc[key] = { selected: key === memberKey ? !member.selected : false }; - return acc; - }, - {} - ) - }); - } - - async getJoinedRoll({ overrideIsCritical, displayVersion } = {}) { - const memberValues = Object.values(this.party.system.tagTeam.members); - const selectedRoll = memberValues.find(x => x.selected); - let baseMainRoll = selectedRoll ?? memberValues[0]; - let baseSecondaryRoll = selectedRoll - ? memberValues.find(x => !x.selected) - : memberValues.length > 1 - ? memberValues[1] - : null; - - if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null; - - const mainRoll = new MemberData(baseMainRoll.toObject()); - const secondaryRollData = new MemberData(baseSecondaryRoll.toObject()).rollData; - const systemData = mainRoll.rollData.options; - const isCritical = overrideIsCritical ?? systemData.roll.isCritical; - if (isCritical) systemData.damage = await this.getCriticalDamage(systemData.damage); - - if (secondaryRollData?.options.hasDamage) { - const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical) - ? await this.getCriticalDamage(secondaryRollData.options.damage) - : secondaryRollData.options.damage; - if (systemData.damage) { - for (const key in secondaryDamage) { - const damage = secondaryDamage[key]; - systemData.damage[key].formula = [systemData.damage[key].formula, damage.formula] - .filter(x => x) - .join(' + '); - systemData.damage[key].total += damage.total; - systemData.damage[key].parts.push(...damage.parts); - } - } else { - systemData.damage = secondaryDamage; - } - } - - return mainRoll; - } - - static async #onCancelRoll(_event, _button, options = { confirm: true }) { - this.cancelRoll(options); - } - - async cancelRoll(options = { confirm: true }) { - if (options.confirm) { - const confirmed = await foundry.applications.api.DialogV2.confirm({ - window: { - title: game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.cancelConfirmTitle') - }, - content: game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.cancelConfirmText') - }); - - if (!confirmed) return; - } - - await this.updatePartyData( - { - 'system.tagTeam': { - initiator: null, - members: _replace({}) - } - }, - { render: false } - ); - - this.close(); - game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data: { refreshType: RefreshType.TagTeamRoll, action: 'close' } - }); - } - - static async #finishRoll() { - const error = this.checkInitiatorHopeError(this.party.system.tagTeam.initiator); - if (error) return error; - - const mainRoll = (await this.getJoinedRoll()).rollData; - - const mainActor = this.party.system.partyMembers.find(x => x.uuid === mainRoll.options.source.actor); - mainRoll.options.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.chatMessageRollTitle'); + systemData.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.chatMessageRollTitle'); const cls = getDocumentClass('ChatMessage'), msgData = { type: 'dualityRoll', user: game.user.id, title: game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.title'), - speaker: cls.getSpeaker({ actor: mainActor }), - system: mainRoll.options, - rolls: [mainRoll], + speaker: cls.getSpeaker({ actor: this.party.find(x => x.id === mainRollId) }), + system: systemData, + rolls: mainRoll.rolls, sound: null, flags: { core: { RollTable: true } } }; await cls.create(msgData); - /* Handle resource updates from the finished TagTeamRoll */ - const tagTeamData = this.party.system.tagTeam; const fearUpdate = { key: 'fear', value: null, total: null, enabled: true }; - for (let memberId in tagTeamData.members) { + for (let memberId of Object.keys(this.data.members)) { const resourceUpdates = []; - const rollGivesHope = mainRoll.options.roll.isCritical || mainRoll.options.roll.result.duality === 1; - if (memberId === tagTeamData.initiator.memberId) { - const value = tagTeamData.initiator.cost + const rollGivesHope = systemData.roll.isCritical || systemData.roll.result.duality === 1; + if (memberId === this.data.initiator.id) { + const value = this.data.initiator.cost ? rollGivesHope - ? 1 - tagTeamData.initiator.cost - : -tagTeamData.initiator.cost + ? 1 - this.data.initiator.cost + : -this.data.initiator.cost : 1; resourceUpdates.push({ key: 'hope', value: value, total: -value, enabled: true }); } else if (rollGivesHope) { resourceUpdates.push({ key: 'hope', value: 1, total: -1, enabled: true }); } - if (mainRoll.options.roll.isCritical) - resourceUpdates.push({ key: 'stress', value: -1, total: 1, enabled: true }); - if (mainRoll.options.roll.result.duality === -1) { + if (systemData.roll.isCritical) resourceUpdates.push({ key: 'stress', value: -1, total: 1, enabled: true }); + if (systemData.roll.result.duality === -1) { fearUpdate.value = fearUpdate.value === null ? 1 : fearUpdate.value + 1; fearUpdate.total = fearUpdate.total === null ? -1 : fearUpdate.total - 1; } - game.actors.get(memberId).modifyResource(resourceUpdates); + this.party.find(x => x.id === memberId).modifyResource(resourceUpdates); } if (fearUpdate.value) { - mainActor.modifyResource([fearUpdate]); + this.party.find(x => x.id === mainRollId).modifyResource([fearUpdate]); } - /* Fin */ - this.cancelRoll({ confirm: false }); + /* Improve by fetching default from schema */ + const update = { members: [], initiator: { id: null, cost: 3 } }; + if (game.user.isGM) { + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, update); + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll }); + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { + refreshType: RefreshType.TagTeamRoll + } + }); + } else { + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.GMUpdate, + data: { + action: GMUpdateEvent.UpdateSetting, + uuid: CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, + update: update, + refresh: { refreshType: RefreshType.TagTeamRoll } + } + }); + } } - //#endregion + static async assignRoll(char, message) { + const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); + const character = settings.members[char.id]; + if (!character) return; + + await settings.updateSource({ [`members.${char.id}.messageId`]: message.id }); + + if (game.user.isGM) { + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, settings); + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll }); + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { + refreshType: RefreshType.TagTeamRoll + } + }); + } else { + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.GMUpdate, + data: { + action: GMUpdateEvent.UpdateSetting, + uuid: CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, + update: settings, + refresh: { refreshType: RefreshType.TagTeamRoll } + } + }); + } + } + + async close(options = {}) { + Hooks.off(socketEvent.Refresh, this.setupHooks); + await super.close(options); + } } diff --git a/module/applications/sheets-configs/_module.mjs b/module/applications/sheets-configs/_module.mjs index 4b83a042..8b09dc29 100644 --- a/module/applications/sheets-configs/_module.mjs +++ b/module/applications/sheets-configs/_module.mjs @@ -3,8 +3,10 @@ export { default as ActionSettingsConfig } from './action-settings-config.mjs'; export { default as CharacterSettings } from './character-settings.mjs'; export { default as AdversarySettings } from './adversary-settings.mjs'; export { default as CompanionSettings } from './companion-settings.mjs'; +export { default as SettingActiveEffectConfig } from './setting-active-effect-config.mjs'; export { default as SettingFeatureConfig } from './setting-feature-config.mjs'; export { default as EnvironmentSettings } from './environment-settings.mjs'; +export { default as ArmorActiveEffectConfig } from './armorActiveEffectConfig.mjs'; export { default as ActiveEffectConfig } from './activeEffectConfig.mjs'; export { default as DhTokenConfig } from './token-config.mjs'; export { default as DhPrototypeTokenConfig } from './prototype-token-config.mjs'; diff --git a/module/applications/sheets-configs/action-settings-config.mjs b/module/applications/sheets-configs/action-settings-config.mjs index 9cb866bc..91b85802 100644 --- a/module/applications/sheets-configs/action-settings-config.mjs +++ b/module/applications/sheets-configs/action-settings-config.mjs @@ -55,7 +55,7 @@ export default class DHActionSettingsConfig extends DHActionBaseConfig { static async editEffect(event) { const id = event.target.closest('[data-effect-id]')?.dataset?.effectId; - const updatedEffect = await game.system.api.applications.sheetConfigs.ActiveEffectConfig.configureSetting( + const updatedEffect = await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure( this.getEffectDetails(id) ); if (!updatedEffect) return; diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs index e4a18831..2bd7d5b9 100644 --- a/module/applications/sheets-configs/activeEffectConfig.mjs +++ b/module/applications/sheets-configs/activeEffectConfig.mjs @@ -150,10 +150,6 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac minLength: 0 }); }); - - htmlElement - .querySelector('.armor-change-checkbox') - ?.addEventListener('change', this.armorChangeToggle.bind(this)); } async _prepareContext(options) { @@ -191,74 +187,38 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac break; case 'changes': const fields = this.document.system.schema.fields.changes.element.fields; - - const singleTypes = ['armor']; - const { base, ...typedChanges } = context.source.changes.reduce((acc, change, index) => { - const type = CONFIG.DH.GENERAL.baseActiveEffectModes[change.type] ? 'base' : change.type; - if (singleTypes.includes(type)) { - acc[type] = { ...change, index }; - } else { - if (!acc[type]) acc[type] = []; - acc[type].push({ ...change, index }); - } - - return acc; - }, {}); partContext.changes = await Promise.all( - foundry.utils.deepClone(base ?? []).map(c => this._prepareChangeContext(c, fields)) + foundry.utils + .deepClone(context.source.changes) + .map((c, i) => this._prepareChangeContext(c, i, fields)) ); - partContext.typedChanges = typedChanges; break; } return partContext; } - armorChangeToggle(event) { - if (event.target.checked) { - this.addArmorChange(); - } else { - this.removeTypedChange(event.target.dataset.index); - } - } - - /* Could be generalised if needed later */ - addArmorChange() { - const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); - const changes = Object.values(submitData.system?.changes ?? {}); - changes.push(game.system.api.data.activeEffects.changeTypes.armor.getInitialValue()); - return this.submit({ updateData: { system: { changes } } }); - } - - removeTypedChange(indexString) { - const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); - const changes = Object.values(submitData.system.changes); - const index = Number(indexString); - changes.splice(index, 1); - return this.submit({ updateData: { system: { changes } } }); - } - - _prepareChangeContext(change, fields) { + _prepareChangeContext(change, index, fields) { if (typeof change.value !== 'string') change.value = JSON.stringify(change.value); const defaultPriority = game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type]?.defaultPriority; Object.assign( change, ['key', 'type', 'value', 'priority'].reduce((paths, fieldName) => { - paths[`${fieldName}Path`] = `system.changes.${change.index}.${fieldName}`; + paths[`${fieldName}Path`] = `system.changes.${index}.${fieldName}`; return paths; }, {}) ); return ( game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type].render?.( change, - change.index, + index, defaultPriority ) ?? foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/sheets/activeEffect/change.hbs', { change, - index: change.index, + index, defaultPriority, fields } @@ -287,34 +247,4 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac return submitData; } - - /** @inheritDoc */ - _processSubmitData(event, form, submitData, options) { - if (this.options.isSetting) { - // Settings should update source instead - this.document.updateSource(submitData); - this.render(); - } else { - return super._processSubmitData(event, form, submitData, options); - } - } - - /** Creates an active effect config for a setting */ - static async configureSetting(effect, options = {}) { - const document = new CONFIG.ActiveEffect.documentClass({ ...foundry.utils.duplicate(effect), _id: effect.id }); - return new Promise(resolve => { - const app = new this({ document, ...options, isSetting: true }); - app.addEventListener( - 'close', - () => { - const newEffect = app.document.toObject(true); - newEffect.id = newEffect._id; - delete newEffect._id; - resolve(newEffect); - }, - { once: true } - ); - app.render({ force: true }); - }); - } } diff --git a/module/applications/sheets-configs/armorActiveEffectConfig.mjs b/module/applications/sheets-configs/armorActiveEffectConfig.mjs new file mode 100644 index 00000000..3dca8ef1 --- /dev/null +++ b/module/applications/sheets-configs/armorActiveEffectConfig.mjs @@ -0,0 +1,67 @@ +const { HandlebarsApplicationMixin, DocumentSheetV2 } = foundry.applications.api; + +export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin(DocumentSheetV2) { + static DEFAULT_OPTIONS = { + tag: 'form', + classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config', 'armor-effect-config'], + form: { + handler: this.updateForm, + submitOnChange: true, + closeOnSubmit: false + }, + position: { width: 560 }, + actions: { + finish: ArmorActiveEffectConfig.#finish + } + }; + + static PARTS = { + header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' }, + tabs: { template: 'templates/generic/tab-navigation.hbs' }, + details: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/details.hbs' }, + settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/settings.hbs' }, + footer: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/footer.hbs' } + }; + + static TABS = { + sheet: { + tabs: [ + { id: 'details', icon: 'fa-solid fa-book' }, + { id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' } + ], + initial: 'details', + labelPrefix: 'EFFECT.TABS' + } + }; + + async _prepareContext(options) { + const context = await super._prepareContext(options); + context.systemFields = context.document.system.schema.fields; + + return context; + } + + /** @inheritDoc */ + async _preparePartContext(partId, context) { + const partContext = await super._preparePartContext(partId, context); + if (partId in partContext.tabs) partContext.tab = partContext.tabs[partId]; + + switch (partId) { + case 'details': + partContext.isActorEffect = this.document.parent?.documentName === 'Actor'; + partContext.isItemEffect = this.document.parent?.documentName === 'Item'; + break; + } + + return partContext; + } + + static async updateForm(_event, _form, formData) { + await this.document.update(formData.object); + this.render(); + } + + static #finish() { + this.close(); + } +} diff --git a/module/applications/sheets-configs/setting-active-effect-config.mjs b/module/applications/sheets-configs/setting-active-effect-config.mjs new file mode 100644 index 00000000..12ac90d1 --- /dev/null +++ b/module/applications/sheets-configs/setting-active-effect-config.mjs @@ -0,0 +1,223 @@ +import autocomplete from 'autocompleter'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class SettingActiveEffectConfig extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(effect) { + super({}); + + this.effect = foundry.utils.deepClone(effect); + this.changeChoices = game.system.api.applications.sheetConfigs.ActiveEffectConfig.getChangeChoices(); + } + + static DEFAULT_OPTIONS = { + classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config', 'standard-form'], + tag: 'form', + position: { + width: 560 + }, + form: { + submitOnChange: false, + closeOnSubmit: false, + handler: SettingActiveEffectConfig.#onSubmit + }, + actions: { + editImage: SettingActiveEffectConfig.#editImage, + addChange: SettingActiveEffectConfig.#addChange, + deleteChange: SettingActiveEffectConfig.#deleteChange + } + }; + + static PARTS = { + header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' }, + tabs: { template: 'templates/generic/tab-navigation.hbs' }, + details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] }, + settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' }, + changes: { + template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs', + scrollable: ['ol[data-changes]'] + }, + footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' } + }; + + static TABS = { + sheet: { + tabs: [ + { id: 'details', icon: 'fa-solid fa-book' }, + { id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' }, + { id: 'changes', icon: 'fa-solid fa-gears' } + ], + initial: 'details', + labelPrefix: 'EFFECT.TABS' + } + }; + + /**@inheritdoc */ + async _onFirstRender(context, options) { + await super._onFirstRender(context, options); + } + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.source = this.effect; + context.fields = game.system.api.documents.DhActiveEffect.schema.fields; + context.systemFields = game.system.api.data.activeEffects.BaseEffect._schema.fields; + + return context; + } + + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + const changeChoices = this.changeChoices; + + htmlElement.querySelectorAll('.effect-change-input').forEach(element => { + autocomplete({ + input: element, + fetch: function (text, update) { + if (!text) { + update(changeChoices); + } else { + text = text.toLowerCase(); + var suggestions = changeChoices.filter(n => n.label.toLowerCase().includes(text)); + update(suggestions); + } + }, + render: function (item, search) { + const label = game.i18n.localize(item.label); + const matchIndex = label.toLowerCase().indexOf(search); + + const beforeText = label.slice(0, matchIndex); + const matchText = label.slice(matchIndex, matchIndex + search.length); + const after = label.slice(matchIndex + search.length, label.length); + + const element = document.createElement('li'); + element.innerHTML = + `${beforeText}${matchText ? `${matchText}` : ''}${after}`.replaceAll( + ' ', + ' ' + ); + if (item.hint) { + element.dataset.tooltip = game.i18n.localize(item.hint); + } + + return element; + }, + renderGroup: function (label) { + const itemElement = document.createElement('div'); + itemElement.textContent = game.i18n.localize(label); + return itemElement; + }, + onSelect: function (item) { + element.value = `system.${item.value}`; + }, + click: e => e.fetch(), + customize: function (_input, _inputRect, container) { + container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ; + }, + minLength: 0 + }); + }); + } + + async _preparePartContext(partId, context) { + if (partId in context.tabs) context.tab = context.tabs[partId]; + switch (partId) { + case 'details': + context.statuses = CONFIG.statusEffects.map(s => ({ value: s.id, label: game.i18n.localize(s.name) })); + context.isActorEffect = false; + context.isItemEffect = true; + const useGeneric = game.settings.get( + CONFIG.DH.id, + CONFIG.DH.SETTINGS.gameSettings.appearance + ).showGenericStatusEffects; + if (!useGeneric) { + context.statuses = [ + ...context.statuses, + Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({ + value: status.id, + label: game.i18n.localize(status.name) + })) + ]; + } + break; + case 'changes': + context.modes = Object.entries(CONST.ACTIVE_EFFECT_MODES).reduce((modes, [key, value]) => { + modes[value] = game.i18n.localize(`EFFECT.MODE_${key}`); + return modes; + }, {}); + + context.priorities = ActiveEffectConfig.DEFAULT_PRIORITIES; + break; + } + + return context; + } + + static async #onSubmit(_event, _form, formData) { + this.data = foundry.utils.expandObject(formData.object); + this.close(); + } + + /** + * Edit a Document image. + * @this {DocumentSheetV2} + * @type {ApplicationClickAction} + */ + static async #editImage(_event, target) { + if (target.nodeName !== 'IMG') { + throw new Error('The editImage action is available only for IMG elements.'); + } + + const attr = target.dataset.edit; + const current = foundry.utils.getProperty(this.effect, attr); + const fp = new FilePicker.implementation({ + current, + type: 'image', + callback: path => (target.src = path), + position: { + top: this.position.top + 40, + left: this.position.left + 10 + } + }); + + await fp.browse(); + } + + /** + * Add a new change to the effect's changes array. + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #addChange() { + const { changes, ...rest } = foundry.utils.expandObject(new FormDataExtended(this.form).object); + const updatedChanges = Object.values(changes ?? {}); + updatedChanges.push({}); + + this.effect = { ...rest, changes: updatedChanges }; + this.render(); + } + + /** + * Delete a change from the effect's changes array. + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #deleteChange(event) { + const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object); + const updatedChanges = Object.values(submitData.changes); + const row = event.target.closest('li'); + const index = Number(row.dataset.index) || 0; + updatedChanges.splice(index, 1); + + this.effect = { ...submitData, changes: updatedChanges }; + this.render(); + } + + static async configure(effect, options = {}) { + return new Promise(resolve => { + const app = new this(effect, options); + app.addEventListener('close', () => resolve(app.data), { once: true }); + app.render({ force: true }); + }); + } +} diff --git a/module/applications/sheets-configs/setting-feature-config.mjs b/module/applications/sheets-configs/setting-feature-config.mjs index f90bb52f..fb790f7f 100644 --- a/module/applications/sheets-configs/setting-feature-config.mjs +++ b/module/applications/sheets-configs/setting-feature-config.mjs @@ -147,7 +147,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App const effectIndex = this.move.effects.findIndex(x => x.id === id); const effect = this.move.effects[effectIndex]; const updatedEffect = - await game.system.api.applications.sheetConfigs.ActiveEffectConfig.configureSetting(effect); + await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect); if (!updatedEffect) return; await this.updateMove({ diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index bddbca62..db05a9c7 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -1,5 +1,6 @@ import DHBaseActorSheet from '../api/base-actor.mjs'; import DhDeathMove from '../../dialogs/deathMove.mjs'; +import { abilities } from '../../../config/actorConfig.mjs'; import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs'; import DhCharacterCreation from '../../characterCreation/characterCreation.mjs'; import FilterMenu from '../../ux/filter-menu.mjs'; @@ -720,16 +721,35 @@ export default class CharacterSheet extends DHBaseActorSheet { * Rolls an attribute check based on the clicked button's dataset attribute. * @type {ApplicationClickAction} */ - static async #rollAttribute(_event, button) { - const result = await this.document.rollTrait(button.dataset.attribute); + static async #rollAttribute(event, button) { + const abilityLabel = game.i18n.localize(abilities[button.dataset.attribute].label); + const config = { + event: event, + title: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${this.actor.name}`, + headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + ability: abilityLabel + }), + effects: await game.system.api.data.actions.actionsTypes.base.getEffects(this.document), + roll: { + trait: button.dataset.attribute, + type: 'trait' + }, + hasRoll: true, + actionType: 'action', + headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${this.actor.name}`, + title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + 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 })) || {}; - result.resourceUpdates.addResources(costResources); - await result.resourceUpdates.updateResources(); + config.resourceUpdates.addResources(costResources); + await config.resourceUpdates.updateResources(); } //TODO: redo toggleEquipItem method @@ -946,13 +966,10 @@ export default class CharacterSheet extends DHBaseActorSheet { const armorSources = []; for (var effect of Array.from(this.document.allApplicableEffects())) { const origin = effect.origin ? await foundry.utils.fromUuid(effect.origin) : effect.parent; - if (!effect.system.armorData || effect.disabled || effect.isSuppressed) continue; - - const originIsActor = origin instanceof Actor; - const name = originIsActor ? effect.name : origin.name; + if (effect.type !== 'armor' || effect.disabled || effect.isSuppressed) continue; armorSources.push({ uuid: effect.uuid, - name, + name: origin.name, ...effect.system.armorData }); } @@ -1001,14 +1018,15 @@ export default class CharacterSheet extends DHBaseActorSheet { /** Update specific armor source */ static async armorSourceUpdate(event) { const effect = await foundry.utils.fromUuid(event.target.dataset.uuid); - const armorChange = effect.system.armorChange; - if (!armorChange) return; + if (effect.system.changes.length !== 1) return; const value = Math.max(Math.min(Number.parseInt(event.target.value), effect.system.armorData.max), 0); - const newChanges = effect.system.changes.map(change => ({ - ...change, - value: change.type === 'armor' ? value : change.value - })); + const newChanges = [ + { + ...effect.system.changes[0], + value + } + ]; event.target.value = value; const progressBar = event.target.closest('.status-bar.armor-slots').querySelector('progress'); @@ -1020,19 +1038,19 @@ export default class CharacterSheet extends DHBaseActorSheet { static async armorSourcePipUpdate(event) { const target = event.target.closest('.armor-slot'); const effect = await foundry.utils.fromUuid(target.dataset.uuid); - const armorChange = effect.system.armorChange; - if (!armorChange) return; - - const { value } = effect.system.armorData; + if (effect.system.changes.length !== 1) return; + const { value, max } = effect.system.armorData; const inputValue = Number.parseInt(target.dataset.value); const decreasing = value >= inputValue; const newValue = decreasing ? inputValue - 1 : inputValue; - const newChanges = effect.system.changes.map(change => ({ - ...change, - value: change.type === 'armor' ? newValue : change.value - })); + const newChanges = [ + { + ...effect.system.changes[0], + value: newValue + } + ]; const container = target.closest('.slot-bar'); for (const armorSlot of container.querySelectorAll('.armor-slot i')) { diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs index c5e77112..20792141 100644 --- a/module/applications/sheets/actors/party.mjs +++ b/module/applications/sheets/actors/party.mjs @@ -35,7 +35,9 @@ export default class Party extends DHBaseActorSheet { refeshActions: Party.#refeshActions, triggerRest: Party.#triggerRest, tagTeamRoll: Party.#tagTeamRoll, - groupRoll: Party.#groupRoll + groupRoll: Party.#groupRoll, + selectRefreshable: DaggerheartMenu.selectRefreshable, + refreshActors: DaggerheartMenu.refreshActors }, dragDrop: [{ dragSelector: '[data-item-id]', dropSelector: null }] }; @@ -118,7 +120,6 @@ export default class Party extends DHBaseActorSheet { secrets: this.document.isOwner, relativeTo: this.document }); - context.tagTeamActive = Boolean(this.document.system.tagTeam.initiator); } /** @@ -257,7 +258,11 @@ export default class Party extends DHBaseActorSheet { } static async #tagTeamRoll() { - new game.system.api.applications.dialogs.TagTeamDialog(this.document).render({ force: true }); + new game.system.api.applications.dialogs.TagTeamDialog( + this.document.system.partyMembers.filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type)) + ).render({ + force: true + }); } static async #groupRoll(_params) { diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 83313454..98d68ed9 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -762,6 +762,10 @@ export default function DHApplicationMixin(Base) { data.system.domain = parent.system.domains[0]; } + if (documentClass === 'ActiveEffect') { + return cls.createDialog(data, { parent: this.document }); + } + const doc = await cls.create(data, { parent, renderSheet: !event.shiftKey }); if (parentIsItem && type === 'feature') { await this.document.update({ diff --git a/module/applications/sheets/items/armor.mjs b/module/applications/sheets/items/armor.mjs index 93325405..4c69c822 100644 --- a/module/applications/sheets/items/armor.mjs +++ b/module/applications/sheets/items/armor.mjs @@ -64,7 +64,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) { const armorEffect = this.document.system.armorEffect; if (Number.isNaN(value) || !armorEffect) return; - await armorEffect.system.armorChange.typeData.updateArmorMax(value); + await armorEffect.system.updateArmorMax(value); this.render(); } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index e29498e6..2b489f58 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -190,24 +190,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo const target = event.target.closest('[data-die-index]'); if (target.dataset.type === 'damage') { - const { damageType, part, dice, result } = target.dataset; - const damagePart = message.system.damage[damageType].parts[part]; - const { parsedRoll, rerolledDice } = await game.system.api.dice.DamageRoll.reroll(damagePart, dice, result); - const damageParts = message.system.damage[damageType].parts.map((damagePart, index) => { - if (index !== Number(part)) return damagePart; - return { - ...damagePart, - total: parsedRoll.total, - dice: rerolledDice - }; - }); - const updateMessage = game.messages.get(message._id); - await updateMessage.update({ - [`system.damage.${damageType}`]: { - total: parsedRoll.total, - parts: damageParts - } - }); + game.system.api.dice.DamageRoll.reroll(target, message); } else { let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0]; const rollClass = @@ -221,16 +204,20 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); - const { newRoll, parsedRoll } = await rollClass.reroll( - originalRoll_parsed, - target.dataset.dieIndex, - target.dataset.type - ); + const { newRoll, parsedRoll } = await rollClass.reroll(originalRoll_parsed, target, message); await game.messages.get(message._id).update({ 'system.roll': newRoll, 'rolls': [parsedRoll] }); + + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll }); + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { + refreshType: RefreshType.TagTeamRoll + } + }); } } diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 2d2e8cdc..0c3a77fa 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -253,8 +253,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { for (const item of this.items) { if (['weapon', 'armor'].includes(item.type)) { item.system.enrichedTags = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/ui/itemBrowser/item-tags.hbs', - { item: item.system } + 'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs', + item.system ); } item.system.enrichedDescription = diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index c15cae56..4ac46618 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -959,22 +959,12 @@ export const sceneRangeMeasurementSetting = { } }; -export const tagTeamRollTypes = { - trait: { - id: 'trait', - label: 'DAGGERHEART.CONFIG.TagTeamRollTypes.trait' +export const activeEffectModes = { + armor: { + id: 'armor', + priority: 20, + label: 'TYPES.ActiveEffect.armor' }, - ability: { - id: 'ability', - label: 'DAGGERHEART.CONFIG.TagTeamRollTypes.ability' - }, - damageAbility: { - id: 'damageAbility', - label: 'DAGGERHEART.CONFIG.TagTeamRollTypes.damageAbility' - } -}; - -export const baseActiveEffectModes = { custom: { id: 'custom', priority: 0, @@ -1012,15 +1002,6 @@ export const baseActiveEffectModes = { } }; -export const activeEffectModes = { - armor: { - id: 'armor', - priority: 20, - label: 'TYPES.ActiveEffect.armor' - }, - ...baseActiveEffectModes -}; - export const activeEffectArmorInteraction = { none: { id: 'none', label: 'DAGGERHEART.CONFIG.ArmorInteraction.none.label' }, active: { id: 'active', label: 'DAGGERHEART.CONFIG.ArmorInteraction.active.label' }, diff --git a/module/config/hooksConfig.mjs b/module/config/hooksConfig.mjs index 8d04be6d..61ba594c 100644 --- a/module/config/hooksConfig.mjs +++ b/module/config/hooksConfig.mjs @@ -1,5 +1,4 @@ export const hooksConfig = { effectDisplayToggle: 'DHEffectDisplayToggle', - lockedTooltipDismissed: 'DHLockedTooltipDismissed', - tagTeamStart: 'DHTagTeamRollStart' + lockedTooltipDismissed: 'DHLockedTooltipDismissed' }; diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index a3e785c3..d586a1ce 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -493,14 +493,18 @@ export const weaponFeatures = { key: 'system.evasion', mode: 2, value: '-1' - }, + } + ] + }, + { + type: 'armor', + name: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.effects.barrier.name', + description: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.effects.barrier.description', + img: 'icons/skills/melee/shield-block-bash-blue.webp', + changes: [ { - key: 'Armor', type: 'armor', - typeData: { - type: 'armor', - max: 'ITEM.@system.tier + 1' - } + max: 'ITEM.@system.tier + 1' } ] } @@ -808,20 +812,24 @@ export const weaponFeatures = { } }, { + type: 'armor', name: 'DAGGERHEART.CONFIG.WeaponFeature.doubleDuty.effects.doubleDuty.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.doubleDuty.effects.doubleDuty.description', img: 'icons/skills/melee/sword-shield-stylized-white.webp', changes: [ { - key: 'Armor', type: 'armor', - value: 0, - typeData: { - type: 'armor', - max: 1 - } + max: 1 } - ] + ], + system: { + rangeDependence: { + enabled: true, + range: 'melee', + target: 'hostile', + type: 'withinRange' + } + } } ] }, @@ -1200,18 +1208,14 @@ export const weaponFeatures = { description: 'DAGGERHEART.CONFIG.WeaponFeature.protective.description', effects: [ { + type: 'armor', name: 'DAGGERHEART.CONFIG.WeaponFeature.protective.effects.protective.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.protective.effects.protective.description', img: 'icons/skills/melee/shield-block-gray-orange.webp', changes: [ { - key: 'Armor', type: 'armor', - value: 0, - typeData: { - type: 'armor', - max: 'ITEM.@system.tier' - } + max: 'ITEM.@system.tier' } ] } diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index 52a316cf..0b28f0ab 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -34,6 +34,7 @@ export const gameSettings = { LevelTiers: 'LevelTiers', Countdowns: 'Countdowns', LastMigrationVersion: 'LastMigrationVersion', + TagTeamRoll: 'TagTeamRoll', SpotlightRequestQueue: 'SpotlightRequestQueue', CompendiumBrowserSettings: 'CompendiumBrowserSettings' }; diff --git a/module/data/_module.mjs b/module/data/_module.mjs index 43ff7807..52fa689e 100644 --- a/module/data/_module.mjs +++ b/module/data/_module.mjs @@ -1,9 +1,9 @@ export { default as DhCombat } from './combat.mjs'; export { default as DhCombatant } from './combatant.mjs'; +export { default as DhTagTeamRoll } from './tagTeamRoll.mjs'; export { default as DhRollTable } from './rollTable.mjs'; export { default as RegisteredTriggers } from './registeredTriggers.mjs'; export { default as CompendiumBrowserSettings } from './compendiumBrowserSettings.mjs'; -export { default as TagTeamData } from './tagTeamData.mjs'; export * as countdowns from './countdowns.mjs'; export * as actions from './action/_module.mjs'; diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index a2d47309..3671613d 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -50,8 +50,9 @@ export default class DHAttackAction extends DHDamageAction { async 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; await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.characterAttack.id); } diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 01139b30..57e34d11 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -207,10 +207,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * @param {Event} event Event from the button used to trigger the Action * @returns {object} */ - async use(event, configOptions = {}) { + async use(event) { if (!this.actor) throw new Error("An Action can't be used outside of an Actor context."); - let config = this.prepareConfig(event, configOptions); + let config = this.prepareConfig(event); if (!config) return; config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects(this.actor, this.item); @@ -231,7 +231,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return; - if (this.chatDisplay && !config.skips.createMessage && !config.actionChatMessageHandled) await this.toChat(); + if (this.chatDisplay && !config.actionChatMessageHandled) await this.toChat(); return config; } @@ -241,7 +241,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * @param {Event} event Event from the button used to trigger the Action * @returns {object} */ - prepareBaseConfig(event, configOptions = {}) { + prepareBaseConfig(event) { const isActor = this.item instanceof CONFIG.Actor.documentClass; const actionTitle = game.i18n.localize(this.name); const itemTitle = isActor || this.item.name === actionTitle ? '' : `${this.item.name} - `; @@ -268,8 +268,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel data: this.getRollData(), evaluate: this.hasRoll, resourceUpdates: new ResourceUpdateMap(this.actor), - targetUuid: this.targetUuid, - ...configOptions + targetUuid: this.targetUuid }; DHBaseAction.applyKeybindings(config); @@ -281,8 +280,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * @param {Event} event Event from the button used to trigger the Action * @returns {object} */ - prepareConfig(event, configOptions = {}) { - const config = this.prepareBaseConfig(event, configOptions); + prepareConfig(event) { + const config = this.prepareBaseConfig(event); for (const clsField of Object.values(this.schema.fields)) { if (clsField?.prepareConfig) if (clsField.prepareConfig.call(this, config) === false) return false; } @@ -357,11 +356,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } get hasDamage() { - return Boolean(Object.keys(this.damage?.parts ?? {}).length) && this.type !== 'healing'; + return !foundry.utils.isEmpty(this.damage?.parts) && this.type !== 'healing'; } get hasHealing() { - return Boolean(Object.keys(this.damage?.parts ?? {}).length) && this.type === 'healing'; + return !foundry.utils.isEmpty(this.damage?.parts) && this.type === 'healing'; } get hasSave() { diff --git a/module/data/activeEffect/_module.mjs b/module/data/activeEffect/_module.mjs index 3c933a9c..62f10e3e 100644 --- a/module/data/activeEffect/_module.mjs +++ b/module/data/activeEffect/_module.mjs @@ -1,12 +1,17 @@ import BaseEffect from './baseEffect.mjs'; import BeastformEffect from './beastformEffect.mjs'; import HordeEffect from './hordeEffect.mjs'; -export { changeTypes, changeEffects } from './changeTypes/_module.mjs'; +import ArmorEffect from './armorEffect.mjs'; -export { BaseEffect, BeastformEffect, HordeEffect }; +export { BaseEffect, BeastformEffect, HordeEffect, ArmorEffect }; export const config = { base: BaseEffect, beastform: BeastformEffect, - horde: HordeEffect + horde: HordeEffect, + armor: ArmorEffect +}; + +export const changeTypes = { + armor: ArmorEffect.armorChangeEffect }; diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs new file mode 100644 index 00000000..6bf0ecec --- /dev/null +++ b/module/data/activeEffect/armorEffect.mjs @@ -0,0 +1,244 @@ +import { getScrollTextData, itemAbleRollParse } from '../../helpers/utils.mjs'; + +/** + * ArmorEffects are ActiveEffects that have a static changes field of length 1. It includes current and maximum armor. + * When applied to a character, it adds to their currently marked and maximum armor. + */ +export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + ...super.defineSchema(), + changes: new fields.ArrayField( + new fields.SchemaField({ + key: new fields.StringField({ + required: true, + nullable: false, + initial: 'system.armorScore' + }), + type: new fields.StringField({ + required: true, + blank: false, + initial: CONFIG.DH.GENERAL.activeEffectModes.armor.id, + validate: ArmorEffect.#validateType + }), + phase: new fields.StringField({ required: true, blank: false, initial: 'initial' }), + priority: new fields.NumberField({ integer: true, initial: 20 }), + value: new fields.NumberField({ + required: true, + integer: true, + initial: 0, + min: 0, + label: 'DAGGERHEART.GENERAL.value' + }), + max: new fields.StringField({ + required: true, + nullable: false, + initial: '1', + label: 'DAGGERHEART.GENERAL.max' + }) + }), + { + initial: [ + { + key: 'system.armorScore', + type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, + phase: 'initial', + priority: 20, + value: 0, + max: '1' + } + ] + } + ), + armorInteraction: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.activeEffectArmorInteraction, + initial: CONFIG.DH.GENERAL.activeEffectArmorInteraction.none.id, + label: 'DAGGERHEART.EFFECTS.Armor.FIELDS.armorInteraction.label', + hint: 'DAGGERHEART.EFFECTS.Armor.FIELDS.armorInteraction.hint' + }) + }; + } + + get isSuppressed() { + if (this.parent.actor?.type !== 'character') return false; + + switch (this.armorInteraction) { + case CONFIG.DH.GENERAL.activeEffectArmorInteraction.active.id: + return !this.parent.actor.system.armor; + case CONFIG.DH.GENERAL.activeEffectArmorInteraction.inactive.id: + return Boolean(this.parent.actor.system.armor); + default: + return false; + } + } + + /* Type Functions */ + + /** + * Validate that an {@link EffectChangeData#type} string is well-formed. + * @param {string} type The string to be validated + * @returns {true} + * @throws {Error} An error if the type string is malformed + */ + static #validateType(type) { + if (type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id) + throw new Error('An armor effect must have change.type "armor"'); + + return true; + } + + static armorChangeEffect = { + label: 'Armor', + defaultPriortiy: 20, + handler: (actor, change, _options, _field, replacementData) => { + game.system.api.documents.DhActiveEffect.applyChange( + actor, + { + ...change, + key: 'system.armorScore.value', + type: CONFIG.DH.GENERAL.activeEffectModes.add.id, + value: change.value + }, + replacementData + ); + game.system.api.documents.DhActiveEffect.applyChange( + actor, + { + ...change, + key: 'system.armorScore.max', + type: CONFIG.DH.GENERAL.activeEffectModes.add.id, + value: change.max + }, + replacementData + ); + return {}; + }, + render: null + }; + + /* Helpers */ + + get armorChange() { + if (this.changes.length !== 1) + throw new Error('Unexpected error. An armor effect should have a changes field of length 1.'); + + const actor = this.parent.actor?.type === 'character' ? this.parent.actor : null; + const changeData = this.changes[0]; + const maxParse = actor ? itemAbleRollParse(changeData.max, actor, this.parent.parent) : null; + const maxRoll = maxParse ? new Roll(maxParse).evaluateSync() : null; + const maxEvaluated = maxRoll ? (maxRoll.isDeterministic ? maxRoll.total : null) : null; + + return { + ...changeData, + max: maxEvaluated ?? changeData.max + }; + } + + get armorData() { + return { value: this.armorChange.value, max: this.armorChange.max }; + } + + async updateArmorMax(newMax) { + const { effect, ...baseChange } = this.armorChange; + const newChanges = [ + { + ...baseChange, + max: newMax, + value: Math.min(this.armorChange.value, newMax) + } + ]; + await this.parent.update({ 'system.changes': newChanges }); + } + + static orderEffectsForAutoChange(armorEffects, increasing) { + const getEffectWeight = effect => { + switch (effect.parent.type) { + case 'class': + case 'subclass': + case 'ancestry': + case 'community': + case 'feature': + case 'domainCard': + return 2; + case 'armor': + return 3; + case 'loot': + case 'consumable': + return 4; + case 'weapon': + return 5; + case 'character': + return 6; + default: + return 1; + } + }; + + return armorEffects + .filter(x => !x.disabled && !x.isSuppressed) + .sort((a, b) => + increasing ? getEffectWeight(b) - getEffectWeight(a) : getEffectWeight(a) - getEffectWeight(b) + ); + } + + /* Overrides */ + + static getDefaultObject() { + return { + key: 'system.armorScore', + type: 'armor', + name: game.i18n.localize('DAGGERHEART.EFFECTS.Armor.newArmorEffect'), + img: 'icons/equipment/chest/breastplate-helmet-metal.webp' + }; + } + + async _preUpdate(changes, options, user) { + const allowed = await super._preUpdate(changes, options, user); + if (allowed === false) return false; + + if (changes.system?.changes) { + const changesChanged = changes.system.changes.length !== this.changes.length; + if (changesChanged) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectChanges') + ); + return false; + } + + if (changes.system.changes.length === 1) { + if (changes.system.changes[0].type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectType') + ); + return false; + } + + if (changes.system.changes[0].key !== 'system.armorScore') { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectKey') + ); + return false; + } + + if ( + changes.system.changes[0].value !== this.armorChange.value && + this.parent.actor?.type === 'character' + ) { + options.scrollingTextData = [ + getScrollTextData(this.parent.actor, changes.system.changes[0], 'armor') + ]; + } + } + } + } + + _onUpdate(changes, options, userId) { + super._onUpdate(changes, options, userId); + + if (options.scrollingTextData && this.parent.actor?.type === 'character') + this.parent.actor.queueScrollText(options.scrollingTextData); + } +} diff --git a/module/data/activeEffect/baseEffect.mjs b/module/data/activeEffect/baseEffect.mjs index fc87b353..98a961d7 100644 --- a/module/data/activeEffect/baseEffect.mjs +++ b/module/data/activeEffect/baseEffect.mjs @@ -12,8 +12,6 @@ * "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) */ -import { changeTypes } from './_module.mjs'; - export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { static defineSchema() { const fields = foundry.data.fields; @@ -32,8 +30,7 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { }), value: new fields.AnyField({ required: true, nullable: true, serializable: true, initial: '' }), phase: new fields.StringField({ required: true, blank: false, initial: 'initial' }), - priority: new fields.NumberField(), - typeData: new fields.TypedSchemaField(changeTypes, { nullable: true, initial: null }) + priority: new fields.NumberField() }) ), duration: new fields.SchemaField({ @@ -89,17 +86,6 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { return true; } - get armorChange() { - return this.changes.find(x => x.type === CONFIG.DH.GENERAL.activeEffectModes.armor.id); - } - - get armorData() { - const armorChange = this.armorChange; - if (!armorChange) return null; - - return armorChange.typeData.getArmorData(armorChange); - } - static getDefaultObject() { return { name: 'New Effect', diff --git a/module/data/activeEffect/changeTypes/_module.mjs b/module/data/activeEffect/changeTypes/_module.mjs deleted file mode 100644 index cf872304..00000000 --- a/module/data/activeEffect/changeTypes/_module.mjs +++ /dev/null @@ -1,9 +0,0 @@ -import Armor from './armor.mjs'; - -export const changeEffects = { - armor: Armor.changeEffect -}; - -export const changeTypes = { - armor: Armor -}; diff --git a/module/data/activeEffect/changeTypes/armor.mjs b/module/data/activeEffect/changeTypes/armor.mjs deleted file mode 100644 index 5f6047fd..00000000 --- a/module/data/activeEffect/changeTypes/armor.mjs +++ /dev/null @@ -1,147 +0,0 @@ -import { itemAbleRollParse } from '../../../helpers/utils.mjs'; - -const fields = foundry.data.fields; - -export default class Armor extends foundry.abstract.DataModel { - static defineSchema() { - return { - type: new fields.StringField({ required: true, initial: 'armor', blank: false }), - max: new fields.StringField({ - required: true, - nullable: false, - initial: '1', - label: 'DAGGERHEART.GENERAL.max' - }), - armorInteraction: new fields.StringField({ - required: true, - choices: CONFIG.DH.GENERAL.activeEffectArmorInteraction, - initial: CONFIG.DH.GENERAL.activeEffectArmorInteraction.none.id, - label: 'DAGGERHEART.EFFECTS.ChangeTypes.armor.FIELDS.armorInteraction.label', - hint: 'DAGGERHEART.EFFECTS.ChangeTypes.armor.FIELDS.armorInteraction.hint' - }) - }; - } - - static changeEffect = { - label: 'Armor', - defaultPriortiy: 20, - handler: (actor, change, _options, _field, replacementData) => { - const parsedMax = itemAbleRollParse(change.typeData.max, actor, change.effect.parent); - - game.system.api.documents.DhActiveEffect.applyChange( - actor, - { - ...change, - key: 'system.armorScore.value', - type: CONFIG.DH.GENERAL.activeEffectModes.add.id, - value: change.value - }, - replacementData - ); - game.system.api.documents.DhActiveEffect.applyChange( - actor, - { - ...change, - key: 'system.armorScore.max', - type: CONFIG.DH.GENERAL.activeEffectModes.add.id, - value: parsedMax - }, - replacementData - ); - return {}; - }, - render: null - }; - - get isSuppressed() { - switch (this.armorInteraction) { - case CONFIG.DH.GENERAL.activeEffectArmorInteraction.active.id: - return !this.parent.parent?.actor.system.armor; - case CONFIG.DH.GENERAL.activeEffectArmorInteraction.inactive.id: - return Boolean(this.parent.parent?.actor.system.armor); - default: - return false; - } - } - - static getInitialValue(locked) { - return { - key: 'Armor', - type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, - value: 0, - typeData: { - type: 'armor', - max: 0, - locked - }, - phase: 'initial', - priority: 20 - }; - } - - static getDefaultArmorEffect() { - return { - name: game.i18n.localize('DAGGERHEART.EFFECTS.ChangeTypes.armor.newArmorEffect'), - img: 'icons/equipment/chest/breastplate-helmet-metal.webp', - system: { - changes: [Armor.getInitialValue(true)] - } - }; - } - - /* Helpers */ - - getArmorData(parentChange) { - const actor = this.parent.parent?.actor?.type === 'character' ? this.parent.parent.actor : null; - const maxParse = actor ? itemAbleRollParse(this.max, actor, this.parent.parent.parent) : null; - const maxRoll = maxParse ? new Roll(maxParse).evaluateSync() : null; - const maxEvaluated = maxRoll ? (maxRoll.isDeterministic ? maxRoll.total : null) : null; - - return { - value: parentChange.value, - max: maxEvaluated ?? this.max - }; - } - - async updateArmorMax(newMax) { - const newChanges = [ - ...this.parent.changes.map(change => ({ - ...change, - value: change.type === 'armor' ? Math.min(change.value, newMax) : change.value, - typeData: change.type === 'armor' ? { ...change.typeData, max: newMax } : change.typeData - })) - ]; - await this.parent.parent.update({ 'system.changes': newChanges }); - } - - static orderEffectsForAutoChange(armorEffects, increasing) { - const getEffectWeight = effect => { - switch (effect.parent.type) { - case 'class': - case 'subclass': - case 'ancestry': - case 'community': - case 'feature': - case 'domainCard': - return 2; - case 'armor': - return 3; - case 'loot': - case 'consumable': - return 4; - case 'weapon': - return 5; - case 'character': - return 6; - default: - return 1; - } - }; - - return armorEffects - .filter(x => !x.disabled && !x.isSuppressed) - .sort((a, b) => - increasing ? getEffectWeight(b) - getEffectWeight(a) : getEffectWeight(a) - getEffectWeight(b) - ); - } -} diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index 89ba5db2..9f571edb 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -189,6 +189,19 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { return true; } + async _preDelete() { + /* Clear all partyMembers from tagTeam setting.*/ + /* Revisit this when tagTeam is improved for many parties */ + if (this.parent.parties.size > 0) { + const tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); + await tagTeam.updateSource({ + initiator: this.parent.id === tagTeam.initiator ? null : tagTeam.initiator, + members: Object.keys(tagTeam.members).find(x => x === this.parent.id) ? { [this.parent.id]: _del } : {} + }); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, tagTeam); + } + } + async _preUpdate(changes, options, userId) { const allowed = await super._preUpdate(changes, options, userId); if (allowed === false) return; diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index e69c4dc1..9036d3ca 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -469,8 +469,8 @@ export default class DhCharacter extends DhCreature { const increasing = armorChange >= 0; let remainingChange = Math.abs(armorChange); - const armorEffects = Array.from(this.parent.allApplicableEffects()).filter(x => x.system.armorData); - const orderedEffects = game.system.api.data.activeEffects.changeTypes.armor.orderEffectsForAutoChange( + const armorEffects = Array.from(this.parent.allApplicableEffects()).filter(x => x.type === 'armor'); + const orderedEffects = game.system.api.data.activeEffects.ArmorEffect.orderEffectsForAutoChange( armorEffects, increasing ); @@ -482,11 +482,11 @@ export default class DhCharacter extends DhCreature { usedArmorChange -= armorEffect.system.armorChange.value; } else { if (increasing) { - const remainingArmor = armorEffect.system.armorData.max - armorEffect.system.armorData.value; + const remainingArmor = armorEffect.system.armorChange.max - armorEffect.system.armorChange.value; usedArmorChange = Math.min(remainingChange, remainingArmor); remainingChange -= usedArmorChange; } else { - const changeChange = Math.min(armorEffect.system.armorData.value, remainingChange); + const changeChange = Math.min(armorEffect.system.armorChange.value, remainingChange); usedArmorChange -= changeChange; remainingChange -= changeChange; } @@ -499,13 +499,12 @@ export default class DhCharacter extends DhCreature { embeddedUpdates[armorEffect.parent.id].updates.push({ '_id': armorEffect.id, - 'system.changes': armorEffect.system.changes.map(change => ({ - ...change, - value: - change.type === 'armor' - ? armorEffect.system.armorChange.value + usedArmorChange - : change.value - })) + 'system.changes': [ + { + ...armorEffect.system.armorChange, + value: armorEffect.system.armorChange.value + usedArmorChange + } + ] }); } @@ -822,6 +821,7 @@ export default class DhCharacter extends DhCreature { static migrateData(source) { if (typeof source.scars === 'object') source.scars = 0; + if (source.resources?.hope?.max) source.scars = Math.max(6 - source.resources.hope.max, 0); return super.migrateData(source); } diff --git a/module/data/actor/environment.mjs b/module/data/actor/environment.mjs index e06f038c..0aaf8eb0 100644 --- a/module/data/actor/environment.mjs +++ b/module/data/actor/environment.mjs @@ -75,6 +75,10 @@ export default class DhEnvironment extends BaseDataActor { ); 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 } + }); }); } } diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs index 2c797803..3eddf235 100644 --- a/module/data/actor/party.mjs +++ b/module/data/actor/party.mjs @@ -1,6 +1,5 @@ import BaseDataActor from './base.mjs'; import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; -import TagTeamData from '../tagTeamData.mjs'; export default class DhParty extends BaseDataActor { /**@inheritdoc */ @@ -15,8 +14,7 @@ export default class DhParty extends BaseDataActor { handfuls: new fields.NumberField({ initial: 1, integer: true }), bags: new fields.NumberField({ initial: 0, integer: true }), chests: new fields.NumberField({ initial: 0, integer: true }) - }), - tagTeam: new fields.EmbeddedDataField(TagTeamData) + }) }; } @@ -42,6 +40,23 @@ export default class DhParty extends BaseDataActor { } } + async _preDelete() { + /* Clear all partyMembers from tagTeam setting.*/ + /* Revisit this when tagTeam is improved for many parties */ + const tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); + await tagTeam.updateSource({ + initiator: this.partyMembers.some(x => x.id === tagTeam.initiator) ? null : tagTeam.initiator, + members: Object.keys(tagTeam.members).reduce((acc, key) => { + if (this.partyMembers.find(x => x.id === key)) { + acc[key] = _del; + } + + return acc; + }, {}) + }); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, tagTeam); + } + _onDelete(options, userId) { super._onDelete(options, userId); diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index b7ef852e..e79a91a2 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -50,9 +50,9 @@ export default class DamageField extends fields.SchemaField { formulas = DamageField.formatFormulas.call(this, formulas, config); const damageConfig = { - dialog: {}, ...config, roll: formulas, + dialog: {}, data: this.getRollData() }; delete damageConfig.evaluate; diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index 1a003e2b..6afd470b 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -27,7 +27,7 @@ export default class EffectsField extends fields.ArrayField { static async execute(config, targets = null, force = false) { if (!config.hasEffect) return; let message = config.message ?? ui.chat.collection.get(config.parent?._id); - if (!message && !config.skips.createMessage) { + if (!message) { const roll = new CONFIG.Dice.daggerheart.DHRoll(''); roll._evaluated = true; message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); diff --git a/module/data/fields/action/saveField.mjs b/module/data/fields/action/saveField.mjs index 0629353e..c9030036 100644 --- a/module/data/fields/action/saveField.mjs +++ b/module/data/fields/action/saveField.mjs @@ -38,7 +38,7 @@ export default class SaveField extends fields.SchemaField { if (!config.hasSave) return; let message = config.message ?? ui.chat.collection.get(config.parent?._id); - if (!message && !config.skips.createMessage) { + if (!message) { const roll = new CONFIG.Dice.daggerheart.DHRoll(''); roll._evaluated = true; message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); diff --git a/module/data/fields/actorField.mjs b/module/data/fields/actorField.mjs index 25e04317..69ba6bf1 100644 --- a/module/data/fields/actorField.mjs +++ b/module/data/fields/actorField.mjs @@ -82,24 +82,6 @@ class ResourcesField extends fields.TypedObjectField { } return data; } - - /** - * Foundry bar attributes are unable to handle finding the schema field nor the label normally. - * This returns the element if its a valid resource key and overwrites the element's label for that retrieval. - */ - _getField(path) { - if (path.length === 0) return this; - const first = path.shift(); - if (first === this.element.name) return this.element_getField(path); - - const resources = CONFIG.DH.RESOURCE[this.actorType].all; - if (first in resources) { - this.element.label = resources[first].label; - return this.element._getField(path); - } - - return undefined; - } } export { attributeField, ResourcesField, stressDamageReductionRule, bonusField }; diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index ba70e4b9..76f05859 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -52,7 +52,7 @@ export default class DHArmor extends AttachableItem { } get armorEffect() { - return this.parent.effects.find(x => x.system.armorData); + return this.parent.effects.find(x => x.type === 'armor'); } get armorData() { @@ -80,9 +80,9 @@ export default class DHArmor extends AttachableItem { async _onCreate(_data, _options, userId) { if (userId !== game.user.id) return; - if (!this.parent.effects.some(x => x.system.armorData)) { + if (!this.parent.effects.some(x => x.type === 'armor')) { this.parent.createEmbeddedDocuments('ActiveEffect', [ - game.system.api.data.activeEffects.changeTypes.armor.getDefaultArmorEffect() + game.system.api.data.activeEffects.ArmorEffect.getDefaultObject() ]); } } diff --git a/module/data/tagTeamData.mjs b/module/data/tagTeamData.mjs deleted file mode 100644 index 25158606..00000000 --- a/module/data/tagTeamData.mjs +++ /dev/null @@ -1,47 +0,0 @@ -export default class TagTeamData extends foundry.abstract.DataModel { - static defineSchema() { - const fields = foundry.data.fields; - - return { - initiator: new fields.SchemaField( - { - memberId: new fields.StringField({ - required: true, - label: 'DAGGERHEART.APPLICATIONS.TagTeamSelect.FIELDS.initiator.memberId.label' - }), - cost: new fields.NumberField({ - integer: true, - initial: 3, - label: 'DAGGERHEART.APPLICATIONS.TagTeamSelect.FIELDS.initiator.cost.label' - }) - }, - { nullable: true, initial: null } - ), - members: new fields.TypedObjectField(new fields.EmbeddedDataField(MemberData)) - }; - } -} - -export class MemberData extends foundry.abstract.DataModel { - static defineSchema() { - const fields = foundry.data.fields; - - return { - name: new fields.StringField({ required: true }), - img: new fields.StringField({ required: true }), - rollType: new fields.StringField({ - required: true, - choices: CONFIG.DH.GENERAL.tagTeamRollTypes, - initial: CONFIG.DH.GENERAL.tagTeamRollTypes.trait.id, - label: 'Roll Type' - }), - rollChoice: new fields.StringField({ nullable: true, initial: null }), - rollData: new fields.JSONField({ nullable: true, initial: null }), - selected: new fields.BooleanField({ initial: false }) - }; - } - - get roll() { - return this.rollData ? CONFIG.Dice.daggerheart.DualityRoll.fromData(this.rollData) : null; - } -} diff --git a/module/data/tagTeamRoll.mjs b/module/data/tagTeamRoll.mjs new file mode 100644 index 00000000..de71a11b --- /dev/null +++ b/module/data/tagTeamRoll.mjs @@ -0,0 +1,20 @@ +import { DhCharacter } from './actor/_module.mjs'; + +export default class DhTagTeamRoll extends foundry.abstract.DataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + initiator: new fields.SchemaField({ + id: new fields.StringField({ nullable: true, initial: null }), + cost: new fields.NumberField({ integer: true, min: 0, initial: 3 }) + }), + members: new fields.TypedObjectField( + new fields.SchemaField({ + messageId: new fields.StringField({ required: true, nullable: true, initial: null }), + selected: new fields.BooleanField({ required: true, initial: false }) + }) + ) + }; + } +} diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 8cd3caac..b322aae7 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -1,5 +1,6 @@ import DamageDialog from '../applications/dialogs/damageDialog.mjs'; import { parseRallyDice } from '../helpers/utils.mjs'; +import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs'; import DHRoll from './dhRoll.mjs'; export default class DamageRoll extends DHRoll { @@ -280,7 +281,10 @@ export default class DamageRoll extends DHRoll { return mods; } - static async reroll(rollPart, dice, result) { + static async reroll(target, message) { + const { damageType, part, dice, result } = target.dataset; + const rollPart = message.system.damage[damageType].parts[part]; + let diceIndex = 0; let parsedRoll = game.system.api.dice.DamageRoll.fromData({ ...rollPart.roll, @@ -349,6 +353,29 @@ export default class DamageRoll extends DHRoll { }; }); - return { parsedRoll, rerolledDice }; + const updateMessage = game.messages.get(message._id); + const damageParts = updateMessage.system.damage[damageType].parts.map((damagePart, index) => { + if (index !== Number(part)) return damagePart; + return { + ...rollPart, + total: parsedRoll.total, + dice: rerolledDice + }; + }); + await updateMessage.update({ + [`system.damage.${damageType}`]: { + ...updateMessage, + total: parsedRoll.total, + parts: damageParts + } + }); + + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll }); + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { + refreshType: RefreshType.TagTeamRoll + } + }); } } diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index b74adaf3..ec2ea0c7 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -21,9 +21,6 @@ export default class DHRoll extends Roll { static async build(config = {}, message = {}) { const roll = await this.buildConfigure(config, message); if (!roll) return; - - if (config.skips?.createMessage) config.messageRoll = roll; - await this.buildEvaluate(roll, config, (message = {})); await this.buildPost(roll, config, (message = {})); return config; @@ -33,6 +30,12 @@ export default class DHRoll extends Roll { config.hooks = [...this.getHooks(), '']; config.dialog ??= {}; + const actorIdSplit = config.source?.actor?.split('.'); + if (actorIdSplit) { + const tagTeamSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); + config.tagTeamSelected = Boolean(tagTeamSettings.members[actorIdSplit[actorIdSplit.length - 1]]); + } + for (const hook of config.hooks) { if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null; } @@ -143,7 +146,6 @@ export default class DHRoll extends Roll { return foundry.applications.handlebars.renderTemplate(template, { ...chatData, parent: chatData.parent, - targetMode: chatData.targetMode, metagamingSettings }); } diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 03035f68..eb15fa5e 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -374,9 +374,9 @@ export default class DualityRoll extends D20Roll { } } - static async reroll(rollBase, dieIndex, diceType) { - let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollBase, evaluated: false }); - const term = parsedRoll.terms[dieIndex]; + static async reroll(rollString, target, message) { + let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollString, evaluated: false }); + const term = parsedRoll.terms[target.dataset.dieIndex]; await term.reroll(`/r1=${term.total}`); const result = await parsedRoll.evaluate(); @@ -393,35 +393,35 @@ export default class DualityRoll extends D20Roll { options: { appearance: {} } }; - const diceSoNicePresets = await getDiceSoNicePresets(`d${term._faces}`, `d${term._faces}`); - if (diceSoNicePresets[diceType]) { - diceSoNiceRoll.dice[0].options = diceSoNicePresets[diceType]; + const diceSoNicePresets = await getDiceSoNicePresets(result, `d${term._faces}`, `d${term._faces}`); + const type = target.dataset.type; + if (diceSoNicePresets[type]) { + diceSoNiceRoll.dice[0].options = diceSoNicePresets[type]; } await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true); - } else { - foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); } const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, { - targets: parsedRoll.options.targets ?? [], + targets: message.system.targets, roll: { - advantage: parsedRoll.options.roll.advantage?.type, - difficulty: parsedRoll.options.roll.difficulty ? Number(parsedRoll.options.roll.difficulty) : null + advantage: message.system.roll.advantage?.type, + difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null } }); const extraIndex = newRoll.advantage ? 3 : 2; newRoll.extra = newRoll.extra.slice(extraIndex); - const actor = parsedRoll.options.source.actor - ? await foundry.utils.fromUuid(parsedRoll.options.source.actor) - : null; + const tagTeamSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); + + const actor = message.system.source.actor ? await foundry.utils.fromUuid(message.system.source.actor) : null; const config = { - source: { actor: parsedRoll.options.source.actor ?? '' }, - targets: parsedRoll.targets, + source: { actor: message.system.source.actor ?? '' }, + targets: message.system.targets, + tagTeamSelected: Object.values(tagTeamSettings.members).some(x => x.messageId === message._id), roll: newRoll, - rerolledRoll: parsedRoll.roll, + rerolledRoll: message.system.roll, resourceUpdates: new ResourceUpdateMap(actor) }; diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 043d7f33..0fa84255 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -78,7 +78,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { throw new Error('The array of sub-types to restrict to must not be empty.'); } - const creatableEffects = types || ['base']; + const creatableEffects = types || ['base', 'armor']; const documentTypes = this.TYPES.filter(type => creatableEffects.includes(type)).map(type => { const labelKey = `TYPES.ActiveEffect.${type}`; const label = game.i18n.has(labelKey) ? game.i18n.localize(labelKey) : type; diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 023beaa0..7326995f 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -4,7 +4,6 @@ import DHFeature from '../data/item/feature.mjs'; import { createScrollText, damageKeyToNumber, getDamageKey } from '../helpers/utils.mjs'; import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs'; import { ResourceUpdateMap } from '../data/action/baseAction.mjs'; -import { abilities } from '../config/actorConfig.mjs'; export default class DhpActor extends Actor { parties = new Set(); @@ -510,30 +509,6 @@ export default class DhpActor extends Actor { return await rollClass.build(config); } - async rollTrait(trait, options = {}) { - const abilityLabel = game.i18n.localize(abilities[trait].label); - const config = { - event: event, - title: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${this.name}`, - headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { - ability: abilityLabel - }), - effects: await game.system.api.data.actions.actionsTypes.base.getEffects(this), - roll: { - trait: trait, - type: 'trait' - }, - hasRoll: true, - actionType: 'action', - headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${this.name}`, - title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { - ability: abilityLabel - }), - ...options - }; - return await this.diceRoll(config); - } - get rollClass() { return CONFIG.Dice.daggerheart[['character', 'companion'].includes(this.type) ? 'DualityRoll' : 'D20Roll']; } diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index 8b094678..53921329 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -177,6 +177,14 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { 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 }); + await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { + refreshType: RefreshType.TagTeamRoll + } + }); } async onApplyDamage(event) { diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index 6cfecb00..4793f1f7 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -224,7 +224,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti if (locked || element.dataset.hasOwnProperty('locked')) this.lockTooltip(); } - _setAnchor(direction, options = {}) { + _setAnchor(direction, options) { const directions = this.constructor.TOOLTIP_DIRECTIONS; const pad = this.constructor.TOOLTIP_MARGIN_PX; const pos = this.element.getBoundingClientRect(); diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 7f30d970..1c47f8dc 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -49,8 +49,7 @@ export default class RegisterHandlebarsHelpers { } static damageSymbols(damageParts) { - const allTypes = [...new Set([...damageParts].flatMap(x => Array.from(x.type)))]; - const symbols = allTypes.map(p => CONFIG.DH.GENERAL.damageTypes[p].icon); + const symbols = [...new Set(damageParts.map(x => x.type))].map(p => CONFIG.DH.GENERAL.damageTypes[p].icon); return new Handlebars.SafeString(Array.from(symbols).map(symbol => ``)); } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 59ebbbb3..bb05fc39 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -528,8 +528,7 @@ export function expireActiveEffects(actor, allowedTypes = null) { export async function getCritDamageBonus(formula) { const critRoll = new Roll(formula); - await critRoll.evaluate(); - return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.results.filter(r => r.active).length, 0); + return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.number, 0); } export function htmlToText(html) { diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index 36df8b54..f51e1035 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -39,7 +39,6 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs', 'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs', 'systems/daggerheart/templates/dialogs/downtime/activities.hbs', - 'systems/daggerheart/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs', 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs', 'systems/daggerheart/templates/ui/chat/parts/roll-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/description-part.hbs', @@ -48,7 +47,6 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs', 'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs', 'systems/daggerheart/templates/scene/dh-config.hbs', - 'systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs', - 'systems/daggerheart/templates/sheets/activeEffect/typeChanges/armorChange.hbs' + 'systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs' ]); }; diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index df837ef4..dff85fef 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -193,7 +193,7 @@ export async function runMigrations() { } if (foundry.utils.isNewerVersion('1.2.7', lastMigrationVersion)) { - const tagTeam = game.settings.get(CONFIG.DH.id, 'TagTeamRoll'); + const tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); const initatorMissing = tagTeam.initiator && !game.actors.some(actor => actor.id === tagTeam.initiator); const missingMembers = Object.keys(tagTeam.members).reduce((acc, id) => { if (!game.actors.some(actor => actor.id === id)) { @@ -206,7 +206,7 @@ export async function runMigrations() { initiator: initatorMissing ? null : tagTeam.initiator, members: missingMembers }); - await game.settings.set(CONFIG.DH.id, 'TagTeamRoll', tagTeam); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, tagTeam); lastMigrationVersion = '1.2.7'; } @@ -374,18 +374,15 @@ export async function runMigrations() { if (migrationArmorScore !== undefined && !hasArmorEffect) { await item.createEmbeddedDocuments('ActiveEffect', [ { - ...game.system.api.data.activeEffects.changeTypes.armor.getDefaultArmorEffect(), + ...game.system.api.data.activeEffects.ArmorEffect.getDefaultObject(), changes: [ { - key: 'Armor', - type: CONFIG.DH.GENERAL.activeEffectModes.armor, + key: 'system.armorScore', + type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, phase: 'initial', priority: 20, value: 0, - typeData: { - type: 'armor', - max: migrationArmorScore.toString() - } + max: migrationArmorScore.toString() } ] } diff --git a/module/systemRegistration/settings.mjs b/module/systemRegistration/settings.mjs index 658d2bd1..e7ec37f5 100644 --- a/module/systemRegistration/settings.mjs +++ b/module/systemRegistration/settings.mjs @@ -15,7 +15,7 @@ import { DhMetagamingSettings, DhVariantRuleSettings } from '../applications/settings/_module.mjs'; -import { CompendiumBrowserSettings } from '../data/_module.mjs'; +import { CompendiumBrowserSettings, DhTagTeamRoll } from '../data/_module.mjs'; export const registerDHSettings = () => { registerMenuSettings(); @@ -157,6 +157,12 @@ const registerNonConfigSettings = () => { type: DhCountdowns }); + game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, { + scope: 'world', + config: false, + type: DhTagTeamRoll + }); + game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings, { scope: 'world', config: false, diff --git a/module/systemRegistration/socket.mjs b/module/systemRegistration/socket.mjs index fb152959..173ef02b 100644 --- a/module/systemRegistration/socket.mjs +++ b/module/systemRegistration/socket.mjs @@ -15,9 +15,6 @@ export function handleSocketEvent({ action = null, data = {} } = {}) { case socketEvent.DowntimeTrigger: Party.downtimeMoveQuery(data); break; - case socketEvent.TagTeamStart: - Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.tagTeamStart, data); - break; } } @@ -25,8 +22,7 @@ export const socketEvent = { GMUpdate: 'DhGMUpdate', Refresh: 'DhRefresh', DhpFearUpdate: 'DhFearUpdate', - DowntimeTrigger: 'DowntimeTrigger', - TagTeamStart: 'DhTagTeamStart' + DowntimeTrigger: 'DowntimeTrigger' }; export const GMUpdateEvent = { diff --git a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json index 6939ff7f..ce1f499f 100644 --- a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json +++ b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json @@ -85,7 +85,7 @@ { "trigger": "dualityRoll", "triggeringActorType": "self", - "command": "/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}
\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}
\n
${game.i18n.localize('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsActionExplanation')}
\n
\n \n \n
\n
`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;" + "command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}
\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}
\n
${game.i18n.localize('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsActionExplanation')}
\n
\n \n \n
\n
`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;" } ] } diff --git a/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json b/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json index e328325d..91d8cdd2 100644 --- a/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json +++ b/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json @@ -90,22 +90,19 @@ "effects": [ { "name": "Armorer", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "1", - "armorInteraction": "active" - } + "max": "1" } - ] + ], + "armorInteraction": "active" }, "_id": "tJw2JIPcT9hEMRXg", "img": "icons/tools/hand/hammer-and-nail.webp", diff --git a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json index 69d1a2b2..40af13a7 100644 --- a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json +++ b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json @@ -22,22 +22,19 @@ "effects": [ { "name": "Bare Bones Armor", - "type": "base", + "type": "armor", "system": { "changes": [ { "value": 0, - "key": "Armor", + "max": "3 + @system.traits.strength.value", + "key": "system.armorScore", "type": "armor", "phase": "initial", - "priority": 20, - "typeData": { - "type": "armor", - "max": "3 + @system.traits.strength.value", - "armorInteraction": "inactive" - } + "priority": 20 } - ] + ], + "armorInteraction": "inactive" }, "_id": "FCsgz7Tdsw6QUzBs", "img": "icons/magic/control/buff-strength-muscle-damage-orange.webp", diff --git a/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json b/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json index 7852356b..04f7c2aa 100644 --- a/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json +++ b/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json @@ -253,7 +253,7 @@ "origin": "Compendium.daggerheart.domains.Item.YtZzYBtR0yLPPA93", "transfer": false, "_id": "ptYT10JZ2WJHvFMd", - "type": "base", + "type": "armor", "system": { "rangeDependence": { "enabled": false, @@ -263,15 +263,12 @@ }, "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "1" - } + "max": "1" } ], "duration": { diff --git a/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json b/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json index 870ff246..05537863 100644 --- a/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json +++ b/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json @@ -91,19 +91,16 @@ "effects": [ { "name": "Valor-Touched", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "1" - } + "max": "1" } ] }, diff --git a/src/packs/environments/folders_Tier_1_GQ0VnOLrKBIHR6Us.json b/src/packs/environments/folders_Tier_1_GQ0VnOLrKBIHR6Us.json index 38f35169..7e6595b9 100644 --- a/src/packs/environments/folders_Tier_1_GQ0VnOLrKBIHR6Us.json +++ b/src/packs/environments/folders_Tier_1_GQ0VnOLrKBIHR6Us.json @@ -1,5 +1,5 @@ { - "type": "Actor", + "type": "Item", "folder": null, "name": "Tier 1", "color": null, diff --git a/src/packs/environments/folders_Tier_2_XMeecO3IRvu5ck6F.json b/src/packs/environments/folders_Tier_2_XMeecO3IRvu5ck6F.json index 2af9df23..9a77a641 100644 --- a/src/packs/environments/folders_Tier_2_XMeecO3IRvu5ck6F.json +++ b/src/packs/environments/folders_Tier_2_XMeecO3IRvu5ck6F.json @@ -1,5 +1,5 @@ { - "type": "Actor", + "type": "Item", "folder": null, "name": "Tier 2", "color": null, diff --git a/src/packs/environments/folders_Tier_3_MfrIkJK12PAEfbPL.json b/src/packs/environments/folders_Tier_3_MfrIkJK12PAEfbPL.json index 400a2ca9..1ae0a609 100644 --- a/src/packs/environments/folders_Tier_3_MfrIkJK12PAEfbPL.json +++ b/src/packs/environments/folders_Tier_3_MfrIkJK12PAEfbPL.json @@ -1,5 +1,5 @@ { - "type": "Actor", + "type": "Item", "folder": null, "name": "Tier 3", "color": null, diff --git a/src/packs/environments/folders_Tier_4_IKumu5HTLqONLYqb.json b/src/packs/environments/folders_Tier_4_IKumu5HTLqONLYqb.json index 3a171ddb..ed5b9f0a 100644 --- a/src/packs/environments/folders_Tier_4_IKumu5HTLqONLYqb.json +++ b/src/packs/environments/folders_Tier_4_IKumu5HTLqONLYqb.json @@ -1,5 +1,5 @@ { - "type": "Actor", + "type": "Item", "folder": null, "name": "Tier 4", "color": null, diff --git a/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json b/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json index 1be3d76b..91b1f890 100644 --- a/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json +++ b/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json @@ -65,21 +65,18 @@ "_key": "!items.effects!LzLOJ9EVaHWAjoq9.qlzHOAnpBYzosQxK" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "6" - } + "max": "6" } ] }, diff --git a/src/packs/items/armors/armor_Advanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json b/src/packs/items/armors/armor_Advanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json index 60cc3da9..f1dd3086 100644 --- a/src/packs/items/armors/armor_Advanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json +++ b/src/packs/items/armors/armor_Advanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json @@ -70,21 +70,18 @@ "_key": "!items.effects!crIbCb9NZ4K0VpoU.awdHgEaM54G3emOU" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "6" - } + "max": "6" } ] }, diff --git a/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json b/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json index c8bf64d5..1618fe85 100644 --- a/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json +++ b/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json @@ -65,21 +65,18 @@ "_key": "!items.effects!epkAmlZVk7HOfUUT.Fq9Q93IHCchhfSss" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json b/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json index 6597bea0..b7a63b40 100644 --- a/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json +++ b/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json @@ -27,21 +27,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json new file mode 100644 index 00000000..a0b2aae3 --- /dev/null +++ b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json @@ -0,0 +1,71 @@ +{ + "folder": "tI3bfr6Sgi16Z7zm", + "name": "Bare Bones", + "type": "armor", + "_id": "ITAjcigTcUw5pMCN", + "img": "icons/magic/control/buff-strength-muscle-damage.webp", + "system": { + "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

", + "actions": {}, + "attached": [], + "tier": 1, + "equipped": false, + "baseScore": 3, + "armorFeatures": [], + "marks": { + "value": 0 + }, + "baseThresholds": { + "major": 9, + "severe": 19 + } + }, + "effects": [ + { + "name": "Bare Bones", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "@system.traits.strength.value" + } + ] + }, + "_id": "C7as6q5bx3S0Xxfn", + "img": "icons/magic/control/buff-strength-muscle-damage.webp", + "disabled": false, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!ITAjcigTcUw5pMCN.C7as6q5bx3S0Xxfn" + } + ], + "sort": 0, + "ownership": { + "default": 0, + "MQSznptE5yLT7kj8": 3 + }, + "flags": {}, + "_key": "!items!ITAjcigTcUw5pMCN" +} diff --git a/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json b/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json index a5ac42ba..9242aed9 100644 --- a/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json +++ b/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json @@ -65,21 +65,18 @@ "_key": "!items.effects!WuoVwZA53XRAIt6d.Hy0sNtFS1JAXxgwC" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json index 12f9441c..cc245d50 100644 --- a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json +++ b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json @@ -65,21 +65,18 @@ "_key": "!items.effects!mNN6pvcsS10ChrWF.s8KtTIngTjnOlaTP" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "6" - } + "max": "6" } ] }, diff --git a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json index e1e8be0a..a45cfa04 100644 --- a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json +++ b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json @@ -65,21 +65,18 @@ "_key": "!items.effects!haULhuEg37zUUvhb.ZfO5NjpqEIzZVlPq" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "4" - } + "max": "4" } ] }, diff --git a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json index e4a50d08..6f8ec29d 100644 --- a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json +++ b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json @@ -65,21 +65,18 @@ "_key": "!items.effects!vMJxEWz1srfwMsoj.8bwf1Ri3jYkjphEv" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json index d7a9e182..335ab56f 100644 --- a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json +++ b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json @@ -64,21 +64,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json index 91a58fcf..fe37485c 100644 --- a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json +++ b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json @@ -90,21 +90,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "7" - } + "max": "7" } ] }, diff --git a/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json index c217a626..e19c363b 100644 --- a/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json +++ b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json @@ -66,21 +66,18 @@ "_key": "!items.effects!Q6LxmtFetDDkoZVZ.xGxqTCO8MjNq5Cw6" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "4" - } + "max": "4" } ] }, diff --git a/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json b/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json index dfdefdcb..03662d06 100644 --- a/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json +++ b/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json @@ -88,21 +88,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "6" - } + "max": "6" } ] }, diff --git a/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json b/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json index 4a3bcdc9..52aa784b 100644 --- a/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json +++ b/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json @@ -65,21 +65,18 @@ "_key": "!items.effects!7emTSt6nhZuTlvt5.QIefVb73cm9gYju8" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "4" - } + "max": "4" } ] }, diff --git a/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json b/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json index 4427c082..c603305b 100644 --- a/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json +++ b/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json @@ -70,21 +70,18 @@ "_key": "!items.effects!UdUJNa31WxFW2noa.mfKMW9SX3Mnos1nY" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "4" - } + "max": "4" } ] }, diff --git a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json index a0bdf8d6..36484785 100644 --- a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json +++ b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json @@ -65,21 +65,18 @@ "_key": "!items.effects!yJFp1bfpecDcStVK.v1FNEsypRF5W6vVc" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "3" - } + "max": "3" } ] }, diff --git a/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json b/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json index 2bfc3ff6..a6397482 100644 --- a/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json +++ b/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json @@ -81,21 +81,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "4" - } + "max": "4" } ] }, diff --git a/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json b/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json index 79c52596..dd674a8b 100644 --- a/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json +++ b/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json @@ -65,21 +65,18 @@ "_key": "!items.effects!K5WkjS0NGqHYmhU3.JHupzYULxdQzFzuj" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Improved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json b/src/packs/items/armors/armor_Improved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json index bbbd6183..00b2afa9 100644 --- a/src/packs/items/armors/armor_Improved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json +++ b/src/packs/items/armors/armor_Improved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json @@ -70,21 +70,18 @@ "_key": "!items.effects!9f7RozpPTqrzJS1m.wstJ1aKKtmXgCwxB" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json b/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json index 6c32e3ca..3e2a52ba 100644 --- a/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json +++ b/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json @@ -65,21 +65,18 @@ "_key": "!items.effects!jphnMZjnS2FkOH3s.BFwU3ErPaajUSMUz" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "4" - } + "max": "4" } ] }, diff --git a/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json b/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json index 26092645..7dfe14e7 100644 --- a/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json +++ b/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json @@ -27,21 +27,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "4" - } + "max": "4" } ] }, diff --git a/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json b/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json index 756fccdc..8b978654 100644 --- a/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json +++ b/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json @@ -77,21 +77,18 @@ "_key": "!items.effects!tzZntboNtHL5C6VM.P3aCN8PQgPXP4C9M" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "4" - } + "max": "4" } ] }, diff --git a/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json b/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json index 601cf23c..1b3cae95 100644 --- a/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json +++ b/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json @@ -27,21 +27,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "3" - } + "max": "3" } ] }, diff --git a/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json b/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json index 867c2420..fd3d1f9f 100644 --- a/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json +++ b/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json @@ -65,21 +65,18 @@ "_key": "!items.effects!EsIN5OLKe9ZYFNXZ.8Oa6Y375X8UpcPph" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "7" - } + "max": "7" } ] }, diff --git a/src/packs/items/armors/armor_Legendary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json b/src/packs/items/armors/armor_Legendary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json index 2fb8c064..a32a6be5 100644 --- a/src/packs/items/armors/armor_Legendary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json +++ b/src/packs/items/armors/armor_Legendary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json @@ -70,21 +70,18 @@ "_key": "!items.effects!SXWjUR2aUR6bYvdl.zvzkRX2Uevemmbz4" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "7" - } + "max": "7" } ] }, diff --git a/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json b/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json index 8ab2894d..85fdb01b 100644 --- a/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json +++ b/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json @@ -65,21 +65,18 @@ "_key": "!items.effects!c6tMXz4rPf9ioQrf.3AUNxBoj7mp1ziJQ" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "6" - } + "max": "6" } ] }, diff --git a/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json b/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json index 44c29bfb..7019a908 100644 --- a/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json +++ b/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json @@ -27,21 +27,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "6" - } + "max": "6" } ] }, diff --git a/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json b/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json index db0c20d1..464d6505 100644 --- a/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json +++ b/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json @@ -65,21 +65,18 @@ "_key": "!items.effects!AQzU2RsqS5V5bd1v.3n4O7PyAWMEFdr5p" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "6" - } + "max": "6" } ] }, diff --git a/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json b/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json index 55cc6eb8..172e100b 100644 --- a/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json +++ b/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json @@ -57,21 +57,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json index 2ca27f45..66a88157 100644 --- a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json +++ b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json @@ -64,21 +64,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "6" - } + "max": "6" } ] }, diff --git a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json index 54f6322e..3039c375 100644 --- a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json +++ b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json @@ -64,21 +64,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "4" - } + "max": "4" } ] }, diff --git a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json index f40df5c7..739f15d9 100644 --- a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json +++ b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json @@ -95,21 +95,18 @@ "_key": "!items.effects!8X16lJQ3xltTwynm.rkrqlwqtR9REgRx7" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "8" - } + "max": "8" } ] }, diff --git a/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json b/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json index 8f15ae28..b8b12bc3 100644 --- a/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json +++ b/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json @@ -70,21 +70,18 @@ "_key": "!items.effects!QjwsIhXKqnlvRBMv.V8CcTcVAIxHq8KNd" }, { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json b/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json index eb89421d..8be5ce42 100644 --- a/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json +++ b/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json @@ -57,21 +57,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "5" - } + "max": "5" } ] }, diff --git a/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json b/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json index 6e5f24bc..301c1d25 100644 --- a/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json +++ b/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json @@ -57,21 +57,18 @@ }, "effects": [ { - "type": "base", + "type": "armor", "name": "Armor Effect", "img": "icons/equipment/chest/breastplate-helmet-metal.webp", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "6" - } + "max": "6" } ] }, diff --git a/src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json b/src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json new file mode 100644 index 00000000..65c4eca8 --- /dev/null +++ b/src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json @@ -0,0 +1,12 @@ +{ + "type": "Item", + "folder": null, + "name": "Special", + "color": null, + "sorting": "a", + "_id": "tI3bfr6Sgi16Z7zm", + "description": "", + "sort": 0, + "flags": {}, + "_key": "!folders!tI3bfr6Sgi16Z7zm" +} diff --git a/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json b/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json index 85321808..c7f9aac1 100644 --- a/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json +++ b/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json @@ -114,19 +114,16 @@ "description": "

Add the item's Tier to your Armor Score

", "img": "icons/skills/melee/shield-block-gray-orange.webp", "_id": "7285CRGdZfHCEtT2", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "ITEM.@system.tier" - } + "max": "ITEM.@system.tier" } ] }, diff --git a/src/packs/items/weapons/weapon_Advanced_Tower_Shield_OfOzQbs4hg6QbfTG.json b/src/packs/items/weapons/weapon_Advanced_Tower_Shield_OfOzQbs4hg6QbfTG.json index 568e7ebc..666fae0f 100644 --- a/src/packs/items/weapons/weapon_Advanced_Tower_Shield_OfOzQbs4hg6QbfTG.json +++ b/src/packs/items/weapons/weapon_Advanced_Tower_Shield_OfOzQbs4hg6QbfTG.json @@ -152,19 +152,16 @@ "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", "img": "icons/skills/melee/shield-block-bash-blue.webp", "_id": "J0f7zqqOr61ADpdy", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "ITEM.@system.tier + 1" - } + "max": "ITEM.@system.tier + 1" } ] }, diff --git a/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json b/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json index 322682d9..14977b0c 100644 --- a/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json +++ b/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json @@ -114,19 +114,16 @@ "description": "

Add the item's Tier to your Armor Score

", "img": "icons/skills/melee/shield-block-gray-orange.webp", "_id": "pZCrWd7zLTarvEQK", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "ITEM.@system.tier" - } + "max": "ITEM.@system.tier" } ] }, diff --git a/src/packs/items/weapons/weapon_Improved_Tower_Shield_bxt3NsbMqTSdI5ab.json b/src/packs/items/weapons/weapon_Improved_Tower_Shield_bxt3NsbMqTSdI5ab.json index 70647216..7e3b178b 100644 --- a/src/packs/items/weapons/weapon_Improved_Tower_Shield_bxt3NsbMqTSdI5ab.json +++ b/src/packs/items/weapons/weapon_Improved_Tower_Shield_bxt3NsbMqTSdI5ab.json @@ -152,19 +152,16 @@ "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", "img": "icons/skills/melee/shield-block-bash-blue.webp", "_id": "XugJeHJdnC6IymSa", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "ITEM.@system.tier + 1" - } + "max": "ITEM.@system.tier + 1" } ] }, diff --git a/src/packs/items/weapons/weapon_Knuckle_Blades_U8gfyvxoHm024inM.json b/src/packs/items/weapons/weapon_Knuckle_Blades_U8gfyvxoHm024inM.json index ffe92071..56ff6d62 100644 --- a/src/packs/items/weapons/weapon_Knuckle_Blades_U8gfyvxoHm024inM.json +++ b/src/packs/items/weapons/weapon_Knuckle_Blades_U8gfyvxoHm024inM.json @@ -33,7 +33,7 @@ "tier": 2, "equipped": false, "secondary": false, - "burden": "twoHanded", + "burden": "oneHanded", "weaponFeatures": [ { "value": "brutal", diff --git a/src/packs/items/weapons/weapon_Labrys_Axe_ijWppQzSOqVCb3rE.json b/src/packs/items/weapons/weapon_Labrys_Axe_ijWppQzSOqVCb3rE.json index 1adda37c..9c454119 100644 --- a/src/packs/items/weapons/weapon_Labrys_Axe_ijWppQzSOqVCb3rE.json +++ b/src/packs/items/weapons/weapon_Labrys_Axe_ijWppQzSOqVCb3rE.json @@ -114,19 +114,16 @@ "description": "Add your character's Tier to your Armor Score", "img": "icons/skills/melee/shield-block-gray-orange.webp", "_id": "vnR4Zhnb0rOqwrFw", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "ITEM.@system.tier" - } + "max": "ITEM.@system.tier" } ] }, diff --git a/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json b/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json index 7e0d5374..af83aaaf 100644 --- a/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json +++ b/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json @@ -114,19 +114,16 @@ "description": "

Add the item's Tier to your Armor Score

", "img": "icons/skills/melee/shield-block-gray-orange.webp", "_id": "EixxJrRHyc6kj3Wg", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "ITEM.@system.tier" - } + "max": "ITEM.@system.tier" } ] }, diff --git a/src/packs/items/weapons/weapon_Legendary_Tower_Shield_MaJIROht7A9LxIZx.json b/src/packs/items/weapons/weapon_Legendary_Tower_Shield_MaJIROht7A9LxIZx.json index 945678e5..70e71d6c 100644 --- a/src/packs/items/weapons/weapon_Legendary_Tower_Shield_MaJIROht7A9LxIZx.json +++ b/src/packs/items/weapons/weapon_Legendary_Tower_Shield_MaJIROht7A9LxIZx.json @@ -152,19 +152,16 @@ "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", "img": "icons/skills/melee/shield-block-bash-blue.webp", "_id": "1fgUIaXl6VQrhP7j", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "ITEM.@system.tier + 1" - } + "max": "ITEM.@system.tier + 1" } ] }, diff --git a/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json b/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json index 987892b2..997ce4d0 100644 --- a/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json +++ b/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json @@ -114,19 +114,16 @@ "description": "

Add the item's Tier to your Armor Score

", "img": "icons/skills/melee/shield-block-gray-orange.webp", "_id": "eV4lFIpQMiKERj4U", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "ITEM.@system.tier" - } + "max": "ITEM.@system.tier" } ] }, diff --git a/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json b/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json index fed95455..fe01d808 100644 --- a/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json +++ b/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json @@ -160,20 +160,17 @@ "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "1" - } + "max": "1" } ] }, "_id": "mvUY9LGfwICak7cE", - "type": "base", + "type": "armor", "disabled": false, "duration": { "value": null, diff --git a/src/packs/items/weapons/weapon_Tower_Shield_C9aWpK1shVMWP4m5.json b/src/packs/items/weapons/weapon_Tower_Shield_C9aWpK1shVMWP4m5.json index 5cf27983..5d72d4f5 100644 --- a/src/packs/items/weapons/weapon_Tower_Shield_C9aWpK1shVMWP4m5.json +++ b/src/packs/items/weapons/weapon_Tower_Shield_C9aWpK1shVMWP4m5.json @@ -152,19 +152,16 @@ "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", "img": "icons/skills/melee/shield-block-bash-blue.webp", "_id": "tLRc4UAnGuIq7er3", - "type": "base", + "type": "armor", "system": { "changes": [ { - "key": "Armor", + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "typeData": { - "type": "armor", - "max": "ITEM.@system.tier + 1" - } + "max": "ITEM.@system.tier + 1" } ] }, diff --git a/styles/less/dialog/dice-roll/roll-selection.less b/styles/less/dialog/dice-roll/roll-selection.less index a1a01e6b..7fdae77a 100644 --- a/styles/less/dialog/dice-roll/roll-selection.less +++ b/styles/less/dialog/dice-roll/roll-selection.less @@ -69,6 +69,29 @@ background: light-dark(@dark-blue-40, @golden-40); } } + + .tag-team-controller { + display: flex; + align-items: center; + border-radius: 5px; + width: fit-content; + gap: 5px; + cursor: pointer; + padding: 5px; + background: light-dark(@dark-blue-10, @golden-10); + color: light-dark(@dark-blue, @golden); + + .label { + font-style: normal; + font-weight: 400; + font-size: var(--font-size-14); + line-height: 17px; + } + + &.selected { + background: light-dark(@dark-blue-40, @golden-40); + } + } } .roll-dialog-container { diff --git a/styles/less/dialog/index.less b/styles/less/dialog/index.less index 73738eaa..0c70df9f 100644 --- a/styles/less/dialog/index.less +++ b/styles/less/dialog/index.less @@ -32,8 +32,6 @@ @import './reroll-dialog/sheet.less'; @import './group-roll/group-roll.less'; - -@import './tag-team-dialog/initialization.less'; @import './tag-team-dialog/sheet.less'; @import './image-select/sheet.less'; diff --git a/styles/less/dialog/tag-team-dialog/initialization.less b/styles/less/dialog/tag-team-dialog/initialization.less deleted file mode 100644 index 30676f82..00000000 --- a/styles/less/dialog/tag-team-dialog/initialization.less +++ /dev/null @@ -1,59 +0,0 @@ -.daggerheart.dialog.dh-style.views.tag-team-dialog { - .initialization-container { - h2 { - text-align: center; - } - - .members-container { - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; - gap: 8px; - - .member-container { - position: relative; - display: flex; - justify-content: center; - - &.inactive { - opacity: 0.4; - } - - .member-name { - position: absolute; - } - } - } - - .initiator-container { - margin-top: 8px; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 8px; - - &.inactive { - opacity: 0.4; - } - } - - footer { - margin-top: 8px; - display: flex; - gap: 8px; - - button { - flex: 1; - } - - .finish-tools { - flex: none; - display: flex; - align-items: center; - gap: 4px; - - &.inactive { - opacity: 0.4; - } - } - } - } -} diff --git a/styles/less/dialog/tag-team-dialog/sheet.less b/styles/less/dialog/tag-team-dialog/sheet.less index e72b4956..767c66ca 100644 --- a/styles/less/dialog/tag-team-dialog/sheet.less +++ b/styles/less/dialog/tag-team-dialog/sheet.less @@ -1,251 +1,178 @@ .daggerheart.dialog.dh-style.views.tag-team-dialog { - .tag-team-roll-container { + .tag-team-container { display: flex; flex-direction: column; gap: 16px; - &.inactive { - opacity: 0.4; - pointer-events: none; + .tag-team-data-container { + display: flex; + align-items: center; + gap: 8px; + + .form-group { + flex: 0; + + label { + white-space: nowrap; + } + + &.flex-group { + flex: 1; + } + } } - .team-container { + .title-row { display: flex; - gap: 16px; + align-items: center; + gap: 8px; - .member-container { + h2 { + text-align: start; + } + + select { + flex: 1; + } + } + + .participants-container { + margin-top: 8px; + display: flex; + flex-direction: column; + gap: 4px; + + .participant-outer-container { + padding: 8px; display: flex; flex-direction: column; - justify-content: space-between; - gap: 8px; - flex: 1; + gap: 4px; + cursor: pointer; + border-radius: 6px; - &.inactive { - opacity: 0.4; - pointer-events: none; + &.selected, + &:hover { + background-color: light-dark(@golden-40, @golden-40); } - .data-container { - display: flex; - flex-direction: column; - gap: 8px; - width: 100%; - } - - .member-info { + .participant-container { display: flex; align-items: center; justify-content: space-between; - width: 100%; - - img { - height: 64px; - border-radius: 6px; - border: 1px solid light-dark(@dark-blue, @golden); - } - - .member-name { - flex: 1; - text-align: center; - font-size: var(--font-size-18); - } - } - - .roll-setup { - width: 100%; - } - - .roll-container { - display: flex; - flex-direction: column; - } - - .roll-title { - font-size: var(--font-size-20); - font-weight: bold; - color: light-dark(@dark-blue, @golden); - text-align: center; - display: flex; - align-items: center; gap: 8px; - &::before, - &::after { - color: light-dark(@dark-blue, @golden); - content: ''; + .participant-inner-container { flex: 1; - height: 2px; - } - - &::before { - background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, light-dark(@dark-blue, @golden) 100%); - } - - &::after { - background: linear-gradient(90deg, light-dark(@dark-blue, @golden) 0%, rgba(0, 0, 0, 0) 100%); - } - } - - .roll-tools { - display: flex; - gap: 4px; - align-items: center; - - img { - height: 16px; - } - - a { display: flex; - font-size: 16px; + align-items: center; + gap: 4px; - &:hover { - text-shadow: none; - filter: drop-shadow(0 0 8px var(--golden)); + img { + height: 48px; + width: 48px; + border-radius: 50%; + } + + .participant-labels { + display: flex; + flex-direction: column; + gap: 2px; + + .participant-label-title { + font-size: 18px; + } + + .participant-label-info { + display: flex; + gap: 4px; + + .participant-label-info-part { + border: 1px solid light-dark(white, white); + border-radius: 4px; + padding: 2px 4px; + background-color: light-dark(@beige-80, @soft-white-shadow); + color: white; + } + } } } } - .roll-data { + .participant-empty-roll-container { + border: 1px dashed white; + padding: 8px 2px; + text-align: center; + font-style: italic; + } + + .participant-roll-outer-container { display: flex; flex-direction: column; - align-items: center; - gap: 4px; + gap: 2px; + color: light-dark(@dark-blue, @golden); - &.hope { - --text-color: @golden; - --bg-color: @golden-40; - } - - &.fear { - --text-color: @chat-blue; - --bg-color: @chat-blue-40; - } - - &.critical { - --text-color: @chat-purple; - --bg-color: @chat-purple-40; - } - - .duality-label { - color: var(--text-color); - font-size: var(--font-size-20); - font-weight: bold; + h4 { text-align: center; - - .unused-damage { - text-decoration: line-through; - } + color: light-dark(@dark-blue, @golden); } - .roll-dice-container { + .participant-roll-container { display: flex; align-items: center; justify-content: center; - flex-wrap: wrap; + white-space: nowrap; + + .participant-roll-text-container { + padding: 0 8px; + white-space: nowrap; + display: flex; + } + } + + .damage-values-container { + display: flex; + justify-content: space-around; gap: 8px; - .roll-dice { - position: relative; + .damage-container { + border: 1px solid light-dark(white, white); + border-radius: 6px; + padding: 0 4px; display: flex; - align-items: center; - justify-content: center; - - .dice-label { - position: absolute; - color: white; - font-size: 1rem; - paint-order: stroke fill; - -webkit-text-stroke: 2px black; - } - - img { - height: 32px; - } - } - - .roll-operator { - font-size: var(--font-size-24); - } - - .roll-value { - font-size: 18px; - } - } - - .roll-total { - background: var(--bg-color); - color: var(--text-color); - border-radius: 4px; - padding: 3px; - } - } - - .select-roll-button { - margin-top: 8px; - - i { - color: light-dark(@dark-blue, @golden); - font-size: 48px; - - &.inactive { - opacity: 0.4; + gap: 4px; } } } } } - .results-container { - display: flex; - flex-direction: column; - align-items: center; - gap: 4px; - border: 2px solid light-dark(@dark-blue, @golden); - border-radius: 6px; - padding: 8px 10px; - - .result-container-label { - font-size: var(--font-size-24); - font-weight: bold; - } - - .results-inner-container { - display: flex; - justify-content: space-between; - gap: 8px; - width: 100%; - - .result-section-label { - font-size: var(--font-size-20); - } - - .result-container { - width: 100%; - text-align: center; - - .result-info { - display: flex; - gap: 4px; - align-items: center; - justify-content: center; - } - } - } - } - - .finish-container { - gap: 16px; - display: grid; - grid-template-columns: 1fr 1fr 1fr; - - .finish-button { - grid-column: span 2; - } - } - - .hint { + h2 { text-align: center; } + + .result-container { + display: grid; + grid-template-columns: 1fr 1fr; + align-items: center; + gap: 8px; + + .result-damages-container { + display: flex; + flex-wrap: wrap; + gap: 4px; + + .result-damage-container { + border: 1px solid light-dark(white, white); + border-radius: 6px; + padding: 0 4px; + } + } + } + + .roll-leader-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + } } } diff --git a/styles/less/global/sheet.less b/styles/less/global/sheet.less index 6f77a481..1e7bad0a 100755 --- a/styles/less/global/sheet.less +++ b/styles/less/global/sheet.less @@ -14,7 +14,11 @@ body.game:is(.performance-low, .noblur) { .themed.theme-dark .application.daggerheart.sheet.dh-style, .themed.theme-dark.application.daggerheart.sheet.dh-style, &.theme-dark .application.daggerheart { - background: @dark-blue; + &.adversary, + &.character, + &.item { + background: @dark-blue; + } } } diff --git a/styles/less/sheets/activeEffects/activeEffects.less b/styles/less/sheets/activeEffects/activeEffects.less index 077369cf..ba3ff43f 100644 --- a/styles/less/sheets/activeEffects/activeEffects.less +++ b/styles/less/sheets/activeEffects/activeEffects.less @@ -31,27 +31,5 @@ text-align: center; } } - - .armor-change-container { - padding-top: 0; - padding-bottom: 4px; - row-gap: 0; - - legend { - display: flex; - align-items: center; - padding-left: 3px; - } - - header { - padding: 0; - left: -0.25rem; // TODO: Find why this header is offset 0.25rem to the right so this can be removed. - } - - header, - ol { - grid-template-columns: 6rem 6rem 12rem 4rem; - } - } } } diff --git a/styles/less/sheets/activeEffects/armorEffects.less b/styles/less/sheets/activeEffects/armorEffects.less new file mode 100644 index 00000000..fd5c89b1 --- /dev/null +++ b/styles/less/sheets/activeEffects/armorEffects.less @@ -0,0 +1,5 @@ +.application.sheet.daggerheart.dh-style.armor-effect-config { + .tab-form-footer { + margin-top: 8px; + } +} diff --git a/styles/less/sheets/actors/party/sheet.less b/styles/less/sheets/actors/party/sheet.less index 6b51de53..2d1344e8 100644 --- a/styles/less/sheets/actors/party/sheet.less +++ b/styles/less/sheets/actors/party/sheet.less @@ -7,12 +7,8 @@ background-image: url('../assets/parchments/dh-parchment-dark.png'); } }, { - &.sheet.actor.dh-style.party { + &.party { background: url('../assets/parchments/dh-parchment-light.png'); - - .tab .actions-section .active-action { - animation: glow-dark 0.75s infinite alternate; - } } }); @@ -44,10 +40,6 @@ font-size: 12px; } } - - .active-action { - animation: glow 0.75s infinite alternate; - } } } } diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less index e5ffbf3e..25ec6fc3 100644 --- a/styles/less/sheets/index.less +++ b/styles/less/sheets/index.less @@ -44,3 +44,4 @@ @import './actions/actions.less'; @import './activeEffects/activeEffects.less'; +@import './activeEffects/armorEffects.less'; diff --git a/system.json b/system.json index 9a78bf50..dfbb473e 100644 --- a/system.json +++ b/system.json @@ -5,7 +5,7 @@ "version": "2.0.0", "compatibility": { "minimum": "14.355", - "verified": "14.357", + "verified": "14.356", "maximum": "14" }, "authors": [ @@ -278,7 +278,8 @@ }, "ActiveEffect": { "beastform": {}, - "horde": {} + "horde": {}, + "armor": {} }, "Combat": { "combat": {} diff --git a/templates/dialogs/dice-roll/header.hbs b/templates/dialogs/dice-roll/header.hbs index 486fcc8f..0cb58a01 100644 --- a/templates/dialogs/dice-roll/header.hbs +++ b/templates/dialogs/dice-roll/header.hbs @@ -8,4 +8,10 @@ {{/if}} + {{#if (and @root.hasRoll @root.activeTagTeamRoll)}} +
+ + {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.title"}} +
+ {{/if}} \ No newline at end of file diff --git a/templates/dialogs/tagTeamDialog.hbs b/templates/dialogs/tagTeamDialog.hbs new file mode 100644 index 00000000..3c96a573 --- /dev/null +++ b/templates/dialogs/tagTeamDialog.hbs @@ -0,0 +1,110 @@ +
+
+
+ {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.partyTeam"}} + +
+ + +
+ +
+ +
+ {{#each members as |member|}} +
+
+
+ +
+
{{member.character.name}}
+
+ {{#if member.character.system.class.value}} +
{{member.character.system.class.value.name}}
+ {{/if}} + {{#if member.system.multiclass.value}} +
{{member.character.system.multiclass.value.name}}
+ {{/if}} +
+
+
+ +
+ {{#if member.roll}} +
+
+

+ + {{member.roll.system.title}} +

+
+
+ +
+ {{member.roll.system.roll.total}} + {{localize "DAGGERHEART.GENERAL.withThing" thing=member.roll.system.roll.result.label}} +
+ +
+ {{#if member.roll.system.hasDamage}} +

{{localize "DAGGERHEART.GENERAL.damage"}}

+
+ {{#if member.damageValues}} + {{#each member.damageValues as |damage|}} +
+
{{damage.name}}
+
{{damage.total}}
+
+ {{/each}} + {{else}} + {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.damageNotRolled"}} + {{/if}} +
+ {{/if}} +
+ {{else}} +
+ {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.linkMessageHint"}} +
+ {{/if}} +
+ {{/each}} +
+
+ +
+

+ {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.initiatingCharacter"}} + +

+

+ {{localize "DAGGERHEART.GENERAL.Cost.single"}} + +

+
+ {{#if showResult}} + {{#if selectedData.result}} +
+

{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.title"}}: {{selectedData.result}}

+ {{#if usesDamage}} +
+ + {{#each selectedData.damageValues as |damage|}} +
+ {{damage.name}} + {{damage.total}} +
+ {{/each}} +
+ {{/if}} +
+ {{/if}} + {{/if}} + + +
+
\ No newline at end of file diff --git a/templates/dialogs/tagTeamDialog/initialization.hbs b/templates/dialogs/tagTeamDialog/initialization.hbs deleted file mode 100644 index 60b11c7e..00000000 --- a/templates/dialogs/tagTeamDialog/initialization.hbs +++ /dev/null @@ -1,34 +0,0 @@ -
-

{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.selectParticipants"}}

-
- {{#each memberSelection as |member|}} - - {{member.name}} - - - {{/each}} -
- -
-
- -
- -
-
- {{formGroup tagTeamFields.initiator.fields.cost name="initiator.cost" value=initiator.cost disabled=initiatorDisabled localize=true }} -
- - -
\ No newline at end of file diff --git a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs deleted file mode 100644 index 058777a5..00000000 --- a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs +++ /dev/null @@ -1,25 +0,0 @@ -{{#each damage as |damage key|}} -
-
- {{localize (concat "DAGGERHEART.CONFIG.HealingType." key ".name")}}: - {{damage.total}} -
- {{#each damage.parts as |part|}} -
- {{#each part.dice as |dice index|}} - - {{dice.total}} - - - {{#unless @last}} - + - {{/unless}} - {{/each}} - {{#if part.modifierTotal}} - {{#if (gte part.modifierTotal 0)}}+{{else}}-{{/if}} - {{positive part.modifierTotal}} - {{/if}} -
- {{/each}} -
-{{/each}} \ No newline at end of file diff --git a/templates/dialogs/tagTeamDialog/tagTeamRoll.hbs b/templates/dialogs/tagTeamDialog/tagTeamRoll.hbs deleted file mode 100644 index 30369d52..00000000 --- a/templates/dialogs/tagTeamDialog/tagTeamRoll.hbs +++ /dev/null @@ -1,173 +0,0 @@ -
-
-
- {{#each members as |member key|}} -
-
-
- - {{member.name}} -
-
-
-
- - -
-
- - {{#if (eq member.rollType 'trait')}} -
-
- - -
-
- {{else if (eq member.rollType 'damageAbility')}} -
-
- - -
-
- {{else}} -
-
- - -
-
- {{/if}} -
- - {{#if member.readyToRoll}} -
- - {{localize "DAGGERHEART.GENERAL.roll"}} -
- - - - - {{#if member.hasRolled}} - - - - {{/if}} -
-
- - {{#if member.rollData}} - {{#with member.rollData.options.roll}} -
-
{{this.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=this.result.label}}
-
- - {{this.hope.value}} - - - + - - {{this.fear.value}} - - - {{#if this.advantage.type}} - {{#if (eq this.advantage.type 1)}}+{{else}}-{{/if}} - - {{this.advantage.value}} - - - {{/if}} - {{#if (gte this.modifierTotal 0)}}+{{else}}-{{/if}} - {{positive this.modifierTotal}} -
-
- {{/with}} - {{else}} - {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}} - {{/if}} -
- {{/if}} - - {{#if member.rollData.options.hasDamage}} -
- - {{localize "DAGGERHEART.GENERAL.damage"}} -
- - - - - {{#if damage}} - - - - {{/if}} -
-
- {{#if damage}} - {{#if useCritDamage}} - {{> "systems/daggerheart/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs" damage=critDamage isCritical=true }} - {{else}} - {{> "systems/daggerheart/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs" damage=damage }} - {{/if}} - {{else}} - {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}} - {{/if}} -
- {{/if}} -
- - {{#if member.hasRolled}} - - - - {{/if}} -
- {{/each}} -
- -
- {{localize "DAGGERHEART.GENERAL.result.plural"}} -
- {{#if hintText}} -
{{localize hintText}}
- {{else}} - {{#if joinedRoll.rollData}} -
- -
-
{{joinedRoll.rollData.options.roll.total}}
-
{{localize "DAGGERHEART.GENERAL.withThing" thing=joinedRoll.rollData.options.roll.result.label}}
-
-
- {{/if}} - {{#if hasDamage}} -
- - {{#each joinedRoll.rollData.options.damage as |damage key|}} -
-
{{localize (concat "DAGGERHEART.CONFIG.HealingType." key ".name")}}
-
{{damage.total}}
-
- {{/each}} -
- {{/if}} - {{/if}} -
-
- -
- - -
-
-
\ No newline at end of file diff --git a/templates/sheets/activeEffect/changes.hbs b/templates/sheets/activeEffect/changes.hbs index a699e951..026ffd90 100644 --- a/templates/sheets/activeEffect/changes.hbs +++ b/templates/sheets/activeEffect/changes.hbs @@ -13,19 +13,4 @@ {{{change}}} {{/each}} - -
- {{localize "DAGGERHEART.GENERAL.armor"}} - {{#if typedChanges.armor}} -
-
{{localize "EFFECT.FIELDS.changes.element.value.label"}}
-
{{localize "DAGGERHEART.GENERAL.max"}}
-
{{localize "DAGGERHEART.EFFECTS.ChangeTypes.armor.FIELDS.armorInteraction.label"}}
-
{{localize "EFFECT.FIELDS.changes.element.priority.label"}}
-
-
    - {{> "systems/daggerheart/templates/sheets/activeEffect/typeChanges/armorChange.hbs" typedChanges.armor fields=@root.systemFields.changes.element.fields}} -
- {{/if}} -
diff --git a/templates/sheets/activeEffect/typeChanges/armorChange.hbs b/templates/sheets/activeEffect/typeChanges/armorChange.hbs deleted file mode 100644 index 6a04aba2..00000000 --- a/templates/sheets/activeEffect/typeChanges/armorChange.hbs +++ /dev/null @@ -1,10 +0,0 @@ -
  • - - - - - {{formInput fields.value name=(concat "system.changes." index ".value") value=value elementType="input" data-dtype="Number"}} - {{formInput fields.typeData.types.armor.fields.max name=(concat "system.changes." index ".typeData.max") value=typeData.max data-dtype="Number"}} - {{formInput fields.typeData.types.armor.fields.armorInteraction name=(concat "system.changes." index ".typeData.armorInteraction") value=typeData.armorInteraction localize=true}} - {{formInput fields.priority name=(concat "system.changes." index ".priority") value=priority}} -
  • \ No newline at end of file diff --git a/templates/sheets/actors/party/party-members.hbs b/templates/sheets/actors/party/party-members.hbs index b3dd53e6..b5903cfc 100644 --- a/templates/sheets/actors/party/party-members.hbs +++ b/templates/sheets/actors/party/party-members.hbs @@ -5,7 +5,7 @@ >
    - diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index a758a28f..86d2e2d3 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -40,20 +40,23 @@ Parameters: {{!-- Name & Tags --}}
    - {{!-- Item Name --}} - {{localize item.name}} {{#unless (or noExtensible (not item.system.description))}}{{/unless}} - {{!-- Tags Start --}} + {{!-- Item Name --}} + {{localize item.name}} {{#unless (or noExtensible (not item.system.description))}}{{/unless}} + + {{!-- Tags Start --}} {{#if (not ../hideTags)}} - {{#> "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs" item}} - {{#if (eq ../type 'feature')}} - {{#if (and system.featureForm (or (eq @root.document.type "adversary") (eq @root.document.type "environment")))}} + {{#> "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs" item }} + {{#if (eq ../type 'feature')}} + {{#if (or (eq @root.document.type 'adversary') (eq @root.document.type 'environment'))}} + {{#if system.featureForm}}
    {{localize (concat "DAGGERHEART.CONFIG.FeatureForm." system.featureForm)}}
    {{/if}} {{/if}} - {{/ "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs"}} + {{/if}} + {{/ "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs"}} {{/if}} {{!--Tags End --}} diff --git a/templates/sheets/global/partials/item-tags.hbs b/templates/sheets/global/partials/item-tags.hbs index 2edc1eac..b30fcbf2 100644 --- a/templates/sheets/global/partials/item-tags.hbs +++ b/templates/sheets/global/partials/item-tags.hbs @@ -4,5 +4,5 @@ {{tag}}
    {{/each}} - {{> @partial-block}} + {{#if @partial-block}}{{> @partial-block}}{{/if}}
    \ No newline at end of file diff --git a/templates/ui/itemBrowser/item-tags.hbs b/templates/ui/itemBrowser/item-tags.hbs deleted file mode 100644 index ba2d39de..00000000 --- a/templates/ui/itemBrowser/item-tags.hbs +++ /dev/null @@ -1,2 +0,0 @@ -{{#> "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs" item }} -{{/"systems/daggerheart/templates/sheets/global/partials/item-tags.hbs"}}