In my current project, I have a number of *gosub calls. If there’s an error inside those *gosubbed 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}
