Help - Search - Members - Calendar
Full Version: AND and OR in scripting
The Black Wyrm's Lair - Forums > Mod development resources & discussion > Tutorials, hints & tips
Vlasák
This is rather the compilation of my posts from Black Wyrm Lair, Pocket Plane and Czech modding forums than tutorial. It provides the method how to simulate OR operators and nested ANDs in various Infinity games.

First of all, a small recapitulation of how the script proceeds (for the detailed explaining, see SimDing0's Complete Scripting Guide).
The script is parsed from the top. When the triggers of one block are evaluated as true, the actions of the block are performed and the parsing restarts from the top (unless there is the Continue() action in actions of the block - in this case, the parsing continues to the next block).
When the trigger of one block is evaluated as false, the parsing skips to the next block.
When it reaches the bottom of script (last block), it starts from the top again.


Simulating of OR in Baldur's Gate I

It is known that there is no OR in BG1. It is also well-known how solve this situation - divide the blocks with n triggers in OR relation into n separate blocks - each with one trigger which would be in OR.

CODE
IF
  Race(Player1,ELF)
  Global("Variable","GLOBAL",1)
  OR(4)
    Class(Player1,FIGHTER_ALL)
    Class(Player1,PALADIN_ALL)
    Class(Player1,RANGER_ALL)
    Class(Player1,CLERIC_ALL)
THEN
  RESPONSE #100
     Action()
END


Previous block have to be divided into four blocks in BG1

CODE
IF
  Race(Player1,ELF)
  Global("Variable","GLOBAL"1)
  Class(Player1,FIGHTER_ALL)
THEN
  RESPONSE #100
     Action()
END

IF
  Race(Player1,ELF)
  Global("Variable","GLOBAL"1)
  Class(Player1,PALADIN_ALL)
THEN
  RESPONSE #100
     Action()
END

IF
  Race(Player1,ELF)
  Global("Variable","GLOBAL"1)
  Class(Player1,RANGER_ALL)
THEN
  RESPONSE #100
     Action()
END

IF
  Race(Player1,ELF)
  Global("Variable","GLOBAL"1)
  Class(Player1,CLERIC_ALL)
THEN
  RESPONSE #100
     Action()
END


However, there is another way how to accomplish this. The way is based on deMorgan's theorems from Boolean algebra. This way it is possible to simulate OR operator by fixed amount of block - this amount is three blocks regardless of the number of triggers in OR relation.

According deMorgan's theorem we can negate OR(n) formula.

With Trigger1 OR Trigger2 OR ... OR TriggerN we have:

!Trigger1 AND !Trigger2 AND ... AND !TriggerN

Ok, but what advantage is hidden in it?

We can catch by this negated OR formula the cases when our original OR formula is not evaluated as true. This trick is the core of this method, so some examples how to imagine its logic:

Let's compare:

"If the enemy is a dwarf or an elf, I'll do something."
-
"If the enemy is not a dwarf as well as an elf (neither a dwarf, nor an elf), I'll do something else (or maybe nothing)".

First block's triggers is true if the enemy is a dwarf or an elf. Second block's triggers is true if the enemy is neither dwarf nor elf - half-elf for example.


"If the variable has the value 2 or 4, I'll do something."
-
"If the variable has not the value 2 and 4, I'll do something else (or maybe nothing)." - the variable could have the value 0, 3, 1000,... - this condition is true in these cases (because of !(var=2 or var=4) ); in case when the variable has the value 2 (or 4), this condition is false.

With this logic we can transform following block with OR to the blocks with the same result but without OR.

CODE
//this is the original OR block (can be coded in BG2, cannot be coded in BG1)
IF
  OR(3)
     Trigger1
     Trigger2
     Trigger3
THEN
  RESPONSE #100
     Action()
END


The block will be transformed to:

CODE
IF
  !Trigger1
  !Trigger2
  !Trigger3
THEN
  RESPONSE #100
     NoAction()
END


If all triggers are evaluated as true it is the case when my original OR would be evaluated as false, so this block catches this case - the case what is a negation of what we want to do. From the parsing logic of the script we know that when the triggers are evaluated as true, the script restarts from the top. Here, when my original OR would be false, this block is true so the cycling of the script occurs. But when my original OR can be evaluated as true (at least one trigger is true in it), this block would be false (one or more trigger in this AND is not true -> whole AND is not true) and the script continues to the next block which looks like this:

CODE
IF
  True()
THEN
  RESPONSE #100
     MyAction() //actions which I want to be performed in my original OR block
END


So it is clear that these two blocks simulate the OR operator.

However, it is just a rough form. It can be used just in the case when the script would be made by these two blocks only. Because of one of them is always true, they make the cycle and other block after them would be cut off from the proceeding of the script.
For the complete simulation of OR usable anywhere we'll need the third block and one local variable.

Following three blocks represent the correct behavior of my original OR block - if the OR block would be performed, the script will restart; if the OR block would not be performed, the script continues to next block...

Local variable "OR" -
1: my original OR block would not be performed (it would be false)
0: my original OR block would be performed (it would be true)


CODE
// "OR" variable must be set to 0 - we don't know whether original OR block would be performed and initially we must expect both cases

IF
  GlobalGT("OR","LOCALS",0) // "OR" is greater than 0
THEN
  RESPONSE #100
     SetGlobal("OR","LOCALS",0) // "OR" is set to 0
     Continue() // script continues to the next block !!!
END

IF
  !Trigger1
  !Trigger2
  !Trigger3
THEN
  RESPONSE #100
     SetGlobal("OR","LOCALS",1) // original OR would be false
     Continue() // script continues to the next block according to the script parsing rules (original OR would not be performed, the script continues to the next block)
END

IF
  !GlobalGT("OR","LOCALS",0) // "OR" is not greater than 0, this block is performed. If "OR" is greater than 0, this block is not performed and the script continues to the next block
THEN
  RESPONSE #100
     MyAction() // the actions that we want to be performed
//no Continue() unless we explicitly want to. The original OR block would be performed, so the script should restart
END


We can return to the beggining of this article. The block:

CODE
IF
  Race(Player1,ELF)
  Global("Variable","GLOBAL",1)
  OR(4)
    Class(Player1,FIGHTER_ALL)
    Class(Player1,PALADIN_ALL)
    Class(Player1,RANGER_ALL)
    Class(Player1,CLERIC_ALL)
THEN
  RESPONSE #100
     Action()
END


will look like this:

CODE
IF
  GlobalGT("OR","LOCALS",0)
THEN
  RESPONSE #100
     SetGlobal("OR","LOCALS",0)
     Continue()
END

IF
  Race(Player1,ELF)
  Global("Variable","GLOBAL",1)
  !Class(Player1,FIGHTER_ALL)
  !Class(Player1,PALADIN_ALL)
  !Class(Player1,RANGER_ALL)
  !Class(Player1,CLERIC_ALL)
THEN
  RESPONSE #100
     SetGlobal("OR","LOCALS",1)
     Continue()
END

IF
  Race(Player1,ELF)
  Global("Variable","GLOBAL",1)
  !GlobalGT("OR","LOCALS",0)
THEN
  RESPONSE #100
     Action()
END


With possible more triggers in OR, this construction will have always three blocks regardless of the number of triggers in OR.


Simulating of AND in IE scripts.

(Trigger1 AND Trigger2) OR (Trigger3 AND Trigger4)

AND is implicit in triggers and no single AND(n) as OR(n) doesn't exist. But there is a three block solution that can simulate various Sum of Products boolean formulas ( (...AND...AND...AND...) OR (...AND...AND...AND...) OR (...AND...AND...AND...) OR...)

I wanted do this two years ago - I tried to simulate AND by !OR() and it didn't work.But it can be accomplished in the following way...

The principle - make the negation of: (Trigger1 AND Trigger2) OR (Trigger3 AND Trigger4) - according deMorgan we have:
(!Trigger1 OR !Trigger2) AND (!Trigger3 Or !Trigger4). This negation can be coded in IE script block.

The action of that block must be the reverse of the original action - or just NoAction() - "I want to do something if condition (Trigger1 AND Trigger2) OR (Trigger3 AND Trigger4) is true and do nothing when it is not true".
There is the second block which follow after this. This block has its condition always true and its purpose is to perform the action that we want to perform.

Because of the proccesing of scripts the following two possibilites occurs:
- the first block condition is true (="Oposition of my original requirements are fulfiled"="My original requirements are not fulfiled") - NoAction (or reverse action) is performed and the script restarts from its beginning. So the second "always true" block is not performed. So I "catch" the undesired cases.
- the first block's condition is false (="Oposition of my original requirements are not fulfiled"="My original requirements are fulfiled") - script skips to second block and it is performed then...


CODE
IF
 OR(2)
   !Trigger1
   !Trigger2
 OR(2)
   !Trigger3
   !Trigger4
THEN
 RESPONSE #100
   NoAction()
END

IF
 True()
THEN
 RESPONSE #100
   Action()
END



Well, this solution is rough, however - the rest of the script after the mentioned second block is always cut off from the script procceeding due to second block's always true condition. I can add there Continue() action that ensures the continue of the script proceeding. But by this I'll break a little the standart rules of the script proceeding (when the block is performed, the script restarts).

So, I'll add "AND" local variable - its values meaning:
- 0: my desired action can be performed
- 1: my desired action cannot be performed due to the fact that the opposite conditions are true

CODE
IF
  GlobalGT("AND",LOCALS",0) //if "AND" is greater than 0
THEN
  RESPONSE #100
      SetGlobal("AND","LOCALS",0) // reset to 0
      Continue() // I want to continue in the script
END

IF
 OR(2)
   !Trigger1
   !Trigger2
 OR(2)
   !Trigger3
   !Trigger4
THEN
 RESPONSE #100
   SetGlobal("AND","LOCALS",1) //My desired action cannot be performed (opposite conditions are true), so AND=1
   Continue() //!! I want to continue in script because in my imaginary "(Trigger1 AND Trigger2) OR (Trigger3 AND Trigger4)" block I have now the case when its conditions are not true and the script continues to following block in these cases
END

IF
  !GlobalGT("AND","LOCALS",0) // "AND" is not greater than 0
THEN
  RESPONSE #100
     Action() //perform desired action
//no Continue() - if this block is performed, the script restarts as it is standart.
END
Baronius
Shouldn't it be OR(4) instead of OR(3) in the first script block or did something escape my attention?
Vlasák
QUOTE(Baronius @ Aug 12 2005, 06:18 PM)
Shouldn't it be OR(4) instead of OR(3) in the first script block or did something escape my attention?

Yep, corrected, thx wink.gif
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2024 Invision Power Services, Inc.