|★ CODING ★ 12. STRUCTURAL OVERVIEW OF AKS ★|
|Writing Adventure Games on the Amstrad - 00 - Contents||Writing Adventure Games on the Amstrad - 12 - Structural overview of AKS|
The first stage of the program requires it to initialise all the variables it is going to use. This is done by the three routines Invocations, Initobjects, and Initevents. As their names suggest, they initialise all the variables associated with the locations, the objects, and the events respectively. We'll look at each of these separately.
This routine starts at the beginning of the data, with location 0, and it searches through the data, until it encounters a data declaration for an object, event or the F end marker. While it is scanning through these data declarations, the location numbers declared in the "L, < locnum >" command are noted, and any missing or out of order locations are reported. The array element “locline(loc)" is set to the program line at which the data for the location "loc" begins. This process of scanning continues until a non-location declaration is found, at which point "nooflocs" is set to the number of locations found, and the routine returns to the main initialisation.
This routine works in a similar way to Invocations, except that it deals with Objects, and it scans until it finds a declaration for an Event or the "F" end marker. During this scanning process, the array element l,objline(obj)" is set to the data line which begins the declaration for the object “obj", in the same way as “locline(loc)" is used. Any out of order, or missing object numbers are reported by the system, and the scanning is stopped.
This initial scanning process also allows us to find the start locations for each of the objects and to set their position in the “objloc(obj)" array where all the object positions are stored. When the scanning search for objects has finished, the “noofobjs" can be set, and the routine returns to the main initialisation section.
This initialisation routine deals with any events which may have been declared at the end of the data block, and sets the array “eventlin(cnt)" to mark the data line at which each event begins. The search for events finishing when the "F" end marker is encountered.
After initialising the locations, objects and events, all the program flags are initialised by Resetflags, which simply sets all of them to the value FALSE. This then completes all the initialisation the program has to do.
MAIN PROGRAM LOOP
This is a very simple WHILE loop, which runs continually until the flag "eogame” becomes TRUE, whereupon the game ends. This flag is obviously only set by the player losing, winning or quitting the game. Thus, the loop consists of five different operations:
(a) Describing the current location.
The second action is simply a case of setting a flag corresponding to the current location in the visited array to TRUE, which is very straightforward. The other routines are a little more complex, so we will consider these separately.
This is the routine which prints out the appropriate description for the current location, in a neat and formatted form. The first step is to find the data line at which the player's current location is defined, and then to call Describeln, to print out the current description. We then test to see if any objects are at the current location. If there are objects here, we search through all the objects, printing out the object descriptions for all objects at this location. This done, we return to the main loop.
This routine searches through the data statements for the current location, looking for description data (beginning with a “D”), until it reaches the start of another location, object or event. Once it finds a line of description, the condition attached to that description has to be evaluated, and this is done with a call to the expression evaluator (which is covered in great detail in the next chapter). If the condition evaluates to TRUE, we can then print the description by calling Print-descr, and carry on searching for the next one.
This is the routine which performs the actual act of printing the location description to the screen. This is not a simple matter of printing out the text in a straightforward fashion. If we did this, some words would overlap the edge of the screen, being split across two lines. This would not only be difficult for the player to read, but it would give our adventure games a very untidy, messy appearance. A lot of the appeal of the adventure game is the way in which it is presented to the player. A slapdash, untidy presentation only a discourages the player from bothering with the game.
The printdescr routine gets around this problem by making sure that words are not split over two lines, and that punctuation is not put in at the beginning of a line. This is done by taking the description string a screen width's worth at a time, e.g. if our screen width is 80 characters, and we have already printed the first 10 characters on the current screenline, we consider the next 80 characters of the description. This is then checked to see if the end of the string occurs in the middle of a word, if it does, we search backwards through the string to find the end of the previous word. The string can now be printed up to that point. We then consider the next 80 characters from this point and so on, until the string left is less than (or equal) to the screen width when we simply print it, and return from the routine.
This printing routine is very general-purpose and not just specific to the AKS program, so you could easily use it to present neat, wordwrapped output in your own programs.
This is a very short and simple routine, which prompts the player with the question "What now?". The player's command line is then input into the variable "in$". Obviously, we cannot provide for all the possible combinations of upper and lowercase which the player might type in, so we use LOWERS to convert the input string to be totally lowercase. This does mean that all the commands, and object name which you include in the object and location declarations must all be in lowercase also. But, this is small price to pay in return for faster processing of the command line.
This is the most important routine in the program, in many ways, as it checks the player input against the database and causes actions to be performed if matches have been found. The first stage is to search for triggers in current location, and then in the global location; matching the input string against the trigger phrases. It is worthwhile noting at this point that it is important to order trigger phrases in the correct order — substrings after the main string. By this, we mean that "FEED THE DUCKS” should come before "FEED”, "WIND UP THE CLOCK" should come before "WIND UP” and so on. If you fail to do this, then the main phrases will never be activated, due to the search method that the Triggertest routine uses. If no triggers are found then the "Sorry I do not understand that” message is printed and control returns to the main loop. If the command is recognised, then the Actions routine is called to carry out the required action.
This routine steps through the current location data (until it reaches a new declaration for location, object or event) searching for Trigger commands. If a command is found, it is tested by the routine triggertest.
This routine attempts to match the command line with the trigger phrases for the current trigger. This is done by scanning the trigger line until the end marking is found, which marks the start of the condition. A test is made for each of the trigger phrases to see if it occurs within the command line (using the string function INSTR). If a match is made, the condition for that trigger is evaluated. Only if there is a match and the condition evaluates to TRUE, is success reported back to Triggers and PROCESSCOMLINE.
This routine simply scans down the list of actions which follow a Trigger command. The type of action is read in, and then the appropriate action routine is called, depending on the action. When all the actions have been read in, the routine returns to the main loop. Each of these actions then performs the required tests and manipulations to carry out their function, before returning to the main action routine. If you wished to add more actions to the AKS system, it is simply a matter of adding a new test on "act$" inside this routine's WHILE loop, with a GOSUB to your new action routine.
This routine steps through all the counters possible and tests to see if they are currently counting. If a counter is active, then it is decremented by one. If the counter value is still above 0, nothing further is done, and the routine returns to the main program loop.
However, if the counter has reached a value of zero, the counter is reset to a non-counting state and the appropriate event is activated. This is done by setting the current dataline to the start of that event, and then calling the Actions routine to step through and perform the required actions. Once this is complete, the main program loop is resumed.
That completes the main program structure. You should find all the above routine descriptions relatively easy to follow, using the comments in the program as a guideline. Many of the routines described in this chapter can quite easily be used in other programs, not just adventure games. The pretty printing abilities of Printdescr being just one such example, along with the whole method of data searching employed by AKS.