In my current project, I have a number of *gosub
calls. If there’s an error inside those *gosub
bed subroutines and I use *bug
to report it, I don’t find out where in my game the call came from. I just get the reference to the subroutine’s line number.
To work around that, I created a subroutine that finds out where in the game the *gosub
call was from. (It also works for *gosub_scene
.) Note that this is prying open ChoiceScript internals and so may not work in the future. But it does right now! Note that this subroutine stores its result in a variable called return_1
, so you’’ need a *create return_1 false
or *temp return_1
statement in your game so the subroutine can store its results.
*comment Generate a stack trace from *gosub and *gosub_scene statements
*label stacktrace
*temp scratch false
*temp trace_contents ""
*script temps["_stacktrace_temps"] = {...temps};
*script this.setVar("scratch", (stats.choice_subscene_stack !== undefined && stats.choice_subscene_stack.length > 0));
*if choice_quicktest
*script stats.choice_subscene_stack = [];
*if scratch
*script temps["_stacktrace_subscene_stack"] = [...stats.choice_subscene_stack];
*else
*script temps["_stacktrace_subscene_stack"] = [];
*gosub stacktrace_loop
*set return_1 trace_contents
*return
*label stacktrace_loop
*temp scratch false
*temp stack_length
*temp stack_scene
*temp stack_line
*script this.setVar("scratch", (temps["_stacktrace_temps"].choice_substack !== undefined && temps["_stacktrace_temps"].choice_substack.length > 0));
*if scratch
*if choice_quicktest
*script temps["_stacktrace_temps"].choice_substack = [];
*script temps["_current_stack"] = [...temps["_stacktrace_temps"].choice_substack];
*label loop1
*script this.setVar("stack_length", temps["_current_stack"].length);
*if stack_length > 0
*script temps["_current_frame"] = temps["_current_stack"].pop();
*if choice_quicktest
*script temps["_current_frame"] = { name: '', lineNum: 0 };
*script this.setVar("stack_scene", String(temps["_current_frame"].name));
*script this.setVar("stack_line", temps["_current_frame"].lineNum);
*set trace_contents "${stack_scene} (${stack_line+1}) -> ${trace_contents}"
*goto loop1
*script this.setVar("stack_length", temps["_stacktrace_subscene_stack"].length);
*if stack_length > 0
*script temps["_current_frame"] = temps["_stacktrace_subscene_stack"].pop();
*if choice_quicktest
*script temps["_current_frame"] = { name: '', lineNum: 0 };
*script this.setVar("stack_scene", String(temps["_current_frame"].name));
*script this.setVar("stack_line", temps["_current_frame"].lineNum);
*set trace_contents "${stack_scene} (${stack_line}) -> ${trace_contents}"
*script temps["_stacktrace_temps"] = {...temps["_current_frame"].temps};
*gosub stacktrace_loop
*return
Here’s an example of how I use this inside a subroutine:
*if <an error occurred>
*gosub stacktrace
*bug An error occurred! Stack: ${return_1}