PDA

View Full Version : 5E Reading contents of local variable



jammaster
October 20th, 2021, 19:22
Hello! I wrote this extension: https://www.fantasygrounds.com/forums/showthread.php?71005-5E-Rolling-Mirror-(Automatic-Mirror-Image), however, it suffers from 2 problems, which I hope to solve:

It is likely not compatible with other extensions that change the attack mechanics
Passing of values from one resultHandler to another is very inellegant


So, the first problem:

In the ActionManager, (5E\scripts\manager_action_attack.lua), a resultHandler for `attack` is registered. The implementation of the handler registration can be found in (CoreRPG\scripts\manager_actions.lua) and it looks as follows:


local aResultHandlers = {};
function registerResultHandler(sActionType, callback)
ActionsManager.initAction(sActionType);
aResultHandlers[sActionType] = callback;
end


For my extension to be compatible for others, I need to save the already existing handler, so that I can call it from my implementation when necessary. But how do I do that? The variable aResultHandlers is local, and there is no getter.


Second problem:
From the resultHandler of `attack`, I need to pass an object to another resultHandler. However, to my knowledge, all of the parameters to ActionManager.performAction, are only shallow copied, meaning I cant pass an actual object there.
What I did to solve this, is to instead have a global map of objects, and to pass the object to the other handler, I save it in the map, and put the key of that object in the roll that gets passed to ActionManager.performAction. I then read this key in the other handler, and then read the object from the map. Is there any other way?

Moon Wizard
October 20th, 2021, 19:46
You can only completely override the result handlers; you can not interrogate what result handler is current registered. Local variables are not accessible by design.

You'll have to copy the current result handler code and use it in your own; as well as monitor releases for updates to the ActionAttack script for that ruleset.

Regards,
JPG

MeAndUnique
October 29th, 2021, 09:42
The below solution will be relatively robust to ruleset updates, and play nicely with other extensions that are also being considerate of the potential for conflict. This approach can be leveraged by an arbitrary number of extensions and will result in all of their code executing (whether the logic itself is compatible is another story).


local onAttackOriginal;
function onInit()
onAttackOriginal = ActionAttack.onAttack;
ActionAttack.onAttack = onAttack;
ActionsManager.registerResultHandler("attack", onAttack);
end
function onAttack(rSource, rTarget, rRoll)
-- Do stuff before original code if needed
onAttackOriginal(rSource, rTarget, rRoll);
-- Do stuff after original code if needed
end


As for passing data around, most complex objects are just data tables which are passed by reference. This is true for the draginfo, rActor, and rRoll parameters passed to ActionsManager.performAction.

jammaster
October 30th, 2021, 14:28
The below solution will be relatively robust to ruleset updates, and play nicely with other extensions that are also being considerate of the potential for conflict. This approach can be leveraged by an arbitrary number of extensions and will result in all of their code executing (whether the logic itself is compatible is another story).


local onAttackOriginal;
function onInit()
onAttackOriginal = ActionAttack.onAttack;
ActionAttack.onAttack = onAttack;
ActionsManager.registerResultHandler("attack", onAttack);
end
function onAttack(rSource, rTarget, rRoll)
-- Do stuff before original code if needed
onAttackOriginal(rSource, rTarget, rRoll);
-- Do stuff after original code if needed
end


As for passing data around, most complex objects are just data tables which are passed by reference. This is true for the draginfo, rActor, and rRoll parameters passed to ActionsManager.performAction.

Thank you do the response. So far, I have done it the same as you, but I fear that someone may miss to do the crucial step of "ActionAttack.onAttack = onAttack;", in which case their handler will be lost forever. But I guess there is no way around it. Thank you for the solution! Its also good to have it here so that people use this in their extensions.

TheoGeek
December 18th, 2021, 04:20
Sorry to resurrect this. :)

One other thing to consider...

I use that mechanic in my extensions as well, but in one instance, another extnesion author DID NOT want to cascade calls to ActionDamage.onDamage to call down to the ruleset's onDamage. So he suggested that I add a wrapper around a different function he could call.

So, it now looks like:


------------------------------------------------
-- Function to call from other extensions that
-- don't want to use the ruleset onDamage
------------------------------------------------
function onDamageWrapperDo(rSource, rTarget, rRoll)
-- Only override the roll if the extension is enabled and the bypass flag is not set in the
-- onDamageRollWrapper function above and the damage roll is for critical damage
if extensionEnabled() and (not bBypassImprovedCritical) and rRoll.sDesc:match("%[CRITICAL%]") then
-- Determine if the target has immunity to critical damage
local aImmune = ActionDamage.getReductionType(rSource, rTarget, "IMMUNE")

-- Determine if we are to roll damage for critical immune targets
local bRollImmune = rollImmune() and (aImmune["critical"] ~= nil)

-- Compute damages based on Improved Critical options
computeCriticalDamage(rRoll, bRollImmune)
end

return rRoll
end

------------------------------------------------
-- onDamage wrapper function handler
------------------------------------------------
function onDamageWrapper(rSource, rTarget, rRoll)
rRoll = onDamageWrapperDo(rSource, rTarget, rRoll)

-- Call the standard onDamage function with the modified rRoll
ActionDamage.IC_Original_onDamage(rSource, rTarget, rRoll)
end


Normally, other extensions don't care, so they just use onDamageWrapper. For extensions that do care, they can use onDamageWrapperDo.

Of the many handlers I hijack, that's the only one that ever mattered.

Also, my extensions simply much with data that is passed around so I don't need to actually modify the handlers proper. I can accomplish what I need by parsing the structures and modifying them in my code. If you can, you might want to try that too.