Talk:If

From the Oblivion ConstructionSet Wiki
Jump to navigation Jump to search

Do brackets help in logical operations?

IE: elseif Random > 75 && ( LilRandom == 4 || LilRandom2 == 4 )

That should be if Random is over 75, and LilRandom or LilRandom2 equals 4, but what I gather is that Oblivion will interpret it as:

if Random is over 75 and LilRandom is equal to 4, OR LilRandom equals 4.

Logically it should interpret it the prior, but with Oblivion, I'm not so sure. --MaXiMiUS 21:42, 9 April 2006 (EDT)


--JOG 15:21, 14 April 2006 (EDT)Doesn't help as far as I've tested it.

--JOG 04:51, 17 April 2006 (EDT) Didn't really read and thought you asked about AND/OR as bitwise operators. Of course brackets work but since OR always has priority over AND you need to include the AND part.

tabs and spaces[edit source]

I just spent an hour searching for a bug in a script using &&:

Apparently certain combination of tabs and spaces between the two parameters cause the IF-Block to break processing of the script. No commands within the IF-Block or after the IF-Block are executed.


Here is an example:

http://home.tiscali.de/jo.ge1/tabs_working.txt

This one works fine: for each of the three objects you get a Journal entry when you activate it and can activate it as often as you like.


http://home.tiscali.de/jo.ge1/tabs_not_working.txt

The second one doesn't work: You just get the journal entry when you activate the first object, no activation, And the second and third object can't be activated at all.

--JOG 15:27, 14 April 2006 (EDT)

This is verified that the problem in the second script is due to using spaces instead of tabs. To avoid this problem ensure that all operators, numbers, identifiers, and brackets in expressions have a space on each side of them. -- Daveh 19:24, 22 September 2006 (EDT)
Actually you need to use the same separator on each side (either space or tab) and seperators are only necessary for a minus operator to differentiate it from a negative sign. The first example script uses 07 & & 07 and the second one 07 & & 20--JOG 03:27, 23 September 2006 (EDT)

Gotchas[edit source]

The following if condition compiled (I forgot to type the last angleA):

if (angleA >= 0 && angleA <= 90) || (angleA >= 180 && < 270)

Once my script hit this line, it stopped working completely. No indication that anything was wrong. --Mrflippy 21:06, 15 April 2006 (EDT)

That's a CS "Gotcha", but a "normal" behaviour in regards to OB's engine: you have 2 operators following each other, "&& <", without their required right or left operands. Although the CS "compiler" itself does not seem to have the required verification functionality in this regard (from what you are saying), thus letting you package a mod with a blatant error, it will bug when playing the game. --HawkFest 17:58, 26 May 2008 (EDT)
In Oblivion modding we often refer to any kind of silent failure as a "Gotcha" - you get absolutely no indication of what's wrong, it just doesn't work. You don't know if its your code or just some stupid error like this. It's very frustrating, so having a list of these kinds of things is important.
There are a number of these... we have a list somewhere. I need to find that so we can make it more noticeable.
Dragoon Wraith TALK 18:09, 26 May 2008 (EDT)

Also,

Sometimes a runtime error will occur where:

      if cond1 && cond2
         ;code
      endif

is not evaluated correctly in scripts. If you find a similar conditional statement is not functioning correctly, try:

      if cond1
         if cond2
            ;code
         endif
      endif

The reason for this anomaly is currently unknown.

A confusing text[edit source]

The paragraph about Combining comparisons: « Note that "||" is evaluated before "&&", just like "*" is evaluated before "+" in normal algebra. » is a confusing argument, and should give an explanation for a matter of major consequences:

  • In arithmetic and algebra, from the earliest use of mathematical notation, multiplication took precedence over addition. The standard order of operators is: 1-exponents and roots; 2-multiplication and division; 3-addition and subtraction.
  • In terms of computing, we're talking about precedence here. The precedence is a number order, and operator precedence is usually ordered with the corresponding number order. For expressions where two operators of different precedences compete for the same operand, the operator with the higher precedence wins.
  • In Common operator notation, involving "normal" alegebra or boolean algebra, "*" is evaluated before "+", it has a higher precedence number than the "+" operator. For example, 3×4+5 = ((3×4)+5), not (3×(4+5))

