Jump to content
DoctaSax

Tutorial: NVSE4+ Part 2: User Defined Functions (UDFs)

Recommended Posts

Don't you just hate to see your scripts packed with chunks of code that rarely ever need to run, on the off chance that some condition is met? To see most of your script's body plastered all across the editor window at 5 levels of indentation? Or to see yourself repeating chunks of code all the time for each time it applies, until you hit your max script size limit? Jesus, what a drag!

Don't you just wish, sometimes, you could outsource some of that - but then, setting up new spell or quest scripts for one-time-only code is just way more trouble than it's worth, spells only really work in gamemode, and quest stage result scripts can't handle complicated code or loops. Not to mention you have to copy across any vars via a quest script, copying that shit twice, with again a lot of code lines being spent on just that!
Well, no more.

NVSE4+ comes with the ability to set up your own functions, in the form of scripts that you call in when you need them:


call SomeFunction

Who you gonna call? Some Function! :lol:

Case in point: if you've got settings in your mod, eg like with an MCM menu, you need to set the default values of the various variables it refers to when the mod is first installed, and if you buildref stuff you need to do buildref that stuff every time a new gaming session starts just in case there was a load order change. So far, people've tended to stick that in their main quest script, which is supposed to run all the time anyway, depending on an iInit or bDoonce variable.


Begin GameMode

if iInit == 0 ; code that only has to run the first time a mod is ever loaded
    set Var1 to someValue
    set Var2 to someValue
    set Var3 to someValue
    etc etc
    set iInit to 1
elseif iInit && fVersion < somefloat ; code that only runs on a mod update    
    if Var1 < someValue
        ; do stuff
        if someActor.GetAV someActorValue > someValue
            ; do stuff
        endif
    else
        ; do stuff
        if somecondition || (somecondition && somecondition)
            ; do stuff
        endif
    endif
    set fVersion to someFloat
elseif iInit && (GetGameLoaded || GetGameRestarted) ; code that only has to run once on each game load
    if IsModLoaded "SomeMod"
        set iModIndex to GetModIndex "SomeMod"
        set rRef to BuildRef iModIndex someDecimalNumber
    endif
    if IsModLoaded "SomeOtherMod"
        ; etc.
    endif
endif

; finally, the actual chunk of script that does need to run all the time

Now, I don't know about you, but I can't stand the thought of that code being processed throughout a gaming session when it's not needed and just skipped, being dead weight. Of course, as said above, you could splice off some of that with old-school methods, but how about parking those chunks in a UDF instead?

 

 

1. BASIC UDF STRUCTURE & CALLING A UDF

You create a UDF by opening a brand new object script. Do not attach it to anything; UDFs are scripts that are called directly, you don't need to stick it to an item or NPC, in fact, you really shouldn't. They are free-floating scripts in the geck's object window, and if you right-click on them and select "use info", the only thing that should appear in that window are the scripts that call them.

UDFs can only have one script block: a Function block.

 


scn MyFirstInitFunction  ; the script name will be what you "call" in the calling script

; variables  
; all variables need to be declared before the block begins (in UDFs it's obligatory, in other scripts it's just stupid not to)

Begin Function {} ; don't forget those accolades

set Var1 to someValue
set Var2 to someValue
set Var3 to someValue
set Var4 to someValue
; etc

End
scn MyUpdateInitFunction

; variables

Begin Function {}

if Var1 < someValue
    ; do stuff
    if someActor.GetAV someActorValue > someValue
        ; do stuff
    endif
else
    ; do stuff
    if somecondition || (somecondition && somecondition)
        ; do stuff
    endif
endif
let MyQuestID.fVersion := someFloat

End
scn MyOnEveryLoadInitFunction

; variables

Begin Function {}

    if IsModLoaded "SomeMod"
        set iModIndex to GetModIndex "SomeMod"
        set rRef to BuildRef iModIndex someDecimalNumber
    endif
    if IsModLoaded "SomeOtherMod"
        ; etc.
    endif
    
End

