5E Product Walkthrough Playlist
  1. #1

    5E Reading contents of local variable

    Hello! I wrote this extension: https://www.fantasygrounds.com/forum...-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:
    Code:
    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?

  2. #2
    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

  3. #3
    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).
    Code:
    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.
    My Forge creations: https://forge.fantasygrounds.com/crafter/9/view-profile
    My GitHub: https://github.com/MeAndUnique
    Buy me a coffee: https://ko-fi.com/meandunique
    Discord: MeAndUnique#6805

  4. #4
    Quote Originally Posted by MeAndUnique View Post
    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).
    Code:
    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.

  5. #5
    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:

    Code:
    ------------------------------------------------
    -- 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.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
5E Character Create Playlist

Log in

Log in