PDA

View Full Version : FGU support of custom unit size?



celestian
November 18th, 2019, 03:18
Will FGU support custom unit size at release? Meaning will you be able to set the size of a grid to 10ft, or 20ft or 1.3ml or the like?

I ask because I'd like if this was supported out of the box but if not I'll try and port MUSE over.

Moon Wizard
November 18th, 2019, 07:15
I've added the beginnings of the APIs to do this in FGU; but there is no UI for it yet (and probably won't be at launch). You're welcome to try the new hidden APIs and let me know if they're broken. ;) If you do try this out, please try player side as well.

The hidden/preliminary APIs are:
imagecontrol.setDistanceBaseUnits(0) = no override
imagecontrol.setDistanceBaseUnits(>0) = override value
imagecontrol.getDistanceBaseUnits() = current base units (override if >0; otherwise ruleset value)
imagecontrol.setDistanceSuffix(nil) = no override
imagecontrol.setDistanceSuffix(string) = override value
imagecontrol.getDistanceSuffix() = current suffix value (override if not nil; otherwise ruleset value)

Regards,
JPG

celestian
November 18th, 2019, 16:20
I've added the beginnings of the APIs to do this in FGU; but there is no UI for it yet (and probably won't be at launch). You're welcome to try the new hidden APIs and let me know if they're broken. ;) If you do try this out, please try player side as well.

The hidden/preliminary APIs are:
imagecontrol.setDistanceBaseUnit(0) = no override
imagecontrol.setDistanceBaseUnit(>0) = override value
imagecontrol.getDistanceBaseUnit() = current base units (override if >0; otherwise ruleset value)
imagecontrol.setDistanceSuffix(nil) = no override
imagecontrol.setDistanceSuffix(string) = override value
imagecontrol.getDistanceSuffix() = current suffix value (override if not nil; otherwise ruleset value)

Regards,
JPG

I'll poke around with it and let ya know what I find.

Dr0W
November 18th, 2019, 17:58
Thanks a lot for this, Celestian.

Brazilian D&D and systems that derive from it use "1.5 meters" as a standard, instead of 5'. I had to use MUSE and tweak it a little, it would be great to have it on FGU natively.

celestian
November 19th, 2019, 01:14
Got a few things lined up and started testing.



[11/18/2019 7:01:39 PM] [NOTICE] Launcher scene starting.
[11/18/2019 7:01:44 PM] [NOTICE] Spawning private server.
[11/18/2019 7:01:46 PM] [NOTICE] Connected to game server.
[11/18/2019 7:01:46 PM] [NOTICE] Launcher scene exiting.
[11/18/2019 7:01:46 PM] [NOTICE] Tabletop scene starting.
[11/18/2019 7:04:05 PM] [WARNING] Frame dicetower_normal contains out-of-range values in Middle.
[11/18/2019 7:04:11 PM] [NOTICE] s'image_record.lua' | s'onInit' | s'test1' | #4 | #0 | #0
[11/18/2019 7:04:11 PM] [<color="red">ERROR</color>] Script execution error: [string "campaign/scripts/image_record.lua"]:11: attempt to call global 'getDistanceBaseUnit' (a nil value)
[11/18/2019 7:04:11 PM] [WARNING] Frame d10red contains out-of-range values in TopLeft.


Here is the code I was attempting to run. I've this in the onInit() to set 10 and ft for values (I'll sort out user places to edit after I've verified this works). It seems the functions do not exist? I can run getGridType() and getGridSize() and hasGrid() so im fairly sure I'm using the right options in the right scope.



if Interface.getVersion() >= 4 then
Debug.console("image_record.lua","onInit","test1",Interface.getVersion());
local nTestSize = getGridSize();
Debug.console("image_record.lua","onInit","test2",getDistanceBaseUnit());
Debug.console("image_record.lua","onInit","test3",getDistanceSuffix());
Debug.console("image_record.lua","onInit","test4",nTestSize);
setDistanceBaseUnit(10);
Debug.console("image_record.lua","onInit","test5",Interface.getVersion());
--getDistanceBaseUnit() = current base units (override if >0; otherwise ruleset value)
setDistanceSuffix('ft');
Debug.console("image_record.lua","onInit","test6",Interface.getVersion());
--getDistanceSuffix() = current suffix value (override if not nil; otherwise ruleset value)
--
end



Initially I just tried running sets and when I started getting the error moved the gets to the top to see if it was just the set missing.

Tangentially related:
I noticed (before I began to test) is that "half way points" have enormously large float values even when using whole numbers. I think this came with 3.3.9 but I can't be sure. I've not looked/tested this bit for a while.

https://i.imgur.com/UldVRld.png

Moon Wizard
November 19th, 2019, 01:36
I just added this line into imagewindow.lua at the end of the onLockChanged function in CoreRPG:
Debug.console(image.getDistanceBaseUnits());
And it returned a value.

Are you sure that you are pointing at the image control?

Regards,
JPG

celestian
November 19th, 2019, 02:39
I just added this line into imagewindow.lua at the end of the onLockChanged function in CoreRPG:
Debug.console(image.getDistanceBaseUnits());
And it returned a value.

Are you sure that you are pointing at the image control?

Regards,
JPG

So, this might clear up things a little. Here is where the script is attached (trying to just tweak the current code with the adjusted API calls).



<!-- standard image template -->
<template name="image_record">
<image_record_step>
<bounds>21,63,-27,-29</bounds>
<script file="campaign/scripts/image_record.lua"/>
</image_record_step>
</template>
<!-- desktop image template -->
<template name="imagepanel_record">
<image_record_step>
<bounds>1,37,-1,-1</bounds>
<script file="campaign/scripts/image_record.lua"/>
</image_record_step>
</template>