and then your main script would only have to spend this on your inits:


scn MyMainQstScript

int iInit

Begin GameMode

if iInit == 0
    call MyFirstInitFunction
elseif iInit && fVersion < someFloat
    call MyUpdateInitFunction
elseif iInit && (GetGameLoaded || GetGameRestarted)
    call MyOnEveryLoadInitFunction
endif

; your actual main script

End

Nice and tidy, huh? When that quest script runs and encounters a UDF it should call, it stops what it's doing, runs the UDF and then returns to the exact same spot to pick up where it left off - all in the same frame. And if the condition doesn't apply, all it has to do is skip that one line.
Well, that's just the beginning. Right now we've only used these UDFs as code holders, just to have simpler, more readable scripts; let's start using them as proper functions. They're not called user-defined functions for nothing.

Vanilla has quite a variety of function types. Some of them can/have to be called on a reference (reference functions, like moveto or enable), others not (like IsHardCore, IsPC1stPerson). Some of them return a float (eg the math functions, or GetEquipped) or reference (eg GetActionRef), others don't return anything (enable, disable). Some of them take or require parameters (GetAV), others not (GetCurrentAIPackage). UDFs can do any of that too, or not, depending on what you want.

 

 

2. CALLING UDFS ON A REFERENCE & NESTING UDFS

In the examples above, I didn't call my UDFs on a reference because there was no point. What happened there is similar to what would've happened if I'd called let's say a quest stage result script, a one-frame script doing something in general. But if you want your UDF to do something to a reference, or retrieve information from it, then you call it on a ref just like any other reference function.


BuddyRef.call SomeFunction
scn SomeFunction

Begin Function {}

CIOS SomeSpell ; this spell will be cast on BuddyRef

End

As you can see, right now the UDF works as a one-frame-only object script attached to Buddy Ref, or a spell script cast on BuddyRef: the implied reference is the reference the UDF was called on. (In fact if you want to display the reference it's called on in a debugprint readout, you'll find it with the GetSelf function.) And if the UDF includes a call to another UDF, it carries that relationship across:


BuddyRef.call someFunction1
scn SomeFunction1

Begin Function {}

; do other shit
call SomeFunction2

End
scn SomeFunction2

Begin Function {}

CIOS SomeSpell ; this spell is still cast on BuddyRef

End

Whether called on a reference or not, you can nest up to 30 UDFs like that, although I think that's a bit overkill and you should really reconsider your overall mod structure ;)  Also, a UDF can call itself - if you comment out the line that does it and compile it first.


3. RETURNING VALUES FROM A UDF

If you choose, you can let a UDF return a value, which can be a number, a form, a string, or an array. To do that, you use the SetFunctionValue function:


SetFunctionValue fSomeFloat
SetFunctionValue 3
SetFunctionValue rSomeRefVar
SetFunctionValue playerref
SetFunctionValue Scotch
SetFunctionValue sv_SomeStringVar
SetFunctionValue "An actual string"
SetFunctionValue ar_SomeArrayVar

and of course, set up the calling script  to catch that value properly:


let fSomeFloat := call MyUDF
let iSomeInt := call MyUDF
let fSomeFloat := 1 + (call SomeUDF) * 3 ; which will obviously only work if the UDF returns a number
let rSomeRefVar := call MyUDF
let sv_SomeStringVar := call MyUDF
let ar_SomeArrayVar := call MyUDF

so in the case of my version update UDF I could've done this:


scn MyMainQstScript

...
elseif iInit && fVersion < somefloat
    let fVersion := call MyUpdateInitFunction
elseif ...
scn MyUpdateInitFunction

; variables

Begin Function {}

if Var1 < someValue
    ; do stuff
    if someActor.GetAV someActorValue > someValue
        ; do stuff
    endif
else
    ; do stuff
    if somecondition || (somecondition && somecondition)
        ; do stuff
    endif
endif
SetFunctionValue someFloat

End

for the same effect. It makes sense for you to do that near the end of your UDF, although to be sure, that just depends on the structure.


