PDA

View Full Version : Can the range calculation be overridden?



GKEnialb
March 25th, 2021, 03:56
I created an extension to allow height the height of tokens to be set https://www.fantasygrounds.com/forums/showthread.php?66566-5E-Token-Height-Indicator and somebody asked if I could update the range arrow with a distance taking height into account. The distance calculations are straight-forward enough, but I can't find a way to either update the text displayed on the range arrow or modify the underlying range calculation (and let the arrow just use that). Is it possible to do either? I see there's an Interface function to setDistanceDiagMult, which isn't listed in the Interface package API, so I was hoping there was some other undocumented API with more distance controls (or any other method).

Weltenbrand
March 25th, 2021, 13:21
https://fantasygroundsunity.atlassian.net/wiki/spaces/FGCP/pages/996645070/imagecontrol#onMeasurePointer
That lets you define the lable of the measure pointer.

GKEnialb
March 26th, 2021, 01:10
Great - thanks!

GKEnialb
March 31st, 2021, 02:19
I was able to do this after getting info from several threads, so figured I'd put what I found here so it's consolidated (and to see if I got anything wrong :p).

- onMeasurePointer is found in campaign/scipts/image.lua (from CoreRPG) - it's not actually in the CoreRPG version, so it needs to be appended
- You have to override everything in image.lua and the original functions aren't available to call (so can't just override a portion and call the original for the rest) -- actually, see the post below
- image.lua has to called out in your xml file as below (including being inside of <base>)


<base>
<template name="image_record_step">
<imagecontrol name="image">
<indicators locked="image_locked" loading="image_loading" />
<default snap="on" drawingsize="500,500" />
<script file="campaign/scripts/image.lua" />
</imagecontrol>
</template>
</base>
- getGridSize(), getDistanceBaseUnits(), and Interface.getDistanceDiagMult() are your friends
- You can't just override the targeting arrows; the same logic is used for the pointers as well
- I had to look at the endpoint of the arrow to see what token it was pointing to (so I could get its height). Comparing that endpoint to the position of a tiny/small/medium creature worked just fine; for a large or huge creature, I had to adjust their position by half a gridsize and a full gridsize, respectively; gargantuan creatures are a little wacky - I had to just check if the endpoint was within 1.5 gridsizes (as opposed to being exactly 1.5 gridsizes off)