...image_record.lua...

function onInit()
--
Debug.console("image_record.lua","onInit","window",window);
Debug.console("image_record.lua","onInit","self",self);
Debug.console("image_record.lua","onInit","getGridType",getGridType());
Debug.console("image_record.lua","onInit","self.getGridType",self.getGridType());
if Interface.getVersion() >= 4 then
local nTestSize = getGridSize();
Debug.console("image_record.lua","onInit","getDistanceBaseUnit",getDistanceBaseUnit());
Debug.console("image_record.lua","onInit","self.getDistanceBaseUnit",self.getDistanceBaseUnit());
self.setDistanceBaseUnit(10);
--getDistanceBaseUnit() = current base units (override if >0; otherwise ruleset value)
self.setDistanceSuffix('ft');
--getDistanceSuffix() = current suffix value (override if not nil; otherwise ruleset value)
--
end

end


This is the debug output.



[11/18/2019 8:31:40 PM] [NOTICE] Launcher scene starting.
[11/18/2019 8:32:17 PM] [NOTICE] Spawning private server.
[11/18/2019 8:32:19 PM] [NOTICE] Connected to game server.
[11/18/2019 8:32:19 PM] [NOTICE] Launcher scene exiting.
[11/18/2019 8:32:19 PM] [NOTICE] Tabletop scene starting.
[11/18/2019 8:34:31 PM] [WARNING] Frame dicetower_normal contains out-of-range values in Middle.
[11/18/2019 8:34:49 PM] [NOTICE] s'image_record.lua' | s'onInit' | s'window' | windowinstance = { class = imagewindow, node = image.id-00016@B2: The Keep on the Bordelands (L1-3), x,y,w,h = 0,0,1807,1211 }
[11/18/2019 8:34:49 PM] [NOTICE] s'image_record.lua' | s'onInit' | s'self' | imagecontrol = { name = s'image', x,y,w,h = 21,63,1759,1119 }
[11/18/2019 8:34:49 PM] [NOTICE] s'image_record.lua' | s'onInit' | s'getGridType' | s'square'
[11/18/2019 8:34:49 PM] [NOTICE] s'image_record.lua' | s'onInit' | s'self.getGridType' | s'square'
[11/18/2019 8:34:49 PM] [<color="red">ERROR</color>] Script execution error: [string "campaign/scripts/image_record.lua"]:14: attempt to call global 'getDistanceBaseUnit' (a nil value)
[11/18/2019 8:34:50 PM] [WARNING] Frame d10red contains out-of-range values in TopLeft.
[11/18/2019 8:38:32 PM] [NOTICE] Campaign saved.



Line 14: "Debug.console("image_record.lua","onInit","getDistanceBaseUnit",getDistanceBaseUnit()); "

If the functions are in imagecontrol then I should be able to use "getDistanceBaseUnit()" or "setDistanceBaseUnit(10)" right? In the same imagecontrol is "getGridType()" which I can get.

Trenloe
November 19th, 2019, 16:56
Line 14: "Debug.console("image_record.lua","onInit","getDistanceBaseUnit",getDistanceBaseUnit()); "


I just added this line into imagewindow.lua at the end of the onLockChanged function in CoreRPG:
Debug.console(image.getDistanceBaseUnits());

Looks like MW has decided to add a "s".

celestian
November 19th, 2019, 17:39
Looks like MW has decided to add a "s".

Good catch, I didn't notice that difference from his original post listing the functions. That was it ;) It got past my debug tests after correcting that and changing input to "10" but I'll have to play with it more after day job duties tonight.

Thanks!

https://i.imgur.com/iWV5zvi.png

(I did notice that the half-way markers are no longer calculated for half points as they do in FGC, does the same in CoreRPG).

celestian
November 20th, 2019, 02:31
I was going to add options to custom/configure the unit size and suffix and ... ran into an issue.

https://i.imgur.com/lJjOirn.png

So, that menu. Where is it in XML? I attempted to find it using my normal detective work (start with finding the tooltip text in the strings file and then find where that is used) but when I searched for "nudge" I couldn't find it in CoreRPG. Is that menu system hard coded currently?

celestian
November 20th, 2019, 03:27
I was going to add options to custom/configure the unit size and suffix and ... ran into an issue.

So, that menu. Where is it in XML? I attempted to find it using my normal detective work (start with finding the tooltip text in the strings file and then find where that is used) but when I searched for "nudge" I couldn't find it in CoreRPG. Is that menu system hard coded currently?

Moon popped into Discord and replied there.



Moon Wizard: That’s a self contained control called imagedatacontrol


Unfortunately that means I don't think I can add in the unit/suffix there. Which is a shame because it'd really fit well right below the nudge grid section.

Tentatively tho the set/get options added seem to work using the current MUSE style code.



--
-- Please see the license.html file included with this distribution for
-- attribution and copyright information.
--

function onInit()
--
if Interface.getVersion() >= 4 then
setDistanceBaseUnits("10");
Debug.console("image_record.lua","onInit","getDistanceBaseUnits",getDistanceBaseUnits());
--getDistanceBaseUnits() = current base units (override if >0; otherwise ruleset value)
setDistanceSuffix('ft');
Debug.console("image_record.lua","onInit","getDistanceSuffix",getDistanceSuffix());
--getDistanceSuffix() = current suffix value (override if not nil; otherwise ruleset value)
end

-- when the map is loaded setup these.
TokenManagerADND.updateCTEntries();
end

-- get the unit scale and measurement
function getScaleControlValue()
local sValue = '5ft';
if Interface.getVersion() < 4 then
local node = getDatabaseNode().getChild("..");
sValue = DB.getValue(node,"scale","10ft"); -- default to 10ft here but we should never see this.
else
sValue = getDistanceBaseUnit() .. getDistanceSuffix();
end
return sValue;
end