scn MyUDF

Begin Function {}

if somecondition
    SetFunctionValue someFloat
    return
elseif someothercondition
    SetFunctionValue someOtherFloat
endif

End

See that return command there? As in other scripts, it breaks off the script, and because a UDF is a one-time-only script, that means we return straight to the calling script. Obviously you try to put the simpler, faster condition checks near the top of your UDF so you can "return" before you hit the more complicated code if it isn't required to run it. Same as with everything else.

 

 

 

4. PASSING PARAMETERS TO UDFS

By now, you must've wondered what the deal is with those accolades behind the Function block command. Well, vanilla functions are hardcoded to take parameters or not, but NVSE obviously can't know if you mean to pass parameters at all, or how many, or which ones, or in what order, until you specify that yourself. That's what those accolades are for.

You specify parameters by declaring variables, and sticking them between those accolades:


scn MyUDF

float fFloat1
int iInt
ref rForm1
ref rForm2
string_var sv_somestring1
string_var sv_somestring2
array_var ar_somearray

; your UDF's local variables go here

Begin Function {fFloat iInt rForm1 rForm2 sv_somestring1 sv_somestring2 ar_somearray}

; do stuff with all of that

End
scn MyCallingScript

Begin SomeBlock

call MyUDF fSomeFloat 4 someRefVar playerref sv_somestringvar "I'm a string" ar_somearray

End

As you can see you can use actual values or the variables for the parameters when you call the UDF, as long as the variables on the UDF's end can "catch" them, ie a float variable can catch both an actual number and another float variable, a string var both a string var and an actual string.  It was the same in the opposite direction with SetFunctionValue.
Once those UDF variables are declared as parameters by sticking them between those accolades, you will be required to specify that exact same number of parameters in the function call, in exactly that order of types, ie


call MyUDF fSomeFloat rSomeRef rSomeRef playerref sv_somestringvar "I'm a string" ar_somearray
call MYUDF fSomeFloat 4 rSomeRef playerref sv_somestringvar "I'm a string"

will both not work, the first because the second parameter is supposed to be an int and you're passing a ref, the second because the array parameter's missing.

Example 1: A pretty simple one from the obse docs this time:


ScriptName Multiply

float arg1
float arg2
; I like to leave a blank line between parameter vars and local vars, to keep things clear
float localVar    ; a local variable

Begin Function {arg1, arg2}        ; function body, with parameter list in {braces}
    Let localVar := arg1 * arg2
    SetFunctionValue localVar    ; this is the value that will be returned
End

and the calling script can be:


float someVar
Let someVar := Call Multiply 10 5

Example 2: Some vanilla functions take formlists as parameters, but a whole lot don't. IsSpellTarget is one of them.


scn MyUDF

ref rList

int iCount
ref rSpell

Begin Function {rList}

let iCount := ListGetCount rList
while (iCount -= 1) >= 0
    let rSpell := ListGetNthForm rList iCount
    if IsSpellTarget rSpell
        SetFunctionValue 1
        break
    endif
loop

End

and then the calling script would be something like:


if rActor.call MyUDF BoozeList
    ; actor is drunk, omg, do something
endif
if rActor.call MyUDF DrugsList
    ; actor is high, omg, do something
endif

Note: in this case it'd be in our best interests to stick the most commonly used drinks / drugs at the end of our formlist, right?

 

Example 3: Let's say we're tired of typing out the particulars to a Sexout act in every dialog result script in our mod:
 


rActor1.NX_SetEVFl "Sexout:Start::CallVer" 1
rActor1.NX_SetEVFo "Sexout:Start::ActorA" rActor2
rActor1.NX_SetEVFo "Sexout:Start::ActorB" rActor1
rActor1.NX_SetEVFl "Sexout:Start::IsOral" 1
rActor1.NX_SetEVFl "Sexout:Start::Anim" 201
rActor1.CIOS SexoutBegin

type, type, type...

We could just write ourselves this UDF:


scn StartSexin

ref rActorA
ref rActorB
int isOral
int isVaginal
int isAnal
int iAnim

