User Tools

Site Tools


hpl2:amnesia:script_language_reference_and_guide:constants_and_enumerations

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
hpl2:amnesia:script_language_reference_and_guide:constants_and_enumerations [2013/01/11 13:30]
thegreatcthulhu [Enumerations]
hpl2:amnesia:script_language_reference_and_guide:constants_and_enumerations [2015/10/06 21:06] (current)
thegreatcthulhu [At a Glance]
Line 6: Line 6:
 ===== At a Glance ===== ===== At a Glance =====
  
 +Constants and enumerations allow you to define names which refer to values that should never change during the execution of a script.
  
-<code c++> +**Constants** 
-// Constants and enumerations allow you to define names which refer to values which  +<code c++>// ​Some math constants: 
-// should never change during the execution of the scriptIf you accidentally attempt ​ +const float PI = 3.1415926f; 
-// to change them, you'll get a compiler error. They are also useful as a more meaningful  +const float E = 2.7182818f;
-// and readable replacement for "magic numbers"​ - numerical values accepted as parameters  +
-// by some functions, which have special meanings for those functions.+
  
-// Integer constants: ​+// Integer constants:
 // The constants in this example define when should collision events take place; // The constants in this example define when should collision events take place;
 // intended to be used with the AddEntityCollideCallback() engine function // intended to be used with the AddEntityCollideCallback() engine function
Line 24: Line 23:
 // Usage: // Usage:
 AddEntityCollideCallback("​Player",​ "​Area_Example",​ "​ExampleCallback",​ false, COLLIDE_ON_BOTH);​ AddEntityCollideCallback("​Player",​ "​Area_Example",​ "​ExampleCallback",​ false, COLLIDE_ON_BOTH);​
 +</​code>​
  
- +**Enumerated ​Constants ​(Enumerations)** 
-// Some math constants:​ +<code c++>enum Color     // Note: Enums are based on the int type.
-const float PI = 3.1415926f;​ +
-const float E = 2.7182818f;​ +
- +
- +
- +
-// Enumerated ​constants ​(enumerations- often used as parameters to functions +
-enum Color     // Note: Enums are based on the int type.+
 { {
     Red,        // has the default value of: 0     Red,        // has the default value of: 0
Line 39: Line 32:
     Blue        // value: (previous + 1) = 2, etc, if more added...     Blue        // value: (previous + 1) = 2, etc, if more added...
 } }
- 
  
 // Usage: // Usage:
Line 48: Line 40:
  
 // Assigning an integer value is not possible without an explicit conversion: // Assigning an integer value is not possible without an explicit conversion:
-Color col = 2;    // Causes compilation error! ​+Color col = 2;    // Causes compilation error!
  
-// Converting from integers - should generally be avoided: ​+// Converting from integers - should generally be avoided:
 Color col = Color(2); ​  // Assigns Blue to col, since 2 corresponds to Color::Blue Color col = Color(2); ​  // Assigns Blue to col, since 2 corresponds to Color::Blue
  
Line 58: Line 50:
 // This is allowed: // This is allowed:
 int colValue = col;   // so, enums can be passed to functions expecting ints --> see example below int colValue = col;   // so, enums can be passed to functions expecting ints --> see example below
- 
- 
  
 // Enumerations - choosing your own values // Enumerations - choosing your own values
Line 69: Line 59:
 } }
  
-// Usage:+// Used as parameters to a function:
 AddEntityCollideCallback("​Player",​ "​Area_Example",​ "​ExampleCallback",​ false, CollisionState::​Both);​ AddEntityCollideCallback("​Player",​ "​Area_Example",​ "​ExampleCallback",​ false, CollisionState::​Both);​
  
- +// You can define all or some of the values; those left undefined will be
-// You can define all or some of the values; those left undefined will be +
 // assigned the value of previous_constant + 1 // assigned the value of previous_constant + 1
 enum Ending enum Ending