However, from what I've read this is not the case here. It seems that OR has a higher precedence than AND, since the article mentions the following: if myVar1 == 1 && myVar2 == 1 || myVar2 == 5 is equivalent to (if myVar1 == 1 && (myVar2 == 1 || myVar2 == 5)). Else, we'd have ((if myVar1 == 1 && myVar2 == 1) || myVar2 == 5).

Which explains why one has to be very careful in positioning conditions in a condition list of an editor item: for the CS/OB's engine, OR has order preference, has precedence over AND. For example, the condition items (A AND B OR C AND D) are evaluated as (A AND (B OR C) AND D), and not (( A AND B) OR (C AND D)), as opposed to common operator notation for most languages: we call this an inversed or negative notation. Always keep the later in mind when applying boolean algebra for evaluating some given expression, as standard operator notations will lead you to errors. --HawkFest 17:37, 26 May 2008 (EDT)

OK, I agree with everything you've written, but I'm not exactly clear on what you are suggesting be done with this article. You've described everything very accurately as I know it. What's wrong with the way the article is written, then? It seems to be saying just what you are, to me at least.
Feel free to change the article if you think you can write it better - your explanation here is certainly much more detailed than the article, so if you think the article needs the more detailed explanation, go ahead. As always on the Wiki, be bold - just go ahead and change things when you think there is a problem. Explain what you do on the Talk page, and if people disagree with your edit they can talk about it and we can compromise - it's trivial to undo things in the Wiki if necessary thanks to the History page.
Dragoon Wraith TALK 18:06, 26 May 2008 (EDT)
I found it confusing as the statement is right, but also showed an opposite behaviour which, IMHO, needs to be clarified as it impacts the design of boolean expressions, in terms operators notations for scripting any conditional expressions. Ok, I will modify the article. I just wanted some feedback about my own understanding before doing so: even though it is trivial to modify something in the Wiki, it's not that trivial to detect if the modified article can mislead to some error of understanding from the neophyte reader (until the article actually gets changed), something I wouldn't want to do... Thanks! :) --HawkFest 18:19, 26 May 2008 (EDT)

Testing negative numbers[edit source]

I had some weird results with

if (SomeVar >= -1)

but the line worked fine with

if ((SomeVar + 1) >= 0)

Are negative numbers not handled properly? Do they need to be set to a variable first? Thought I'd seen so on another page, but it's not on this one (where it should be).--Haama 19:04, 8 July 2008 (EDT)

Nested IF's[edit source]

Can someone explain to me nested IF's and how exactly they differ from using an && operator? I feel this is unclear in the article (I have read the appropriate section multiple times and still find it difficult to understand) and it has produced its fair share of "Gotcha's" in my scripting endeavours. For example:

 If ( GetItemCount Item1 == 1 ) && ( GetItemCount Item2 == 1 )
  ;Do something here
 endif

How exactly is the above code different from this:

 If GetItemCount Item1 == 1
  If GetItemCount Item2 == 1
   ;Do something here
 endif
  endif

Except, of course, for the added lines of code. It would seem using the && operator is more effecient but I prefer to use nested IF's as I've found they work for me???--Antares 22:33, 25 August 2008 (EDT)