Begin Function {rActorA rActorB isOral isVaginal isAnal iAnim}

NX_SetEVFl "Sexout:Start::CallVer" 1
NX_SetEVFo "Sexout:Start::ActorA" rActorA
NX_SetEVFo "Sexout:Start::ActorB" rActorB
NX_SetEVFl "Sexout:Start::IsOral" isOral
NX_SetEVFl "Sexout:Start::IsVaginal" isVaginal
NX_SetEVfl "Sexout:Start::IsAnal" isAnal
NX_SetEVFl "Sexout:Start::Anim" iAnim
CIOS SexoutBegin

End

and only have to do something like this in the calling script:


BuddyRef.call StartSexin BuddyRef playerref 1 0 0 201
or
BuddyRef.call StartSexin BuddyRef VeronicaRef 0 1 0 605

Shucks, we could expand this with the refSufarce, fSurfaceX/Y/Z/Angle, and RefMoveA/BTo variables as well.

You can stipulate up to 10 parameters to a UDF (15 as of NVSE 4.5 beta 1).
 
5. WHAT HAPPENS TO YOUR UDF VARIABLES

When a UDF's run its course and returned to the calling script, whatever variables in the UDF that were used to store the parameters are destroyed and don't refer to anything anymore. The vars and values they referred to in the first place aren't touched. That goes for all types of parameter variable.

Local variables are destroyed/nullified too, except for one type of local variable: string vars. Strings referred to by a UDF's local string variables aren't destroyed and will continue to linger on in your .nvse file unless you destroy them yourself when you're done with them, so do that:

sv_destruct mystringvar

6. OTHER SWEET STUFF

Because UDFs are standalone scripts that are still held as entries in the object window, you can refer to them with a ref variable, and pretty much do anything with them that you can do with other ref vars that refer to base forms without a world model. Ie you obviously can't moveto a UDF to you, but you can park it in a formlist or array, return its formID in a debugprint readout with %i, NX_SetEVFo it, and of course just have it be "set" depending on different conditions.


if somecondition
    let someRefVar := someUDF1
elseif somecondition
    let someRefVar := someUDF2
endif
call someRefVar

And because you can park them in a ref variable, you can buildref them!


set someRefVar to BuildRef someModIndex somedecimalint
call SomeRefVar

Which means of course that if some other mod has pretty UDFs you'd like to use, you can, just like that. No copying the entire thing, no making it a master or worrying about load order etc.

Share this post


Link to post

