Skip to content

References

Daniel Spinola edited this page Apr 11, 2021 · 7 revisions

References

This article assumes a basic understanding of dereferencing values in ChoiceScript with the curly brace notation:

*temp my_real_var 5
*temp my_ref "my_real_var"
*set {my_ref} 10

*comment The below line will print 10.
${my_real_var}

ChoiceScript's parameter passing (wiki) is pass-by-value, which means that when you specify a variable as a parameter to a subroutine, the subroutine is receiving only a copy of that variable's value -- not a reference to the variable itself. For the majority of use-cases, this is fine, and many cslib routines work happily on this principle.

However, if we want to adjust a variable outside of the scope of the routine, then we need to rely on our function's return value, stored in cslib_ret.

The Problem

But why would we ever need more than that? Well, below is a simple example of a call to 'max' in cslib_number, to help us determine our player's highest stat.

*temp highest_value 0
*gosub_scene number max stat_str stat_dex stat_con stat_wis
*set highest_value cslib_ret

But, there's a problem here: cslib_ret can return the highest NUMBER, but how do we know which stat that number belonged to? This is only useful if we want to know what the highest stat VALUE is. It doesn't tell us WHICH stat is the highest.

So, why not just make cslib_ret return the name of the variable? Well, as we mentioned above the cslib routine 'max' is only getting the values. It actually has no way of knowing where those values come from.

The Solution

What we need to do instead then, is pass "references" to our variables, so cslib knows where the values are coming from. In fact, the max_stat routine in cslib_number does exactly this:

*temp highest_stat ""
*gosub_scene number max_stat "stat_str" "stat_dex" "stat_con" "stat_wis"
*set highest_stat cslib_ret
${highest_stat} = stat_something
Largest value: ${{highest_stat}}

Here, instead of passing the values to compare, we're passing the names of the variables. The cslib max_stat routine can then use those names with ChoiceScript's curly brace notation to access their values 'by reference'. This allows it to map the numbers to variables, and return the name of the variable, which itself can be dereferenced back in the original scene in order to get the value -- best of both worlds!

Unfortunately, dereferencing in this manner will only work with global variables (due to cslib's use of gosub_scene). So it isn't possible to pass the names of variables created with *temp to cslib routines.

Usage

References are already used in many ways throughout cslib (cslib_array uses them very heavily), and may well be used even more in the future. It's a powerful paradigm, albeit one that can be a little tricky to get your head around.

One of the best ways to understand the concept is to think about the problems it can be applied to.

Mapping Values To Their Source

This is the fundamental principle underlying the example above: it wasn't enough to have the raw data, we also needed to know where it came from and what it was associated with. cslib_array often needs to access multiple array elements, including its attribute variables, which is significantly easier to do by reference (as opposed to expecting the end user to pass every single element as a routine parameter).

Returning Multiple Values

Passing a reference to a cslib function allows cslib to manipulate the original variable. Whilst this is generally discouraged, in favour of returning a value with cslib_ret (simply because it's a complicated behaviour that is not always easy to understand), it does provide for some unique possibilities.

Again, cslib_array relies very heavily on this principle, as it often needs to manipulate an entire "array" worth of variables -- something that isn't practical to do by returning a single value.

Passing Routines Around

Probably the most powerful (and dangerous!? ⚠️) use-case. The ChoiceScript commands *gosub and *gosub_scene also support the curly brace notation, which allows us to pass scenes and labels via reference. This means we can effectively pass arbitrary routines to other routines to call (see how that could get confusing?).

The best current examples of this in cslib are cslib_array's 'filter_by_routine' and cslib_loop's 'repeat'. Both of which expect user-given references to a scene and label, to which they then make multiple calls, passing different values each time to provide dynamic behaviour.

Clone this wiki locally