Logically, they're the same... but logic doesn't work well here...
There are essentially two reasons you'd want to use nested ifs instead of &&s: safety and speed.
  1. Safety - Oblivion will run every test, even if the first test fails. That's not a big deal for your example, but in cases where you need a valid reference (If Ref && (Ref.GetItemCount Item1)) you can easily cause a CTD when the reference is 0.
  2. Speed - It's faster to run two checks on the same line (If... && If...) than to run two if checks, but only when you expect both conditions to be true most of the time. If either one is rare, it'd be faster to use two if checks. Scripts that check for input are a great example of this - the Keychain mod waits for you to press Activate, makes sure you have the Keychain, and then moves the keys over. I could combine the checks into if (GetControlPress 5) && if (player.GetItemCount Keychain), but because Activate is pressed maybe once every 1000 script runs, it'd take less processing to run if (GetControlPress 5), have it return false, and skip the second, if (player.GetItemCount Keychain) check.
--Haama 00:20, 26 August 2008 (EDT)

Else/elseif?[edit source]

These two commands are not covered here. While I would add a section for the two commands myself, scripting is my current weak point in Oblivion modding. If anyone would, add an entry explaining the functions of else and elseif. --Relax and Play 19:54, 20 November 2008 (EST)

What about ref variables ?[edit source]

The article needs to be expanded with things like :

if refVar  == CreatureRat ; an EditorID
if refVar  == "25314"     ; the FormID of the above - Valid ?

I could not find a clear account of this syntax anywhere. All examples in the article are for long/short variables (or just checking if a refVar is empty). Also, what exactly are the "expression" things ? Function calls like refVar.GetIsID ? How far can this go ? UDUN 05:41, 10 August 2009 (EDT)

You are correct; the article should be updated for this kind of thing. It's frankly odd that comparisons go in the If article in the first place, but whatever. At any rate, using GetIsReference is better than using the logical operators with references. They do work, usually, but have been known to be unreliable in certain cases - I unfortunately forget exactly when and how they fail, but it's been known to happen.
So instead of:
if ( refVar == player )

use