-- is the scale value VALID?
function getScaleControlisValid()
local bValid = false;
if Interface.getVersion() < 4 then
bValid = getScaleControlValue():find("^[%d%.]+") ~= nil
else
bValid = true;
end
return bValid;
end

-- get the size of each unit
function getScaleControlScaleValue()
local nScaleValue = 0;
if Interface.getVersion() < 4 then
if getScaleControlisValid() then
nScaleValue = tonumber(getScaleControlValue():match("^([%d%.]+)")) or 0
end
else
nScaleValue = getDistanceBaseUnit() or 5;
end
return nScaleValue;
end

-- get the scale measurement name/string
function getScaleControlScaleLabel()
local sValue = "ft";
if Interface.getVersion() < 4 then
sValue = StringManager.trim(getScaleControlValue():gsub("^[%d%.]+%s*", ""))
else
sValue = getDistanceSuffix();
end
return sValue;
end

-- measureVector
function measureVector(nVectorX, nVectorY, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
local nDiag = 1;
if OptionsManager.isOption("HRDD", "variant") then
nDiag = 1.5;
end
local nDistance = 0

if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nCol, nRow = 0, 0
if sGridType == "hexcolumn" then
nCol = nVectorX / (nGridHexWidth*3)
nRow = (nVectorY / (nGridHexHeight*2)) - (nCol * 0.5)
else
nRow = nVectorY / (nGridHexWidth*3)
nCol = (nVectorX / (nGridHexHeight*2)) - (nRow * 0.5)
end

if ((nRow >= 0 and nCol >= 0) or (nRow < 0 and nCol < 0)) then
nDistance = math.abs(nCol) + math.abs(nRow)
else
nDistance = math.max(math.abs(nCol), math.abs(nRow))
end

else -- if sGridType == "square" then
local nDiagonals = 0
local nStraights = 0

local nGridX = math.abs(nVectorX / nGridSize)
local nGridY = math.abs(nVectorY / nGridSize)

if nGridX > nGridY then
nDiagonals = nDiagonals + nGridY
nStraights = nStraights + nGridX - nGridY
else
nDiagonals = nDiagonals + nGridX
nStraights = nStraights + nGridY - nGridX
end

nDistance = nDiagonals * nDiag + nStraights
end

return nDistance
end

-- when measuring a vector distance
function onMeasureVector(token, aVector)
if hasGrid() then
local sGridType = getGridType()
local nGridSize = getGridSize()

local nDistance = 0
if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nGridHexWidth, nGridHexHeight = getGridHexElementDimensions()
for i = 1, #aVector do
local nVector = measureVector(aVector[i].x, aVector[i].y, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
nDistance = nDistance + nVector
end
else -- if sGridType == "square" then
for i = 1, #aVector do
local nVector = measureVector(aVector[i].x, aVector[i].y, sGridType, nGridSize)
nDistance = nDistance + nVector
end
end

if getScaleControlisValid() then
return (nDistance * getScaleControlScaleValue()) .. getScaleControlScaleLabel();
else
return ""
end
else
return ""
end
end

-- when measuring a pointer distance
function onMeasurePointer(nLength, sPointerType, nStartX, nStartY, nEndX, nEndY)
if hasGrid() then
local sGridType = getGridType()
local nGridSize = getGridSize()

if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nGridHexWidth, nGridHexHeight = getGridHexElementDimensions()
nDistance = measureVector(nEndX - nStartX, nEndY - nStartY, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
else -- if sGridType == "square" then
nDistance = measureVector(nEndX - nStartX, nEndY - nStartY, sGridType, nGridSize)
end

if getScaleControlisValid() then
return (nDistance * getScaleControlScaleValue()) .. getScaleControlScaleLabel()
else
return ""
end
else
return ""
end
end

celestian
November 22nd, 2019, 00:03
I tinkered with this a bit more and it's functional outside of the issues I've mentioned before (half-way-points not working across the board).

It would be much better if this was included in the actual image data control window (in the grid config section) instead of options I've added at the lock/unlock header but this was for testing and right now I got no clues about the image data control.

https://i.imgur.com/RjhMB3n.gif

Changing the size and suffix works. The caveat is that when working with existing images in a module it doesn't seem to work at all. It appears that you cannot modify module data or something? I didn't spend much time debugging that.

celestian
November 22nd, 2019, 05:28
Additional testing to verify user side is working and... it's not. I tried various things using OOB to make sure it's getting run properly and it continues to work for the Host side but not the PC side.

I was a bit surprised it didn't just "work" as I recall it in FGC. All I needed to do was update the Host side for unit/suffix and it would force the player side to update.

On the player side all they get is the unit size 5 and suffix ' even when configured for something else. Even when it appeared the OOB method was working (see extensive logging entries).

Code snippet in case someone is as caught up in this as me cares to eyeball it.



--
-- Please see the license.html file included with this distribution for
-- attribution and copyright information.
--

OOB_MSGTYPE_MUSE_SETUP = "musesetup";
OOB_MSGTYPE_MUSE_UPDATE = "museclose";


function onInit()
--
if Interface.getVersion() >= 4 then
OOBManager.registerOOBMsgHandler(OOB_MSGTYPE_MUSE_ SETUP, muse_Setup);
OOBManager.registerOOBMsgHandler(OOB_MSGTYPE_MUSE_ UPDATE, muse_Close);
notifyMuseSetup(getDatabaseNode());
if User.isHost() then
DB.addHandler(DB.getPath(getDatabaseNode().getPare nt(), "locked"), "onUpdate", onLockChanged);
end
end

-- when the map is loaded setup these.
TokenManagerADND.updateCTEntries();
end

function onClose()
if Interface.getVersion() >= 4 then
notifyMuseClose(getDatabaseNode());
if User.isHost() then
DB.removeHandler(DB.getPath(getDatabaseNode().getP arent(), "locked"), "onUpdate", onLockChanged);
end
end
end
function notifyMuseClose(node)
Debug.console("image_record.lua","notifyMuseClose","node",node);
local msgOOB = {};
msgOOB.type = OOB_MSGTYPE_MUSE_CLOSE;
local sSourceNode = node.getPath();
msgOOB.sSourceNode = sSourceNode;
Comm.deliverOOBMessage(msgOOB, "");
end
-- oob takes control and makes change (sends to apply)
function muse_Close(msgOOB)
Debug.console("image_record.lua","muse_Close","msgOOB",msgOOB);
local node = DB.findNode(msgOOB.sSourceNode);
Debug.console("image_record.lua","muse_Close","node1",node);
node = node.getParent();
Debug.console("image_record.lua","muse_Close","node2",node);
DB.removeHandler(DB.getPath(node, "muse_unitsize_number"), "onUpdate", onUnitSizeUpdates);
DB.removeHandler(DB.getPath(node, "muse_suffix_string"), "onUpdate", onUnitSizeUpdates);
end


function notifyMuseSetup(node)
Debug.console("image_record.lua","notifyMuseSetup","node",node);
local msgOOB = {};
msgOOB.type = OOB_MSGTYPE_MUSE_SETUP;
local sSourceNode = node.getPath();
msgOOB.sSourceNode = sSourceNode;
Comm.deliverOOBMessage(msgOOB, "");
end
-- oob takes control and makes change (sends to apply)
function muse_Setup(msgOOB)
Debug.console("image_record.lua","muse_Setup","msgOOB",msgOOB);
local node = DB.findNode(msgOOB.sSourceNode);
Debug.console("image_record.lua","muse_Setup","node",node);
DB.addHandler(DB.getPath(node.getParent(), "muse_unitsize_number"), "onUpdate", onUnitSizeUpdates);
DB.addHandler(DB.getPath(node.getParent(), "muse_suffix_string"), "onUpdate", onUnitSizeUpdates);
onUnitSizeUpdates(node);
onLockChanged(node);
end

-- update size/suffix on the map when they are reconfigured.
function onUnitSizeUpdates(node)
Debug.console("image_record.lua","onUnitSizeUpdates","node",node);
node = node.getParent();
Debug.console("image_record.lua","onUnitSizeUpdates","node",node);
--local node = getDatabaseNode().getParent();
setDistanceBaseUnits(tostring(DB.getValue(node,"muse_unitsize_number","5")));
setDistanceSuffix(DB.getValue(node,"muse_suffix_string","ft"));
--getDistanceBaseUnits() = current base units (override if >0; otherwise ruleset value)
--getDistanceSuffix() = current suffix value (override if not nil; otherwise ruleset value)
Debug.console("image_record.lua","onUnitSizeUpdates","getDistanceBaseUnits()",getDistanceBaseUnits());
Debug.console("image_record.lua","onUnitSizeUpdates","getDistanceSuffix()",getDistanceSuffix());
end

-- when the map unlocked show the size/suffix fields.
function onLockChanged()
local node = getDatabaseNode().getParent();
Debug.console("image_record.lua","onLockChanged","node",node);
local bUnLocked = DB.getValue(node, "locked", 0) == 0;
window.header.subwindow.muse_unitsize_label.setVis ible(bUnLocked);
window.header.subwindow.muse_unitsize_number.setVi sible(bUnLocked);
window.header.subwindow.muse_suffix_label.setVisib le(bUnLocked);
window.header.subwindow.muse_suffix_string.setVisi ble(bUnLocked);
end

-- get the unit scale and measurement
function getScaleControlValue()
local sValue = '5ft';
if Interface.getVersion() < 4 then
local node = getDatabaseNode().getChild("..");
sValue = DB.getValue(node,"scale","10ft"); -- default to 10ft here but we should never see this.
else
sValue = getDistanceBaseUnit() .. getDistanceSuffix();
end
return sValue;
end

-- is the scale value VALID?
function getScaleControlisValid()
local bValid = false;
if Interface.getVersion() < 4 then
bValid = getScaleControlValue():find("^[%d%.]+") ~= nil
else
bValid = true;
end
return bValid;
end

-- get the size of each unit
function getScaleControlScaleValue()
local nScaleValue = 0;
if Interface.getVersion() < 4 then
if getScaleControlisValid() then
nScaleValue = tonumber(getScaleControlValue():match("^([%d%.]+)")) or 0
end
else
nScaleValue = getDistanceBaseUnit() or 5;
end
return nScaleValue;
end

-- get the scale measurement name/string
function getScaleControlScaleLabel()
local sValue = "ft";
if Interface.getVersion() < 4 then
sValue = StringManager.trim(getScaleControlValue():gsub("^[%d%.]+%s*", ""))
else
sValue = getDistanceSuffix();
end
return sValue;
end

-- measureVector
function measureVector(nVectorX, nVectorY, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
local nDiag = 1;
if OptionsManager.isOption("HRDD", "variant") then
nDiag = 1.5;
end
local nDistance = 0

if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nCol, nRow = 0, 0
if sGridType == "hexcolumn" then
nCol = nVectorX / (nGridHexWidth*3)
nRow = (nVectorY / (nGridHexHeight*2)) - (nCol * 0.5)
else
nRow = nVectorY / (nGridHexWidth*3)
nCol = (nVectorX / (nGridHexHeight*2)) - (nRow * 0.5)
end

if ((nRow >= 0 and nCol >= 0) or (nRow < 0 and nCol < 0)) then
nDistance = math.abs(nCol) + math.abs(nRow)
else
nDistance = math.max(math.abs(nCol), math.abs(nRow))
end

else -- if sGridType == "square" then
local nDiagonals = 0
local nStraights = 0

local nGridX = math.abs(nVectorX / nGridSize)
local nGridY = math.abs(nVectorY / nGridSize)

if nGridX > nGridY then
nDiagonals = nDiagonals + nGridY
nStraights = nStraights + nGridX - nGridY
else
nDiagonals = nDiagonals + nGridX
nStraights = nStraights + nGridY - nGridX
end

nDistance = nDiagonals * nDiag + nStraights
end

return nDistance
end

-- when measuring a vector distance
function onMeasureVector(token, aVector)
if hasGrid() then
local sGridType = getGridType()
local nGridSize = getGridSize()

local nDistance = 0
if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nGridHexWidth, nGridHexHeight = getGridHexElementDimensions()
for i = 1, #aVector do
local nVector = measureVector(aVector[i].x, aVector[i].y, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
nDistance = nDistance + nVector
end
else -- if sGridType == "square" then
for i = 1, #aVector do
local nVector = measureVector(aVector[i].x, aVector[i].y, sGridType, nGridSize)
nDistance = nDistance + nVector
end
end

if getScaleControlisValid() then
return (nDistance * getScaleControlScaleValue()) .. getScaleControlScaleLabel();
else
return ""
end
else
return ""
end
end

-- when measuring a pointer distance
function onMeasurePointer(nLength, sPointerType, nStartX, nStartY, nEndX, nEndY)
if hasGrid() then
local sGridType = getGridType()
local nGridSize = getGridSize()

if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nGridHexWidth, nGridHexHeight = getGridHexElementDimensions()
nDistance = measureVector(nEndX - nStartX, nEndY - nStartY, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
else -- if sGridType == "square" then
nDistance = measureVector(nEndX - nStartX, nEndY - nStartY, sGridType, nGridSize)
end

if getScaleControlisValid() then
return (nDistance * getScaleControlScaleValue()) .. getScaleControlScaleLabel()
else
return ""
end
else
return ""
end
end

Moon Wizard
November 29th, 2019, 23:14
Just found a couple issues with the base units / suffix player-side updating that I fixed in the build I just pushed, if you want to give it another try.

Regards,
JPG

celestian
November 30th, 2019, 07:58
Just found a couple issues with the base units / suffix player-side updating that I fixed in the build I just pushed, if you want to give it another try.

Regards,
JPG

Sounds good. I wont be able to sitdown until probably sunday evening and test.

Do you expect I'll need to keep OOB functions or will setting the unit/suffix on host side be sufficient as it was in FGC?

Moon Wizard
December 1st, 2019, 04:14
If you set the individual image value setting on the host, it will be saved in the data and sent via network to the player clients.

Regards,
JPG

celestian
December 1st, 2019, 05:37
If you set the individual image value setting on the host, it will be saved in the data and sent via network to the player clients.

Regards,
JPG

Seems to work outside of the issues previously mentioned (the half-way marks).

I do have one suggestion, that the unit/suffix option be added into the image-control-data you discussed. It seems such a perfect place for it. It would fit right around here in grid management mode. I placed it in the image header just for this test.

https://i.imgur.com/OEW1Ts7.png

This is the code I ended up with to make it work with FGU and FGC.



--
-- Please see the license.html file included with this distribution for
-- attribution and copyright information.
--

function onInit()
--
if Interface.getVersion() >= 4 then
if User.isHost() then
local node = getDatabaseNode();
DB.addHandler(DB.getPath(node.getParent(), "muse_unitsize_number"), "onUpdate", onUnitSizeUpdates);
DB.addHandler(DB.getPath(node.getParent(), "muse_suffix_string"), "onUpdate", onUnitSizeUpdates);
DB.addHandler(DB.getPath(node.getParent(), "locked"), "onUpdate", onLockChanged);
onUnitSizeUpdates(node);
onLockChanged(node);
end
end

-- when the map is loaded setup these.
TokenManagerADND.updateCTEntries();
end

function onClose()
if Interface.getVersion() >= 4 then
if User.isHost() then
local node = getDatabaseNode();
DB.removeHandler(DB.getPath(node.getParent(), "muse_unitsize_number"), "onUpdate", onUnitSizeUpdates);
DB.removeHandler(DB.getPath(node.getParent(), "muse_suffix_string"), "onUpdate", onUnitSizeUpdates);
DB.removeHandler(DB.getPath(node().getParent(), "locked"), "onUpdate", onLockChanged);
end
end
end

-- update size/suffix on the map when they are reconfigured.
function onUnitSizeUpdates(node)
node = node.getParent();
--local node = getDatabaseNode().getParent();
setDistanceBaseUnits(tostring(DB.getValue(node,"muse_unitsize_number","5")));
setDistanceSuffix(DB.getValue(node,"muse_suffix_string","ft"));
--getDistanceBaseUnits() = current base units (override if >0; otherwise ruleset value)
--getDistanceSuffix() = current suffix value (override if not nil; otherwise ruleset value)
--Debug.console("image_record.lua","onUnitSizeUpdates","getDistanceBaseUnits()",getDistanceBaseUnits());
--Debug.console("image_record.lua","onUnitSizeUpdates","getDistanceSuffix()",getDistanceSuffix());
end

-- when the map unlocked show the size/suffix fields.
function onLockChanged()
local node = getDatabaseNode().getParent();
--Debug.console("image_record.lua","onLockChanged","node",node);
local bUnLocked = DB.getValue(node, "locked", 0) == 0;
window.header.subwindow.muse_unitsize_label.setVis ible(bUnLocked);
window.header.subwindow.muse_unitsize_number.setVi sible(bUnLocked);
window.header.subwindow.muse_suffix_label.setVisib le(bUnLocked);
window.header.subwindow.muse_suffix_string.setVisi ble(bUnLocked);
end

-- get the unit scale and measurement
function getUnitControlSetting()
local sValue = '5ft';
if Interface.getVersion() < 4 then
local node = getDatabaseNode().getChild("..");
sValue = DB.getValue(node,"scale","10ft"); -- default to 10ft here but we should never see this.
else
sValue = getDistanceBaseUnit() .. getDistanceSuffix();
end
return sValue;
end

-- is the scale value VALID?
function getUnitControlisValid()
local bValid = false;
if Interface.getVersion() < 4 then
bValid = getUnitControlSetting():find("^[%d%.]+") ~= nil
else
bValid = true;
end
return bValid;
end

-- get the size of each unit
function getUnitControlValue()
local nScaleValue = 0;
if Interface.getVersion() < 4 then
if getUnitControlisValid() then
nScaleValue = tonumber(getUnitControlSetting():match("^([%d%.]+)")) or 0
end
else
nScaleValue = getDistanceBaseUnit() or 5;
end
return nScaleValue;
end

-- get the scale measurement name/string
function getUnitControlLabel()
local sValue = "ft";
if Interface.getVersion() < 4 then
sValue = StringManager.trim(getUnitControlSetting():gsub("^[%d%.]+%s*", ""))
else
sValue = getDistanceSuffix();
end
return sValue;
end

-- measureVector
function measureVector(nVectorX, nVectorY, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
local nDiag = 1;
if OptionsManager.isOption("HRDD", "variant") then
nDiag = 1.5;
end
local nDistance = 0

if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nCol, nRow = 0, 0
if sGridType == "hexcolumn" then
nCol = nVectorX / (nGridHexWidth*3)
nRow = (nVectorY / (nGridHexHeight*2)) - (nCol * 0.5)
else
nRow = nVectorY / (nGridHexWidth*3)
nCol = (nVectorX / (nGridHexHeight*2)) - (nRow * 0.5)
end

if ((nRow >= 0 and nCol >= 0) or (nRow < 0 and nCol < 0)) then
nDistance = math.abs(nCol) + math.abs(nRow)
else
nDistance = math.max(math.abs(nCol), math.abs(nRow))
end

else -- if sGridType == "square" then
local nDiagonals = 0
local nStraights = 0

local nGridX = math.abs(nVectorX / nGridSize)
local nGridY = math.abs(nVectorY / nGridSize)

if nGridX > nGridY then
nDiagonals = nDiagonals + nGridY
nStraights = nStraights + nGridX - nGridY
else
nDiagonals = nDiagonals + nGridX
nStraights = nStraights + nGridY - nGridX
end

nDistance = nDiagonals * nDiag + nStraights
end

return nDistance
end

-- when measuring a vector distance
function onMeasureVector(token, aVector)
if hasGrid() then
local sGridType = getGridType()
local nGridSize = getGridSize()

local nDistance = 0
if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nGridHexWidth, nGridHexHeight = getGridHexElementDimensions()
for i = 1, #aVector do
local nVector = measureVector(aVector[i].x, aVector[i].y, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
nDistance = nDistance + nVector
end
else -- if sGridType == "square" then
for i = 1, #aVector do
local nVector = measureVector(aVector[i].x, aVector[i].y, sGridType, nGridSize)
nDistance = nDistance + nVector
end
end

if getUnitControlisValid() then
--return (nDistance * getUnitControlValue()) .. getUnitControlLabel();
return (round(nDistance * getUnitControlValue(),1)) .. getUnitControlLabel()
else
return ""
end
else
return ""
end
end

-- when measuring a pointer distance
function onMeasurePointer(nLength, sPointerType, nStartX, nStartY, nEndX, nEndY)
if hasGrid() then
local sGridType = getGridType()
local nGridSize = getGridSize()

if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nGridHexWidth, nGridHexHeight = getGridHexElementDimensions()
nDistance = measureVector(nEndX - nStartX, nEndY - nStartY, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
else -- if sGridType == "square" then
nDistance = measureVector(nEndX - nStartX, nEndY - nStartY, sGridType, nGridSize)
end

if getUnitControlisValid() then
return (round(nDistance * getUnitControlValue(),1)) .. getUnitControlLabel()
else
return ""
end
else
return ""
end
end

-- round num to nearest decimal
function round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end

Moon Wizard
December 1st, 2019, 07:10
Can you re-iterate on the halfway marks, and what the issue is?
I thought I remember seeing something about precision not being right with arrows that go halfway; but I couldn't recreate in CoreRPG or 5E.

Regards,
JPG

celestian
December 1st, 2019, 08:15
Can you re-iterate on the halfway marks, and what the issue is?
I thought I remember seeing something about precision not being right with arrows that go halfway; but I couldn't recreate in CoreRPG or 5E.

Regards,
JPG

The measurements for half-way points don't show up as expected. Previously in FGC half-way points had a accurate measurement.

https://i.imgur.com/Q2TKkkq.png

The odd thing I noticed is that in FGC 3.3.9 the half-way points are now enormous floats.

https://i.imgur.com/TQaDsIe.png

I checked with 5e ruleset and in FGU it seems to just measure 5, no half-way points, nothing or the last unit measure that was not half-way. I don't play 5e enough to know if this was the normal behavior.

https://i.imgur.com/zKCFJm1.png

I can probably work around the FGC large float issue but FGU doesn't have that right now so there is a difference somewhere.

I am beginning to wonder if the half-way points ever displayed/measured properly... looking back at some old SS's it doesn't seem they did. I keep thinking it did but I don't think it did. Would be nice tho.

Moon Wizard
December 1st, 2019, 18:45
The measurements were always supposed to be measured by grid blocks, wherein the actual displayed distance would be modified by the units and suffix. This is the way that FGC has worked for many years; and mirrors what I've seen as the intended behavior of most game systems from a tactical perspective. (i.e. if it's more than 1 block distance, than it counts as two blocks distance.)

There is a new true distance mode using a <diagmult>*<diagmult> for the ruleset. However, it does not have a local image setting counterpart and API. When using this distance mode, all pointers are measured "tape measure" style. This is to support "wargaming" style systems for non-grid movement and distances.

Regards,
JPG

celestian
December 1st, 2019, 21:32
The measurements were always supposed to be measured by grid blocks, wherein the actual displayed distance would be modified by the units and suffix. This is the way that FGC has worked for many years; and mirrors what I've seen as the intended behavior of most game systems from a tactical perspective. (i.e. if it's more than 1 block distance, than it counts as two blocks distance.)



So this will not function in FGU (this is current FGC)?

https://i.imgur.com/TQaDsIe.png

Moon Wizard
December 1st, 2019, 23:31
It's supposed to be in whole grid units (multiplied by base units). If not, then FGC is not working as intended.

Also, I'm not sure how you got that to appear like that in your version, since it does not do that when I try in CoreRPG, 3.5E or 5E on FGC.

Thanks,
JPG

celestian
December 2nd, 2019, 04:05
It's supposed to be in whole grid units (multiplied by base units). If not, then FGC is not working as intended.

Also, I'm not sure how you got that to appear like that in your version, since it does not do that when I try in CoreRPG, 3.5E or 5E on FGC.

Thanks,
JPG

Only bringing this up because you told me to not make kludges around FGU when FGC works another way.

To be clear this is not untouched ruleset, it's related to the unit/suffix code. I had it already in the AD&D ruleset. I updated the code to function within FGU (with your new API functions) and FGC.

I added this script to the image_record class.



<!-- standard image template -->
<template name="image_record">
<image_record_step>
<bounds>21,63,-27,-29</bounds>
<script file="campaign/scripts/image_record.lua"/>
</image_record_step>
</template>




--
-- Please see the license.html file included with this distribution for
-- attribution and copyright information.
--

function onInit()
--
if Interface.getVersion() >= 4 then
if User.isHost() then
local node = getDatabaseNode();
DB.addHandler(DB.getPath(node.getParent(), "muse_unitsize_number"), "onUpdate", onUnitSizeUpdates);
DB.addHandler(DB.getPath(node.getParent(), "muse_suffix_string"), "onUpdate", onUnitSizeUpdates);
DB.addHandler(DB.getPath(node.getParent(), "locked"), "onUpdate", onLockChanged);
onUnitSizeUpdates(node);
onLockChanged(node);
end
end

-- when the map is loaded setup these.
TokenManagerADND.updateCTEntries();
end

function onClose()
if Interface.getVersion() >= 4 then
if User.isHost() then
local node = getDatabaseNode();
DB.removeHandler(DB.getPath(node.getParent(), "muse_unitsize_number"), "onUpdate", onUnitSizeUpdates);
DB.removeHandler(DB.getPath(node.getParent(), "muse_suffix_string"), "onUpdate", onUnitSizeUpdates);
DB.removeHandler(DB.getPath(node.getParent(), "locked"), "onUpdate", onLockChanged);
end
end
end

-- update size/suffix on the map when they are reconfigured.
function onUnitSizeUpdates(node)
node = node.getParent();
--local node = getDatabaseNode().getParent();
setDistanceBaseUnits(tostring(DB.getValue(node,"muse_unitsize_number","5")));
setDistanceSuffix(DB.getValue(node,"muse_suffix_string","ft"));
--getDistanceBaseUnits() = current base units (override if >0; otherwise ruleset value)
--getDistanceSuffix() = current suffix value (override if not nil; otherwise ruleset value)
--Debug.console("image_record.lua","onUnitSizeUpdates","getDistanceBaseUnits()",getDistanceBaseUnits());
--Debug.console("image_record.lua","onUnitSizeUpdates","getDistanceSuffix()",getDistanceSuffix());
end

-- when the map unlocked show the size/suffix fields.
function onLockChanged()
local node = getDatabaseNode().getParent();
--Debug.console("image_record.lua","onLockChanged","node",node);
local bUnLocked = DB.getValue(node, "locked", 0) == 0;
window.header.subwindow.muse_unitsize_label.setVis ible(bUnLocked);
window.header.subwindow.muse_unitsize_number.setVi sible(bUnLocked);
window.header.subwindow.muse_suffix_label.setVisib le(bUnLocked);
window.header.subwindow.muse_suffix_string.setVisi ble(bUnLocked);
end

-- get the unit scale and measurement
function getUnitControlSetting()
local sValue = '5ft';
if Interface.getVersion() < 4 then
local node = getDatabaseNode().getChild("..");
sValue = DB.getValue(node,"scale","10ft"); -- default to 10ft here but we should never see this.
else
sValue = getDistanceBaseUnit() .. getDistanceSuffix();
end
return sValue;
end

-- is the scale value VALID?
function getUnitControlisValid()
local bValid = false;
if Interface.getVersion() < 4 then
bValid = getUnitControlSetting():find("^[%d%.]+") ~= nil
else
bValid = true;
end
return bValid;
end

-- get the size of each unit
function getUnitControlValue()
local nScaleValue = 0;
if Interface.getVersion() < 4 then
if getUnitControlisValid() then
nScaleValue = tonumber(getUnitControlSetting():match("^([%d%.]+)")) or 0
end
else
nScaleValue = getDistanceBaseUnit() or 5;
end
return nScaleValue;
end

-- get the scale measurement name/string
function getUnitControlLabel()
local sValue = "ft";
if Interface.getVersion() < 4 then
sValue = StringManager.trim(getUnitControlSetting():gsub("^[%d%.]+%s*", ""))
else
sValue = getDistanceSuffix();
end
return sValue;
end

-- measureVector
function measureVector(nVectorX, nVectorY, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
local nDiag = 1;
if OptionsManager.isOption("HRDD", "variant") then
nDiag = 1.5;
end
local nDistance = 0

if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nCol, nRow = 0, 0
if sGridType == "hexcolumn" then
nCol = nVectorX / (nGridHexWidth*3)
nRow = (nVectorY / (nGridHexHeight*2)) - (nCol * 0.5)
else
nRow = nVectorY / (nGridHexWidth*3)
nCol = (nVectorX / (nGridHexHeight*2)) - (nRow * 0.5)
end

if ((nRow >= 0 and nCol >= 0) or (nRow < 0 and nCol < 0)) then
nDistance = math.abs(nCol) + math.abs(nRow)
else
nDistance = math.max(math.abs(nCol), math.abs(nRow))
end

else -- if sGridType == "square" then
local nDiagonals = 0
local nStraights = 0

local nGridX = math.abs(nVectorX / nGridSize)
local nGridY = math.abs(nVectorY / nGridSize)

if nGridX > nGridY then
nDiagonals = nDiagonals + nGridY
nStraights = nStraights + nGridX - nGridY
else
nDiagonals = nDiagonals + nGridX
nStraights = nStraights + nGridY - nGridX
end

nDistance = nDiagonals * nDiag + nStraights
end

return nDistance
end

-- when measuring a vector distance
function onMeasureVector(token, aVector)
if hasGrid() then
local sGridType = getGridType()
local nGridSize = getGridSize()

local nDistance = 0
if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nGridHexWidth, nGridHexHeight = getGridHexElementDimensions()
for i = 1, #aVector do
local nVector = measureVector(aVector[i].x, aVector[i].y, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
nDistance = nDistance + nVector
end
else -- if sGridType == "square" then
for i = 1, #aVector do
local nVector = measureVector(aVector[i].x, aVector[i].y, sGridType, nGridSize)
nDistance = nDistance + nVector
end
end

if getUnitControlisValid() then
--return (nDistance * getUnitControlValue()) .. getUnitControlLabel();
return (UtilityManagerADND.round(nDistance * getUnitControlValue())) .. getUnitControlLabel()
else
return ""
end
else
return ""
end
end

-- when measuring a pointer distance
function onMeasurePointer(nLength, sPointerType, nStartX, nStartY, nEndX, nEndY)
if hasGrid() then
local sGridType = getGridType()
local nGridSize = getGridSize()

if sGridType == "hexrow" or sGridType == "hexcolumn" then
local nGridHexWidth, nGridHexHeight = getGridHexElementDimensions()
nDistance = measureVector(nEndX - nStartX, nEndY - nStartY, sGridType, nGridSize, nGridHexWidth, nGridHexHeight)
else -- if sGridType == "square" then
nDistance = measureVector(nEndX - nStartX, nEndY - nStartY, sGridType, nGridSize)
end

if getUnitControlisValid() then
return (UtilityManagerADND.round(nDistance * getUnitControlValue())) .. getUnitControlLabel()
else
return ""
end
else
return ""
end
end



All I added recently was the round bit to get rid of that large float value.

It now looks like this. If I updated the MUSE extension it would do the same for CoreRPG/etc.

https://i.imgur.com/mIm6YuY.png

Moon Wizard
December 2nd, 2019, 07:29
It's because you are measuring your own vectors that you were seeing the large numbers; and the half measures.

The built-in measuring and the way the original onMeasureVector was supposed to work before we built it into the client is that it was based on grid squares first, then multiplied by base units.

Regards,
JPG

celestian
January 11th, 2020, 03:14
I touched this tonight to see if there had been any changes related to the issue of tweaking the value in FGU.

You'll notice in this image a problem.

https://i.imgur.com/YV4QMgN.png

The left image is the image from the module. The right image is the image I copied locally from the module.

The problem with left version is even when the image is unlocked it doesn't trigger the handler to set the input blocks visible.

The right version does trigger the handler and it's visible.

Looks like the moduledb/* version doesn't have a "locked" value.

ronnke
March 18th, 2020, 12:19
It's because you are measuring your own vectors that you were seeing the large numbers; and the half measures.

The built-in measuring and the way the original onMeasureVector was supposed to work before we built it into the client is that it was based on grid squares first, then multiplied by base units.

Regards,
JPG

In the GURPS ruleset we display a calculated Range Attack modifier at the end of the pointer suffix. It is also displayed on the pointers displayed when viewing targeted tokens.
32228

Is there a way to get the get the length of a pointer from the built-in measuring, without having to do measuring of the vectors? If not, it would be great, if at some point, it was. Even better would be some API overrides like.

function onPointerLengthChanged(length, pointertype)

Parameters:
length (number) The length of the pointer in base units.
pointertype (string) The type of the pointer: "arrow", "circle", "cone", "rectangle"

Return value:
(string) The label applied to the pointer.


function onVectorLengthChanged(token, vector)

Parameters:
token (tokeninstance)
vector (table) A table of the lengths of path segments in a vector.

Return value:
(string) The label applied to the vector.

Those events, ideally, should only trigger when the length changes as opposed to the current onMeasurePointer, which happens each screen redraw. Another potential bonus it could allow for each pointer to have different labels.