-   +{
     Good = 1,           // =   1     Good = 1,           // =   1
     ReallyGood, ​        // =   ​2 ​  ​(previous + 1)     ReallyGood, ​        // =   ​2 ​  ​(previous + 1)
Line 91: Line 80:
     FoundNote2 ​  = 0x02,    // HEX for  2, which is in binary: 0000 0010     FoundNote2 ​  = 0x02,    // HEX for  2, which is in binary: 0000 0010
     FoundItem ​   = 0x04,    // HEX for  4, which is in binary: 0000 0100     FoundItem ​   = 0x04,    // HEX for  4, which is in binary: 0000 0100
-    FoundPassage = 0x08     ​// HEX for  8, which is in binary: 0000 1000+    FoundPassage = 0x08,    ​// HEX for  8, which is in binary: 0000 1000
     FoundAll ​    = 0x0F     // HEX for 15, which is in binary: 0000 1111     FoundAll ​    = 0x0F     // HEX for 15, which is in binary: 0000 1111
 } }
Line 279: Line 268:
 enum BodyArmor enum BodyArmor
 { {
-    ​Small  = 50, +    ​Weak  = 50, 
-    ​Large  = 100+    ​Strong ​ = 100
 } }
  
 // Later on: // Later on:
 HealingPotion vial = HealingPotion::​Small;​ HealingPotion vial = HealingPotion::​Small;​
-HealingPotion leatherArmor = BodyArmor::Small;+HealingPotion leatherArmor = BodyArmor::Weak;
  
  
Line 292: Line 281:
  
 // Use: // Use:
-GivePlayerItems(HealingPotion::​Small,​ BodyArmor::Small);+GivePlayerItems(HealingPotion::​Small,​ BodyArmor::Weak);
  
 // Rather than: // Rather than:
-GivePlayerItems(Small, ​Small);   // which is which?+GivePlayerItems(Small, ​Weak);   // which is which?
  
 // Alternatively:​ // Alternatively:​
 GivePlayerItems(vial,​ leatherArmor);​ GivePlayerItems(vial,​ leatherArmor);​
 </​code>​ </​code>​
 +
 +
 +<note important>​Although enumerated constants are grouped together under a single name, and can be accessed through that name using the scope resolution operator (''::''​),​ they actually have a global (script-level) scope, so two constants in two different enums cannot share a name. For example, this is not allowed by the language:
 +<code c++>
 +enum HealingPotion
 +{
 +    Small  = 10,
 +    Medium = 30,
 +    Large  = 60,
 +    Mega   = 100
 +}
 +
 +enum BodyArmor
 +{
 +    Small  = 50,    // compiler error
 +    Large  = 100    // compiler error
 +}
 +</​code>​
 +</​note>​
  
  
Line 351: Line 359:
 === Using Enums to Define Binary Flags === === Using Enums to Define Binary Flags ===
  
