User Defined Function

From GECK
Jump to: navigation, search

Overview

User defined functions (UDF) are added by NVSE V4. They must be saved as 'object' type scripts that must use the "Function" block type only. Although 'object' type, they must not be attached to any object and are instead called directly by script name from within other scripts, using the Call command.

UDFs may optionally return a single value of any type, using the SetFunctionValue command within them. They may optionally accept up to 15 arguments of any type, which are defined within curly braces:

-- Obscript --
scn SomeUDF

int arg1 ; * args are optional
ref arg2
float arg3 ....

Begin Function { arg1, arg2, arg3... }
    ; * do whatever like any other script
End 

-- NVSEScript --
name SomeUDF;

fn (int arg1, ref arg2, float arg3) {
    // Do whatever like any other script
}

and called in a similar way to regular functions:

-- Obscript
scn SomeOtherScript
...
call SomeUDF arg1, arg2, arg3...

-- NVSEScript
name SomeOtherScript;
...
call(SomeUDF, arg1, arg2, arg3...);

OBSCRIPT ONLY: However, to assign the return to a variable, Let must be used rather than set .. to .., and to use in a conditional, if eval rather than simply if:

let SomeVariable := call SomeOtherUDF arg1

if eval (call SomeSuitableUDF)
    ; the UDF return evaluates to true
endif

Lambda Functions

Since xNVSE 6.1, UDFs may also be represented in the form of Lambdas, which are nameless functions which can be passed and stored as values:

-- Obscript
let ref rFunction := (begin function { arg1 }
...
end)
call rFunction arg1

-- NVSEScript
ref rFunction = fn (int arg1) {
...
};

rFunction(10);
-- Obscript
CallAfterSeconds 10 ({} => print "You Look So Shiftless (Can I get a witness?)")

-- NVSEScript
CallAfterSeconds(10, fn () -> print("You Look So Shiftless (Can I get a witness?)"));

Examples (Obscript)

scn GetRandomForm

; *** This script accepts a form list and returns one random form from it

; local variables
int iCount
int iChoice
ref RandomForm

; arguments
ref rFormList

Begin Function { rFormList }

    let iCount := ListGetCount rFormList
    let iChoice := Rand 0, iCount
    let RandomForm := ListGetNthForm rFormList, iChoice
    
    SetFunctionValue RandomForm

End
scn fnShuffle

; *** This script accepts a list array and returns it in a shuffled order
; ***

; * local vars
array_var aNew
int iKey
int iLast

; * args
array_var aList

Begin Function { aList }

    let aNew := Ar_Construct "array" ; * get a new empty list array

    while (Ar_Size aList) > 0        ; * while input array contains an element
        let iLast := Ar_Size aList   ; * get size of input array
        let iKey := Rand 0, iLast    ; * get random valid key for input array
        Ar_Append aNew, aList[iKey]  ; * append the random choice element to output array
        Ar_Erase aList, iKey         ; * erase that element from input array
    loop

    SetFunctionValue aNew            ; * return the shuffled array
    return

End

The above example shows a user defined function that accepts a list array and returns it in a shuffled order. This might be called in another script, like:

scn SomeOtherScript

array_var aBeatles

Begin GameMode ; * or some other block type

    let aBeatles := Ar_List JohnREF, PaulREF, GeorgeREF, RingoREF

    let aBeatles := Call fnShuffle aBeatles

    ; * The Beatles array is now in a shuffled order

End

Examples (NVSEScript)

Note: These are the same examples as above, so you can compare the syntax differences.

name GetRandomForm;

// This script accepts a form list and returns one random form from it
fn (ref rFormList) {
    int iCount = ListGetCount(rFormList);
    int iChoice = Rand(0, iCount);
    return ListGetNthForm(rFormList, iChoice);
}
name fnShuffle;

// This script accepts a list array and returns it in a shuffled order
fn (array aList) {
    array aNew = [];                   // Create a new empty list array

    while (Ar_Size(aList) > 0) {       // While input array contains an element
        int iLast = Ar_Size(aList);    // Get size of input array
        int iKey = Rand(0, iLast);     // Get random valid key for input array
        Ar_Append(aNew, aList[iKey]);  // Append the random choice element to output array
        Ar_Erase(aList, iKey);         // Erase that element from input array
    }

    return aNew;                       // Return the shuffled array
}

The above example shows a user defined function that accepts a list array and returns it in a shuffled order. This might be called in another script, like:

name SomeOtherScript;

GameMode {
    array aBeatles = [JohnREF, PaulREF, GeorgeREF, RingoREF];
    aBeatles = Call(fnShuffle, aBeatles);
    // The Beatles array is now in a shuffled order
}

Accepting any type arguments (advanced)

In some contexts, it is useful to allow arguments of any type for a UDF. Since array elements can be any type, this can be conveniently achieved using the NVSE Expressions, * ('unbox') and & ('box'), and the TypeOf / GetType functions.

-- Obscript
scn MyUDF

array_var AnyTypeArgument

Begin Function { AnyTypeArgument }

    if eval (TypeOf *AnyTypeArgument) == "form"
        ...
End

-- NVSEScript
name MyUDF;

fn (array anyTypeArgument) {
    if (TypeOf(*anyTypeArgument) == "form") {
        // Do something with form
    }
}
-- Obscript
ref MyRef
int MyInt

call MyUDF &MyRef
call MyUDF &MyInt

-- NVSEScript
ref myRef;
int myInt;

call(MyUDF, &myRef);
call(MyUDF, &myInt);

Notes

  • UDFs may be used recursively by saving the script once first without the sub call. There is a maximum of 30 nested calls before NVSE aborts further recursion.

See Also

External Links