ZoriaRPG

zscript_Arrays_and_Strings.txt for ZC 2.55

Nov 25th, 2016
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 55.75 KB | None | 0 0
  1. /// ZScript Arrays and Strings
  2. /// 12-JUNE-2016
  3.  
  4. /// ! from Underdtanding ZScript Arrays
  5.  
  6. Understanding Arrays in ZScript
  7. v0.4
  8. Author: ZoriaRPG
  9.  
  10. Part I: Basic Usage
  11.  
  12. In the simplest context, arrays are a series of variables, stored in memory as one large variable table.
  13. Instead of each individual value occupying memory space, the entire aray (of any given size) uses
  14. the same amount of space as one normal variable.
  15.  
  16. For example, here we have twelve variables that we would normally store as:
  17.    
  18. int x = 0;
  19. int y = 1;
  20. int z = 2;
  21. int dir = 3;
  22. bool flying = false;
  23. bool swiming = false;
  24. bool usingRing = true;
  25. int screen = 30;
  26. int dmap = 7;
  27. float spaceFree = 10.30;
  28. int hp = 64;
  29. int keys = 8;
  30.  
  31. Each of these uses one 'slot' available to us, for a variable.
  32.    
  33. Instead of using twelve unique variables, we can condense this into one array. Because we have variables of
  34. each of the three main datatypes (bool, int, float), and because of the way ZScript handles both variable types,
  35. and typecasting, we can use one float array for all of them:
  36.  
  37. float Vars[12]={0,1,2,4,0,0,1,30,7,10.3000,64,8};
  38.  
  39. In this example, the first index (position Vars[0], holding a value of '0') would take the place of int x; above.
  40. The second, taking the place of int y; is Vars[1], holding a value of '1'.
  41. The third, taking the place of int z; is Vars[2], holding a value of '2'.
  42. The fourth, taking the place of int dir; is Vars[3], holding a value of '4'.
  43.  
  44. The fifth, was originally a boolean, bool flying;. We have that in Vars[4], the fifth position, with a value of '0'.
  45. Because you can typecase from float/int to boolean, ZC will interpret a '0' here as 'false', and any other value as 'true'.
  46.  
  47. The sixth, Vars[5], takes the place of bool swimming; Again, the value here is '0' (false).
  48. The seventh, Vars[6], takes the place of usingRing. Here, we've initialised it with a value of '1' (true).
  49.  
  50. The eigHth, Vars[7] replaces int screen. We've initialised this with a value of '30'.
  51. The ninth, Vars[8], replaces int dmap, and is initialised with a value of '7'.
  52.  
  53. For the tenth position, Vars[9], is replacing float freeSpace; and holds a value of '10.3000'.
  54.  
  55. In this case, we are genuinely using a float. Because ZC uses ints and floats interchangably, it is best to
  56.     use float arrays so that you may use all three datatypes in the same place.
  57.  
  58. The eleventh position, Vars[10], replaces int hp;, and is initialised with a value of '64'.
  59. The twelvth, and final position, Vars[11], replaces int keys;, and holds a value of '8'.
  60.  
  61. Indices:
  62.  
  63. Positions in an array, are defined as 'indices' (singular: 'index'), a.k.a. 'elements' (singular: 'element').
  64.  
  65. In maths, an array, or set, is noted by representing a series of limnked values between two curled braces, but it
  66. can often be more useful to imagine it as a table; in this case, of twelve SLOTS...
  67.  
  68. Vars[][][][][][][][][][][][]
  69.  
  70. The first position, is index '0', represented as Vars[0].
  71.  
  72. When we reference that, we target index 0, which is the first slot, indicated here with a star:
  73.  
  74.         Vars[*][][][][][][][][][][][]
  75.         ...
  76.             or in this SET , as '1'
  77.                 vars[12]= { 1,0,0,0,0,0,0,0,0,0,0,0 };
  78.                     ...
  79.                         This is how you will *initialise arrays*, which we will soon explore, so keep it in mind.
  80.  
  81. The number between the braces is the index number, starting with '0': The last index of the array, is always
  82. its size *less one*, so in the case of Vars[], the last inddex (or element, or slot), is eleven, and we reference it as
  83. Vars[11].
  84.  
  85. In visual terms, that would be this slot (marked with a star):
  86.     When we reference that, we target index 0, which is the first slot, indicated here with a star:
  87.  
  88.         Vars[][][][][][][][][][][][*]
  89.         ... again, in this SET, marked as '1'
  90.                 vars[12]= { 0,0,0,0,0,0,0,0,0,0,0,1 };
  91.                
  92. Always remember that array index mumbering BEGINS AT ZERO, and ends at 'ARRAY SIZE - 1'.
  93.     * This is critical in using the function, SizeOfArray(), and why we use <= when making loops based on the array size.
  94.            
  95. An array with 512 indices ( e.g. arr[512] ) will have indices arr[0] through arr[511].
  96.  
  97. Unfortunately, we do not want to remember all of this using raw numerals to represent the array indices, on a regular basis.
  98. Instead, we want to asign some constants to these INDEX values:
  99.  
  100. //Constant      Index
  101. const int POS_X     = 0;
  102. const int POS_Y     = 1;
  103. const int POS_Z     = 2;
  104. const int DIR       = 3;
  105. const int FLYING    = 4;
  106. const int SWIMMING  = 5;
  107. const int USINGRING     = 6;
  108. const int SCREEN    = 7;
  109. const int DMAP      = 8;
  110. const int FREESPACE = 9;
  111. const int HP        = 10;
  112. const int KEYS      = 11;
  113.  
  114. In so doig, we can reference an array index using language, instead of numbers...
  115.  
  116. The constant (on the left), matches the index number, on the right.
  117. Thus, Vars[DIR] is Vars[3], and Vars[KEYS] is the same as Vars[11].
  118.  
  119. This is far more convienent, in that it makes your code easier to read (and comprehend), and that it makes it brutally easy
  120. to change the purpose of an index.
  121.  
  122. Say for example, that you make a lot of code that uses Vars[DMAP], and you decide that you (for whatever reason) wish to change
  123. the array index holding the value of vars[DMAP] from vars[8] to Vars[6]. If you did that, with *hardcoded values*, you would
  124. need to search through all of your code for every instance of Vars[8] (you are bound to miss some instances), and change
  125. every single one to read as Vars[6].
  126.  
  127. With the values assigned as constants, and by always referencing them in your code using those constants
  128. (i.e. always referencing this index as Vars[DMAP], and NEVER as vars[8]. all you need do is change the conatant:
  129. const int DMAP = 8
  130. ...to
  131. cont int DMAP = 6.
  132.  
  133. THen, every instance in your code that calls that index will be automatically updated when you compile.
  134.  
  135. Using *well-named* constants will also healp you immensely when debugging your code, as it's easy to become lost in a sea of
  136. numbers in braces.
  137.  
  138. //! Initialising Arrays with SETS
  139.  
  140. //A set is represented hen declaring an array, as a series of numbers, separated by commas, beteen a paid of curly braces:
  141. {    }
  142.  
  143.  
  144.  
  145.  
  146.  
  147. ////////////!!! FINISH SECTION
  148.  
  149. Im the following example, the number between the braces [ x ] is the index, whereas the corresponding value
  150. between the curly braces, is the value OF that index, so:
  151.  
  152. float Vars[12]={0,1,2,5,0,0,1,30,7,10.3000,64,8};
  153.  
  154. Vars[DIR] is INDEX 3, which is the FOURTH position here, holding a value of '5'.
  155.  
  156. The value of INDEX 9 (Vars[9]), is the tenth position here, and is '10.3000'.
  157.  
  158.  
  159. Assigning constants allows us to access information in the array, using that constant, to represent the index
  160. number, so that we never again need to remember it.
  161.  
  162. Thus, Vars[KEYS] is the same as Vars[11].
  163.  
  164. Thus, we can wasily make a call using the information int he array, as we would a normal variable:
  165.  
  166. if ( Vars[SWIMMING] )
  167.    
  168. would be used, instead of if ( swimming )
  169.  
  170. ...
  171.  
  172. Of course, sometimes the name of an array can be long, and we don;t always want to type all of that out in detail,
  173. so we instead, use a special function, designed to read into the array, as a shortcut:
  174.  
  175. float Is(int pos){
  176.     return Vars[pos];
  177. }
  178.  
  179. This function returns the value of Vars[ pos ]. It accepts one argument ('pos') and passes that argument to the return
  180. instruction, so that we may call:
  181.  
  182. if ( Is(SWIMMING) )
  183.  
  184. This is identical to calling ( if Vars[SWIMMING] ) because we are passing the constant 'SWIMMING' to the
  185. function as an argument, and it is using that constant when returning the value held in that array index.
  186.  
  187. Now, that usage is as a boolean, but we can also do:
  188.    
  189. if ( Is(DMAP) > 20 )
  190.    
  191. That is a fully valid expression, and because Vars[DMAP] will return a value of '30', the expression is evaluated
  192. as 'true'.
  193.  
  194. We can also invert this, with a not ( ! , bang ) :
  195.  
  196. if ( !Is(DMAP) > 20 )
  197.    
  198. The value held in Vars[DMAP] is still 30, so this would evaluate as 'false'.
  199.  
  200. Those are some basic models, for using arrays.
  201.    
  202. Likewise, we can also create a function to store, or change a value in an array:
  203.  
  204. void UpdateSwimming(float set){
  205.     Vars[SWIMMING] = set;
  206. }
  207.  
  208. Using this:
  209.  
  210. UpdateSwimming(1);
  211.  
  212. This changes the value of Vars[SWIMMING] from '0' to '1'.
  213.  
  214. void AddKey(){
  215.     Vars[KEYS]++;
  216. }
  217.  
  218. //Adds +1 to Vars[KEYS].
  219.  
  220.  
  221. //////////////////////////
  222. /// Part II: For Loops ///
  223. /////////////////////////////////////////////////////////
  224. /// The 'for loop' is one of the most powerful tools at your disposal, particularly when dealing with arrays.
  225. /// In conrast to a while loop, that runs while a condition is evaluated as true, or false, a FOR LOOP runs
  226. /// **for** a specific duration, expressed as follows:
  227.  
  228. for ( int i = 0; i < min; i++ )
  229.  
  230. /// In this example, we create a for loop, starting at 0. It runs, increasing the value of 'i' each cycle
  231. /// until i == min.
  232.  
  233.  
  234. A for loop, has three components. The base declaration (int i = 0 above), followed by a separator ';'.
  235. Then, the operating limit, declared as i < min above, where min is an external variable, followed by the separator ';'.
  236. Finally, an operational factor (increment, or decrmement; stated above as i++, which increases the value of 'i'
  237. each pass. Thus, the loop will run through every index of the array, allowing you to perform batch operations.
  238.  
  239. /// Note: If you have an external variable that you wish to use as the main variable in a for loop, you can do this:
  240. int i = 10;
  241. for (; i < 0; i--)
  242.  
  243. /// The already-declared value of 'i' (10) us used here. Instead of declaring it in the for loop, you may just add a leading separator ';'
  244. /// and the rest of the instructions follow.
  245.  
  246. /// This is useful when you are running multiple loops that operate on one main variable; but this is a rare occurence.
  247.  
  248. // ! Iteration
  249.  
  250. Here is a practise routine, to learn, and practise making arrays for use
  251. with iterative for loops:
  252.  
  253. First, a reminder: Many of the internal ZScript variables, are arrays.
  254. In this example, we will make use of Link->Counter[], which is an array
  255. with a size of [32].
  256.  
  257. Here is a list of the indices used by Game->Counter[32]:
  258.  
  259. Game->Counter[32]=
  260.        {   CR_LIFE,        CR_RUPEES,      CR_BOMBS,       CR_ARROWS,      
  261.            CR_MAGIC,       CR_KEYS,        CR_SBOMBS,      CR_SCRIPT1,    
  262.            CR_SCRIPT2,     CR_SCRIPT3,     CR_SCRIPT4,     CR_SCRIPT5,  
  263.            CR_SCRIPT6,     CR_SCRIPT7,     CR_SCRIPT8,     CR_SCRIPT9,
  264.            CR_SCRIPT10,    CR_SCRIPT11,    CR_SCRIPT12,    CR_SCRIPT13,
  265.            CR_SCRIPT14,    CR_SCRIPT15,    CR_SCRIPT16,    CR_SCRIPT17,    
  266.            CR_SCRIPT18,    CR_SCRIPT19,    CR_SCRIPT20,    CR_SCRIPT21,    
  267.            CR_SCRIPT22,    CR_SCRIPT23,    CR_SCRIPT24,    CR_SCRIPT25     };
  268.            
  269. Knowing that each player uses 32 indices to hold their stats, we will set a constant for that, so that we may call it in our loops.
  270. This is extremely prudent, as if we expand the size of any given set, we can simply update the value of this one consstant.
  271. rather than updating any instance sof a hardcoded value for it.
  272.  
  273. const int PLAYER_STATS_INDICES_PER_CHAR = 32;
  274.  
  275. For this example, let us presume that we will be making a quet with three player characters: Link, Marty, and Jennifer.
  276.  
  277. in  doing, we have a function that sets the present character:
  278.    void SetPlayerChar(int character_id); //Sets the present (selected character) to character_id.
  279. ...
  280. and a function to read the present player character:
  281.    int getPlayerChar(); //Returns the ID of the present (selected character)
  282.    
  283.    
  284. //Constant for the backup set of values
  285.  
  286.  
  287. We know that we use 32 values to store the counters for Link, and as we will be needing a backup set for each character,
  288. we'll be using a routine to store, and read the backup values. The number of these will be the same as the number of main
  289. values, so in addition to the 32 counter values, we'll need another 32 to hold backups.
  290.  
  291. Therefore, we make a constant with the offset (the first index that wills tore a backup value). In this case, the offset is
  292. '32', as we use indices 0 through 31 for the first set of values, and indices 32 through 63 for the backups.
  293.  
  294. In this way, we can make a function that calls : index+PLAYER_STATS_BAK
  295.  
  296. //Sets the backup value of a single stat in the array t a specified value.
  297. void SetStatBackup(int stat, int val){
  298.    PlayerStats[stat+PLAYER_STATS_BAK] = val;
  299. }
  300.  
  301.  
  302. In sl doig, we can easily modify the values stored in this subset of the main array, by using this OFFSET as an adjustment.
  303.  
  304. const int PLAYER_STATS_BAK = 32; //32 attribs duplicated, = +32
  305.  
  306. //! Constants for the characters
  307. const int CHARACTER_LINK = 0; //32 indices for stats, plus 32 backups = indices 0- through 63.
  308. const int CHARACTER_MARTY = 64; //Link's indices end at 63, so the next index becomes the FIRAT INDEX of the next character.
  309.                                 //...thus, index 64 is the '0' index for character 2. THis means that we can reference in in for loops
  310.                                 //without needing to make special adjustments tot he loop. The loop remains the same size, and calls
  311.                                 //using the Marty's constant, so instead of starting at zero, the loop starts at 64.
  312.                                 //Marty uses indices 64 through 127.
  313. const int CHARACTER_JENNIFER = 128; //Likeise, the next player starts at index 128. THe size used by any one of the characters is 64 indices.
  314.                                     //Therefore, our required array size is 64* ( number of characters )
  315.                                     //In this example, we have three characters, so the array size is 64 * 3, or 192.
  316.                                    
  317. float PlayerStats[192];
  318.  
  319.  
  320.  
  321. //! Making an interation function using this array, and comstants.
  322.  
  323. Now, let's use a for loop to make a function t store the present values of each counter, into the backup slots in the array.
  324.  
  325. void BackupPlayerStats(int char_id){
  326.    for ( int q = 0; q <= PLAYER_STATS_INDICES_PER_CHAR; q++ ){
  327.        PlayerStats[char_id+q+PLAYER_STATS_BAK] = Game->Counter[q];
  328.    }
  329. }
  330.  
  331. In this example, we use our offsets, tp copy the entire set of counters from Game->Counter[q] to the backup set
  332. for any character.
  333.  
  334. Let's break that down:
  335.  
  336. The function uses char_id as an input to define what character to use, so let's consider that we're using marty.
  337.  
  338. We assigned Marty an offset of 64, in the constant CHARACTER_MARTY, so we'll call the function as follows:
  339.  
  340. BackupPlayerStats(CHARACTER_MARTY);
  341.  
  342. As an instruction, this would become:
  343.  
  344. for ( int q = 0; q <= PLAYER_STATS_INDICES_PER_CHAR; q++ ){
  345.        PlayerStats[CHARACTER_MARTY+q+PLAYER_STATS_BAK] = Game->Counter[q];
  346. }
  347.  
  348. Let's further break this down,a nd see how these constants are doing our work for us:
  349.  
  350. PLAYER_STATS_INDICES_PER_CHAR = 32
  351. CHARACTER_MARTY = 64
  352. PLAYER_STATS_BAK = 32
  353.  
  354. Thus, our for loop would look like this, with hardcoded values:
  355. for ( int q = 0; q <= 32; q++ ){
  356.         PlayerStats[64+q+32] = Game->Counter[q];
  357. }
  358.  
  359. Therefore, if we assume that q = 0l as it would on the first pass, the value of Game->Counter[CR_LIFE] will be copied into
  360. PlayerStats[64+0+32]
  361. ...or
  362. PlayerStats[96]
  363.  
  364. That's precisely where we want it, and the function will copy the values of ALL 32 COUNTERS  T THEIR RESPECTIVE POSITIONS
  365. in the array, in one stroke.
  366.  
  367. Nnote however, hw useful our constants have become...
  368.  
  369. If we need to change the number of total indixes, or the offsets, for anything, all it takes is adjusting one constant, rather
  370. than chasing down hardcoded values.
  371.  
  372. Note also how the use of a constant for the character offset allows us to use the same function to back up the stata for any
  373. of our three chARACTERS, BY CALLING that function and using the named constant for the desired character as its argument:
  374.  
  375. BackupPlayerStats(CHARACTER_LINK);
  376.  
  377. That would do precisely the same backup, again, in one command, reading every counter, and backing the up, but for the Link character.
  378. No additional function is needed, all action is swift, and it is stored int he same array as Marty's information, in its own
  379. block of indices.
  380.  
  381. See how the maths change. using the Link constant... For Marty, the information was stored in indices 96 through 127, but
  382. for Lin, with a character ID offset of zero:
  383.  
  384. for ( int q = 0; q <= PLAYER_STATS_INDICES_PER_CHAR; q++ ){
  385.         PlayerStats[CHARACTER_LINK+q+PLAYER_STATS_BAK] = Game->Counter[q];
  386. }
  387.  
  388. PLAYER_STATS_INDICES_PER_CHAR = 32
  389. CHARACTER_LINK = 0
  390. PLAYER_STATS_BAK = 32
  391.  
  392. Thus, our for loop would look like this, with hardcoded values:
  393. for ( int q = 0; q <= 32; q++ ){
  394.         PlayerStats[0+q+32] = Game->Counter[q];
  395. }
  396.  
  397. This stores his datum in indices 32 through 63.
  398.  
  399. Thus, the table looks like this:
  400.  
  401. Game->Counter[32]
  402. [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
  403. -----> Copied into:
  404.  
  405. //Playertats[192]                                                //Values                   ( Indices )
  406. [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] //Link Main Stats          ( 0 to 31 )
  407. [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] //Link Backup Stats        ( 32 to 63 )
  408. [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] //MARTY's Main Stats       ( 64 to 95 )
  409. [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] //MARTY's BACKUP Stats     ( 96 to 127 )
  410. [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] //Jennifer's Main Stats    ( 128 to 159 )
  411. [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] //jennifer's Backup Stats  ( 160 to 191 )
  412.  
  413.  
  414. //! WhaT IF WE WANT TO ADD ANOTHER CHARACTER
  415. //Add another conatnt for it
  416.  
  417. //! Why to make arrays larger than needed.
  418.  
  419.  
  420. ///////////////////////////////
  421. //// SizeOfArray() Function ///
  422. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  423.  
  424. Another critical component when using for loops, with arrays, is the function SizeOfArray(int array).
  425. To use this, you specify the aray ny name as the argument for SizeOfArray(). Thus, if you are checking
  426. against the aray Vars[12], you instruct as follows:
  427.  
  428. for ( int q = 0; q <= SizeOfArray(Vars); q++)
  429.  
  430. /// SizeOfArray() always uses one argument, the identifier name of the array, as declared. You enter this as the name only
  431. /// without braces. Thus, SizeOfArray(Vars) is correct, **not** SizeOfArray(Vars[]).
  432.  
  433. /// You need to ensure that you use <= or >= here, so that the last, or first index position is read.
  434. /// ***An alternative to this, a custom function in RPG.zh, Array(int arr) simplifies this, by automatically adding +1 to the array size.
  435. /// If you use my custom Array(int arr) function, you can use < or > as normal.
  436.  
  437. /// With an array, you want to run loops like this:
  438.  
  439. for ( int q = 0; q <= SizeOfArray(arrName); q++ )
  440.  
  441. /// This will run a loop, starting at index '0', until the loop reaches the last index of the array.
  442.  
  443. /// You may also decrement the loop, as follows:
  444.  
  445. for ( q = SizeOfArray(arrName); q >= 0; q-- );
  446.  
  447. /// Normally, you want an increment type (var++) return; as this reads indices starting at '0' and is useful
  448. /// in performing Trace() functions, based on the pass.
  449. /// However, should you want to run special decremented versions, for a particular application, you may do this.
  450.  
  451. /// You may also specify a specific starting index, and a specific decrment.
  452. /// Thus, if you have:
  453.  
  454. int Table[100]; and you want to read each TENTH position, you would do:
  455. for ( int q = 0; q <= SizeOfArray(Table); q+10 )
  456.  ...which will return index 0, 10, 20, 30, 40, 50, 60, 70, 80, and 90.
  457.  
  458. /// You may also begin reading at a specific index:
  459. for ( q = 10; q <= SizeOfArray)Table); q+10 )
  460. ...which will return values of index 10, 20, 30, 40, 50, 60, 70, 80, and 90.
  461.  
  462. /// For the interim, you need only worry about incremental arrays, starting at 0,a nd reading the entire array.
  463.  
  464. //////////////////////////
  465. /// Why this is useful...
  466. /// Practical Applications
  467. ///
  468. /// Let's say that you have an array, acting as a table of enemies possible on a screen.
  469. /// ...and you want to check is a given screen enemy is from that table:
  470.  
  471. int EnemyTable[10]={3,16,17,18,24,27,40,45,60,131};
  472.     for ( int q = 1; q <= Screen->LoadNPCs(); q++ ) { //Reads all enemies on the screen.
  473.     npc enem = Game->LoadNPC(q); ///Uses the present value of 'q' (the pass number), to identify a specific NPC.
  474.     for ( int w = 0; w <= SizeOfArray(EnemyTable); w++ ){
  475.         int listenem = EnemyTable[w]; //Assigns the value held in the index of EnemyTable, at the current pass to a variable.
  476.         if ( enem->ID == listEnem ) { //If the present pass reading screen enemies, matches a value in the array holding enemy numbers...
  477.             enem->HP = 100; //Changes that enemy's HP to 100.
  478.         }
  479.     }
  480. }
  481.  
  482. /// This is an example of a NESTED FOR LOOP. The main loop (operating on variable q) is running, and in each
  483. /// pass of that for loop, a SECOND for loop checks the array EnemyTable, operating on the value 'w'.
  484. /// If the value of EnemyTable[w] matches the value of LoadNPC[q], it gives it 100 HP.
  485.  
  486. /// This is a prime example of how to use for loops with arrays, to accomplish a goal.
  487. /// You can next as many for loops as you want.
  488. /// Without a Waitframe() instruction in a for loop, all of its passes occur in ONE FRAME. You want this
  489. /// for these types of operations; but not for DRAWING commands (to be explained in 'How To: Draw Commands').
  490. ///
  491. ///
  492.  
  493.  
  494. /////////////////////////////////////////////////////
  495. /// Part III: Building and Using Table Structures ///
  496. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  497.  
  498. We have covered using arrays to do basic checks, but what if you want to check a host of things, by making a
  499.     table, such as matching XP to a given enemy by ID, and assigning drops?
  500.  
  501. To do this, we need to make the array a 'virtual structure'.
  502.     When doing this, imaging making a table in a spreadsheet:
  503.  
  504. int EnemTable[100] = {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  505.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  506.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  507.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  508.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  509.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  510.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  511.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  512.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  513.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  514.            
  515. Here, we essentially have a structure/table, with ten rows. Each row has ten elements, for a total of 100 indices.
  516. In this case, we will start with that each property is for the first row (indices 0 to 9):
  517.  
  518. Index 0: Enemy ID
  519. Index 1: Enemy HP
  520. Index 2: Enemy Tile
  521. Index 3: Enemy CSet
  522. Index 4: Enemy X
  523. Index 5: Enemy Y
  524. Index 6: Enemy Direction
  525. Index 7: Enemy touch damage.
  526. Index 8: Enemy Weapon
  527. Index 9: Enemy Weapon Damage
  528.  
  529. Now, each of the nine following rows, will maintain the same information for an additional enemy.
  530.     Thus, we can store all ten of these details, for ten enemies, in one structure.
  531.        
  532. Next, we need to assign constants:
  533.    
  534. const int ENEMYROW = 10; //This is used in combination with PASS, in calculating table position.
  535.  
  536. //Table Columns: Each of these corresponds to a virtual 'column' in our structure.  
  537. const int ENEMY_ID = 0;
  538. const int ENEMY_HP = 1;
  539. const int ENEMY_TILE = 2;
  540. const int ENEMY_CSET = 3;
  541. const int ENEMY_X = 4;
  542. const int ENEMY_Y = 5;
  543. const int ENEMY_DIR = 6;
  544. const int ENEMY_TOUCH = 7;
  545. const int ENEMY_WEAP = 8;
  546. const int ENEMY_WDAMAGE = 9;
  547.    
  548. Let us assume that we want to read something from this array.
  549.  
  550.    
  551. int pass = 0;
  552. for (; pass <= SizeOfArray(EnemTable); pass++){
  553.     npc enem = Game->LoadNPC(q); ///Uses the present value of 'q' (the pass number), to identify a specific NPC.
  554.     enem->HP = EnemTable[pass*ENEMYROW + ENEMY_HP];
  555. }
  556.  
  557. //This uses the variable pass, as a basis. For the first ten screen enemies, we change the HP of those enemies to
  558. //the value of EnemTable[ENEMY_HP] for each row (0 to 9).
  559.  
  560. We use basic math, for the main calculations in specifying the array index in the for loop:
  561.     pass (starting with 0, ending at 9) is the base value, multiplied by ENEMROW (a value of 10).
  562. Thus, on the first pass, the first value in the calculation [EnemTable pass*ENEMROW] is 0, the next pass, '10'
  563. and so forth.
  564.  
  565. The second part of the calculation, is '+ENEM_HP'. This moves the pointer, from index 0 on the first pass,
  566. to index 1 on the first pass (the enemy HP for the first row).
  567.  
  568. On the next pass, the value of the variable 'pass' becomes '1'.
  569.  
  570. Thus, we calculate: EnemTable[pass*10 + ENEM_HP]
  571. as
  572. EnemTable[1*10 + ENEM_HP]
  573. ...or...
  574. EnemTable[10 + ENEM_HP]
  575. ...or...
  576. EnemTable[11].
  577.  
  578. ..thus assigning the value held in EnemTable[11], HP for the second row, to that enemy.
  579.    
  580. Using rows, and columns as constants, we can perform calculations with our for loops, and assign the needed
  581. datum en masse.
  582.  
  583.     ///////////FINISH THIS SECTION
  584.  
  585. Now, we set up a for loop, that
  586.    
  587. for ( int pass = 0; pass
  588.  
  589.          ////////////////////////////
  590.  
  591. //////////////////////////////////////
  592. ///            Part IV             ///
  593. ///  COLUMS, and ROWS = STRUCTURES ///
  594. //////////////////////////////////////
  595.  
  596. Traditional programming languages allow the user to make *structures*, which you can think of, as arrays built
  597. out of arrays. These fall into a few categories, including the 2D array, and 3D array. I shall explain both of
  598. those presently, but it is important to note that NEITHER are POSSIBLE with ZC.
  599.  
  600. A 2D array is built like this:
  601.  
  602. int Table[5]={ int characters[20], int powers[50], int spells[100], int items[256], int statistics[9] };
  603.  
  604. A 3D array might look like this:
  605.  
  606.  
  607. int Table[5]={ int characters[20], int powers[50], int spells[100], int items[256], int statistics[3]={ int Mental[2], int Physical[2], int Spiritual[3] } };
  608.  
  609. My goodness, that looks complicated; hmm?
  610.  
  611. Well, the way it works, is that each of those, has arrays, inside arrays, allowing someone to cross-index
  612. elements easily.
  613.  
  614. In ZC, we can;t build structures, but we can develop TABLES that work in a way that is useful, to bind together
  615. related bits of information.
  616.  
  617. Let us say that you want to make a structure, that has 20 characters.
  618. Each character, can have 50 powers, 100 spells, 256 items, and nine total statistics.
  619.  
  620. Instead of storing all of these in many small arrays, we can out them ALL into ONE array.
  621.  
  622. Then you ask, 'How will we ever find anything? Won;t it be too confusing?'
  623.  
  624. The short answer, is 'No, not confusing to use', but it does require some set-up.
  625.  
  626. The first thing that we do, is to determine the total number of variables PER CHARACTER:
  627.  
  628. We may need
  629.  
  630. Add up the total nunber of additional fields that we will need. In this case:
  631. powers : 50
  632. spells : 100
  633. items: 256
  634. stats : 9
  635.  
  636. -------------
  637. 415
  638.  
  639.  
  640. THus, we need 415 index positions, PER CHARACTER< to store everything.
  641.  
  642. Now, we need to put up some constants, as identifiers for where each set of values starts:
  643.    
  644. const int POWERS = 0; //This starts at arr[1], and ends at arr[50]
  645. const int SPELLS = 50; //This starts at arr[51] and ends at arr[151]
  646. const int ITEMS = 150; //This starts at arr[151] and ends at arr[406]
  647. const int STATS = 405; //This starts at arr[406] and ends at arr[415]
  648.  
  649. These form our MAIN IDENTIFIER constants!
  650.  
  651. Now, let us look at our array:
  652.  
  653. CharStats[415];
  654.  
  655. CharStats, now holds enough information, to store the datum for one character.
  656.     We shall call this ONE ROW of datum.
  657.  
  658. The next step, is to start giving useful constants to the indivisual elements.
  659.  
  660. ***We DO NOT want to do this by their exact array index position!***
  661.    
  662. Instead, we want to give them more logical values, AS IF they were in THEIR OWN arrays.
  663.  
  664. Thus,
  665. /// Powers
  666. const int FLIGHT = 0;
  667. const int HEATVISION = 1;
  668. const int SUPERSTRENGTH = 2;
  669. const int SUPERSPEED = 3;
  670. const int XRAYVISION = 4;
  671. const int PHASING = 30;
  672. ///etc
  673.  
  674. /// Spells
  675.  
  676. const int FIREBALL = 0;
  677. const int RAYOFFROST = 1;
  678. const int DEATHBOLT = 2;
  679. const int DOOMFINGER = 3;
  680. const int RECOVERY = 4;
  681. const int DRAGONSLAVE = 80;
  682. const int NIGHTFALL = 91;
  683. ///etc
  684.  
  685. /// Items
  686. const int IT_SWORD = 0;
  687. const int IT_LASERGUN = 1;
  688. const int IT_POTLAUNCHER = 2;
  689. const int IT_BATARANG = 3;
  690. const int IT_CRIBBAGEDECK = 4;
  691. const int IT_LUCKYCOIN = 50;
  692. const int IT_ATOMICBOMB = 201;
  693. //etc
  694.  
  695. /// Stats
  696. const int MUSCLE = 0;
  697. const int STAMINA = 1;
  698. const int AGILITY = 2;
  699. const int BALANCE = 3;
  700. const int SPEED = 4;
  701. const int INTELLECT = 5;
  702. const int SOUL = 6;
  703. const int PERCEPTION = 7;
  704. const int INFLUENCE = 8;
  705.  
  706. //!* These form our GROUP IDENTIFIER constants.
  707.  
  708. Now, we can COMBINE these constants, in each groupm with one of the MAIN IDENTIFIER constants like this:
  709.  
  710. POWERS+DEATHBOLT
  711.  
  712. Thus, ( POWERS = 0 ) + ( SUPERSPEED = 3 ) = 3. Index position 3 is the spell, the Super Speed Power.
  713.  
  714. SPELLS+DRAGONSLAVE
  715.  
  716. Thus, ( SPELLS = 50 ) + ( DRAGONSLAVE = 80 ) = 130. index position 130, is the spell, Dragonslave.
  717.  
  718. ITEMS+CRIBBAGEDECK
  719.  
  720. Thus, ( ITEMS = 150 ) + ( CRIBBAGEDECK = 4 ) = 154. Index position 154 is the Cribbage Deck item.
  721.  
  722. STATS+SOUL
  723.  
  724. Thus, ( STATS = 405 ) + ( SOUL = 6 ) = 411. Index position 411, is the value of the Soul stat.
  725.  
  726.  
  727. To access this, for our character, we would do:
  728.     CharStats[ITEMS+CRIBBAGEDECK] = x;
  729.  
  730. This would set the value of the cribbage deck item, to X.
  731.  
  732. In an statement: if ( CharStats[POWERS+BEATHBOLT] ) {
  733.             //Do something
  734.     This would evaluate if index 155 has a value of 0, or a non-zero value.
  735.  
  736. Thus, not only have we set up COLUMNS for the individual items, spells, and powers, but we have set up a
  737.     structure so that we can access them as GROUPS.
  738.  
  739. Yet, we have twenty characters. Does this mean that we need twenty arrays with all this information in each?
  740.  
  741. Thankfully, no. There is a safe, easy, and simple way to handle all twenty of our marching men.
  742.  
  743. Multiply the index size of the array, by 20:
  744. Each row is for a single character, so we make 20 (virtual) ROWS.
  745.    
  746. Thus, we change: CharStats[416];
  747. to
  748. CharStats[8320];
  749.  
  750. (Note: You cannot declare an array using a formula, or a constant. the number between the braces [ ] needs to be
  751. a numeral in ZC. Thus, CharStats[8320]; is valid, but CharStats[416*20]; is not valid. You **can do this**
  752. when *USING* an array, but **not** when *declaring* it)
  753.  
  754. Now that we have CharStats[8320]; we may proceed, to assign constants to our characters:
  755.  
  756. const int CHAR_MURRAY = 0;
  757. const int CHAR_LARRY = 1;
  758. const int CHAR_CURLEY = 2;
  759. const int CHAR_MOE = 3;
  760. const int CHAR_SHEMP = 4;
  761. const int CHAR_CLARK = 5;
  762. const int CHAR_ANNA = 6;
  763. const int CHAR_LEXI = 7;
  764. const int CHAR_SAM = 8;
  765. const int CHAR_LUCY = 9;
  766. ///etc
  767.  
  768. Now, at last, we have COLUMS (GROUPS, and SUBGROUPS), and ROWS.
  769.  
  770. To find something in this table, for a *SPECIFIC CHARACTER*, we need one more, CHARACTER_ID, and we do this:
  771.  
  772. Thus, we declare a constant:
  773.  
  774. constant int CHARACTER_ID = 416;
  775.  
  776. That is fair enough. We can read into an array, using CHARACTER_ID, and always return the value of arr[416].
  777.  
  778. 'Wait', you ask.., 'how does that help us?'
  779.  
  780. This little gen of an identifier, allows us to use multiplication inside the index fields, when we want to *USE*
  781. the array.
  782.  
  783.  
  784. Note, that this is not the character NAME, it';s a token that we're goint to use to index all twenty ROWS:
  785. ( CHARACTER_ID * CHAR_MURRAY ) == ( 416 * 0 )
  786. This puts our magic pointer, at index 0, which is where all the things associated with Character Murray,
  787. begin (at row '0').
  788.  
  789. ( CHARACTER_ID * CHAR_MURRAY ) + SPELLS + FIREBALL
  790. This puts us at the index of the fireball spell, for Murray.
  791.  
  792. ( CHARACTER_ID * CHAR_MOE ) == ( 416 * 3 )
  793. This puts us at the start of the attributes for Moe. Thus, pointing us to row 3 (the fourth row, as we count
  794.     from zero).
  795. ( CHARACTER_ID * CHAR_MOE ) + ITEMS + IT_LUCKYCOIN
  796. This points to the index of the lucky coin item *FOR* Moe.
  797.  
  798. Let us see that in math:
  799.  
  800.  CHARACTER_ID * CHARACTER_MOE = 1248
  801. ITEMS + IT_LUCKYCOIN = 200
  802.  
  803. Therefore, we're using these token words, to point to index 1448. We don't need to remember that value, ever,
  804. ever.
  805. We only need to remember our special CONSTANTS, and use them wisely.
  806.  
  807. if ( CharStats[ ( CHARACTER_ID * CHARACTER_LUCY ) + ITEMS + ATOMICBOMB ] && CharStats[ ( CHARACTER_ID * CHARACTER_SHEMP ) + POWERS + SUPERSPEED ] ) {
  808.     //The world is over.
  809.    
  810. void SetCharItems(int character){  
  811.     int pass = 0;
  812.     for ( int q = ( (CHARACTER_ID*character) + ITEMS); q < (q + 255); q++ ) {
  813.         if ( CharStats[q] && !Link->Item[pass] ) {
  814.             Link->Item[pass] = true;
  815.         }
  816.         if ( !CharStats[q] && Link->Item[pass] ) {
  817.             Link->Item[pass] = false;
  818.         }
  819.         pass++;
  820.     }
  821. }
  822.  
  823. ///**************************************************************************************
  824.  
  825. /// Arrays_Tutorial_Ext
  826.  
  827. //We make two arrays, for Relatives, and Relations
  828.  
  829. int Relatives[10];
  830. int Relations[10];
  831.  
  832. //We map constants for Relatives, with the first index used as temp memory.
  833. const int MEMORY = 0;
  834. const int MUM = 1;
  835. const int DAD = 2;
  836. const int GRAN = 3;
  837. const int GRAMPS = 4;
  838. const int SIS = 5;
  839.  
  840. //..and repeat bfor Relations. MEMORY is pre-defined as Index[0] and will remain valid for both arrays.
  841. const int HARRY = 0;
  842. const int LIZ = 1;
  843. const int TOM = 3;
  844.  
  845. //..and set some states.
  846. const int ALIVE = 1;
  847. const int DEAD = 0;
  848.  
  849. void ( CheckLivingStatus(){ //We can accept inputs here, which we can type as English text, too...see below.
  850.     if ( Relatives[MUM] == ALIVE ) Relatives[MEMORY] = MUM;
  851.     if (Relations[HARRY] == ALIVE 0 ) Relations[MEMORY] = HARRY;
  852. }
  853.  
  854. void ( CheckLivingStatus(int relative, int relation){ //We can accept inputs here, which we can type as English text, too...see below.
  855.     if ( Relatives[relative] == ALIVE ) Relatives[MEMORY] = relative;
  856.     if ( Relations[relation] == ALIVE ) Relations[MEMORY] = relation;
  857. }
  858.  
  859. //Now we combine them
  860.  
  861. int Relatives[20];
  862.  
  863. const int MEMORY = 0; //We ret5ain the MEMORY index, as a catch-all, then...
  864. const int MEMORY_RELATIVES = 1; //Add a specific one for relatives, and...
  865. const int MEMORY_RELATIONS = 2; //...a specific one for relations, so that we can segregate them, as we did above.
  866.  
  867. //We adjust the index constants as needed\, merging them.
  868. const int MUM = 2;
  869. const int DAD = 4;
  870. const int GRAN = 4;
  871. const int GRAMPS = 6;
  872. const int SIS = 7;
  873. const int HARRY = 8;
  874. const int LIZ = 9;
  875. const int TOM = 10;
  876.  
  877. //Status remains unchanged.
  878. const int ALIVE = 1;
  879. const int DEAD = 0;
  880.  
  881. //Then we do Search and Replace, as follows:
  882. // Find Relatives[MEMORY]
  883. //...replace with Relatives[MEMORY_RELATIVE]
  884.  
  885. // Find Relations[MEMORY]
  886. //...replace with Relatives[MEMORY_RELATION]
  887.  
  888. // Find Relations[
  889. //...replace with Relatives[
  890.  
  891. //! Changed Code! If this was thousands of lines, that made calls to these arrays,
  892. //it would take hours, or days to change it if we used fixed values, but we can do it
  893. //mere *seconds* this way.
  894.  
  895. void ( CheckLivingStatus(){ //We can accept inputs here, which we can type as English text, too...see below.
  896.     if ( Relatives[MUM] == ALIVE ) Relatives[MEMORY_RELATIVE] = MUM;
  897.     if (Relations[HARRY] == ALIVE ) Relatives[MEMORY_RELATION] = HARRY;
  898. }
  899.  
  900. void ( CheckLivingStatus(int relative, int relation){ //We can accept inputs here, which we can type as English text, too...see below.
  901.     if ( Relatives[relative] == ALIVE ) Relatives[MEMORY_RELATIVE] = relative;
  902.     if ( Relatives[relation] == ALIVE ) Relatives[MEMORY_RELATION] = relation;
  903. }
  904.  
  905.  
  906. //You'll see here, that both are absolutely identical in operation, but we use
  907. //one fewer gd register, by merging the arrays.
  908.  
  909. //Because we have the variables assigned to constants, we can do code changes more freely,
  910. //and we can assign the constants to appropriate array indices on-the-fly, before compilation.
  911.  
  912.  
  913. ///*******************************************************************************************
  914.  
  915. /// Arrays for Global Vars
  916.  
  917. Use Arrays Instead of Global vars to Avoid Save File Problems
  918. Tags: scripting, arrays, save file, global, variables, strings, cheat limitations.
  919.  
  920. [u][b]How to Use Arrays, Instead of Global Vars [/b][/u]
  921. [i]....to Avoid Needing New Save Files When Updating a Quest[/i]
  922.  
  923. As some of you may know, if you add a global variable to a quest, when modifying your scripts, it will necessitate a new save slot for your quest. Failing to use a new (fresh) save slot, will almost assuredly mean that whatever you added, won;t work.
  924.  
  925. Coupled with this, is the nasty problem of a limit of 255 global variables--in practical terms, because of how the ZASM registers work, half of that, for around 128 (!) at most.
  926.  
  927. In this tutorial, I'm not only going to teach you how to use arrays and constants to avoid both of these pitfalls, but also a few other tips, and tricks for using 'global strings' without eating up global registers, and how to use constants, coupled with a few handy functions, to make it all work as if it were simple, ordinary variables--at least as much as possible.
  928.  
  929. The most crucial point to understand, is that when you declare an array, ZQuest reats it as a single global variable, no-matter what index size you assign to it. In addition, arrays are dynamically read, and written, so you won;t need to worry about a large array slowing ZC down to a crawl. It's no slower than any other variable usage, in general, or in practise.
  930.  
  931. THus, the first step, is to consider how many global variables you will ever need, and then multiply that, by a factor of no less than ten. Trust me, plan ahead and you won;t have regrets about not doing it, later.
  932.  
  933. For this purpose, I tend to declare this sort of array with a size of NO LESS than 8192.
  934.  
  935. '8,192!?', you ask, 'I will never need that many variables!'. Probably not, but why not play it safe, and assume that you may, hmm?
  936.  
  937. So, let's go ahead and make that array. For the purose of this article, we'll call it GameVariables:
  938.  
  939. [code]
  940. float GameVariables[8192];
  941. [/code]
  942.  
  943. Before we get into this too deeply, let's examine how ZC pre-allocates space for your global variables, and arrays...
  944.  
  945. //! Explaining Arrays and variables for Dummies
  946. Often, newcomers, and veterans alike may have trouble comprehending how variables (including arrays) are stored. I'll try to
  947. simplify this with a metaphoric explanation...
  948.  
  949. Imagine that ZC has 255 'postboxes'. Each one can hold a value, and they are numbered from 1 to 255.
  950.  
  951. Each time you declare a global variable, or a global array, it is assigned a 'postbox'. That 'postbox' is then stored in the
  952. quest save, both as being used, and as having an address number.
  953.  
  954. Thus, let's say you declare this:
  955.  
  956. int myvar;
  957. int myvar2;
  958. int myvar3;
  959.  
  960. Each of these three declarations is given its own postbox. They are assigned an address NUMBER IN THE ORDER IN WHICH YOU DECLARE THEM,
  961. so myvar is postbox 1, myvar2, is postbox 2, and myvar3 is postbox 3.
  962.  
  963. When you are done, and load this quest, ZC will read these, and PERMANANTLY allocate a postbox to them, in the SAVE FILE.
  964.  
  965. The rest of the available 252 postboxes are set to store TEMPORARY values, like sorting bins. They can;t be used to hold
  966. any permanant value.
  967.  
  968. Therefore, if we start the game, boxes 1, 2, and 3, are locked to the address (pointer IDs) of the declared variables.
  969.  
  970. If you later go in and add int myvar4, recompile, and save the quest file, the quest file ITSELF will have an address for
  971. postbox 4, but because it did not exist when you saved the game, ZC won;t know how to deliver post to it!
  972.  
  973. Next, we must consider how an array (of any type) is stored.
  974.  
  975. Let's say that you declare MyArray[256]; That is, an array with 256 indices, numbered from 0 to 255.
  976. Each of those indices is like its own little postbox, but RATHER THAN using one of the main postboxes for each index,
  977. ZScript stores ALL of the indices in one of its postboxes. So, picture it like a big postbox, with many small compartments inside, each directed to a flat (apartment) number.
  978. The array itself is in the address '4', but the indices are in 4-0, 4-1, 4-2, 4-3, and so forth, through 4-255.
  979.  
  980. This means that we can safely tuck 256 variables into one postbox.
  981.  
  982. Where this matters, is that you can declare a very large array--one with far more space than you believe that you need. I usually make these a size of 4096, which looks like this
  983. when declaring it:
  984.  
  985. float ArrayName[4096];
  986.  
  987. That is 4096 compartments, in one postbox. Each of the indices 'compartments' is given its own address too, when created, so, if you want to make it BIGGER by adding compartments (indices) later, you can;t.
  988. This is why we want to start big: Far bigger than we need, as the indices that you do not use, don;t actually take up any extra space.
  989.  
  990. Now, you will probably not need that many--although very complicated scripts certainly MAY--but the nice thing is that while ZScript cannot add more postboxes or compartmwnts
  991. later, BECAUSE the compartments are already there, just not in use, it can assign them to new 'tenants' that need to occupy their space.
  992.  
  993. Thus, if you later need to add variables, you can do so, SAFELY, without needing a new save file, as the compartments to store
  994. whatever you want to store in then, have already been allocated. They're just waiting to be occupied. Thus, by making the array OVERSIZED< we ensure that we have room for FUTURE EXPANSION.
  995.  
  996.  
  997. Wait a moment, you may be thinking. Why did you make that a float, and what do I do when i need booleans? The answer to both of these questions, is that you never* need to declare a boolean array. I learnt that lesson the hard way, and I'm saving you time, right here and now. ZC treats floats, when returning values, juis as it woud for both ints, and bools. The ZC typecasting will treat any 0-value as 'dfalse', and any non-zero value as 'true', so that takes care of your booleans. As for ints, you can always store an int, in a float type variable, or array in ZScript. The reverse however, is NOT true. If you make the array an int type, then you will not be able to store the decimal portion of floating point vakues in it. Thus, by starting with a float declaration, we can safely use all three types at our leisure.
  998.  
  999. Use of Constants
  1000.  
  1001. One of the common complaintd with array usage, is that it's harder to remember what something like MyVars[1240] is, or does. That is absolutely true, and in fact, it's highly advisable not to HARDCOSE numeric values when calling these indices to read them.
  1002.  
  1003. Instead, we make it easy, by declaring constants, with a name that helps us esaily identify what the index holds.
  1004.  
  1005. let's say for example, that an array index, maybe 1260, holds the ID of the last item Link used. Wehat we do here, is declare a constant with a USEFUL NAME
  1006. such as:
  1007.  
  1008. const int LINK_LAST_A_ITEM = 1260;
  1009.  
  1010. Now, instead of calling MyVars[1260] we can instead, call MyVars[LINK_LAST_A_ITEM]
  1011.  
  1012. This has multiple :
  1013.  
  1014. it makes code easier to read. Instead of a garbled mess of numbers, we now explicitely state what the value is that we want to read, or modify
  1015. when we call it.
  1016.  
  1017. It makes it easier to change things. if you want to add a variable, but you want it in index 1260, say
  1018. const int LINKS_PANTS = 1260;
  1019.  
  1020. ...you can MOVE the references in ALL of your scripts to MyVars[LAST_A_ITEM_USED] simply by changing the value assigned TO THE CONSTANT. Thus, is we now want
  1021. LINK_LAST_A_Item to use Array index 3011, we change:
  1022.  
  1023. const int LAST_A_ITEM_USED = 1260;
  1024. to
  1025. const int LAST_A_ITEM_USED = 3011;
  1026.  
  1027. When we recompile, every reference in all of your scripts to MyVars[LAST_A_ITEM_USED] is AUTOMATICALLY updated to use index 3011 instead of 1260.
  1028. This ensures that we don;t need to go through thousands of lines of code to find, and change a pile of hardcoded values, as we would if we instead called MyVars[126];
  1029.  
  1030. All you need do, is change ONE VALUE, and you are DONE.
  1031.  
  1032. It makes code easier for other people to comprehend, by using natural language.
  1033.  
  1034. It is much easier for someone reading your script, to look at MyVars[LAST_A_ITEM_USED} than MyVars[1260[. The former explicitely tells them
  1035. what that value is for, while the latter requires them to spend unholy smounts of time, FIGURING IT OUT.
  1036.  
  1037. It allows us to get the data of an array index, with a function, very easily. Consider that you want to make a function that RETURNS the value of an array index.
  1038.  
  1039. With this method, we can make a function like this:
  1040.  
  1041. int Value(int index){
  1042.     return MyVars[index];
  1043. }
  1044.  
  1045. Now, when we want to know the vaslue of MyVars[LAST_A_ITEM_USED] , we AGAIN use the constant, but this time, when we CALL
  1046. that function:
  1047.  
  1048. Val(LAST_A_ITEM_USED);
  1049.  
  1050. That gives us a lot of fluidity in how we read values, set values, and change values. Again, it also makes our code
  1051. much easier to read, and it saves us from hardcoding NAMED functions such as:
  1052.  
  1053. LastAItemUsed(){
  1054.     return MyVars[3011];
  1055. }
  1056.  
  1057. In fact, this is so powerful, that it gives us incredibly useful shortcuts. Instead of doing:
  1058.  
  1059. if ( MyVars[LAST_A_ITEM_USED] != 0 )
  1060.     we can do
  1061. if ( Val(LAST_A_ITEM_USED) != 0 )
  1062.    
  1063. That may not seem signifigant, but if the array has a long name, such as EnemyCollisionRoutineValues[], we don;t want to type that over an over.
  1064.  
  1065. Likewise, we can use multiple shortcut names for these functions, and give them purpose by name. I personally prefer using Is(int index) to get a value, as
  1066.     this reads as either an integer, or boolean type return very easily. To do this, we OVERLOAD the function as follows:
  1067.        
  1068. int Is(int index){
  1069.     return MyVars[index];
  1070. }
  1071.  
  1072. int Is(int index, int value){
  1073.     return MyVars[index] == value;
  1074. }
  1075.  
  1076. Now we can call:
  1077.  
  1078. Is(SWIMMING) to find out if Link is swimming (by storing a value other than 0 when he is in this index, and 0 if he is not...
  1079. or
  1080. Is(LAST_A_ITEM_USED,I_MAGIC_THING);
  1081.  
  1082. Which will return true if the last item used (as stored in our array) is that item.
  1083.    
  1084. It's also useful to have a set of functions to modify the values:
  1085.  
  1086. //Sets an array value to a specific number.
  1087. void Set(int index, int value){
  1088.     MyVars[index] = value;
  1089. }
  1090.  
  1091. //Increases the value of an index by +1
  1092. void Inc(int index){
  1093.     MyVars[index]++;
  1094. }
  1095.  
  1096. //Reduces the value of an index by -1
  1097. void Dec(int index){
  1098.     MyVars[index]--;
  1099. }
  1100.  
  1101. //Increases the value of an index by 'amount'
  1102. void Inc(int index, int amount){
  1103.     MyVars[index] += amount;
  1104. }
  1105.  
  1106. //Reduces the value of an index by 'amount'
  1107. void Dec(int index, int amount){
  1108.     MyVars[index]-= amount;
  1109. }
  1110.  
  1111. We call them simply:
  1112.  
  1113. Instead of calling
  1114. MyVars[LINK_IS_SPINNING] = 1;
  1115.  
  1116. we call
  1117. Set(LINK_IS_SPINNING,1);
  1118.  
  1119. or instead of
  1120.  
  1121. Using constants for Array Pointers
  1122.    
  1123. //! No you can't, pudding brain. ZScript doesn;t support assigning anything to a constant other than a raw numeric literal!
  1124.    
  1125. All arrays have a pointer, assigned as a unique integer ID when declared. This is very useful, as if we have a long array
  1126.     name that we need to use often, we can make a constant s a short name, like this:
  1127.  
  1128. const int LINKSTATS = LinkStatsAndvariables;
  1129.  
  1130. The pointer for LinkStatsAndVariables, whatever it may be, is assigned to the constant, and all future calls for that constant
  1131.     will automatically point to that array.
  1132.  
  1133. Example:
  1134.  
  1135. //Returns the value of 'index' in the array with pointer 'arr'
  1136. int Val(int arr, int index){
  1137.     return arr[index];
  1138. }
  1139.  
  1140. //Increases the value of index 'index' of array 'arr' by +1
  1141. void Inc(int arr, int index){
  1142.     arr[index]++;
  1143. }
  1144.  
  1145. if we want to know the value of LINK_LAST_X in the array LinkStatsAndvariables[], we call this as:
  1146.    
  1147. Val(LINKSTATS,LAST_LINK_X);
  1148.  
  1149. In use, :
  1150. if ( Val(LINKSTATS,LAST_LINK_X) > 20 )
  1151.    
  1152.  
  1153. COmpare the length of that in characters to:
  1154. if ( LinkStatsAndVars[LAST_LINK_X] > 20 )
  1155.    
  1156.  
  1157. or Inc(LINKSTATS,LAST_LINK_X);
  1158.  
  1159.  
  1160. You'll see that we save a few characters, so you may do this with arrays tha have long names, for which you wish to add an abbreviation to call in your statements, or instructions.
  1161.  
  1162. In addition, it means that when we are writing out instructions, we will only need to use parens, not braces '[ ]'
  1163. in our instructions. This has the benefit of not needing an additional character, so that there is a smaller chance of error. It's also faster, as in a statement,
  1164. we usually have fingers hovering over left and right parens, not (square) braces, so every time you need to call something, you save a second, or two. Those truly do add up fast.
  1165.  
  1166. Each Index, One Constant
  1167.  
  1168. Returning Values With Functions
  1169.  
  1170. Creating Functions for this Application
  1171.  
  1172. Reading our Values
  1173.  
  1174. Setting Values
  1175.  
  1176. Value Increments (use of '++' and '--')
  1177.  
  1178. const int LESS = -214747.9999;
  1179. const int PLUS = 214747.9999;
  1180.  
  1181.  
  1182. ----------------------------------------
  1183. Part II: Initialising Values as Non-Zero
  1184. ----------------------------------------
  1185.         Ensuring that these arrays can be read in a modified script set.
  1186.         Initialising as an arbitrary value to know if an array index has been set.  
  1187.        
  1188. One thing that it is prudent to do, if you want to be able to easily call boolean checks against indices in arrays that
  1189.     you make for this purpose, is to set all of their indices to an arbitrary value that you are unlikely to ever have in a game.
  1190.        
  1191.     For example: -20967.3812
  1192.     It's very unlikely that this EXACT value will ever be used.
  1193.     Thus, if you declare the array, and set all of its indices to this value as part of your standard format, you can then
  1194.         declare a constant:
  1195.     const float NOT_INIT = -20967.3812;
  1196.    
  1197.     Than, in your scripts, if you need to check if an array value was never used, where it could be '0' either way, you can
  1198.         check against this value first:
  1199.     if ( MyArray[INDEX] == NOT_INIT )
  1200.         If it matches that value, it was never used, and you can set it to '0' to establish it as an initial false return, or any other vakue that
  1201.     you desire, which will return true.
  1202.    
  1203.     You can easily initialise these values in a global ~init script with one for loop:
  1204.        
  1205.     for ( int q = 0; q < SizeOfArray(MyVars); q++ ) MyVars[q] = NOT_INIT;
  1206.    
  1207.     THis will set all the indices to the desired values when the game loads.
  1208.    
  1209.     This becomes very important when you have general checks that return if an index is 0/false.
  1210.        
  1211.     For example, if you make an entry for : ... //! [[ADD EXPLANATION AND EXAMPLE ]]
  1212.    
  1213.     Then, in a global active script, you can check if a value that you added by adding a constant was never initialised,
  1214.         and if so, set it to whatever value you want it to be as a starting value, or otherwise work with it.
  1215.            
  1216.         This becomes important
  1217.        
  1218.  
  1219. /*
  1220. ----------------------------------------------------------
  1221. Part III : Abusing Screen->D In The Stead of Global Arrays
  1222. ----------------------------------------------------------
  1223. */
  1224.  
  1225. But what if you are modifying a project, where you would need a global array, but you didnlt originally have one? Clearly,
  1226.     you can;t just add one in, and expect it to work, as it would require a global register to be reserved in the save file,
  1227. and as that won;t be reserved, the array will never have a valid pointer in operation.
  1228.  
  1229. If we are already locked into a project, that did not have a vast global array, adding one is effectively the same as adding a global variable.
  1230. People with saved games, can;t use the update, without starting over. Thus, if you failed to plan ahead for this contingency, there is a somewhat cheap way around it.
  1231.  
  1232. How do we do this, if we CANNOT use global arrays?!
  1233.    
  1234. The way to handle this is easy, or well, at least, simple.... What is that, you ask?
  1235.  
  1236. Why, Screen->D of course.
  1237.  
  1238. Every screen on every DMap of a game, has its own array, and these are both accessibly globally, and are *retained through saved games* (unlike local arrays, and most other screen properties).
  1239.  
  1240. If we need to store some new values, and don;t wish to invalidate save files, we can abuse these to meet that need.
  1241.  
  1242. Simply find some screens on any DMap that have free Screen->D[registers], and use those. This works in muc the same way as using a global array, except that it's more logical to make functions specifically to return values from them.
  1243.  
  1244. Let's say that we need to store a variable for a script that makes Link jump, on a timer.
  1245.  
  1246. ordinarily, we could use int JumpTimer; or an array index in a global array to store this, but as we don;t have that luxury, a ZScript function--SetDMapScreenD(), and its counterpart GetDMapScreenD() will help us store, and retrieve that information.
  1247.  
  1248. Using the above example, we make some constants:
  1249.  
  1250. const int JUMP_TIMER_REG_DMAP = 0; //DMap 0
  1251. const int JUMP_TIMER_REG_SCREEN = 79; //Screen 79 of DMap 0
  1252. const int JUMP_TIMER_REG = 3; //Screen->D[3] on Screen 79 of DMap 0
  1253.  
  1254. Now, to set this value, or read it, we make a function set:
  1255.  
  1256. void SetLinkJumpTImer(int value){
  1257.     Game->SetDMapScreenD(JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG,value);
  1258. }
  1259.  
  1260. int GetLinkJumpTimer(){
  1261.     return Game->GetDMapScreenD((JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG);
  1262. }
  1263.  
  1264. int GetLinkJumpTimer(int value){
  1265.     return Game->GetDMapScreenD((JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG) == value;
  1266. }
  1267.  
  1268. int IncLinkJumpTimer(){
  1269.     int val = Game->GetDMapScreenD((JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG) += 1;
  1270.     Game->SetDMapScreenD(JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG,val);
  1271. }
  1272.  
  1273. int IncLinkJumpTimer(int amount){
  1274.     int val = Game->GetDMapScreenD((JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG) += amount;
  1275.     Game->SetDMapScreenD(JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG,val);
  1276. }
  1277.  
  1278. int DecLinkJumpTimer(){
  1279.     int val = Game->GetDMapScreenD((JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG) -= 1;
  1280.     Game->SetDMapScreenD(JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG,val);
  1281. }
  1282.  
  1283. int DecLinkJumpTimer(int amount){
  1284.     int val = Game->GetDMapScreenD((JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG) -= amount;
  1285.     Game->SetDMapScreenD(JUMP_TIMER_REG_DMAP,JUMP_TIMER_REG_SCREEN,JUMP_TIMER_REG,val);
  1286. }
  1287.  
  1288. Now, in any script that we have, in which we need to manipulate this data, we can quick;y call it:
  1289.  
  1290. if ( GetLinkJumpTimer(16) )
  1291.     that would return true if our faux globsal variable is exactly 16
  1292.        
  1293. if ( GetLinkJumpTimer() > 16 )
  1294.     this would evaluate true if it is 16 or more.
  1295.        
  1296. Example in use:
  1297.    
  1298.     const int JUMP_TIME =
  1299. if ( GetLinkJumpTimer() > 1  ) {
  1300.     if ( Link->Jump > 0 ) Link->Jump = 0;
  1301.     //prevent Link from Jumping again until the cooldown timer expires.
  1302.     DecLinkJumpTimer();
  1303.     //If the timer has a value over 0, decreases the timr by '1' every frame.
  1304. if ( GetLinkJumpTimer <= 0 && Link->Jump > 0 ) SetLinkJumpTimer(JUMP_TIME);
  1305.     //If Link is jumping and the timer is 0, allow the jump, and set the timer to '100'.
  1306.  
  1307.  
  1308. To ensure the highest level of compatibility, we occasionally want to insure that registers do not old values of exactly 0. This allows us to know, each time that we load the game
  1309.     if the register was previously used. To do this, we set its value inside the run function of the global active script, to -1.
  1310.         Then, if it is -1, we know it was never initialised.
  1311.            
  1312.        
  1313.  
  1314. /*     
  1315. ---
  1316.         Part IV :
  1317.         What We Can Always Add
  1318.         */
  1319.        
  1320.         Another important thing to remember, is that you can always add instructions to any script, including *local variables, or arrays of any type*
  1321.         without invalidating a svae file. You may add anything inside the run() function in any script, and not need to worry about it.
  1322.         Furthermore, you may add new functions--including global functions, and constants, without invalidating a quest save file.
  1323.        
  1324.         Functions are store in the .qst file and accessed directly, while constants are converted to their numeric literal, during compilation.
  1325.            
  1326.         This may, or may not be true of headers, as headers may create their own global variables, and arrays.
  1327.        
  1328.         For headers, see Chapter V.
  1329.        
  1330.        
  1331.     /*
  1332.         Chapter V: Modifying Headers
  1333.     */
  1334.        
  1335.         If you decide to add a header to a quest, you may encounter the same problem as adding a global variable,
  1336.         except it is multipled many times, based on the number off vars that the header uses, and the complexity of the header.
  1337.        
  1338.         In this case, the only solutution is to modify the header, pointing it to global arrays, or Screen->D.
  1339.            
  1340.         If it uses arrays, or vars that do not need to be global, you can also declare arrays inside the run function in your global active script, and make
  1341.             data handling functions to push values into those.
  1342.            
  1343. //******************************************************************************
Add Comment
Please, Sign In to add comment