Search the Community
Showing results for tags 'UDF'.
-
Version 1.3 NTVM
164 downloads
Description: This is much or less the translation with UDFs of my Cammy, the first thing I did using NVSE some months ago. It doesn't do anything on its own, since it's just UDFs that handle some math. This ESM (ESP marked as ESM) comes as it is and mainly for modders' reference. There are a bunch of functions I usually call in my ESPs, mainly for cutscenes/machinima, but they're pretty generic and they can be used in different environments. They all are prefixed with aCam. They all are tested, but due to the difficulty in handling some stuff I can't exclude there are bugs or unexpected results under specific conditions. Or my mistakes, of course. Use: Just drop the ESP-marked-as-ESM it inside your FONV\Data folder and activate it, then you know what you need to do. Unuseful notes about movement: Compatibility: 1.2 PS is compatible with companions mods and pipboys mods (huh, finally). Permission: This mod can be considered free for non-commercial use only - please credit the deserving, when deserved - Any mod that contains any assets from this mod MUST SHARE the SAME policies. There are NO exceptions for this: if you don't like this rule, don't use this mod or any of its assets. Instead, if you can stand to my will and you understand that my main purpose is mods diffusion, feel free to do whatever you want with this mod and PM me if you need some help, I'll be glad to help you. Credits: Jaam Doctasax's tutorials Examples: A bunch of ingame frames taken when I apply some functions and the result I achieve. https://www.youtube.com/watch?v=G5G-Ps6Zmuc&feature=youtu.be -
Tutorial: NVSE4+ Part 2: User Defined Functions (UDFs)
DoctaSax posted a topic in Tutorials & Guides
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! 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.- 58 replies
-
- UDF
- user-defined
-
(and 3 more)
Tagged with:
-
View File Description: This is much or less the translation with UDFs of my Cammy, the first thing I did using NVSE some months ago. It doesn't do anything on its own, since it's just UDFs that handle some math. This ESM (ESP marked as ESM) comes as it is and mainly for modders' reference. There are a bunch of functions I usually call in my ESPs, mainly for cutscenes/machinima, but they're pretty generic and they can be used in different environments. They all are prefixed with aCam. They all are tested, but due to the difficulty in handling some stuff I can't exclude there are bugs or unexpected results under specific conditions. Or my mistakes, of course. Use: Just drop the ESP-marked-as-ESM it inside your FONV\Data folder and activate it, then you know what you need to do. Unuseful notes about movement: Compatibility: 1.2 PS is compatible with companions mods and pipboys mods (huh, finally). Permission: This mod can be considered free for non-commercial use only - please credit the deserving, when deserved - Any mod that contains any assets from this mod MUST SHARE the SAME policies. There are NO exceptions for this: if you don't like this rule, don't use this mod or any of its assets. Instead, if you can stand to my will and you understand that my main purpose is mods diffusion, feel free to do whatever you want with this mod and PM me if you need some help, I'll be glad to help you. Credits: Jaam Doctasax's tutorials Examples: A bunch of ingame frames taken when I apply some functions and the result I achieve. https://www.youtube.com/watch?v=G5G-Ps6Zmuc&feature=youtu.be Submitter A.J. Submitted 07/30/2014 Category Modders Resources Requires NVSE 4.something+