if ( refVar.getIsReference player )
(Checking a boolean like this is equivalent to "if ( refVar.getIsReference player != 0 )", but is easier to read and marginally faster)
In the same way, using "GetIsID CreatureRat" is better than using "refVar == CreatureRat".
An expression is generally the "to" side of a set ______ to ______ statement, or a similar statement (basically, it's a series of functions/arguments/operators that evaluates to a single value). Comes from mathematics, where an expression is half an equation (or inequality, or other relationship). So you would have "expression 1" == or != "expression 2". Same idea for this, though the entire equality itself could also be treated as an expression ("something == else" is an expression that evaluates to 1 for True and 0 for False; you can add, subtract, multiply, or divide by this number if you wanted to: for example, set numberOfExpressionsGreaterThanZero to (expression1 > 0) + (expression2 > 0) + expression3 > 0)). There's a fair amount of trickery that can happen with this kind of thing; most of it is just short-hand and probably ill-advised for a novice, since it can make code harder to read.
Oh, and by the way: new Talk sections should go at the bottom of the page. I know, it's odd, but it took me a while to find your comment.
Dragoon Wraith TALK 11:50, 10 August 2009 (EDT)
Thanks a lot - I put it first cause others were more or less incorporated in the article (clean up maybe ?) - No time now (sigh) - I was wondering if a thing like refVar.GetContainer.GetIsID CreatureRat is a valid expression - there is no article on Expressions that is. I guess it isn't. I know about the player thingy - now, to be exact (read anal) : "refVar.GetIsID CreatureRat" is better than using "refVar == CreatureRat" - if I got it right. Ah and what about quoted hex FormIds ? Thanks :) UDUN 16:57, 10 August 2009 (EDT)
OK, some things. You are correct, I should have used refVar.GetIsID CreatureRat there. Second, no, you cannot "stack" functions like that (sadly), so you need to do something like set refVarContainer to refVar.GetContainer / if ( refVarContainer.GetIsID CreatureRat ). Note that OBSE v0018 has changed this somewhat, though I must admit that I'm not fully up to date on how far that goes. I know it allows something like (function (anotherFunction argument)), where the return value of "anotherFunction" is used as the argument for "function". Not sure (actually, I tend to doubt) if it extends to references the way you're indicating. Actually, I'm not even sure if it's only for specific functions added in v0018, or if they managed to extend it to all functions.
Also, make sure to read Modding Terminology - very important when dealing with references and base forms like you are. The distinction between the two is not always clear, but it is very important.
As for "quoted hex FormIds", what do you mean? The only time you should ever be worrying about the actual numerical FormID is when you're using the console. In scripts, you can use the EditorID; the compiler will automatically insert the appropriate FormID when you save. EditorIDs are just massively easier to read, so even though FormIDs will work, it's a really bad idea to use them. You do need them in the console, because most of the EditorIDs aren't actually loaded during runtime - only Quest and Reference IDs, IIRC. (actually, I suppose you would also need to know FormIDs for GetFormFromMod, an OBSE function).
Dragoon Wraith TALK 23:22, 10 August 2009 (EDT)
Added section on refVars - amend it as you see fit. Please keep the examples with formID syntax as they are valid and one encounters them occasionally. Please add more info on when/if refVar1 == refVar2 syntax is valid.UDUN 06:12, 1 May 2010 (EDT)
Looks good. I feel like this is an odd place to put the FormID/EditorID thing, but it's better than not being mentioned at all. Sadly, I really don't have the time (or, honestly, the inclination) to give this page the revision it really deserves, so that will stay for now. Other than the FormID stuff, it looks great.
Can't add more detail about when it fails personally, since I don't know. Ultimately, there is no reason not to use GetIsReference/GetIsID, so I simply didn't bother keeping track of when you can get away with a simple comparison.
Dragoon Wraith TALK 01:50, 2 May 2010 (EDT)

Brackets[edit source]

Can Brackets Stop 'if' Statements?[edit source]

I'm still unsure on this. Would the game process this entire line:

if ( Var1 == 0 ) && Var2 == 0
  ;do stuff

even if the first variable were true? --Notthross 03:22, 22 September 2009 (EDT)

Yes. If you need it to not do that (for example, if the first condition is supposed to prevent the second in certain situations where even asking would cause a crash, commonly when you're dealing with ref variables that may or may not actually be set to any reference or object), use nested If statements. Very important, good question.
Dragoon Wraith TALK 06:00, 22 September 2009 (EDT)


Ill-Advised Performance Crap[edit source]

OK, some of you may know that I have a personal problem with this stuff, but here I think is particularly egregious:

In addition, the script engine looks at every line inside an If block, even when the condition was false, until an exit point is found. (Note that the code is only looked at, not evaluated, thus the example above works correctly.) An exit point is an accessible RETURN call, or the end of the script.
This:
;; unoptimized

begin GameMode
    If (somecondition != 0)
        (...some inefficient/complex algorithm...)
    endif
end

Is more expensive than this:

;; optimized

begin GameMode
    If (somecondition == 0) ;; logical negation
        RETURN
    endif
    (...some inefficient/complex algorithm...)
end
In short, best practice is to call RETURN early and often, when possible. For large scripts with complex routines (such as looking for an item in inventory or a container, sorting a list, searching a list, etc.) this subtle difference can yield dramatic performance gains.

Is terrible, in my opinion. Yes, it's faster. Is it "dramatic"ly so? Absolutely not. That's hideously confusing code to read, forgetting about Returns earlier in a script can cause extremely difficult to recognize bugs, and it's just not worth it. Seriously. Also, the complexity of the algorithm, last I checked, doesn't matter nearly so much as the simple number of lines in between, since the script processor ignores them, it just checks to see if they're endif or not. Could be wrong on that detail, but I just feel like this is a way more advanced technique than has any business here. There are so few cases where this would be even statistically relevant, versus a huge number of situations where it could be confusing for a novice or provide a serious gotcha for even an advanced scripter if they forgot about having used this technique, that I really do not think the Wiki should be promoting this concept.

But since I have a strongly held position on this that I realize not everyone shares, I wanted to see how others felt about it.
Dragoon Wraith TALK 01:57, 2 May 2010 (EDT)