-Coming Soon... See "At Glance"+<​note>​The following section discusses slightly more advanced concepts, and also assumes you have the basic understanding of  [[hpl2:​amnesia:​script_language_reference_and_guide:​control_flow_-_conditional_statements|conditional statements]]You can skip it and come back later if you find it too hard to follow.</​note>​ 
 + 
 +As it was discussed on the [[hpl2:​amnesia:​script_language_reference_and_guide:​types|Types]] page, the computer stores data in the form of //bytes//One byte contains 8 //bits//, each of which can be either 0 or 1. This means that the computer encodes //​everything//​ as sequences of zeros and ones (from simple data like integer numbers, floating point numbers, to srings, vectors, player attributes, Amnesia monsters, the image on this screen, etc.). 
 + 
 +{{ http://​farm9.staticflickr.com/​8467/​8371332748_e001db2c28_n.jpg }} 
 + 
 +The binary system represents all numbers using only two symbols, 0 and 1. Each combination of zeros and ones encodes an integer number (for integer types) or maybe something else (for other types). For example, the binary number 00110101 depicted above represents the number 53. Now, we are not too concerned here with the details of //how// exactly all these are encoded (you can read about that elsewhere), but we are going to take a closer look at integer numbers, since this is what enum constants are under the hood. Let as first look at a single byte. 
 + 
 +Because there are 256 different combinations of ones and zeros for 8 binary places, a byte can encode 256 different values at //one time//. But, when used in a clever way, a byte can represent up to 8 different binary states //​simultaneously//​. Such binary states (like on/off, pressed/​depressed,​ lit/unlit, locked/​unlocked etc.), can be encoded by correlating them to a specific position (specific bit) in a byte. For example, in the image below, a single byte is used to encode 6 different states related to the progress of a player through a level. Two of the bits are not used. 
 + 
 +{{ http://​farm9.staticflickr.com/​8513/​8371352462_ccf55f1631.jpg }} 
 + 
 +In this example, knowing what is represented by each of the bits, you can read the value 0011 1010 to mean:  
 +  * 0 - (not used) 
 +  * 0 - (not used) 
 +  * 1 - the monster was slain by the player 
 +  * 1 - the key was found 
 +  * 1 - the light is lit 
 +  * 0 - the note was not found 
 +  * 1 - the switch is in the "on" state 
 +  * 0 - the lock is left unlocked 
 + 
 +All of that information is encoded in //single byte//. This is one of the reasons to use flags - using them saves memory (a single byte vs several state variables). The other reason is that flags can be (and usually are) passed as parameters to functions, whereby several indicators of simple binary states are passed to the function simultaneously,​ through one variable. Flags often encode ​"settings"​-like data, but should be used with care, as they can affect code readability and clarity. 
 + 
 +//How to manipulate individual bits then?// Well, by assigning integer numbers to variables, and by using binary logical operators, described below. However, manipulating bits while working with numbers in the decimal system is not very convenient. This is why programmers often use //​hexadecimal//​ (HEX) numbers for such tasks. The reason is: there'​s a direct correspondence between HEX and binary numbers, as the table below shows, which makes it easy to deal with binary representations. 
 + 
 +{{ http://​farm9.staticflickr.com/​8365/​8371351734_f2f2795a54.jpg }} 
 + 
 +As you can see, unlike the decimal number system, which represents all numbers using 10 different symbols (0-9), the HEX number system represents those same numbers using 16 different symbols (0-F). It just so happens that each of the HEX digits perfectly corresponds to one of all possible 4-bit combinations. Any one byte can thus be represented by 2 HEX digits - all you need to do is to refer to the table above, pick two hexadecimal digits, and write them together. To get the corresponding binary number, just replace the HEX digit with its binary equivalent from the table. 
 + 
 +<​code>​ 
 +Representing bytes using HEX system - some examples: 
 + 
 +---------------------- 
 +HEX          binary 
 +---------------------- 
 + ​00 ​        0000 0000 
 + ​40 ​        0100 0000 
 + ​02 ​        0000 0010 
 + ​88 ​        1000 1000 
 + ​3A ​        0011 1010 
 + ​FC ​        1111 1100 
 +---------------------- 
 +</​code>​ 
 + 
 +Note that to be able to do this, you //​don'​t have to// understand the internal workings of the decimal, binary and hexadecimal number systems (although I encourage you to learn more on the web), nor do you have to know how to convert binary and HEX representations to and from decimal; all you need to know is which HEX digit corresponds to which binary sequence (and you can get that from the table).  
 + 
 +Since HEX digits include both numerals and characters, the script language needs a way to distinguish HEX numerical literals from other numbers and variable names. So, the language provides the ''​0x''​ prefix - when you want to express a number in the HEX format, start by typing ''​0x''​ and then immediately (with no spaces in between) follow with your hex digits: 
 +<code c++> 
 +0x51  // A HEX number. 
 +51    // A decimal number. 
 + 
 +0x03  // A HEX number. 
 +03    // A decimal integer 3. 
 + 
 +0x5A  // A HEX number. 
 +5A    // A compiler error? 
 + 
 +0xD6  // A HEX number. 
 +D6    // A variable? A function? A type name? 
 +</​code>​ 
 + 
 + 
 +On the [[hpl2:​amnesia:​script_language_reference_and_guide:​types|Types]] page, it was said that the ''​int''​ type takes up 4 bytes of memory. This means that a single integer can be used to represent 32 different binary states, of "​flags",​ simultaneously. As with a single byte before, you don't have to use all of them. Also, since a 2-digit HEX number represents one byte, an 8-digit HEX number can be used to represent any 4-byte integer. You don't have to use all of the 8 digits, either -- if you use less than 8, the compiler will assume that the missing digits are all 0. 
 + 
 + 
 +Finally, this can all be applied when specifying values for the enumerated constants, so that they can be used as binary flags. For the purpose of being flags, only binary values which contain a single bit set to 1 are used. In the table above, such values are highlighted in blue. There are only four of them, and they are all different representations for the numbers which are powers of two (just like, in decimal system, numbers like 1, 10, 100, 1000, etc., are all various powers of 10). 
 + 
 +This way, each enum constant corresponds to a single bit in the binary number. 
 +<code c++> 
 +// Note: Although the constants are 4-byte integers, since only a single byte  
 +// is used, all values will be represented as bytes 
 + 
 +enum QuestCompletedFlags 
 +
 +    None             = 0x00,    //  = 0000 0000 
 +    DoorLocked ​      = 0x01,    //  = 0000 0001 
 +    SwitchTurnedOn ​  = 0x02,    //  = 0000 0010 
 +    NoteFound ​       = 0x04,    //  = 0000 0100 
 +    LightsLit ​       = 0x08,    //  = 0000 1000 
 +    KeyFound ​        = 0x10,    //  = 0001 0000 
 +    MonsterSlain ​    = 0x20     // ​ = 0010 0000 
 +
 +</​code>​ 
 + 
 +The enum flags defined in the code box above correspond to the possible states depicted earlier. Once the flags (constants) themselves are defined, they can be combined using the binary OR operator ''​|'':​ 
 + 
 +<code c++> 
 +int questState = QuestCompletedFlags::​DoorLocked | QuestCompletedFlags::​LightsLit;​ 
 + 
 +/* The value of questState is now (in binary): 
 +------------------------------------------------- 
 +   ​DoorLocked: ​  0000 0001 
 + | LightsLit: ​   0000 1000 
 + = questState: ​  0000 1001   (the door is locked and the lights are lit) */ 
 +</​code>​ 
 + 
 +The next code box shows how to represent the state which corresponds to the image below: 
 +{{ http://​farm9.staticflickr.com/​8513/​8371352462_ccf55f1631.jpg }} 
 +<code c++> 
 +// NOTE: "​QuestCompletedFlags::"​ qualifiers ommitted for brevity. 
 + 
 +int questState = SwitchTurnedOn | LightsLit | KeyFound | MonsterSlain;​ 
 + 
 +/* The value of questState is now (in binary): 
 +------------------------------------------------- 
 +   ​SwitchTurnedOn: ​ 0000 0010 
 + | LightsLit: ​      0000 1000 
 + | KeyFound: ​       0001 0000 
 + | MonsterSlain: ​   0010 0000 
 + = questState: ​     0011 1010  */ 
 +</​code>​ 
 + 
 + 
 +If you are familiar with the [[hpl2:​amnesia:​script_language_reference_and_guide:​control_flow_-_conditional_statements#​using_logical_operators|boolean logical operators]],​ you'll be pleased to learn that the binary logical operators work pretty much the same way, only on individual bits. They take two numbers as input, and produce a third number based on the bits of the inputs provided. 
 + 
 +**The binary OR operator** (''​|''​) sets a bit to ''​1''​ in the output if //any// of the corresponding bits in the inputs is ''​1''​. This is why it can be used to combine flags.\\ 
 +0011 | 1100 = 1111 
 + 
 +**The binary AND operator** (''&''​) sets a bit to ''​1''​ in the output //only if both// of the corresponding input bits are ''​1''​. For this reason, it can be used to check if a certain bit is set or not.\\ 
 +1110 & 0100 = 0100  -> if the result equals to the value of the tested flag, the bit was set\\ 
 +1110 & 0001 = 0000  -> if the result is 0 (zero), the bit was not set 
 + 
 +**The binary XOR operator** (''​^''​),​ a.k.a exclusive-OR,​ sets a bit to ''​1''​ only if the corresponding input bits //are different//​. It can be used to invert all the bits.\\ 
 +0101 ^ 0000 = 0101  -> (nothing happens)\\ 
 +0101 ^ 1111 = 1010  -> inversion 
 + 
 + 
 +So, how it all looks like in code? You've already seen how to set flags using the binary OR (''​|''​) operator. 
 +The following code snippet demonstrates,​ continuing from the previous examples, how to check if the flag ''​MonsterSlain''​ was set in the ''​questState''​ variable: 
 +<code c++> 
 +if ((questState & QuestCompletedFlags::​MonsterSlain) == QuestCompletedFlags::​MonsterSlain) 
 +
 +    // the monster was slain by the Player... 
 +
 +else 
 +
 +    // the monster lives! 
 +
 + 
 +//   ​questState: ​   0011 1010 
 +// & MonsterSlain: ​ 0010 0000 
 +// =                0010 0000 
 +</​code>​ 
 + 
 +You can also check for several flags at once, by using AND (''&''​) with binary numbers with multiple bits set to ''​1''​.  
 +<code c++> 
 +// the value of: 0x30 = 0011 0000 
 +int checkState = QuestCompletedFlags::​MonsterSlain | QuestCompletedFlags::​KeyFound; ​  
 + 
 +if ((questState & checkState) == checkState) 
 +
 +    // both flags set 
 +
 +</​code>​ 
 + 
 + 
 +Or, you can check all at once: 
 +<code c++> 
 +int checkState = 0x3F;  //  = 0011 1111 
 + 
 +if ((questState & checkState) == checkState) 
 +
 +    // all quests completed 
 +
 +</​code>​ 
 + 
 +It is often convenient to include the "all flags set" value into the enumeration:​ 
 +<code c++> 
 +enum QuestCompletedFlags 
 +
 +    None             = 0x00,    //  = 0000 0000 
 +    DoorLocked ​      = 0x01,    //  = 0000 0001 
 +    SwitchTurnedOn ​  = 0x02,    //  = 0000 0010 
 +    NoteFound ​       = 0x04,    //  = 0000 0100 
 +    LightsLit ​       = 0x08,    //  = 0000 1000 
 +    KeyFound ​        = 0x10,    //  = 0001 0000 
 +    MonsterSlain ​    = 0x20,    //  = 0010 0000 
 +    AllDone ​         = 0x3F     // ​ = 0011 1111 
 +
 + 
 + 
 +//later on: 
 +int checkState = QuestCompletedFlags::​AllDone;​ 
 + 
 +if ((questState & checkState) == checkState) 
 +
 +    // all quests completed 
 +
 +</​code>​ 
 + 
 + 
 +<note important>​Do not confuse binary logical operators (''​|'',​ ''&'',​ and ''​^''​),​ which return a //number// as a result, with their boolean equivalents (''​||'',​ ''&&'',​ and ''​^^''​),​ which return a //boolean value// (either ''​true''​ or ''​false''​).</​note>​
hpl2/amnesia/script_language_reference_and_guide/constants_and_enumerations.1357911016.txt.gz · Last modified: 2013/01/11 13:30 by thegreatcthulhu