diff --git a/daggerheart.mjs b/daggerheart.mjs index 7593d935..e25f1b09 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -32,6 +32,11 @@ CONFIG.Dice.daggerheart = { FateRoll: FateRoll }; +CONFIG.RegionBehavior.dataModels = { + ...CONFIG.RegionBehavior.dataModels, + ...data.regionBehaviors +}; + Object.assign(CONFIG.Dice.termTypes, dice.diceTypes); CONFIG.Actor.documentClass = documents.DhpActor; diff --git a/lang/en.json b/lang/en.json index a824d379..fe8205b8 100755 --- a/lang/en.json +++ b/lang/en.json @@ -109,6 +109,11 @@ "customFormula": "Custom Formula", "formula": "Formula" }, + "area": { + "sectionTitle": "Areas", + "shape": "Shape", + "size": "Size" + }, "displayInChat": "Display in chat", "deleteTriggerTitle": "Delete Trigger", "deleteTriggerContent": "Are you sure you want to delete the {trigger} trigger?", @@ -2435,6 +2440,7 @@ "maxWithThing": "Max {thing}", "missingDragDropThing": "Drop {thing} here", "multiclass": "Multiclass", + "name": "Name", "newCategory": "New Category", "newThing": "New {thing}", "next": "Next", diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index e073cfa6..f991187f 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -19,15 +19,17 @@ export default class DHActionConfig extends DHActionBaseConfig { return context; } - static async addEffect(_event) { + static async addEffect(event) { + const { areaIndex } = event.target.dataset; if (!this.action.effects) return; const data = this.action.toObject(); const created = await this.action.item.createEmbeddedDocuments('ActiveEffect', [ - game.system.api.data.activeEffects.BaseEffect.getDefaultObject() + game.system.api.data.activeEffects.BaseEffect.getDefaultObject({ transfer: false }) ]); - data.effects.push({ _id: created[0]._id }); + if (areaIndex !== undefined) data.area[areaIndex].effects.push(created[0]._id); + else data.effects.push({ _id: created[0]._id }); this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); this.action.item.effects.get(created[0]._id).sheet.render(true); } @@ -52,9 +54,19 @@ export default class DHActionConfig extends DHActionBaseConfig { static removeEffect(event, button) { if (!this.action.effects) return; - const index = button.dataset.index, + + const { areaIndex, index } = button.dataset; + let effectId = null; + if (areaIndex !== undefined) { + effectId = this.action.area[areaIndex].effects[index]; + const data = this.action.toObject(); + data.area[areaIndex].effects.splice(index, 1); + this.constructor.updateForm.call(this, null, null, { object: foundry.utils.flattenObject(data) }); + } else { effectId = this.action.effects[index]._id; - this.constructor.removeElement.bind(this)(event, button); + this.constructor.removeElement.call(this, event, button); + } + this.action.item.deleteEmbeddedDocuments('ActiveEffect', [effectId]); } diff --git a/module/applications/sheets-configs/action-settings-config.mjs b/module/applications/sheets-configs/action-settings-config.mjs index 9cb866bc..fd62f621 100644 --- a/module/applications/sheets-configs/action-settings-config.mjs +++ b/module/applications/sheets-configs/action-settings-config.mjs @@ -31,21 +31,35 @@ export default class DHActionSettingsConfig extends DHActionBaseConfig { } static async addEffect(_event) { + const { areaIndex } = event.target.dataset; if (!this.action.effects) return; - const effectData = game.system.api.data.activeEffects.BaseEffect.getDefaultObject(); + + const effectData = game.system.api.data.activeEffects.BaseEffect.getDefaultObject({ transfer: false }); const data = this.action.toObject(); this.sheetUpdate(data, effectData); this.effects = [...this.effects, effectData]; - data.effects.push({ _id: effectData.id }); + + if (areaIndex !== undefined) data.area[areaIndex].effects.push(effectData.id); + else data.effects.push({ _id: effectData.id }); + this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } static removeEffect(event, button) { if (!this.action.effects) return; - const index = button.dataset.index, + const { areaIndex, index } = button.dataset; + let effectId = null; + if (areaIndex !== undefined) { + effectId = this.action.area[areaIndex].effects[index]; + const data = this.action.toObject(); + data.area[areaIndex].effects.splice(index, 1); + this.constructor.updateForm.call(this, null, null, { object: foundry.utils.flattenObject(data) }); + } else { effectId = this.action.effects[index]._id; - this.constructor.removeElement.bind(this)(event, button); + this.constructor.removeElement.call(this, event, button); + } + this.sheetUpdate( this.action.toObject(), this.effects.find(x => x.id === effectId), diff --git a/module/canvas/placeables/regionLayer.mjs b/module/canvas/placeables/regionLayer.mjs index 81fd96db..56f3084b 100644 --- a/module/canvas/placeables/regionLayer.mjs +++ b/module/canvas/placeables/regionLayer.mjs @@ -95,4 +95,53 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer { }); return inBounds.length === 1 ? inBounds[0] : null; } + + static getTemplateShape({ type, angle, range, direction } = {}) { + const { line, rectangle, inFront, cone } = CONFIG.DH.GENERAL.templateTypes; + + const usedAngle = + type === cone.id + ? (angle ?? CONFIG.MeasuredTemplate.defaults.angle) + : type === inFront.id + ? '180' + : undefined; + + const { grid, distance } = CONFIG.Scene.documentClass.schema.fields.grid.fields; + const sceneGridSize = canvas.scene?.grid.size ?? grid.size.initial; + const sceneGridDistance = canvas.scene?.grid.distance ?? distance.getInitialValue(); + const dimensionConstant = sceneGridSize / sceneGridDistance; + + const rangeNumber = Number(range); + const settings = canvas.scene?.rangeSettings; + const baseDistance = + (!Number.isNaN(rangeNumber) ? rangeNumber : settings ? settings[range] : 0) * dimensionConstant; + + const length = baseDistance; + const radius = length; + + const shapeWidth = type === line.id ? 5 * dimensionConstant : type === rectangle.id ? length : undefined; + const shapeType = type === inFront.id ? cone.id : type; + + const { width, height } = game.canvas.scene.dimensions; + return { + x: width / 2, + y: height / 2, + base: { + type: 'token', + x: 0, + y: 0, + width: 1, + height: 1, + shape: game.canvas.grid.isHexagonal ? CONST.TOKEN_SHAPES.ELLIPSE_1 : CONST.TOKEN_SHAPES.RECTANGLE_1 + }, + t: shapeType, + length: length, + width: shapeWidth, + height: length, + angle: usedAngle, + radius: radius, + direction: direction ?? 0, + type: shapeType + }; + } } diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs index 0b1bf91d..a6c04e18 100644 --- a/module/config/actionConfig.mjs +++ b/module/config/actionConfig.mjs @@ -115,3 +115,10 @@ export const advantageState = { value: 1 } }; + +export const areaTypes = { + placed: { + id: 'placed', + label: 'Placed Area' + } +}; diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 58a9dff9..6fc85ec5 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -80,12 +80,30 @@ export const groupAttackRange = { /* circle|cone|rect|ray used to be CONST.MEASURED_TEMPLATE_TYPES. Hardcoded for now */ export const templateTypes = { - CIRCLE: 'circle', - CONE: 'cone', - RECTANGLE: 'rectangle', - LINE: 'line', - EMANATION: 'emanation', - INFRONT: 'inFront' + circle: { + id: 'circle', + label: 'Circle' + }, + cone: { + id: 'cone', + label: 'Cone' + }, + rectangle: { + id: 'rectangle', + label: 'Rectangle' + }, + line: { + id: 'line', + label: 'Line' + }, + emanation: { + id: 'emanation', + label: 'Emanation' + }, + inFront: { + id: 'inFront', + label: 'In Front' + } }; export const rangeInclusion = { @@ -1092,3 +1110,18 @@ export const fallAndCollisionDamage = { damageFormula: '1d20 + 5' } }; + +export const simpleDispositions = { + [-1]: { + id: -1, + label: 'TOKEN.DISPOSITION.HOSTILE' + }, + [0]: { + id: 0, + label: 'TOKEN.DISPOSITION.NEUTRAL' + }, + [1]: { + id: 1, + label: 'TOKEN.DISPOSITION.FRIENDLY' + } +}; diff --git a/module/data/_module.mjs b/module/data/_module.mjs index cd691ee1..a54da0e1 100644 --- a/module/data/_module.mjs +++ b/module/data/_module.mjs @@ -15,3 +15,4 @@ export * as chatMessages from './chat-message/_modules.mjs'; export * as fields from './fields/_module.mjs'; export * as items from './item/_module.mjs'; export * as scenes from './scene/_module.mjs'; +export * as regionBehaviors from './regionBehavior/_module.mjs'; diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 23f4776a..631a1211 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -15,7 +15,7 @@ const fields = foundry.data.fields; */ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel) { - static extraSchemas = ['cost', 'uses', 'range']; + static extraSchemas = ['area', 'cost', 'uses', 'range']; /** @inheritDoc */ static defineSchema() { diff --git a/module/data/activeEffect/baseEffect.mjs b/module/data/activeEffect/baseEffect.mjs index bac50c56..4037ca0e 100644 --- a/module/data/activeEffect/baseEffect.mjs +++ b/module/data/activeEffect/baseEffect.mjs @@ -93,6 +93,12 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { max: new fields.NumberField({ integer: true, label: 'DAGGERHEART.GENERAL.max' }) }, { nullable: true, initial: null } + ), + targetDispositions: new fields.SetField( + new fields.NumberField({ + choices: CONFIG.DH.GENERAL.simpleDispositions + }), + { label: 'Affected Dispositions' } ) }; } @@ -131,13 +137,14 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { return armorChange.getArmorData(); } - static getDefaultObject() { + static getDefaultObject(options = { transfer: true }) { return { name: 'New Effect', id: foundry.utils.randomID(), disabled: false, img: 'icons/magic/life/heart-cross-blue.webp', description: '', + transfer: options.transfer, statuses: [], changes: [], system: { diff --git a/module/data/chat-message/abilityUse.mjs b/module/data/chat-message/abilityUse.mjs index 07209fe2..f5986d0f 100644 --- a/module/data/chat-message/abilityUse.mjs +++ b/module/data/chat-message/abilityUse.mjs @@ -7,26 +7,31 @@ export default class DHAbilityUse extends foundry.abstract.TypeDataModel { img: new fields.StringField({}), name: new fields.StringField({}), description: new fields.StringField({}), - actions: new fields.ArrayField( - new fields.ObjectField({ - name: new fields.StringField({}), - damage: new fields.SchemaField({ - type: new fields.StringField({}), - value: new fields.StringField({}) - }), - healing: new fields.SchemaField({ - type: new fields.StringField({}), - value: new fields.StringField({}) - }), - cost: new fields.SchemaField({ - type: new fields.StringField({}), - value: new fields.NumberField({}) - }), - target: new fields.SchemaField({ - type: new fields.StringField({ nullable: true }) - }) - }) - ) + source: new fields.SchemaField({ + actor: new fields.StringField(), + item: new fields.StringField(), + action: new fields.StringField() + }) }; } + + get actionActor() { + if (!this.source.actor) return null; + return fromUuidSync(this.source.actor); + } + + get actionItem() { + const actionActor = this.actionActor; + if (!actionActor || !this.source.item) return null; + + const item = actionActor.items.get(this.source.item); + return item ? item.system.actionsList?.find(a => a.id === this.source.action) : null; + } + + get action() { + const { actionItem: itemAction } = this; + if (!this.source.action) return null; + if (itemAction) return itemAction; + return null; + } } diff --git a/module/data/fields/action/_module.mjs b/module/data/fields/action/_module.mjs index fa3b1cd5..f9a2154b 100644 --- a/module/data/fields/action/_module.mjs +++ b/module/data/fields/action/_module.mjs @@ -1,3 +1,4 @@ +export { default as AreaField } from './areaField.mjs'; export { default as CostField } from './costField.mjs'; export { default as CountdownField } from './countdownField.mjs'; export { default as UsesField } from './usesField.mjs'; diff --git a/module/data/fields/action/areaField.mjs b/module/data/fields/action/areaField.mjs new file mode 100644 index 00000000..a45af8dc --- /dev/null +++ b/module/data/fields/action/areaField.mjs @@ -0,0 +1,40 @@ +const fields = foundry.data.fields; + +export default class AreaField extends fields.ArrayField { + /** + * Action Workflow order + */ + static order = 150; + + /** @inheritDoc */ + constructor(options = {}, context = {}) { + const element = new fields.SchemaField({ + name: new fields.StringField({ + nullable: false, + initial: 'Area', + label: 'DAGGERHEART.GENERAL.name' + }), + type: new fields.StringField({ + nullable: false, + choices: CONFIG.DH.ACTIONS.areaTypes, + initial: CONFIG.DH.ACTIONS.areaTypes.placed.id, + label: 'DAGGERHEART.GENERAL.type' + }), + shape: new fields.StringField({ + nullable: false, + choices: CONFIG.DH.GENERAL.templateTypes, + initial: CONFIG.DH.GENERAL.templateTypes.circle.id, + label: 'DAGGERHEART.ACTIONS.Config.area.shape' + }), + /* Could be opened up to allow numbers to be input aswell. Probably best handled via an autocomplete in that case to allow the select options but also free text */ + size: new fields.StringField({ + nullable: false, + choices: CONFIG.DH.GENERAL.range, + initial: CONFIG.DH.GENERAL.range.veryClose.id, + label: 'DAGGERHEART.ACTIONS.Config.area.size' + }), + effects: new fields.ArrayField(new fields.DocumentIdField()) + }); + super(element, options, context); + } +} diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index ffdc25cd..7bdd75f8 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -281,8 +281,14 @@ export function ActionMixin(Base) { name: this.name, img: this.baseAction ? this.parent.parent.img : this.img, tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'], + area: this.area, summon: this.summon }, + source: { + actor: this.actor.uuid, + item: this.item.id, + action: this.id + }, itemOrigin: this.item, description: this.description || (this.item instanceof Item ? this.item.system.description : '') }; diff --git a/module/data/regionBehavior/_module.mjs b/module/data/regionBehavior/_module.mjs new file mode 100644 index 00000000..d0c8172d --- /dev/null +++ b/module/data/regionBehavior/_module.mjs @@ -0,0 +1 @@ +export { default as applyActiveEffect } from './applyActiveEffect.mjs'; diff --git a/module/data/regionBehavior/applyActiveEffect.mjs b/module/data/regionBehavior/applyActiveEffect.mjs new file mode 100644 index 00000000..51406cfa --- /dev/null +++ b/module/data/regionBehavior/applyActiveEffect.mjs @@ -0,0 +1,40 @@ +export default class DhApplyActiveEffect extends CONFIG.RegionBehavior.dataModels.applyActiveEffect { + static async #getApplicableEffects(token) { + const effects = await Promise.all(this.effects.map(foundry.utils.fromUuid)); + return effects.filter( + effect => !effect.system.targetDispositions.size || effect.system.targetDispositions.has(token.disposition) + ); + } + + static async #onTokenEnter(event) { + if (!event.user.isSelf) return; + const { token, movement } = event.data; + const actor = token.actor; + if (!actor) return; + const resumeMovement = movement ? token.pauseMovement() : undefined; + const effects = await DhApplyActiveEffect.#getApplicableEffects.bind(this)(event.data.token); + const toCreate = []; + for (const effect of effects) { + const data = effect.toObject(); + delete data._id; + if (effect.compendium) { + data._stats.duplicateSource = null; + data._stats.compendiumSource = effect.uuid; + } else { + data._stats.duplicateSource = effect.uuid; + data._stats.compendiumSource = null; + } + data._stats.exportSource = null; + data.origin = this.parent.uuid; + toCreate.push(data); + } + if (toCreate.length) await actor.createEmbeddedDocuments('ActiveEffect', toCreate); + await resumeMovement?.(); + } + + /** @override */ + static events = { + ...CONFIG.RegionBehavior.dataModels.applyActiveEffect.events, + [CONST.REGION_EVENTS.TOKEN_ENTER]: this.#onTokenEnter + }; +} diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index aa4dd75f..c68e91d1 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -145,6 +145,7 @@ export default class DHRoll extends Roll { roll: this, parent: chatData.parent, targetMode: chatData.targetMode, + areas: chatData.action?.area, metagamingSettings }); } diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index ad64336f..6f3080f4 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -137,6 +137,10 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { element.addEventListener('click', this.onApplyEffect.bind(this)) ); + html.querySelectorAll('.action-areas').forEach(element => + element.addEventListener('click', this.onCreateAreas.bind(this)) + ); + html.querySelectorAll('.roll-target').forEach(element => { element.addEventListener('mouseenter', this.hoverTarget); element.addEventListener('mouseleave', this.unhoverTarget); @@ -249,6 +253,55 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { this.system.action?.workflow.get('effects')?.execute(config, targets, true); } + async onCreateAreas(event) { + const createArea = async selectedArea => { + const effects = selectedArea.effects.map(effect => this.system.action.item.effects.get(effect).uuid); + const { shape: type, size: range } = selectedArea; + const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ type, range }); + + await canvas.regions.placeRegion( + { + name: selectedArea.name, + shapes: [shapeData], + restriction: { enabled: false, type: 'move', priority: 0 }, + behaviors: [ + { + name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'), + type: 'applyActiveEffect', + system: { + effects: effects + } + } + ], + displayMeasurements: true, + locked: false, + ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE }, + visibility: CONST.REGION_VISIBILITY.ALWAYS + }, + { create: true } + ); + }; + + if (this.system.action.area.length === 1) createArea(this.system.action.area[0]); + else if (this.system.action.area.length > 1) { + /* Pop a selection. Possibly a context menu? */ + new foundry.applications.ux.ContextMenu.implementation( + event.target, + '.action-areas', + this.system.action.area.map((area, index) => ({ + name: area.name, + callback: () => createArea(this.system.action.area[index]) + })), + { + jQuery: false, + fixed: true + } + ); + + CONFIG.ux.ContextMenu.triggerContextMenu(event, '.action-areas'); + } + } + filterPermTargets(targets) { return targets.filter(t => fromUuidSync(t.actorId)?.canUserModify(game.user, 'update')); } diff --git a/module/enrichers/TemplateEnricher.mjs b/module/enrichers/TemplateEnricher.mjs index cd0e7d9c..8db3ec14 100644 --- a/module/enrichers/TemplateEnricher.mjs +++ b/module/enrichers/TemplateEnricher.mjs @@ -12,7 +12,7 @@ export default function DhTemplateEnricher(match, _options) { )?.id : params.range; - if (!Object.values(CONFIG.DH.GENERAL.templateTypes).find(x => x === type) || !range) return match[0]; + if (!CONFIG.DH.GENERAL.templateTypes[type] || !range) return match[0]; const label = game.i18n.localize(`DAGGERHEART.CONFIG.TemplateTypes.${type}`); const rangeDisplay = Number.isNaN(Number(range)) @@ -49,8 +49,6 @@ export default function DhTemplateEnricher(match, _options) { } export const renderMeasuredTemplate = async event => { - const { LINE, RECTANGLE, INFRONT, CONE } = CONFIG.DH.GENERAL.templateTypes; - const button = event.currentTarget, type = button.dataset.type, range = button.dataset.range, @@ -59,49 +57,16 @@ export const renderMeasuredTemplate = async event => { if (!type || !range || !game.canvas.scene) return; - const usedType = type === 'inFront' ? 'cone' : type; - const usedAngle = - type === CONE ? (angle ?? CONFIG.MeasuredTemplate.defaults.angle) : type === INFRONT ? '180' : undefined; - - let baseDistance = getTemplateDistance(range); - - const { grid, distance } = CONFIG.Scene.documentClass.schema.fields.grid.fields; - const sceneGridSize = canvas.scene?.grid.size ?? grid.size.initial; - const sceneGridDistance = canvas.scene?.grid.distance ?? distance.getInitialValue(); - const dimensionConstant = sceneGridSize / sceneGridDistance; - - baseDistance *= dimensionConstant; - - const length = baseDistance; - const radius = length; - - const shapeWidth = type === LINE ? 5 * dimensionConstant : type === RECTANGLE ? length : undefined; - - const { width, height } = game.canvas.scene.dimensions; - const shapeData = { - x: width / 2, - y: height / 2, - base: { - type: 'token', - x: 0, - y: 0, - width: 1, - height: 1, - shape: game.canvas.grid.isHexagonal ? CONST.TOKEN_SHAPES.ELLIPSE_1 : CONST.TOKEN_SHAPES.RECTANGLE_1 - }, - t: usedType, - length: length, - width: shapeWidth, - height: length, - angle: usedAngle, - radius: radius, - direction: direction, - type: usedType - }; + const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ + type, + angle, + range, + direction + }); await canvas.regions.placeRegion( { - name: usedType.capitalize(), + name: type.capitalize(), shapes: [shapeData], restriction: { enabled: false, type: 'move', priority: 0 }, behaviors: [], @@ -113,11 +78,3 @@ export const renderMeasuredTemplate = async event => { { create: true } ); }; - -const getTemplateDistance = range => { - const rangeNumber = Number(range); - if (!Number.isNaN(rangeNumber)) return rangeNumber; - - const settings = canvas.scene?.rangeSettings; - return settings ? settings[range] : 0; -}; diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index ebd65fa2..7f701b59 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -29,6 +29,7 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/actionTypes/uses.hbs', 'systems/daggerheart/templates/actionTypes/roll.hbs', 'systems/daggerheart/templates/actionTypes/save.hbs', + 'systems/daggerheart/templates/actionTypes/area.hbs', 'systems/daggerheart/templates/actionTypes/cost.hbs', 'systems/daggerheart/templates/actionTypes/range-target.hbs', 'systems/daggerheart/templates/actionTypes/effect.hbs', diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index 4bd4d0bb..a2b9ebd8 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -24,6 +24,15 @@ width: 100%; list-style-type: none; + &.bordered { + border: 1px solid; + border-radius: 6px; + + .inventory-item-header .img-portait img.item-img { + border-radius: 6px 0 0 6px; + } + } + &:not(.single-img) { .img-portait:has(.roll-img):hover { .roll-img { diff --git a/styles/less/sheets/actions/actions.less b/styles/less/sheets/actions/actions.less index 5a18d622..485f8e91 100644 --- a/styles/less/sheets/actions/actions.less +++ b/styles/less/sheets/actions/actions.less @@ -147,4 +147,10 @@ padding-bottom: 7px; } } + + .sub-section-header { + display: flex; + align-items: center; + gap: 4px; + } } diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index db6dceb9..abb98ad9 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -624,9 +624,15 @@ display: flex; gap: 5px; margin-top: 8px; + button { height: 32px; flex: 1; + + &.end-button { + margin-left: auto; + flex: 0; + } } } @@ -698,6 +704,11 @@ } } + .action-roll-buttons { + width: 100%; + padding: 0 8px; + } + .description { padding: 8px; diff --git a/templates/actionTypes/area.hbs b/templates/actionTypes/area.hbs new file mode 100644 index 00000000..bb4a3e05 --- /dev/null +++ b/templates/actionTypes/area.hbs @@ -0,0 +1,46 @@ +
+ + {{localize "DAGGERHEART.ACTIONS.Config.area.sectionTitle"}} + + + + {{#each source as |area index|}} + {{#unless @first}}{{/unless}} +
+ {{formField ../fields.name value=area.name name=(concat "area." index ".name") localize=true}} + +
+
+ {{formField ../fields.type value=area.type name=(concat "area." index ".type") localize=true}} + {{formField ../fields.shape value=area.shape name=(concat "area." index ".shape") localize=true}} + {{formField ../fields.size value=area.size name=(concat "area." index ".size") localize=true}} +
+ +
+ {{localize "DAGGERHEART.GENERAL.Effect.plural"}} + +
+
+ {{#each area.effects as |effectId index|}} + + +
+
+ {{#with (@root.getEffectDetails effectId) as | details |}} +
+ +
+
+
{{name}}
+
+ {{/with}} +
+ +
+
+
+ {{/each}} +
+ {{/each}} + +
\ No newline at end of file diff --git a/templates/actionTypes/range-target.hbs b/templates/actionTypes/range-target.hbs index 114c76a6..ec595df4 100644 --- a/templates/actionTypes/range-target.hbs +++ b/templates/actionTypes/range-target.hbs @@ -1,12 +1,14 @@
{{localize "DAGGERHEART.GENERAL.range"}}{{#if fields.target}} & {{localize "DAGGERHEART.GENERAL.Target.single"}}{{/if}} - {{formField fields.range value=source.range label=(localize "DAGGERHEART.GENERAL.range") name=(concat path "range") localize=true}} {{#if fields.target}} -
- {{#if (and source.target.type (not (eq source.target.type 'self')))}} - {{ formField fields.target.amount value=source.target.amount label=(localize "DAGGERHEART.GENERAL.amount") name=(concat path "target.amount") localize=true}} - {{/if}} - {{ formField fields.target.type value=source.target.type label=(localize "DAGGERHEART.GENERAL.Target.single") name=(concat path "target.type") localize=true }} -
+
+ {{formField fields.range value=source.range label=(localize "DAGGERHEART.GENERAL.range") name=(concat path "range") localize=true}} + {{#if (and source.target.type (not (eq source.target.type 'self')))}} + {{ formField fields.target.amount value=source.target.amount label=(localize "DAGGERHEART.GENERAL.amount") name=(concat path "target.amount") localize=true}} + {{/if}} + {{ formField fields.target.type value=source.target.type label=(localize "DAGGERHEART.GENERAL.Target.single") name=(concat path "target.type") localize=true }} +
+ {{else}} + {{formField fields.range value=source.range label=(localize "DAGGERHEART.GENERAL.range") name=(concat path "range") localize=true}} {{/if}}
\ No newline at end of file diff --git a/templates/sheets-settings/action-settings/configuration.hbs b/templates/sheets-settings/action-settings/configuration.hbs index 5bd29e39..4cd0e3a8 100644 --- a/templates/sheets-settings/action-settings/configuration.hbs +++ b/templates/sheets-settings/action-settings/configuration.hbs @@ -6,4 +6,5 @@ {{> 'systems/daggerheart/templates/actionTypes/uses.hbs' fields=fields.uses.fields source=source.uses}} {{> 'systems/daggerheart/templates/actionTypes/cost.hbs' fields=fields.cost.element.fields source=source.cost costOptions=costOptions}} {{> 'systems/daggerheart/templates/actionTypes/range-target.hbs' fields=(object range=fields.range target=fields.target.fields) source=(object target=source.target range=source.range)}} + {{> 'systems/daggerheart/templates/actionTypes/area.hbs' fields=fields.area.element.fields source=source.area}} \ No newline at end of file diff --git a/templates/sheets/activeEffect/details.hbs b/templates/sheets/activeEffect/details.hbs index f7e465bd..dc19b3dc 100644 --- a/templates/sheets/activeEffect/details.hbs +++ b/templates/sheets/activeEffect/details.hbs @@ -2,6 +2,7 @@ {{formGroup fields.tint value=source.tint rootId=rootId placeholder="#ffffff"}} {{formGroup fields.description value=source.description rootId=rootId}} {{formGroup fields.disabled value=source.disabled rootId=rootId}} + {{formGroup systemFields.targetDispositions value=source.system.targetDispositions name=(concat "system.targetDispositions") localize=true}} {{#if isActorEffect}}
diff --git a/templates/ui/chat/action.hbs b/templates/ui/chat/action.hbs index 2854795c..00d01040 100644 --- a/templates/ui/chat/action.hbs +++ b/templates/ui/chat/action.hbs @@ -26,4 +26,9 @@ {{/if}}
+ {{#if action.area.length}} +
+ +
+ {{/if}} \ No newline at end of file diff --git a/templates/ui/chat/parts/button-part.hbs b/templates/ui/chat/parts/button-part.hbs index 21317939..26855611 100644 --- a/templates/ui/chat/parts/button-part.hbs +++ b/templates/ui/chat/parts/button-part.hbs @@ -14,4 +14,5 @@ {{/unless}} {{/if}} {{#if (and hasEffect)}}{{/if}} + {{#if areas.length}}{{/if}} \ No newline at end of file