diff --git a/daggerheart.mjs b/daggerheart.mjs index 1987ec12..05b57ac9 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -420,10 +420,7 @@ const updateActorsRangeDependentEffects = async token => { // Get required distance and special case 5 feet to test adjacency const required = rangeMeasurement[range]; const reverse = type === CONFIG.DH.GENERAL.rangeInclusion.outsideRange.id; - const inRange = - required === 5 - ? userTarget.isAdjacentWith(token.object) - : userTarget.distanceTo(token.object) <= required; + const inRange = userTarget.distanceTo(token.object) <= required; if (reverse ? inRange : !inRange) { enabledEffect = false; break; diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 49f7dcf0..449880fb 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -431,18 +431,18 @@ export default function DHApplicationMixin(Base) { { name: 'disableEffect', icon: 'fa-solid fa-lightbulb', - condition: target => { - const doc = getDocFromElementSync(target); - return doc && !doc.disabled && doc.type !== 'beastform'; + condition: element => { + const target = element.closest('[data-item-uuid]'); + return !target.dataset.disabled && target.dataset.itemType !== 'beastform'; }, callback: async target => (await getDocFromElement(target)).update({ disabled: true }) }, { name: 'enableEffect', icon: 'fa-regular fa-lightbulb', - condition: target => { - const doc = getDocFromElementSync(target); - return doc && doc.disabled && doc.type !== 'beastform'; + condition: element => { + const target = element.closest('[data-item-uuid]'); + return target.dataset.disabled && target.dataset.itemType !== 'beastform'; }, callback: async target => (await getDocFromElement(target)).update({ disabled: false }) } @@ -536,9 +536,9 @@ export default function DHApplicationMixin(Base) { options.push({ name: 'CONTROLS.CommonDelete', icon: 'fa-solid fa-trash', - condition: target => { - const doc = getDocFromElementSync(target); - return doc && doc.type !== 'beastform'; + condition: element => { + const target = element.closest('[data-item-uuid]'); + return target.dataset.itemType !== 'beastform'; }, callback: async (target, event) => { const doc = await getDocFromElement(target); diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 068f21e1..148466c1 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -54,30 +54,58 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { if (this === target) return 0; const originPoint = this.center; - const destinationPoint = target.center; + const targetPoint = target.center; + const thisBounds = this.bounds; + const targetBounds = target.bounds; + const adjacencyBuffer = canvas.grid.distance * 1.75; // handles diagonals with one square elevation difference + + // Figure out the elevation difference. + // This intends to return "grid distance" for adjacent ones, so we add that number if not overlapping. + const sizePerUnit = canvas.grid.size / canvas.grid.distance; + const thisHeight = Math.max(thisBounds.width, thisBounds.height) / sizePerUnit; + const targetHeight = Math.max(targetBounds.width, targetBounds.height) / sizePerUnit; + const thisElevation = [this.document.elevation, this.document.elevation + thisHeight]; + const targetElevation = [target.document.elevation, target.document.elevation + targetHeight]; + const isSameAltitude = + thisElevation[0] < targetElevation[1] && // bottom of this must be at or below the top of target + thisElevation[1] > targetElevation[0]; // top of this must be at or above the bottom of target + const [lower, higher] = [targetElevation, thisElevation].sort((a, b) => a[1] - b[1]); + const elevation = isSameAltitude ? 0 : higher[0] - lower[1] + canvas.grid.distance; // Compute for gridless. This version returns circular edge to edge + grid distance, // so that tokens that are touching return 5. if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) { const boundsCorrection = canvas.grid.distance / canvas.grid.size; - const originRadius = (this.bounds.width * boundsCorrection) / 2; - const targetRadius = (target.bounds.width * boundsCorrection) / 2; - const distance = canvas.grid.measurePath([originPoint, destinationPoint]).distance; - return Math.floor(distance - originRadius - targetRadius + canvas.grid.distance); + const originRadius = (thisBounds.width * boundsCorrection) / 2; + const targetRadius = (targetBounds.width * boundsCorrection) / 2; + const measuredDistance = canvas.grid.measurePath([ + { ...originPoint, elevation: 0 }, + { ...targetPoint, elevation } + ]).distance; + const distance = Math.floor(measuredDistance - originRadius - targetRadius + canvas.grid.distance); + return Math.min(distance, distance > adjacencyBuffer ? Infinity : canvas.grid.distance); } // Compute what the closest grid space of each token is, then compute that distance - const originEdge = this.#getEdgeBoundary(this.bounds, originPoint, destinationPoint); - const targetEdge = this.#getEdgeBoundary(target.bounds, originPoint, destinationPoint); - const adjustedOriginPoint = canvas.grid.getTopLeftPoint({ - x: originEdge.x + Math.sign(originPoint.x - originEdge.x), - y: originEdge.y + Math.sign(originPoint.y - originEdge.y) - }); - const adjustDestinationPoint = canvas.grid.getTopLeftPoint({ - x: targetEdge.x + Math.sign(destinationPoint.x - targetEdge.x), - y: targetEdge.y + Math.sign(destinationPoint.y - targetEdge.y) - }); - return canvas.grid.measurePath([adjustedOriginPoint, adjustDestinationPoint]).distance; + const originEdge = this.#getEdgeBoundary(thisBounds, originPoint, targetPoint); + const targetEdge = this.#getEdgeBoundary(targetBounds, originPoint, targetPoint); + const adjustedOriginPoint = originEdge + ? canvas.grid.getTopLeftPoint({ + x: originEdge.x + Math.sign(originPoint.x - originEdge.x), + y: originEdge.y + Math.sign(originPoint.y - originEdge.y) + }) + : originPoint; + const adjustDestinationPoint = targetEdge + ? canvas.grid.getTopLeftPoint({ + x: targetEdge.x + Math.sign(targetPoint.x - targetEdge.x), + y: targetEdge.y + Math.sign(targetPoint.y - targetEdge.y) + }) + : targetPoint; + const distance = canvas.grid.measurePath([ + { ...adjustedOriginPoint, elevation: 0 }, + { ...adjustDestinationPoint, elevation } + ]).distance; + return Math.min(distance, distance > adjacencyBuffer ? Infinity : canvas.grid.distance); } _onHoverIn(event, options) { @@ -103,8 +131,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { // Determine the actual range const ranges = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement; - const distanceNum = originToken.distanceTo(this); - const distanceResult = DhMeasuredTemplate.getRangeLabels(distanceNum, ranges); + const distanceResult = DhMeasuredTemplate.getRangeLabels(originToken.distanceTo(this), ranges); const distanceLabel = `${distanceResult.distance} ${distanceResult.units}`.trim(); // Create the element @@ -156,11 +183,6 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { return null; } - /** Tests if the token is at least adjacent with another, with some leeway for diagonals */ - isAdjacentWith(token) { - return this.distanceTo(token) <= canvas.grid.distance * 1.5; - } - /** @inheritDoc */ _drawBar(number, bar, data) { const val = Number(data.value); diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index 78964720..0a446c15 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -190,6 +190,10 @@ export default class DhpAdversary extends DhCreature { } } + prepareDerivedData() { + this.attack.roll.isStandardAttack = true; + } + _getTags() { const tags = [ game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`), diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index efad726c..6439344b 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -165,7 +165,8 @@ export default class DamageField extends fields.SchemaField { if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt; const isAdversary = this.actor.type === 'adversary'; - if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) { + const isHorde = this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id; + if (isAdversary && isHorde && this.roll?.isStandardAttack) { const hasHordeDamage = this.actor.effects.find(x => x.type === 'horde'); if (hasHordeDamage && !hasHordeDamage.disabled) return part.valueAlt; } diff --git a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs index d2534a5a..0a3275d0 100644 --- a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs +++ b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs @@ -56,6 +56,7 @@ Parameters: {{> 'daggerheart.inventory-item' item=item type=../type + disabledEffect=../disabledEffect actorType=../actorType hideControls=../hideControls hideContextMenu=../hideContextMenu diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index 76e13a5c..fec215a0 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -17,8 +17,12 @@ Parameters: - showActions {boolean} : If true show feature's actions. --}} -