Skip to content

Suggestion: Call a conditional script from within another script and evaluate its return value

prwoprwo Member Posts: 69
edited April 2018 in Builders - Scripting
Currently, the ExecuteScript command will only invoke the main procedure of a script. To my knowledge it does not work on conditional scripts (with the StartingConditional function).

There should be a way to invoke a conditional script from within another script and evaluate its return value.
E.g. by having a ExecuteConditionalScript that will do exactly that:
// Make oTarget run sScript's StartingConditional function and then return it's value to the calling script. int ExecuteConditionalScript (string sScript, object oTarget)

Conditional scripts are often used in dialogs to branch out options based on conditions. The same branching may apply in "ordinary" scripts as well. Being able to execute a conditional script from within another script and evaluating its return value would help to reuse these scripts and to avoid code-duplication.


There is a (less elegant) workaround for this today:
  • Use a "normal" (main) scripts to evaluate the condition.
  • Set the determined return value as an int on a quasi-static (always existing) object, e.g. GetFirstPlayer, Module variable, etc.
  • Call the quasi conditional script using ExecuteScript and evaluate its "return value" by reading the previously set int.
Post edited by prwo on

Comments

  • shadguyshadguy Member Posts: 154
    edited April 2018
    Have you tried treating the starting conditional as an include file? You would be limited to one SC include without function name conflicts, but it may still be useful for you.

    Dave
  • squattingmonksquattingmonk Member Posts: 59
    prwo said:

    There is a (less elegant) workaround for this today:

    • Use a "normal" (main) scripts to evaluate the condition.
    • Set the determined return value as an int on a quasi-static (always existing) object, e.g. GetFirstPlayer, Module variable, etc.
    • Call the quais conditional script using ExecuteScript and evaluate its "return value" by reading the previously set int.
    There's a function called ExecuteScriptAndReturnInt() in x2_inc_scwitches that does this. It can't be nested, though.

    There was also GetScriptReturnValue() in nwnx_events, but that hasn't been implemented in NWNXEE yet.

  • prwoprwo Member Posts: 69
    edited April 2018
    shadguy said:

    Have you tried treating the starting conditional as an include file? You would be limited to one SC include without function name conflicts, but it may still be useful for you.

    You can have only either one main or one StartingConditional in one script no matter if the source of it comes from the file itself or an include. Hence an include file must contain neither.

    The workaround using include files would be to extract your StartingConditional function into a separate inculde file, rename the function to something meaningful and then call this function in your StartingConditional file as well as everywhere else where you need the conditional to be evaluated.

    The drawback with this approach is that you have to recompile all dependant files if you change the code of your included function. The safest way is to recompile everything. See also: here

  • prwoprwo Member Posts: 69

    There's a function called ExecuteScriptAndReturnInt() in x2_inc_scwitches that does this. It can't be nested, though.

    I was unaware of ExecuteScriptAndReturnInt (of x2_inc_switches), but it uses exactly the workaround I described in my initial posting:
    //---------------------------------------------------------------------------- // Wrapper for Execute Script to execute a script and get an integer // return value. Do not nest this function! //---------------------------------------------------------------------------- int ExecuteScriptAndReturnInt(string sScript, object oTarget) { DeleteLocalInt(oTarget,"X2_L_LAST_RETVAR"); ExecuteScript(sScript,oTarget); int nRet = GetLocalInt(oTarget,"X2_L_LAST_RETVAR"); DeleteLocalInt(oTarget,"X2_L_LAST_RETVAR"); return nRet; } //---------------------------------------------------------------------------- // Helper function for ExecuteScriptAndReturnInt //---------------------------------------------------------------------------- void SetExecutedScriptReturnValue(int nValue = X2_EXECUTE_SCRIPT_CONTINUE) { SetLocalInt(OBJECT_SELF,"X2_L_LAST_RETVAR",nValue); }

    Interesting fact: Although the documentation says otherwise, this approach is perfectly nestable (because NWN scripts are not executed in parallel).

    There was also GetScriptReturnValue() in nwnx_events, but that hasn't been implemented in NWNXEE yet.

    That's only an option if you want to use NWNX, which basically fiddles around in the game executable's process memory at runtime to compensate for things the original can't do.
  • BalkothBalkoth Member Posts: 161
    What are you trying to do that couldn't be solved by including a custom library with the relevant function calls?
  • meaglynmeaglyn Member Posts: 151
    edited April 2018
    It's only nestable if all of your scripts always set the return value in all cases and never rely on an unset value being 0.

    Edit: and don't set the return value before calling nested scripts. Some of the scripts that use this in the default code do that if I recall.
  • prwoprwo Member Posts: 69
    Oh yes, you must use SetExecutedScriptReturnValue right before the return command, just like you would retrun a value using return . Instead of retrung you write SetExecutedScriptReturnValue(); return;

    I think the ExecuteScriptAndReturnInt is used for more than just returning a value. It is supposed to determine the caller's program flow. At least the default in SetExecutedScriptReturnValue would suggest that. Nevertheless, it is a good example of how to pass a return value.
Sign In or Register to comment.