This is the code I wrote for the actual distance calculations is as follows (and doesn't quite work for "variant" coming from a corner)


function distanceBetween(startx,starty,startz,endx,endy,end z)
local diagMult = Interface.getDistanceDiagMult()
local units = getDistanceBaseUnits()
local gridsize = getGridSize()
local totalDistance = 0
local dx = math.abs(endx-startx)
local dy = math.abs(endy-starty)
local dz = math.abs(endz-startz)

if diagMult == 1 then
-- Just a max of each dimension
local longestLeg = math.max(dx, dy, dz)
totalDistance = math.floor(longestLeg/gridsize+0.5)*units

elseif diagMult == 0 then
-- Get 3D distance directly
local hyp = math.sqrt((dx^2)+(dy^2)+(dz^2))
totalDistance = (hyp / gridsize)* units
else
-- You get full amount of the longest path and half from each of the others
local longest = math.max(dx, dy, dz)
if longest == dx then
totalDistance = dx + (dy+dz)/2
elseif longest == dy then
totalDistance = dy + (dx+dz)/2
else
totalDistance = dz + (dx+dy)/2
end

-- Convert to feet
totalDistance = (totalDistance / gridsize + 0.5) * units
totalDistance = math.floor(totalDistance / units) * units
end

return totalDistance
end

Moon Wizard
March 31st, 2021, 02:27
I believe you can override a control's script using a windowclass merge.

Example:


<windowclass name="imagewindow" merge="join">
<sheetdata>
<image_record name="image">
<script file="campaign/scripts/image_overrides.lua" />
</image_record>
</sheetdata>
</windowclass>

and include the onMeasurePointer code as the only function in the override file.

Regards,
JPG

GKEnialb
March 31st, 2021, 02:32
Awesome - that will be a lot less maintenance!

And confirmed that you're correct; I was able to use the merge and remove all of the base functions.

GKEnialb
April 2nd, 2021, 02:47
To expand on the figuring out the target, I couldn't find a method to determine which target the arrow was pointing to, so I made this general function.



function getCTNodeAt(basex, basey, gridsize)
local allTokens = getTokens()
local theToken = nil
for _, oneToken in pairs(allTokens) do
x,y = oneToken.getPosition()
local ctNode = CombatManager.getCTFromToken(oneToken)

local sSize = StringManager.trim(DB.getValue(ctNode, "size", ""):lower());
local bExact = true
local sizeMultiplier = 0
if sSize == "large" then
sizeMultiplier = 0.5
elseif sSize == "huge" then
sizeMultiplier = 1
elseif sSize == "gargantuan" then
-- Gargantuan creatures behave a bit differently. Can be anywhere within bounds
sizeMultiplier = 1.5
bExact = false
end

local found = false
if bExact then
found = exactMatch(basex, basey, x, y, sizeMultiplier, gridsize)
else
found = matchWithinSize(basex, basey, x, y, sizeMultiplier, gridsize)
end

if found then
local ctNode = CombatManager.getCTFromToken(oneToken)
theToken = ctNode
break
end
end
return theToken
end


For non-gargantuan creatures, this finds an exact match.


function exactMatch(startx, starty, endx, endy, sizeMultiplier, gridsize)
local equal = false

local modx = endx
local mody = endy
if modx > startx then
modx = modx - gridsize * sizeMultiplier
elseif modx < startx then
modx = modx + gridsize * sizeMultiplier
end
if mody > starty then
mody = mody - gridsize * sizeMultiplier
elseif mody < starty then
mody = mody + gridsize * sizeMultiplier
end
if modx == startx and mody == starty then
equal = true
end

return equal
end


For gargantuan creatures, this just checks to see if the pointer is within the creatures token.


function matchWithinSize(startx, starty, endx, endy, sizeMultiplier, gridsize)
local equal = false

local modx = endx
local mody = endy
local lowerBoundx = endx
local lowerBoundy = endy
local upperBoundx = endx
local upperBoundy = endy

if endx > startx then
lowerBoundx = endx - gridsize * sizeMultiplier
elseif endx < startx then
upperBoundx = endx + gridsize * sizeMultiplier
end
if endy > starty then
lowerBoundy = endy - gridsize * sizeMultiplier
elseif endy < starty then
upperBoundy = upperBoundy + gridsize * sizeMultiplier
end

if startx >= lowerBoundx and startx <= upperBoundx and starty >= lowerBoundy and starty <= upperBoundy then
equal = true
end

return equal
end


Of course if there's a better way to do that, I'd be happy to hear it. :)

GKEnialb
April 2nd, 2021, 22:44
And just one more (hopefully) question - is there a way to force a redraw of the targeting arrow programmatically? If I change my height on a token, I have to move the pointer until it loses focus and return it to gain focus to get the onMeasurePointer to activate.

Moon Wizard
April 3rd, 2021, 18:01
No, the onMeasurePointer function is only called when the the targeting line changes.

Regards,
JPG

GKEnialb
April 3rd, 2021, 18:12
Thanks for the confirmation - the workaround isn't that bad. Do you know of an easier way of determining what's at the other end of the arrow within onMeasurePointer than looking up each token's position as I have above?

Moon Wizard
April 3rd, 2021, 18:16
No. There's no information passed in the APIs for that use case. It's more likely that we would embed some sort of height property before we would complicate the APIs further.

Regards,
JPG

GKEnialb
April 3rd, 2021, 18:36
Excellent. Good to know I didn't miss anything. Thanks for the quick response!