Another good one. Although I mostly work in OBSE20+ these days, I mostly write far more functions than traditional Quest and Object scripts. (OK, I mostly work in Bash, Sed, Awk and PhP these days but that's work that pays).

Share this post


Link to post

Example 3: Let's say we're tired of typing out the particulars to a Sexout act in every dialog result script in our mod:

I'm wondering if it's a good idea to implement something like that directly into Sexout. If so, it should be done before every single mod ends up with its own custom function. :P

Share this post


Link to post

I think sexout could have some UDFs like that available. And then modding for sexout can really become a breeze. The only trouble I see is the passing of null ref vars as variables if they don't apply (ActorC, refsurface), someone oughta check out how to do that. Me, I'm kinda pooped right now - the string var one was murder.

Share this post


Link to post

Confirming:  Since the UDF is an object script, it's going to execute every frame, yes?  Which means that you'd need to use stages to manage long scripts, yes?

Share this post


Link to post

No, it's a one-frame-only script, like a result script, it's just that you select it to be an object script in the geck, and if you call it on a ref, that ref becomes the implied ref (GetSelf).

Share this post


Link to post

So if a long script was in a UDF, it would only process as much of the script as it was able to in one frame and then that's that?

Share this post


Link to post

Well, a lot can be done in one frame - it all kinda depends how much else is going on, how 'heavy' the functions in it are, whether there's extensive looping. But seriously, I run a lot of code in UDFs, or in tiers of UDFs and haven't really had much of an issue, except where I could've known in the first place.

That said, the beauty of UDFs is that they usually don"'t have to be very long, because chunks of code that rarely come into play - like under a rare condition - can be exported to another UDF.

Share this post


Link to post

Hello Doctasax,

 

Here I am with my wall of text...

 

My concerns about UDF:

 

A - Do variables declared inside the UDF be resetted at every execution (like if everytime you call UDF it creates a new istance of it) or do they need to be manually resetted in the main quest script? is SetFunctionValue the only way to make values go out from the UDF, or I can simply set a value to an external variable in the form quest.var?

 

B - You made the example of CIOS and then you were calling the UDF like ref.call UDF, so CIOS will be applied to ref. But what happens if ref is not specified, will player be the target or it will CTD because it's null?

 

C - if I make a UDF accepting a number of variables (with accolades), I assume I can even not write them (starting from right, not left... how can I say... think to DisablePlayerControls, I think it's the same, you can write 7 parameters but even miss some and write only 1), if I don't put them I assume their value will be 0 for numbers, is it true? and what happens when a ref is not specified, will it be "null" or zero? It helps me to place conditioned statements inside the function to prevent CTDs but I don't want to put extra code if I can prevent it. EDIT no they don't allow me to skip them, so why vanilla functions allow that?

 

D - Is there a way for the UDF to understand which Quest Script is calling it? it would be useful to set the quest delay automatically to a proper value

 

E - You said you can nest your UDF inside itself. Why that? is for example the case of a loop?

something like:

Begin Function {}
do code
if counter < 10
  let counter += 1
  call sameUDF
endif

But I think that it won't work because counter takes a different value every execution, unless I use quest.counter as variable... could you make me a simple example of where people want to nest an UDF inside itself?

 

F - Here the most important, sorry if it's long but it sums what I need for my UDFs.

 

EDIT: re-reading, I think the example with UDF is completely messed up. Erased for clarity, I keep just the last part:

 

I just changed it, still I'd like a confirmation if I understood well how SetFunctionValue works.

I understood that inside the function it will assume a specific value (or the value of a variable used inside the script), so if it's true can I use it to check when a UDF must stop its execution on a quest GameMode script? let's write down an example:

scn MyQuest
short GoOn
if GoOn
   <something>
   <change stage>
   <or return>
   <not important>
else
   let GoOn := Call myUDF
endif
scn myUDF
Begin Function {}
... do some code...
... do a lot of code...
... no matter if it won't finish in a frame...
... since it's staged...
... and at last stage, do this:
SetFunctionValue 1
end (even without return because I'm at the end)

Now my guess is that since GoOn is zero, the quest will repeat the UDF every (delay time) for (number of stages) times, then it will quit since UDF will give to GoOn the value of 1 and it will interrupt the execution. Did I get the purpose of SetFunctionValue?

 

ok I add G and H because I struggled on it, even if it's about arrays.

G - I can't use an array var as parameter of a function (as GetEquippedObject myarray[1], whatever you want), should I use compiler override? is it one of "those cases"?

H - Since I told you about the limitations of compiler override, is there a way to split the script so that I can use compiler override in a part and the common compiler in another part? like if I declare two different blocktypes, both _Gamemode AND a Gamemode blocktype inside the same script, as I could declare both a OnAdd and a Gamemode for example...

Share this post


Link to post

I'll take that in chunks:

 

A. Yes, local variables are reset every time you call a UDF again. It's a one-frame only script, and when it's done, it's done. Yes, SetFunctionValue is the only way to make a UDF return something (number, form/ref, string, array) directly. But sure, you can make it affect a quest var from inside the udf by going "set somequestid.somevar to somevalue".

B. Haven't tried that. At compile time, there's no way for the script compiler to know if the script will have an implied reference so it will compile fine, but at run-time I assume you'll get an error - perhaps CTD, perhaps not.

C. No, you can't assume that. If a UDF is written to expect a specific ordering of parameters, eg. 1st = number, 2nd = form, 3rd = string, then if you don't call the UDF with that specific order of parameters (skipping some, or placing them in the wrong order) you won't be able to compile your calling script. None of the parameters are optional. For numbers, you can simply put a 0 there, for strings an empty string (""), for arrays an empty but initialized array.

For forms/ref vars, it's a bit trickier. A UDF can return a null ref if you just SetFunctionValue 0, but you can't pass a null ref as a parameter to a UDF that way - the calling script won't compile. What I do is pass some form, any form, that isn't of the type you expect to use. For instance if the code in the UDF is meant to do something to a ref that is an actor, and I want it to not apply, I pass the form Pencil01, and write the UDF to check with a GetType check to see if the received form is an actor first. If anyone knows of an alternative, I'm all ears.

(EDIT: heh, I suppose if I just pass a ref var that I set to 0 first, that might do the trick too.)

D. There is the GetCallingScript function, which returns the calling script as a form. (http://cs.elderscrolls.com/constwiki/index.php/GetCallingScript) I haven't used it yet so don't know if it'd return the quest or the actual quest script - I assume it's the script, so for using setquestdelay that's probably not very useful. Still, you may search the beth forums for "GetCallingScript" and find some useful examples from Oblivion coders.

E. A good example of a nested UDF is the ArrayDeepDump function posted on the CS wiki by QQuix: http://cs.elderscrolls.com/index.php?title=ArrayDeepDump. To print the contents of a regular array to the console, we have ar_dump, but that doesn't help us in the case of arrays inside arrays. The arraydeepdump function prints out the contents of an array, and if one of those is another array, it calls itself on that one too. But you can't get it to work unless you comment out that line first, so that the script is actually added to your mod first. (There's something about that one that I couldn't get to work in spunk, so I adapted it, see attachment.)

SpunkFuDeepDump.7z

Share this post


Link to post

sorry sometimes I find it's not understable after the third or fourth time I re-read it, so after I already posted...

I modified my previous post...

Share this post


Link to post

F. First things first: you don't use accolades/brackets in the line that calls a UDF, only in the Begin Function line of the UDF itself. They are like a net that captures the parameters.

- if you call a UDF on a ref (someref.call someUDF), then you can find that ref inside the UDF by using GetSelf and passing that to a local ref variable, yes.
- The parameter variables that you use to capture the parameters don't have to have the same name as in the calling script. They just have to be the same type.
- As said, a local int/float variable that isn't set will be 0 every time the UDF is called, and a local ref var will be NULL every time the UDF is called, until you set them to something.
- I think you caught it in your example, yes. SetFunctionValue is pretty versatile - it doesn't have to be done at the end of a UDF either. You can conditionalize it. For instance as part of my init cycle in spunk I have this in my calling script:

    if iInit == 0
        let iInit := call SpunkFuInitFirst ; returns 5
    elseif SpunkVARZ.fVersion < currentversionvalue
        if iInit != 2
            let iInit := call SpunkFuInitVersion iInit ; returns 4-3-2 (this is a UDF that runs on a version update)
            return
        endif
        let SpunkVARZ.fVersion := currentversionvalue
    else
    ...
    endif

and this in the UDF
    if iInit == 5
        ; bunch of code
        SetFunctionValue 4
        return
    endif
    if iInit == 4
        ; bunch of code
        SetFunctionValue 3
        return
    endif
    if iInit == 3
        ; bunch of code
        SetFunctionValue 2
    endif

 

Which looks to be similar to your example. Of course, in this case I use the same name for the iInit variable in the calling script and the iInit parameter variable in the UDF - this isn't necessary, just easier to remember what it refers to. It's important to understand here that the variable in the calling script isn't automatically changed, no matter what I do with it in the UDF, it only is because I let it to the result of the UDF, and specify the result with SetFunctionValue. This is different for references, strings, and arrays because they're held elsewhere and ref/string/array vars just refer to them. Changes you make to them affect the original. Number vars are always local to the script (unless they're globals).

G. Yes, looks like a case for the compiler override.
H. Yes, you can have CO and non-CO blocks in the same script. Except inside UDFs, which can only have one block.

Share this post


Link to post

nice, it seems I got it now, thank you very much.

 

EDIT: nevermind, I got it. The parameters you pass think in a slight different way than what I was expecting. so if you pass a baseID it will be used as baseID and not as a reference to that baseID.

Share this post


Link to post

Another question about SetFunctionVar: I understood the way you set different function vars and you let the result to link it to some main script variable, but can you set more values together?

i.e. UDF code:

 

Begin function {}

   do my operations

   SetFunctionVar myref

   SetFunctionVar 5

   SetFunctionVar 4.65

end

 

can you set them all outside the UDF, inside the main script, or you must use external quest.vars?

essentially my UDF must give me a ref as result, but I also need a short to check if the UDF has finished its execution (like in the example I did before). Of course I can use a quest.var but it would create more and more troubles in readability. Also, the script calling the UDF is linked to a quest, so I assume I could, inside the UDF code, simply write "let myUDFref := myquestcallingtheUDF.myquestvar", but as you can see it would make a specific UDF for a specific calling script, loosing one of the meanings of UDFs (which is being generic pieces of code for different purposes).

Maybe the parameters can be used in two ways and I ignore it?

Something like this (Main script):

let myREF := someref

call myUDF {myREF} >>> inside myUDF I use myREF (which is == someref), then I change myREF in someotherref

; and now in the main code myREF is someotherref

PS two days I use them, and I really love them. My scripts can be shortened by A LOT, I mean even from 300 to 30 lines

Share this post


Link to post

Nah, only one SetFunctionValue at a time. For signification, I'd just use the fact that a value is returned at all:

 

let someRefVar := call MyUDF

if someRefVar

  ; you're good to go

endif

 

But if you need to return a bunch of data, consider sticking them in an array and returning that.

Share this post


Link to post

Doc, do you know if passing multiple arrays as params to a UDF is valid or not? In most pass-by-ref oriented languages it works fine, but it doesn't in the pass-by-val ones (the first array seen gobbles up all remaining params).

 

Trying to decide how the 'raw' internal sexout UDFs (there will be simpler public interfaces) should look. I'm leaning towards most of them taking a single hash (stringmap) arg as their only parameter, so I can add support for more params without changing the interface signature; something I do a lot in 'real' programming languages. I didn't see a function to return a normal array of keys in the hash though, and if I end up needing that, it might be easier to pass multiple arrays instead in some cases. HasKey will work 99% of the time, but the edge cases might bite me.

Share this post


Link to post

I'm sure think I managed to pass more than one in one go, yes.

 

Array of keys : ar_Keys should do you fine if you're after what I think you're after. :)

EDIT: I think I forgot about that one in the array tut, so I added it.

Share this post


Link to post

Passing a float to a UDF, which is explicit written as negative number (-90), doesn't compile, it says you miss that parameter, as if the - kills everything.

Any clue on how workaround that?

Share this post


Link to post

because if it is possible, I would prefer jumping that step. It could create less issues when that UDF must be used by someone else.

Share this post


Link to post

Passing a float to a UDF, which is explicit written as negative number (-90), doesn't compile, it says you miss that parameter, as if the - kills everything.

Any clue on how workaround that?

- is a negation operator or something when only given one arg.

 

Try passing in "0 - 90"?

Share this post


Link to post

Yes I'm trying to pass a negative value, but there wasn't way to make it compile. I tried -90 and - 90 but not 0-90 because I already have seen vanilla functions don't want operations in them. But now I just tried it following your suggestion and you know what, now it compiles even with -90. Mmmh compiler confuses me.

Share this post


Link to post

Funny story... when writing a recursive function, you have to save it once first without the recursive call, or it can't be resolved. ;)

Share this post


Link to post

Before I start fiddling with this, because my brain isn't so good with this kind of thinking, if I have an event where a player normally has a token with a "removeme" at the end just so I can process that script when it's called, instead I could just do "call ScriptNameHere" and process the script without adding a token?

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...