DoctaSax Posted February 6, 2014 Share Posted February 6, 2014 This is the first installment of a set of guides that’ll explain and advertize some of the fancy things that come with NVSE4 and are new to the FNV modding community: UDFs, string vars and array vars. Oblivion modders unfamiliar with the more advanced functionalities of OBSE (16+) may also get some use out of it. These guides are aimed at people who understand an average vanilla script – give or take looking up some functions – but feel a little lost looking at the OBSE documents when they try to figure out how to use all that on a practical level. Let’s start with the basics: some brand new syntax that does more with less. Have a look at this list of OBSE expressions. If scripting is a language, let’s call the functions the vocab and idioms, and what you’re seeing there the syntax, the stuff you need to put it all together and make sense of it. Some of those should already be familiar to you: == != > < >= <= + - * / () && || Comparison, some basic math, parentheses overriding regular precedence, the logical operators AND and OR – you got that, right? If not, put this aside, come back when you do. Let’s skip to the most useful of the new syntax features, by which I mean the ones we'll definitely need to get the other parts to work, as well as some that are just too handy to ignore: 1. LET 2. IF EVAL 3. "MATH" AND ASSIGN 4. WHILE LOOPS 5. SCRIPT COMPILER OVERRIDE 1. LET In nvse4, “let” takes over from good old “set”. Sure, you can still use “set” for whatever you used it before, but “let” can do more, and it’s good practice to switch to using that whenever you work with the more advanced syntax below, and string and array vars, which we’ll discuss in another installment. Basically, all you need to remember is that instead of Set someVar to someValue you go let someVar := someValue See that := thingie there? Don't forget it. It means "assign", ie "let A := B" means "let A be B" - in a biblical "let there be light" kinda way. 2. IF EVAL What goes for set-let goes for if-if eval and the elseif counterparts. “If eval” will be needed to have the compiler understand some of the more advanced stuff. Use it exclusively with anything involving array and string variables, and it won’t let you down. If eval SomeCondition Elseif eval SomeCondition Else endif Note that you can mix old ifs with new if evals in the same if-block, if you want. if eval someCondition elseif someCondition endif 3. "MATH" AND ASSIGN += add and assign -= subtract and assign *= multiply and assign /= divide and assign Indispensible, if you ask me, for quickly manipulating any float; it pretty much performs the math function on the var/value to left with the var/value to the right and immediately sets the var/value on the left to the result. It may not look like much, but goddamn, we can finally be done with typing out lines like Set someQuestID.someVar to ThatSameQuestID.ThatSameVAR + 1 and just switch to let someQuestID.someVar += 1 Less typing, shorter scripts, no reason to stick to the old way. Oh, and how about those timers: Set fTimer to fTimer + GetSecondsPassed If fTimer > 10 ; do something Endif can just be If eval (fTimer += GetSecondsPassed) > 10 ; do something Endif Use it, love it, never look back. 4. WHILE LOOPS While loops pretty much combine the looping aspect of the Label/GoTo loops we’ve had so far, with the possibility of making the loop dependent on an inherent “if eval” condition (including that ‘math and assign’ stuff above). A while loop is anything between a "while" condition and a "loop" command; it’s a block like an if-endif block: While somecondition ; check/do stuff Loop If the while-condition is false, what’s in that block will be skipped. If the while-condition is true, the block between it and the loop command will execute, and on hitting “loop”, the script will return to the while-condition to evaluate it again. Let's get some examples in: Want some chunk of code to run 80 times? Used to be you had to do: Label 1 If iNum < 80 Set iNum to iNum + 1 ; run your code GoTo 1 Endif The while equivalent is: While (iNum += 1) < 81 ; run your code loop Again, it's shorter, and it has the added advantage that nvse doesn't have to bother to keep track of label indexes, and neither do you for that matter. Additionally, the loop is represented as a block, whereas the GoTo is hardly ever on the same level of indentation as your label, and the endif to your if was rarely ever in play. That may sound a little finnicky, but the more complicated your script, the more readability is a factor. Label/GoTo is ugly.Do note however that iNum is already added to on the first time the while-condition evaluates, so it starts at "1" rather than at "0" in the if-condition of the old example, so for an exact 80 loops, I raised the value to 81. Let’s apply this to your classic formlist walk. With label/goto, your average backwards loop, doing stuff with each element in the list & then erasing it from your formlist, would look like: Set iCount to ListGetCount rList Label 1 If 0 < iCount Set iCount to iCount – 1 Set rForm to ListGetNthForm rList iCount ; do stuff with rForm ListRemoveNthForm rList iCount GoTo 1 Endif With while: Let iCount := listgetcount rList While 0 <= (iCount -= 1) ; note: iCount is already subtracted from rightaway, so I made it a <= Let rForm := ListGetNthForm rList iCount ; do stuff with rForm ListRemoveNthForm rList iCount Loop And of course, just erasing without doing stuff with the list's elements can be as simple as while (listgetcount rList) ListRemoveNthForm rList 0 loop While loops are obviously pretty sweet and a lot more elegant than Label/GoTo, but like any other loop function you should take care that it does have a chance to end and doesn't go into an infinite loop that's bound to crash your game. while (iNum += 1) > 0 ; do stuff loop is BAD: instant CTD.Also, stop and consider what you're doing before you run a very large, intensive chunk of code in a while loop that runs a lot of times. The while loop is still supposed to run within the frame that the script it's in runs, after all. Definitely limit your while loop body to things that actually depend on the while condition being different. You can stop a while loop with the "break" command, which will move the script to the line below "loop" - this is obviously something you should do whenever you can to avoid code being run that shouldn't or doesn't have to be: let iNum := listgetcount ActorsInCellList while (iNum -= 1) >= 0 let rActor := ListGetNthForm ActorsInCellList iNum if rActor.IsChild let iChildAlert := 1 break endif loop if iChildAlert == 0 ; do stuff you don't want kids to be around endif You can skip a part of the while loop's body with the "continue" command, which, like a 'return', will immediately move the script back to the while-condition: let iNum := listgetcount ActorsInCellList while (iNum -= 1) >= 0 let rActor := ListGetNthForm ActorsInCellList iNum if rActor.GetIsReference playerref continue endif ; do stuff you won't want to apply to the player loop As always, it's good practice to have those "continues" as high up in your loop's body as you can, same as a return in a spell or object script. 5. SCRIPT COMPILER OVERRIDE Note: you may need to know a bit more about string vars, array vars and user-defined functions to really understand some of what’s below. But the compiler override definitely falls under syntax and isn’t limited to any of those subjects, so there. Sure, let, if eval, while, math & assign etc. are pretty sweet in themselves already, and we haven't hit the spot of looking at combinations of UDFs, string vars, array vars and nx vars yet. Still, when we get to that, we're gonna be hampered sometimes by the vanilla script compiler itself, which doesn't accept those as parameters to vanilla and old nvse functions. Example #1: let's say we have a number that we want to get the floor of, the nearest whole number lower than the float. The vanilla compiler will know what we want to do if we specify it as a float variable that we pass to the floor function as a parameter: let iSomeInt := floor fSomeFloat It will not understand what we want though if we tell it to get the float from an array element or from a UDF call: let iSomeInt := floor somearray[someIndex] let iSomeInt := floor call someUDF will not compile. The vanilla compiler can only accept float variables as parameters to the floor function, and has no idea what's waiting in that array or that UDF at compile time anyway. Could be anything, and nuh-uh, it ain't standing for it.To get around that, you have to switch over the script block to use the NVSE compiler instead, which you do by prepending the block name with an underscore: Begin _Gamemode (_Menumode, _Scripteffectstart, _OnActivate, _Function, etc etc) Let someInt := floor somearray[somekey] Let someInt := floor (call someFunction) End and then you can go about your business, getting that floor, you badass. You’re basically telling the compiler to take it on faith that it’s gonna be fine, you know that there’s a float waiting under that key in that array, or that a float is bound to come back from that UDF. You’re taking over some of the responsibility from the compiler, in exchange for more freedom. In turn, you’ll need to keep your head, not fuck up, test stuff in-game instead of relying on error reports in the geck, and use parentheses a little more to make sure the compiler doesn’t neatly compile something that can’t possibly work in-game. Example #2: let’s say we want to check a bunch of different SPECIAL actor values on an actor and give them a bump if they’re under 5, then usually that’d mean: ref rActor Begin OnActivate if rActor.GetAV Charisma < 5 rActor.SetAV Charisma 5 endif if rActor.GetAV Intelligence < 5 rActor.SetAV Intelligence 5 endif ...yawn... I'm not gonna bother with the other ones. Huge waste of script space, bound to give you RSI End Now, you should know that the parameters Charisma, Intelligence etc are each in fact a string, a combination of characters. To shorten the chore of checking and setting 7 actor values, we can stick them in an array, retrieve them in a foreach loop and stick them in a string var, and just check them like that: ref rActor array_var entry array_var somearray string_var somestringvar Begin _OnActivate let somearray := ar_list "Agility", "Charisma", "Endurance", "Intelligence", "Luck", "Perception", "Strength" foreach entry <- somearray let somestringvar := entry[value] if eval 5 > rActor.GetAV somestringvar rActor.SetAV somestringvar 5 endif loop End but, well, that's just a roundabout way of showing the override will allow a string_var rather than the actual string for the actor value. Strictly speaking we can shorten that even more: ref rActor array_var somearray array_var entry Begin _OnActivate let somearray := ar_list "Agility", "Charisma", "Endurance", "Intelligence", "Luck", "Perception", "Strength" foreach entry <- somearray if eval 5 > rActor.GetAV entry[value] rActor.SetAV entry[value] 5 endif loop End Example # 3 : just the example given in the OBSE docs, which if you understood the previous ones, you should be able to get now: string_var axis float pos array_var somearray Begin GameMode let axis := somearray[index] ; the axis paramater for the setpos function is held in an array as a string that can be "x", "y" or "z" let pos := (call someFunction) + someRef.GetPos z ; the setpos function will only accept a float for the position parameter so that needs to be calculated first if eval axis == "z" setpos z pos elseif eval axis == "y" setpos y pos elseif eval axis == "x" setpos x pos endif End becomes the epically short begin _gamemode setPos someArray[index], (call SomeFunction) + someRef.getPos z end ---------------- An added advantage of using the script compiler override is that instead of Set fHealth to rActor.GetAV Health ; hey, hey or: let fHealth := rActor.GetAV somestringvar/somearrayelement, remember? you can refer to the actor value by its code number, which you can find at the bottom of the woefully outdated nvse (read: fose) docs: let fHealth := rActor.GetAV 16 Why is this an advantage? Well, you can do stuff with numbers that you can’t with strings like “health”. Like sticking them in an int: Let fHealth := rActor.GetAV iSomeInt So that you could pretty much loop through a bunch of actor values with a while and math and assign loop too, for instance: int iSomeInt Begin _ScriptEffectStart Let iSomeInt := 24 While (iSomeInt += 1) < 32 ; loops through 25-31, the AV codes for your body parts’ condition if eval 100 > GetAV iSomeInt SetAV iSomeInt 100 endif loop End in case you want to make some "Doc's Super Duper Doctor's Bag" or "Instant Heal Button" something Link to comment
Halstrom Posted February 6, 2014 Share Posted February 6, 2014 Looks good, I'm going to have to poke around some with this array stuff, I'm sure using anything with a name like SCRIPT COMPILER OVERRIDE I can break things with a lot less effort Link to comment
Symon Posted February 6, 2014 Share Posted February 6, 2014 Well, a few comments: I know what you mean but these two statements aren't equivalent. Set someQuestID.someVar to ThatSameQuestID.ThatSameGodDamnVARThatIShouldveSpelledShorter + 1 and let someQuestID.someVar += 1 Surely you meant Set someQuestID.someVar to ThatSameQuestID.somevar + 1 for the first example. I'd add that another important lesson is NOT to choose over-long variable names or ones that are easy to typo. If eval (fTimer += GetSecondsPassed) > 10 ; do something Endif I've always understood that the eval version has more of a performance hit. Not an issue unless you are doing it a lot. Perhaps point this out? Perhaps warn people about while loops that are over-long (or worse) never actually terminate. Set iCount to ListGetCount rList Label 1 If 0 < iCount Set iCount to iCount – 1 Set rForm to ListGetNthForm List iCount ; do stuff with rForm ListRemoveNthForm List iCount GoTo 1 Endif AGH! I loathe labels with a passion and they are never required! I can think of several ways to process each element that don't require the fearsome unconditioned jump! Mentioning them in the section that covers the more elegant while loop seems a bit strange. Link to comment
DoctaSax Posted February 6, 2014 Author Share Posted February 6, 2014 Well, a few comments: I know what you mean but these two statements aren't equivalent. Lol, well, just an expression of frustration there If eval (fTimer += GetSecondsPassed) > 10 ; do something EndifI've always understood that the eval version has more of a performance hit. Not an issue unless you are doing it a lot. Perhaps point this out? I don't know if it is? I think in your average situation - I know, what is average? - that particular example shouldn't give any trouble. Perhaps warn people about while loops that are over-long (or worse) never actually terminate. Good point! AGH! I loathe labels with a passion and they are never required! I can think of several ways to process each element that don't require the fearsome unconditioned jump! Mentioning them in the section that covers the more elegant while loop seems a bit strange. Label/GoTo is all we've had in the way of looping stuff for fallout scripting until a month or 2-3 ago, so I think it makes sense that I use it for contrast when advertizing while. It's something we've quite often used so far in sexout scripting, and you're right, with while and foreach in the picture, label/goto is hardly ever needed anymore, and good riddance. Link to comment
DoctaSax Posted February 6, 2014 Author Share Posted February 6, 2014 Hm, seems I've forgotten about "break" and "continue" with the while loops too. Link to comment
movomo Posted February 6, 2014 Share Posted February 6, 2014 And bitwise operations maybe? Link to comment
DoctaSax Posted February 6, 2014 Author Share Posted February 6, 2014 I don't know anything about them, or what to use them for but hey, if you guys can explain that to me, I'll put it in Link to comment
prideslayer Posted February 7, 2014 Share Posted February 7, 2014 I'd be careful with the bitwise stuff. The versions of NVSE prior to the latest one that got the OBSE expressions referred to logical operations as 'bitwise', at least on the geck wiki. Make sure you're clear that you're talking about real bitwise operations and not that misnamed garbage. That said, bitwise operations are easy. So say you track if the player has ever killed anyone in a list of factions, e.g. if they'd ever killed an ncr, legion, pg, etc. The old school way is to make a quest with a var for each faction, init them all to zero, and then update the var to 1 if/when they kill someone in that faction. With a bitmask, you only need one var. Each bit in the value is used as an independent variable that can only hold 2 values; 0 or 1. You need to decide which bit position's represent which factions. It's best if you store these values in a comment, or better yet, in vars or globals of their own so you can reference them by name. You'll setup the bitmasks with the bitwise shift-left operation. The three above might be represented with: let bmHasKilledLegion = 1 << 0 let bmHasKilledNCR = 1 << 1 let bmHasKilledPG = 1 << 2 The '<<' operator is "shift left". It means take the value on the left (1, in each of these examples), and move it N positions to the left. So the PG one takes the value 1, and moves it two positions left; from 1, to 10, to 100 (binary). That then sets the three bitmasks to the values 1, 2, and 4. In binary those three numbers are 001, 010, and 100. Now suppose the variable you're storing all this in is called 'hasKilledFlags' and starts off 0. If they kill an NCR, you'll set that variable to itself bitwise-or'd with bmHasKilledNCR like: let hasKilledFlags = hasKilledFlags | bmHasKilledNCR Now hasKilledFlags is equal to bmHasKilledNCR. Now some time passes and they kill a PG. You do the same thing as above, but with the PG flag. let hasKilledFlags = hasKilledFlags | bmHasKilledPG hasKilledFlags is now equal to 6, 110 in binary, because it's the original value 010b, bitwise anded with the PG bimask of 100b. 100 | 010 == 110. When you want to check if a flag is on, you use a bitwise AND instead and compare the result to 0. If it's 0, the bit is off, anything else and it's on. So if you want to check if they killed a PG you do this: let hasKilledPG = hasKilledFlags & bmHasKilledPG This sets hasKilledPG to only the bits that are '1' in both sides of the AND. Since bmHasKilledPG (the bitmask) only has one bit on, hasKilledPG will be 0 if that bit is not on in hasKilledFlags, and nonzero otherwise. This is why they're called bitmasks. They are a mask of bits you 'overlay' onto other values with a bitwise operator. The operator defines how they are combined. There are a lot of different types, but we only have OR and AND. No NAND, NOR, XOR, NOT, etc. Off topic geeky fun fact : A NAND (not-and) is an operation on two bits that returns a 1 if the two values are not the same. 0 nand 0 == 0 0 nand 1 == 1 1 nand 0 == 1 1 nand 1 == 0 In electronics, logic gates are physical implementations of logical operators; a NAND gate has two inputs and one output, and the 1 and 0 are represented by high-voltage or low-voltage. Every other bitwise operator can be "built" with a combination of NAND comparisons where the output from one is the input into another, and so, every type of logic gate can be made from NAND gates. Because of this property, all computer memory (including flash), as well as the actual cpu core, is made almost entirely of complicated groupings of NAND gates. NOR gates can also be used, but they require more transistors and other components, so they aren't. Link to comment
DoctaSax Posted February 7, 2014 Author Share Posted February 7, 2014 Thanks, pride, I'll come to grips with it tomorrow - right now I'm incredibly stupid from putting the UDF one together. Even for something relatively simple like that, there's a lot to take into account if you want to break it all down and gradually build it back up for the benefit of... well, guys like me. So... what's the difference with the 'falsely named' logical stuff? If I'm supposed to make it clear, I might as well expose the difference. Link to comment
prideslayer Posted February 7, 2014 Share Posted February 7, 2014 Thanks, pride, I'll come to grips with it tomorrow - right now I'm incredibly stupid from putting the UDF one together. Even for something relatively simple like that, there's a lot to take into account if you want to break it all down and gradually build it back up for the benefit of... well, guys like me. So... what's the difference with the 'falsely named' logical stuff? If I'm supposed to make it clear, I might as well expose the difference. The older ones are boolean/logical operators, '&&' and '||'. I'm sure you use them in comparisons already and are familiar with them. They are the "if (foo == 1) && (bar == 2)" bits. It's important to not mix them up with a typo because a single or double & and | have very different meanings. I cannot remember right now where I saw them referred to as bitwise operators.. I thought it was on the geck wiki, but the page that discusses them has the right names. Link to comment
prideslayer Posted February 7, 2014 Share Posted February 7, 2014 Ah actually I had that backwards. In NVSE there already were bitwise functions: LogicalAnd, LogicalOr, RightShift, etc. These are the same as the shorthand in the new version. In the NVSE documentation (and in their names) they are called Logical, but they are actually Bitwise. Link to comment
movomo Posted February 7, 2014 Share Posted February 7, 2014 Ah actually I had that backwards. In NVSE there already were bitwise functions: LogicalAnd, LogicalOr, RightShift, etc. These are the same as the shorthand in the new version. In the NVSE documentation (and in their names) they are called Logical, but they are actually Bitwise. OBSE has the smae, though they are properly documented under the bitwise section. I don't understand why they named those functions logical xxx. Link to comment
DoctaSax Posted February 7, 2014 Author Share Posted February 7, 2014 Spruced up the while loop bit; added the logical operators && and || to the list of things people should already know about before attempting the new stuff. Link to comment
Symon Posted February 7, 2014 Share Posted February 7, 2014 Good additions! Explaining why you do something or how to choose one of several equivalent techniques ought to be very useful to beginner. Link to comment
DoctaSax Posted February 8, 2014 Author Share Posted February 8, 2014 let bmHasKilledLegion = 1 << 0 let bmHasKilledNCR = 1 << 1 let bmHasKilledPG = 1 << 2 Is this correct syntax, btw? Shouldn't it be "let int := int << int" or something? Which'd correspond to "set int to Leftshift int int". There are a lot of different types, but we only have OR and AND. No NAND, NOR, XOR, NOT, etc. Well, even the old nvse docs mention a "LogicalXOr" which I suppose is similarly really a bitwise xor. And LogicalNot ("!"), which however does seem to function more as "logical" operator (like && and ||) when placed in front of a condition that returns a boolean, even if it is a bitwise one. What a naming mess, heh. I gotta wonder though whether bitwise operations really qualify as new NVSE4 stuff, considering it's been in there since the fose days and the only new thing about it I can see is some of the symbols. I'm also not mentioning some of the math (eg ^=) because the few people who want it will find it, or some new functions that jaam's been able to add now that string and array vars are in the picture, simply because they're "just" new functions returning strings and arrays, whereas the purpose of the series I'm writing is to get a broad target audience of scripters up to speed with a shift in scripting practice, really. I know when this all came to Oblivion modding - just as I was about to try my hand at a mod of my very own, way back when - it was just too much, and I just want to write the kind of guides I could've used back then, as someone without a coding background but able to do read and write Beth code because it's quite lingual. So I'm thinking bitwise operations may just be better off with a separate tutorial of their own, rather than cram them in this one and muddy the water. Ie: I needed to talk about let and if eval anyway to tackle strings, arrays & UDFs later on; I mentioned "math and assign" in the same go because it's the kind of thing everybody has a use for, and the while loops are just plain better than most occasions of label/goto; not to mention I'll talk about foreach ones when I deal with strings and arrays anyway. To me, bitwise ops are a bit out of left field in the context of the series I'm writing, even though there's definitely a use for some explanation about them - also when it comes to manipulating weapon flags and the like. Thoughts? I just don't want to scare people off too much Link to comment
prideslayer Posted February 8, 2014 Share Posted February 8, 2014 let bmHasKilledLegion = 1 << 0 let bmHasKilledNCR = 1 << 1 let bmHasKilledPG = 1 << 2 Is this correct syntax, btw? Shouldn't it be "let int := int << int" or something? Which'd correspond to "set int to Leftshift int int". Oh hell if I know. Still haven't tried any of that yet, so you're probably right Well, even the old nvse docs mention a "LogicalXOr" which I suppose is similarly really a bitwise xor. And LogicalNot ("!"), which however does seem to function more as "logical" operator (like && and ||) when placed in front of a condition that returns a boolean, even if it is a bitwise one. What a naming mess, heh. The LogicalNot sounds like a real logical/boolean Not. A bitwise not for any value but zero would still return true when evaluated as a boolean. If we look at the 8bit value 1, the bitwise negation/compliment (the "not") of that is 11111110b (254); a bitwise not simply flips all the bits. In a boolean context, 1 and 254 are the same thing -- both are 'true' since both are non-zero. Not is a 'funny' one though; it's the only bitwise operator that only has one operand. All the others take two. The normal bitwise operator for not is the tilde ("~") while the exclamation point is usually the boolean one, and it sounds like that's the case here. There is no such thing as a Logical/boolean XOR though, so that one is certainly bitwise. The boolean counterpart to xor is an expression like ((a or and (a != ). I gotta wonder though whether bitwise operations really qualify as new NVSE4 stuff, considering it's been in there since the fose days and the only new thing about it I can see is some of the symbols. I'm also not mentioning some of the math (eg ^=) because the few people who want it will find it, or some new functions that jaam's been able to add now that string and array vars are in the picture, simply because they're "just" new functions returning strings and arrays, whereas the purpose of the series I'm writing is to get a broad target audience of scripters up to speed with a shift in scripting practice, really. I'd probably just agree with you and leave the bitwise stuff completely out in that case, except to make a note that some of the functions (like LogicalXOR) are actually bitwise, but some (like LogicalNOT) are not. A good example (better than mine) of when the bitwise operators are handy -- or rather, would be handy -- is with GetPlayerControlsDisabled and the related Set/Get functions. Those could all be done with a single variable (rather than 5 of them) if the GECK natively supported bitwise operations. edit: damn smileys. Link to comment
prideslayer Posted February 8, 2014 Share Posted February 8, 2014 It figures as soon as I click send that I would think of another few. Consider these just for your personal use/education, no need to put them in the tutorials.. You can tell if two formIDs come from the same mod if their mod index is the same, obviously. The formID format of XXYYYYYY is a perfect example for bitwise operations. The mod index of any form is "formid >> 24"; if "(thisform >> 24) == (thatform >> 24)" then both come from the same mod. You can also build a refid without buildref, with something like "baseid | (gettmodindex('somemod.esp') << 24)" where baseid is the formID YYYYYY part. There is probably no good reason to do this right now, but if you were in a loop doing a lot of buildrefs for some reason, doing it the bitwise way would be a *lot* faster. Likewise if you want to multiply or divide an integer by two (and cut off the remainder if dividing), shifting left one or right one is a lot faster than actually multiplying or dividing. You can check if an integer is even or odd by just ANDing it by 1. "someVar & 1" is 1 if it's odd, 0 if it's even. Without bitwise operators the canonical way to do this is to divide the number by two, multiply the result by 2, and see if you get the original number back. It will be off by 1 if the number is odd. This is also FAR slower than a bitwise AND. Link to comment
DoctaSax Posted February 8, 2014 Author Share Posted February 8, 2014 Yep, definitely useful and underused (storing and retrieving enabled player controls and all the stuff that comes with bit flags sure looks handy), but like I said, I'll probably skip it for this particular series because it doesn't quite fall within the scope of what I'm trying to do with this series. Question though: You can also build a refid without buildref, with something like "baseid | (gettmodindex('somemod.esp') << 24)" where baseid is the formID YYYYYY part. There is probably no good reason to do this right now, but if you were in a loop doing a lot of buildrefs for some reason, doing it the bitwise way would be a *lot* faster. I just tried that and the compiler complains about not knowing what to do with that hex formid string. Converting it to decimal, the logicalor returns the decimal equivalent of the formID, which is good. Still, no way to really pass a decimal int to a ref var, I think, or a hex string for that matter: "let rForm := 3D000ADE" doesn't compile. Link to comment
prideslayer Posted February 8, 2014 Share Posted February 8, 2014 Hmm that's weird. The normal GECK ref vars can take integers ok. Link to comment
prideslayer Posted February 8, 2014 Share Posted February 8, 2014 Oh wait.. the hex format you mean. I'm not sure on that, lemme read a second. Link to comment
prideslayer Posted February 8, 2014 Share Posted February 8, 2014 Well you can try putting it in a string, a lot of the OBSE things that take an integer/formid will also take a string, assuming it's a hex value. Does let rForm := "3D000ADE" work? Link to comment
jaam Posted February 8, 2014 Share Posted February 8, 2014 A bitwise operation is what GetBuildRef do, and C++ is faster than script @prideslayer: I don't think it works. I don't remember implicit conversion from string to integer. Link to comment
DoctaSax Posted February 8, 2014 Author Share Posted February 8, 2014 Hmm that's weird. The normal GECK ref vars can take integers ok. they take them on compile time, but they don't return anything in a printc "%i" readout, other than zeroes Well you can try putting it in a string, a lot of the OBSE things that take an integer/formid will also take a string, assuming it's a hex value. Does let rForm := "3D000ADE" work? nope Link to comment
prideslayer Posted February 9, 2014 Share Posted February 9, 2014 Hmm that's weird. The normal GECK ref vars can take integers ok. they take them on compile time, but they don't return anything in a printc "%i" readout, other than zeroes They work at runtime too, that's how the NX EVFo's work. NX doesn't "really" understand Forms or Refs, just integers. You would use %x to print them though, not %i. Maybe I'm just going crazy/tired though. Link to comment
jaam Posted February 9, 2014 Share Posted February 9, 2014 For ref, runtime cast integer to float, not assign them. Link to comment
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now