home *** CD-ROM | disk | FTP | other *** search
/ Ian & Stuart's Australian Mac 1993 September / September 93.iso / Archives / Games / Strategy / Puzzle / GameMaster / GM Dev Kit / Programming Manual / Programming GM Rule Books (txt) < prev    next >
Encoding:
Text File  |  1991-12-08  |  47.7 KB  |  789 lines  |  [TEXT/nX^n]

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8. GameMaster Release 1.0
  9.  
  10.  
  11. Programming GameMaster Rule Books
  12.  
  13. or
  14. The Quotations of the Prophet Rhys
  15.  
  16.  
  17.  
  18. Version 1.0b3
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28. Rhys Hollow and Quinn
  29. 2 December 1991
  30.  
  31.  
  32. Table of Contents
  33.  
  34.     Table of Contentsi
  35. 1.    An Introduction to GameMaster 1
  36. 1.1.    Network Capabilities 1
  37. 1.2.    The Purpose of this Manual 1
  38. 1.3.    The History of GameMaster2
  39. 1.4.    Contacting the Authors 2
  40. 2.    An Overview of Rule Books3
  41. 2.1.    Game Events 3
  42. 2.2.    Rule and Game Data Structures 3
  43. 2.3.    Game Display 4
  44. 2.4.    Game Dialogs 5
  45. 2.5.    Game Environment 6
  46. 2.6.    Rule Books and Memory 6
  47. 2.7.    Version Numbering6
  48. 3.    The Resources 8
  49. 3.1.    Resource ID Allocation 8
  50. 3.2.    Required Resources 8
  51. 3.3.    The GMRZ Resource 9
  52. 3.4.    Optional Resources 12
  53. 4.    The Game Event Record13
  54. 5.    The Game Events17
  55. 5.1.    Notation Conventions17
  56. 5.2.    Rule Book Open/Close Events18
  57. 5.3.    Game Open/Close Events19
  58. 5.4.    Game Communications Events22
  59. 5.5.    Game Control Events 23
  60. 5.6.    Game Input Events25
  61. 5.7.    Game Return Events28
  62. 6.    Hints and Tips31
  63. 6.1.    GrafPorts, Dialogs and Windows31
  64. 6.2.    Drawing31
  65. 6.3.    Accessing QuickDraw Globals31
  66. 6.4.    Debugging32
  67. 6.5.    Standard State32
  68. 6.6.    Rule Book Miscellanea 32
  69. 6.7.    How GameMaster Connects Games33
  70. 7.    The GameMaster Distribution35
  71. 7.1.    The GameMaster User’s Kit35
  72. 7.2.    The GameMaster Developer’s Kit35
  73. 1.    An Introduction to GameMaster
  74.  
  75. GameMaster is a Macintosh application that makes it very easy to create two player, network capable games.  The principle behind GameMaster is very simple.  GameMaster itself is a standard Macintosh application that handles all of the usual drudgery associated with programming a Macintosh.  All that is required of the game author is to create a file that encapsulates the rules and display of the game.  The file is known as the rule book.  These rule book files are placed in the same folder as GameMaster.
  76. When running GameMaster the user sees a menu that allows them to start  a game using any of the available rule books.  Thus each game is played according to a particular rule book.  A window is created in which the game is displayed.  The exact operation of this game window is determined by the rule book.
  77.  
  78. 1.1.    Network Capabilities
  79.  
  80. Any properly written GameMaster rule book will automatically generate network capable games.  The rule books have a very simple model of networking.  GameMaster handles all the usual horror associated with network programming.
  81. GameMaster uses both AppleTalk (using ADSP) and MacTCP to communicate with other GameMasters running on other Macintoshes.  The MacTCP alternative was provided because it allows for world-wide game play.
  82.  
  83. 1.2.    The Purpose of this Manual
  84.  
  85. This manual was written to help Macintosh programmers learn how to write GameMaster rule books.  It is provided bundled with a number of other files as the GameMaster Developer’s Kit.  The rest of this manual is dedicated to describing exactly what GameMaster expects of its rule books.
  86.  
  87. 1.3.    The History of GameMaster
  88.  
  89. GameMaster was written by Rhys Hollow (gurhs@uniwa.uwa.oz.au) in late 1991.  The AppleTalk support was written by Marcus Jager (marcus@cs.uwa.oz.au).  The MacTCP support is courtesy of Peter Lewis (nlewispn@cc.curtin.edu.au).  This manual was written by Quinn (quinn@cs.uwa.oz.au) and based on Rhys’ programming documentation.  The game rules were written by Rhys (Othello, BattleShips, Connect 4), Peter (Simple Talk, File Transfer, Global Thermonuclear War, Derf Ball, Dots and Boxes), and Quinn (Chess and TicTacToe).
  90. GameMaster was written in Think Pascal 4, with help from ResEdit 2.1.1.  The program was mostly developed on a Macintosh Classic.  The documentation was written in WriteNow 2.2.
  91.  
  92. 1.4.    Contacting the Authors
  93.  
  94. If you wish to contact the author of GameMaster you can Email Rhys at his address above.  Please make sure you include the word GameMaster in the subject line.
  95. If you are reporting bugs (Bugs!  What bugs!) please quote the system on which you were running (ie hardware, system software, extensions) and the version of GameMaster you were using.  Please report bugs in rule books to the author of the rule book.
  96. Please send any comments regarding this manual to Quinn at the address above.
  97.  
  98. 2.    An Overview of Rule Books
  99.  
  100. A GameMaster rule book is a file of type ‘GMRB’ and creator ‘GMST’.  Its data fork should be empty.  The resource fork contains a number of fixed resources that describe the game, a resource that contains the code that embodies the game rules, and an arbitrary number of rule book resources.  A more thorough description of the resource fork’s contents can be found in Chapter 3.
  101.  
  102. 2.1.    Game Events
  103.  
  104. GameMaster works on the following principle.  When launched (and subsequently when idle) it creates a list of all the rule books in the folder from which it was launched.  The user can then select New Game and choose the rule book that they would like to use.  GameMaster opens the rule book’s resource fork, checks it for integrity, and then loads the rule book’s code.  From then on the dialog between GameMaster and the rule book is based around GameMaster calling the rule book whenever certain events happen.  Unlike a standard Macintosh application, the rule book is not called for trivial events.  GameMaster only calls the rule book for events that it can’t handle.  A complete description of each event can be found in Chapter 5.  In addition rule books can indicate to GameMaster that they are not interested in specific events.
  105.  
  106. 2.2.    Game Data Structures
  107.  
  108. Each time GameMaster calls a rule book it passes a reference to a data structure known as the game event record.  This record contains information telling the rule book what to do.  The rule book can also change the record in order to pass information back to GameMaster.  The structure of the game event record is described in Chapter 4.
  109. The first time it calls a rule book GameMaster allows the rule book to store a 4 byte quantity into the game event record’s globals field.  Every subsequent time GameMaster calls the rule book it will pass back that value in the globals field of the game event record.  This allows the rule book to create a handle or pointer to its global variables and access them on each event.
  110. The rule book is shared between all games played with that particular set of rules.  Each instance of the rules (each game) has a game handle that stores the state of the game.  GameMaster creates an empty handle and stores it in the game field of the game event record.  The rule book is free to grow and shrink this heap block at will.  GameMaster expects that this block of memory will contain all of the relevant state information required to start the identical game on a remote machine or to restore the game from a saved file.  However, GameMaster sends a ge_Flatten event to warn the rule book immediately before it relies on this fact.
  111.  
  112. 2.3.    Game Display
  113.  
  114. For each active game GameMaster creates a modeless dialog window that the game uses to display the game state to the user.  The rule book provides two important data structures used to create this window.  The first is a dialog item list.  GameMaster bases the game’s window on this list.  The second is the width and height of the play area as determine by the ge_InitRuleBook event.  The play area is a rectangle within the game window with its top-left at (0,0) and its width and height determined by the rule book.
  115. GameMaster imposes certain restrictions on the size of the play area brought about by the dialog procedure described in the next section.  Firstly the width of the play area must be more than 135 pixels.  Secondly the play area must fit on the screen.  Unless you want to support calculating the play area at run-time this means that it must fit on the standard 9 inch screen of a Mac Classic (512 by 342).  Given that you lose 20 pixels to the menu bar, 20 pixels to the window title bar, and 40 to 50 pixels to GameMaster’s controls, this means that the maximum height of the play area is about 260.
  116. All game window updates are done through the dialog item list.  The rule book can have control, PICT, ICON, button, checkbox, radio button, static text and edit items text in this list.  These are updated automagically by the Dialog Manager.  The rule book can also have user items for any dynamic components.  The rule book can then set each user item’s update procedure on the ge_NewGame and ge_OldGame events.
  117. Rule books can also add and remove menus from the menu bar and be notified when the user selects these menu items.
  118.  
  119. 2.4.    Game Dialogs
  120.  
  121. GameMaster often needs to interact with the user.  The most common case of user interaction is asking the user whether they would like to play a game.  As we all know ‘modal bad, modeless good’ so GameMaster was designed to support non-modal interaction.  It achieves this by adding a small panel to the bottom of each game window.
  122.  
  123.  
  124. Three states of the Dialog Panel
  125.  
  126. When the game requires user interaction, GameMaster slides down this panel to reveal a message and optionally some buttons.  This panel is used for two purposes.  Firstly it can display a message informing the user of the status of the game (centre).  Secondly it can display a mini-dialog (right).  From there the game does not continue until the user dismisses the dialog by clicking on the buttons.  In both of these states the game is known as dialoging.  When a game is dialoging no user input events are sent to its rule book.
  127. This Dialog Panel affects a rule book in three ways.  Firstly, the rule book designer must take into account the size of this panel when laying out the rule book’s display.  Secondly, the rule book can display its own message and buttons in the panel using ge_Ask events.  Lastly, any real-time rule book should note that when the Dialog Panel is down user input to the game is ignored.
  128.  
  129. 2.5.    Game Event Environment
  130.  
  131. Before sending any events to the rule book GameMaster does the following things:–
  132. •    Calls SetPort to set the current GrafPort to that of the game’s window, except for events that can be sent to a rule book when there is no current game ie ge_InitRuleBook and ge_FinishRuleBook.
  133. •    Calls UseResFile to make the rule book’s resource file the first to be searched.
  134. •    Fills out the appropriate fields in the game event record
  135. Note that there may be other resource files (specifically GameMaster’s and other rule books’) in the resource search path.  If the rule book modifies its resource fork (definitely not recommended) then it must be certain that it only modifies its own resources.
  136.  
  137. 2.6.    Rule Books and Memory
  138.  
  139. The rule books share their heap with GameMaster.  Thus the rule books must be careful about the amount of memory that they allocate.  As part of its required structure a rule book must contain a resource that describes how much memory it uses, both to load the rule book and to start a new game using that rule book.  GameMaster prevents the user from playing a game unless there is sufficient memory to fulfil these requirements.  However, this does not mean the rule book will never run out of memory because other rule books might overrun their memory allocations.
  140.  
  141. 2.7.    Version Numbering
  142.  
  143. GameMaster rule books can be connected to one another across networks.  It would be very bad if GameMaster connected two incompatible version of a rule book.  To prevent this the rule book indicates to GameMaster the following version numbers:–
  144. •    The rule book version number.
  145. •    The oldest version number of the rule book to which this rule book can be safely connected.
  146. •    The version number of the GameMaster that this rule book was written for.
  147. •    The oldest version of GameMaster with which this rule book can be used.
  148. Each version number is an integer (two bytes).  This integer is interpreted as a 4 digit BCD number.  The first nibble is ignored and should be zero.  The second nibble is the major version number, the third the minor version number and the fourth the bug fix version number.
  149. When GameMaster displays version numbers (such as in the about box) it displays each of these nibbles in decimal separated by a period (‘.’).  For example a version number $0123 yields version 1.2.3.
  150. 3.    The Resources
  151.  
  152. The resources in a rule book’s resource fork can be divided into three classes:  required resources, optional resources and rule book resources.  Much of the rest of this chapter is dedicated to describing the required resources, in particular the GMRZ resource.   The final section details the optional resources.  Rule book resources are entirely rule book dependent.  However, it is necessary to first describe how required, optional and rule book resources are distinguished.
  153.  
  154. 3.1.    Resource ID Allocation
  155.  
  156. The resource IDs less than 128 are reserved by the system.
  157. The resource IDs between 128 and 999 are reserved for use by GameMaster.  This leaves the resource ID range 1000 to 32767 for use by the rule book resources.
  158. Note that all required and optional resources have IDs between 128 and 999.  Also note that the rule book may contain certain system resources with IDs outside of the allocated range, for example ‘vers’ 1 and 2, ‘ICN#’ -16455.
  159.  
  160. 3.2.    Required Resources
  161.  
  162. GameMaster requires that the rule book contain the following resources:–
  163.  
  164. ‘GMRZ’ 128
  165. This resource contains important information that GameMaster needs to know about each rule book.  Its structure is quite complex and it is described in detail in the next section.
  166.  
  167. ‘PROC’ 128
  168. This resource is the code that makes up the core of the rule book.  The resource must start with a procedure with the following interface:–
  169.  
  170. procedure MyGameRules(var gameevent : gameEventRecord);
  171.  
  172. Naturally the procedure identifier (ie MyGameRules) is unimportant.
  173.  
  174. ‘DITL’ 128
  175. This dialog item list controls the overall layout of the window associated with any game that uses this rule book.  The item list can contain any of the standard dialog items.  When GameMaster starts a new game it creates a modeless dialog based on this DITL.  GameMaster adds its own items after the rule’s items.  Thus the rule book should not rely on the length of the item list.
  176. Note that this DITL resource should be purgeable.  Also note that any secondary resources (PICTs, ICONs, etc) are rule book resources and must have resource IDs greater than 1000.
  177.  
  178. 3.3.    The GMRZ Resource
  179.  
  180. The GMRZ has a large number of fields of which the rule book designer should be aware.  Each of the fields of the GMRZ resource is described in turn below.  Note that the type identifiers are taken from the ResEdit 2.x template supplied as part of the GameMaster Developer’s Kit.
  181.  
  182. PO3F    Name of the Game
  183. This string represents the name of the game that the rule book plays.  It is used to display the name of the game in the about box.
  184.  
  185. PO7F    Author
  186. Your name.  Wow, you’re world famous!
  187.  
  188. PO7F    Copyright String
  189. This string is provided so you can legally retain your claim to fame.
  190.  
  191. PO3F    ID String
  192. This string identifies the rule book to GameMaster.  This string is used when GameMaster connects with another GameMaster across the network.  It is compared with the corresponding string on the remote machine to make sure that the rule  books are the same.  This is important because the user can rename rule books from the Finder and connecting two games based on different rule books would be bad!
  193.  
  194. HWRD    Rule Book Version
  195. This word is the version number of the rule book.  The format of this (and subsequent) version numbers was described in Section 2.7.
  196.  
  197. HWRD    Required Rule Book Version
  198. This word is the oldest version of this rule book with which this rule book can safely be connected.
  199.  
  200. HWRD    GameMaster Version
  201. The version number of GameMaster under which this rule book was developed.
  202.  
  203. HWRD    Required GameMaster Version
  204. The oldest version of GameMaster with which this rule book can be safely used.
  205.  
  206. DLNG    Rule Book Partition
  207. The number of bytes that must be free for this rule book to be used.  If there are no games currently being played with this rule book, GameMaster adds this number to the Game Memory Partition to determine the total amount of space required for a new game.
  208.  
  209. DLNG    Game Memory Partition
  210. The number of bytes taken by each new game using this rule book.
  211.  
  212. BBIT    Disallow Save
  213. If this bit is set then the user will not be able to save games played with this rule book.  Note that unless the Disallow Connect bit is also set the rule book can still be sent ge_OldGame events as part of the network connect process.
  214.  
  215. BBIT    Disallow Connect
  216. If this bit is set then the user will not be able to connect games made with this rule book.  You can set this for rule books that do not support networking.
  217.  
  218. BBIT    Disallow Restart
  219. If this bit is set then the user will not be able to restart games played with this rule book.
  220.  
  221. BBIT    Disallow Swap
  222. If this bit is set then the user will not be able to swap games played with this rule book.  Swapping is only possible when two games are connected and it involves changing which player the local machine is playing.  Note that unless the Disallow Connect bit is also set the rule book can still be sent ge_Swap events as part of the network connect process.
  223.  
  224. BBIT    No Idle Events
  225. If this bit is set then no ge_Idle events will be sent to this rule book.  Idle events are only useful if you wish to animate your display or write a real-time game.  Most rule books can safely set this bit. 
  226.  
  227. BBIT    No SleepQuery Events
  228. If this bit is set then no ge_SleepQuery events will be sent to this rule book.  ge_SleepQuery events are used to time the idle events and change the shape of the cursor.  Set this if you never change the shape of the cursor and you do not require idle events.
  229.  
  230. BBIT    Reserved0
  231. This bit is reserved for future expansion and should be clear.
  232.  
  233. BBIT    Reserved1
  234. This bit is reserved for future expansion and should be clear.
  235.  
  236. DBYT    Reserved2
  237. This bit is reserved for future expansion and should be clear.
  238.  
  239. DWRD    Reserved3
  240. This bit is reserved for future expansion and should be clear.
  241.  
  242. 3.4.    Optional Resources
  243.  
  244. ‘PICT’ 128
  245. If this picture is present GameMaster will put it in the rule book’s about box.  The picture must be wider than 200 pixels and otherwise such that the about box fits on the screen.
  246. 4.    The Game Event Record
  247.  
  248. The game event record has the following structure:–
  249.  
  250. gameEventRecord =
  251.     record
  252.         event: gameEventType;
  253.         game : Handle;
  254.         globals : Handle;
  255.         where : Point;
  256.         modifiers : integer;
  257.         message : Str255;
  258.         but1, but2 : buttonStr;
  259.         int1, int2 : integer;
  260.         long1 : longint;
  261.         modified : boolean;
  262.         myturn : boolean;
  263.         version : integer;
  264.         dialoging: boolean;
  265.         dumspace: array[1..10] of longint;
  266.     end;
  267.  
  268. The following sections describe each field in detail.
  269.  
  270. event : gameEventType;    { Read/Write }
  271. The event field is set appropriately every time GameMaster calls the rule book.  The value of the event field determines the semantics of the message passed to the rule book.  The definition of gameEventType is as follows:–
  272.  
  273. gameEventType = (ge_InitRuleBook, ge_FinishRuleBook,
  274.     ge_NewGame, ge_OldGame, ge_Swap,ge_Restart, ge_Close,
  275.     ge_Activate, ge_Deactivate, ge_UpdateMenus, ge_Menu,
  276.     ge_MouseDown, ge_KeyDown, ge_Idle, ge_SleepQuery,
  277.     ge_ConnectionMade, ge_MessageReceived, ge_ConnectionLost,
  278.     ge_Ask, ge_Answer, ge_Error, ge_SendMessage,
  279.     ge_Flatten);
  280.  
  281. The semantics of each of the values of the event field is discussed in Chapter 5.  The rule book can also modify the event field in order to tell GameMaster to perform certain actions.
  282. If the rule book receives any game event records with the event field set to a value not in this list then it should ignore the event.
  283.  
  284. game : Handle;    { Read Only }
  285. The game field holds a handle to the a heap block that the rule book uses to store the state information (or game record) for each game.  The rule book must cast the handle to the appropriate type of its game storage.  The handle is both allocated and deallocated by GameMaster but the rule book is free to resize it at will.  If the game field is nil then this game record has not yet been created or has already been destroyed.
  286.  
  287. globals : Handle;    { Read/Write }
  288. The globals field can be used by the rule book to hold any arbitrary data it requires.  Normally this would be either a pointer or a handle to its global data.  The rule book must cast the globals handle to the type of the globals storage that it uses.  The globals field is the same for every game played with the rule book.  The only time that the rule book is called without a valid globals field in the game event record is on ge_InitRuleBook at which time it is expected to set it up.
  289.  
  290. where : Point;    { Read Only }
  291. The where field is used to communicate the coordinates of the mouse in local coordinates on certain events.
  292.  
  293. modifiers : integer;    { Read Only }
  294. The modifiers field is used to communicate the current modifiers to the rule book on certain events.  The bits in the field have the same meaning as the modifiers field in the Toolbox EventRecord.
  295.  
  296. message : str255;    { Read/Write }
  297. The message field is used to hold the text of messages displayed to the user and also messages sent between rule books connected via the network.  The length of the string in the message field should always be less than 250 characters.
  298.  
  299. but1, but2 : buttonStr;    { Read/Write }
  300. The but1 and but2 fields are used to hold the labels for buttons that the rule book can get GameMaster to display using the ge_Ask event.  The buttonStr type is defined as follows:–
  301.  
  302. buttonStr = string[7]
  303.  
  304. The buttons are of a fixed size and you should make sure that your button strings fit.
  305.  
  306. int1, int2 : integer;    { Read/Write }
  307. The int1 and int2 fields are used to hold generic integers.  The semantics of these fields is event dependent.
  308.  
  309. long1 : longint;    { Read/Write }
  310. The long1 field is used to hold a generic longint.  The semantics of this field is event dependent.
  311.  
  312. modified : boolean;    { Read/Write }
  313. The modified field is used to keep track of whether the game referenced by the game event record has been modified since it was last saved.  Set this field to true if you modify anything important in the game state.
  314.  
  315. myturn : boolean;    { Read/Write }
  316. The myturn field is used to keep track of whether GameMaster should send you certain events.  If the game has not been connected then the rule book should set myturn to true to receive all events.  If the game has been connected then the state of myturn determines which game is in control.  If myturn is false then GameMaster will not send you the following events:–
  317. •    ge_MouseDown
  318. •    ge_KeyDown
  319. The reason why myturn exists is to prevent all sorts of horrible race conditions while the connection is being arbitrated.
  320. If the local game is in control then the local myturn field will be true.  If the remote game is in control then the local myturn field will be false.  If both games want control then both games should have myturn set.  It would be extremely unusual for both games to have myturn false.
  321.  
  322. version : integer;    { Read Only}
  323. The version field holds the version number of the GameMaster that is executing this rule book.  The format is the same as that described in Section 2.7 and used in the GMRZ resource.
  324.  
  325. dialoging : boolean;    { Read Only}
  326. This boolean is true if GameMaster was displaying a dialog at the time it sent the event to the rule book.  If you try to display a dialog while GameMaster is already displaying your dialog will not be displayed.
  327.  
  328. dumspace : array [1..10] of longint;    { no access }
  329. The dumspace field provide space for future expansion of the GameMaster/rule book interface.  You should neither change nor rely on the contents of this array.
  330. 5.    The Game Events
  331.  
  332. When GameMaster detects certain conditions, or requires specific information, it sends the rule book an event.  The meaning of each event is determined by the event field of the game event record.  The events can be grouped into 6 classes:  Rule Book Open/Close, Game Open/Close, Game Communications, Game Control, Game Input, Game Return.
  333.  
  334. 5.1.    Notation Conventions
  335.  
  336. Each event described has a summary in the following form:–
  337.  
  338. gameEventRecord
  339.     field    fieldType    ∫    comment
  340.     field    fieldType    fi    comment
  341.     field    fieldType    ‹    comment
  342.     field    fieldType    €    comment
  343.  
  344. The first column is the field name.  The next column is the type of the field.  The third column shows who is responsible for setting the field.  The last column gives a comment as to what the field contains.
  345. If the field is marked with a congruent (∫) then the contents of that field are set to a particular value.  The rule book can rely on that value being present but must not change it.
  346. If the field is marked with an out arrow (fi) then the rule book must set the field to an appropriate value before returning to GameMaster.  Unless otherwise noted the value of the field on entry is undefined.
  347. If the field is marked with an in arrow (‹) then the field contains an appropriate value on entry.  Unless otherwise noted GameMaster doesn’t care what the value is when you return.
  348. If the field is marked with a bidirectional arrow (€) then GameMaster sets the field to a value which the rule book can use.  The rule book must update (if necessary) that value before it returns.
  349. If the field is missing then the value of the field is undefined and the rule book must neither rely on nor change it.
  350.  
  351. 5.2.    Rule Book Open/Close Events
  352.  
  353. These Rule Book Open/Close Events are special in that they are the only events that can be sent to the rule book without a valid game.  Thus the game field of the game event record is nil and thePort is not set to a game window.
  354.  
  355. ge_InitRuleBook
  356. GameMaster guarantees that the first event it will ever send to a rule book is the ge_InitRuleBook event.  The rule book can store any 4 byte value in the globals field and perform any other initialisation.  Normally the rule book would allocate any global data on the heap and store a pointer or handle to it in the globals fields.
  357.  
  358. gameEventRecord
  359.     event    gameEventType    ‹    ge_InitRuleBook
  360.     version    integer    ∫    GameMaster version
  361.     globals    Handle    fi    handle to rule globals
  362.     int1    integer    fi    play area width
  363.     int2    integer    fi    play area height
  364.     dialoging    boolean    ∫    true if dialog currently displayed
  365.  
  366. On return from this event GameMaster expects that the width of the game’s play area be stored in int1 and the height in int2.
  367.  
  368. ge_FinishRuleBook
  369. GameMaster guarantees that the last event it will ever send to a rule book (before closing its resource fork) is the ge_FinishRuleBook event.  The rule book should use this opportunity to deallocate any global storage it has.  Setting the globals pointer to nil is also a good idea.
  370.  
  371. gameEventRecord
  372.     event    gameEventType    ‹    ge_FinishRuleBook
  373.     version    integer    ∫    GameMaster version
  374.     globals    Handle    ‹    handle to rule globals
  375.             fi    nil
  376.     dialoging    boolean    ∫    true if dialog currently displayed
  377.  
  378. 5.3.    Game Open/Close Events
  379.  
  380. The Game Open/Close Events provide the basic control necessary to start new and old games, restart games and close games. 
  381.  
  382. ge_NewGame
  383. When the user creates a game using the New Game menu item GameMaster sends this event to the rule book.  GameMaster has already created a zero sized handle and stored it in the game field.  The rule book should grow this handle and initialise it to the appropriate initial state.
  384.  
  385. gameEventRecord
  386.     event    gameEventType    ‹    ge_NewGame
  387.     version    integer    ∫    GameMaster version
  388.     game    Handle    ∫    game record handle
  389.     globals    Handle    ∫    handle to rule globals
  390.     modified    boolean    ∫    false
  391.     myturn    boolean    ‹    true
  392.             fi    whether it’s the game’s turn
  393.     dialoging    boolean    ∫    true if dialog currently displayed
  394.  
  395. On return GameMaster expects that the game handle be filled out with an initial game state.
  396.  
  397. ge_OldGame
  398. When the user resumes a saved game GameMaster sends the ge_OldGame event to the rule book.  The game handle has already been filled out with the saved game state.  The rule book should overwrite any fields in the game which are inappropriate to load from disk and return.
  399.  
  400. gameEventRecord
  401.     event    gameEventType    ‹    ge_OldGame
  402.     version    integer    ∫    GameMaster version
  403.     game    Handle    ∫    game record handle
  404.     globals    Handle    ∫    handle to rule globals
  405.     modified    boolean    ∫    false
  406.     myturn    boolean    ‹    true
  407.             fi    whether it’s the game’s turn
  408.     dialoging    boolean    ∫    true if dialog currently displayed
  409.  
  410. The ge_OldGame event is also used as part of the network connect process.
  411.  
  412. ge_Swap
  413. The ge_Swap event is sent to a rule book when the user chooses Swap from the GameMaster menus.  Swapping is only meaningful when two games are connected.  The idea behind swap is that it should change what side the each player is playing.  For example, if Fred was playing Black and Sheila white, then after a swap Fred would be playing White and Sheila black. 
  414.  
  415. gameEventRecord
  416.     event    gameEventType    ‹    ge_Swap
  417.     version    integer    ∫    GameMaster version
  418.     game    Handle    ∫    game record handle
  419.     globals    Handle    ∫    handle to rule globals
  420.     modified    boolean    €    true if the game has been modified
  421.     myturn    boolean    €    whether it’s the game’s turn
  422.     dialoging    boolean    ∫    true if dialog currently displayed
  423.  
  424. The ge_Swap event is also used as part of the network connect process.
  425.  
  426. ge_Restart
  427. The ge_Restart event is sent to a rule book when the user chooses Restart from the GameMaster menus.  If the game is connected then both sides should organise to be playing different players after the restart.
  428.  
  429. gameEventRecord
  430.     event    gameEventType    ‹    ge_Restart
  431.     version    integer    ∫    GameMaster version
  432.     game    Handle    ∫    game record handle
  433.     globals    Handle    ∫    handle to rule globals
  434.     modified    boolean    €    true if the game has been modified
  435.     myturn    boolean    €    whether it’s the game’s turn
  436.     dialoging    boolean    ∫    true if dialog currently displayed
  437.  
  438. The rule should reinitialise the contents of the game handle to that of a new game.
  439.  
  440. ge_Flatten
  441. GameMaster sends the ge_Flatten event to a rule book when it needs to use the contents of the game handle.  Normally this means that the game is about to be saved or sent across the network.  The game handle must remain ‘flat’ until another event is sent to the rule book.
  442.  
  443. gameEventRecord
  444.     event    gameEventType    ‹    ge_Flatten
  445.     version    integer    ∫    GameMaster version
  446.     game    Handle    ∫    game record handle
  447.     globals    Handle    ∫    handle to rule globals
  448.     dialoging    boolean    ∫    true if dialog currently displayed
  449.  
  450. If the game state as recorded in the game handle is out of date then the game should update the game handle.  The ge_Flatten event gives the rule book writer the option of storing pointers in the game record for efficiency while still allowing games to be saved and connected across the network.
  451.  
  452. ge_Close
  453. GameMaster sends the ge_Close event to a rule book when the user closes one of its game windows.
  454.  
  455. gameEventRecord
  456.     event    gameEventType    ‹    ge_Close
  457.     version    integer    ∫    GameMaster version
  458.     game    Handle    ∫    game record handle
  459.     globals    Handle    ∫    handle to rule globals
  460.     dialoging    boolean    ∫    true if dialog currently displayed
  461.  
  462. This event is simply a chance for the rule book to do any cleaning up not covered by other events.  Specifically, if the rule book stores pointers in the contents of the game handle (and only there) it should note that the game handle is about to be disposed.
  463.  
  464. 5.4.    Game Communications Events
  465.  
  466. The Game Communications Events provide the framework by which rule books on separate machines can talk.
  467.  
  468. ge_ConnectionMade
  469. The ge_ConnectionMade event is sent to a rule book when the game referenced in the game event record is connected to to a remote game.  GameMaster uses the game state stored in the game record to ensure that both the local and remote players are playing the same game.
  470.  
  471. gameEventRecord
  472.     event    gameEventType    ‹    ge_ConnectionMade
  473.     version    integer    ∫    GameMaster version
  474.     game    Handle    ∫    game record handle
  475.     globals    Handle    ∫    handle to rule globals
  476.     modified    boolean    ∫    true if the game has been modified
  477.     myturn    boolean    ‹    true
  478.             fi    whether it’s the game’s turn
  479.     dialoging    boolean    ∫    true if dialog currently displayed
  480.  
  481. The game should update the game handle to allow for the fact that messages may now be sent and received.
  482.  
  483. ge_ConnectionLost
  484. The ge_ConnectionLost  event is sent to a rule book when the connection to another game is lost for some reason, possibly network troubles, the remote machine has crashed, or the other player has just closed his end.
  485.  
  486. gameEventRecord
  487.     event    gameEventType    ‹    ge_ConnectionLost
  488.     version    integer    ∫    GameMaster version
  489.     game    Handle    ∫    game record handle
  490.     globals    Handle    ∫    handle to rule globals
  491.     modified    boolean    ∫    true if the game has been modified
  492.     myturn    boolean    ‹    whether it’s the game’s turn
  493.             fi    true
  494.     dialoging    boolean    ∫    true if dialog currently displayed
  495.  
  496. ge_MessageReceived
  497. The ge_MessageReceived event can only be sent if the game is connected to another remote game, that is between a ge_ConnectionMade and a ge_ConnectionLost event.  When a game receives a message it should perform any action associated with that message.
  498.  
  499. gameEventRecord
  500.     event    gameEventType    ‹    ge_MessageReceived
  501.     version    integer    ∫    GameMaster version
  502.     game    Handle    ∫    game record handle
  503.     globals    Handle    ∫    handle to rule globals
  504.     message    Str255    ‹    message from the remote game
  505.     modified    boolean    €    true if the game has been modified
  506.     myturn    boolean    €    whether it’s the game’s turn
  507.     dialoging    boolean    ∫    true if dialog currently displayed
  508.  
  509. The other connection event is ge_SendMessage.  This is a Game Return Event and is discussed in Section 5.7.
  510.  
  511. 5.5.    Game Control Events
  512.  
  513. The Game Control Events are designed to allow rule books to use menus.  They also give the rule books notification of window (de)activation.  The rule books can also use these events for their own purposes.
  514.  
  515. ge_Activate
  516. The ge_Activate event is sent to a rule book when one of its game windows is activated (ie brought to the front).
  517.  
  518. gameEventRecord
  519.     event    gameEventType    ‹    ge_Activate
  520.     version    integer    ∫    GameMaster version
  521.     game    Handle    ∫    game record handle
  522.     globals    Handle    ∫    handle to rule globals
  523.     modified    boolean    ∫    true if the game has been modified
  524.     myturn    boolean    ∫    whether it’s the game’s turn
  525.     dialoging    boolean    ∫    true if dialog currently displayed
  526.  
  527. The rule book should perform any action associated with bringing the window to the front.  One probable action is to add menus to the menu bar.  Note that if the rule book adds menus it should make sure that the menu ID is greater than 1000 and call DrawMenuBar.
  528.  
  529. ge_Deactivate
  530. The ge_Deactivate event is sent to a rule book when one of its game windows is deactivated (ie another window is brought to the front).
  531.  
  532. gameEventRecord
  533.     event    gameEventType    ‹    ge_Deactivate
  534.     version    integer    ∫    GameMaster version
  535.     game    Handle    ∫    game record handle
  536.     globals    Handle    ∫    handle to rule globals
  537.     modified    boolean    ∫    true if the game has been modified
  538.     myturn    boolean    ∫    whether it’s the game’s turn
  539.     dialoging    boolean    ∫    true if dialog currently displayed
  540.  
  541. The rule book should perform any action associated with deactivating the window.  One probable action is to remove menus added on the ge_Activate.  Note that if the rule book removes menus it should redraw the menu bar.
  542.  
  543. ge_UpdateMenus
  544. GameMaster sends this event to the rule book associated with the active game window when the user clicks in the menu bar or selects a command key.  This gives the game an opportunity to update the menus before the user sees them.
  545.  
  546. gameEventRecord
  547.     event    gameEventType    ‹    ge_UpdateMenus
  548.     version    integer    ∫    GameMaster version
  549.     game    Handle    ∫    game record handle
  550.     globals    Handle    ∫    handle to rule globals
  551.     modifiers    integer    ∫    event modifiers
  552.     modified    boolean    €    true if the game has been modified
  553.     myturn    boolean    €    whether it’s the game’s turn
  554.     dialoging    boolean    ∫    true if dialog currently displayed
  555.  
  556. The rule book should dim, underline, check, etc any menus and/or menu items on menus that it has added to the menu bar.
  557.  
  558. ge_Menu
  559. When the user selects a menu item, either through the menu bar or its command key equivalent, from one of the menus that GameMaster is not responsible for it sends the ge_Menu event to the rule book associated with the active game window.
  560.  
  561. gameEventRecord
  562.     event    gameEventType    ‹    ge_Menu
  563.     version    integer    ∫    GameMaster version
  564.     game    Handle    ∫    game record handle
  565.     globals    Handle    ∫    handle to rule globals
  566.     modifiers    integer    ∫    event modifiers
  567.     int1    integer    ∫    menu id
  568.     int2    integer    ∫    item number
  569.     modified    boolean    €    true if the game has been modified
  570.     myturn    boolean    €    whether it’s the game’s turn
  571.     dialoging    boolean    ∫    true if dialog currently displayed
  572.  
  573. The rule book should perform any action associated with the menu selection.
  574.  
  575. 5.6.    Game Input Events
  576.  
  577. The Game Input Events are used to send user input to the rule books.
  578.  
  579. ge_MouseDown
  580. The ge_MouseDown event is sent whenever the user clicks in one of the enabled items in the game’s item list.  Note that GameMaster has already called DialogSelect and sets int1 to the item number of the selected item.
  581.  
  582. gameEventRecord
  583.     event    gameEventType    ‹    ge_MouseDown
  584.     version    integer    ∫    GameMaster version
  585.     globals    Handle    ∫    handle to rule globals
  586.     game    Handle    ∫    game record handle
  587.     where    Point    ‹    location of mouse down (local coords)
  588.     modifiers    integer    ∫    event modifiers
  589.     int1    integer    ∫    dialog item number
  590.     modified    boolean    €    true if the game has been modified
  591.     myturn    boolean    €    whether it’s the game’s turn
  592.     dialoging    boolean    ∫    true if dialog currently displayed
  593.  
  594. The rule book can perform any action associated with the mouse down.
  595.  
  596. ge_KeyDown
  597. When the user presses a key a ge_KeyDown event is sent to the rule book associated with the active game window.  The modifiers are sent in the modifiers field.  The ASCII of the key pressed is in int1 and the key code in int2.
  598.  
  599. gameEventRecord
  600.     event    gameEventType    ‹    ge_KeyDown
  601.     version    integer    ∫    GameMaster version
  602.     game    Handle    ∫    game record handle
  603.     globals    Handle    ∫    handle to rule globals
  604.     modifiers    integer    ∫    event modifiers
  605.     int1    integer    ∫    char code
  606.     int2    integer    ∫    key code
  607.     modified    boolean    €    true if the game has been modified
  608.     myturn    boolean    €    whether it’s the game’s turn
  609.     dialoging    boolean    ∫    true if dialog currently displayed
  610.  
  611. Note that command keys equivalents are intercepted by GameMaster and are sent as ge_Menu events.
  612.  
  613. ge_Idle
  614. The ge_Idle event is sent to each game periodically.  It allows games to perform events in pseudo-real time.
  615.  
  616. gameEventRecord
  617.     event    gameEventType    ‹    ge_Idle
  618.     version    integer    ∫    GameMaster version
  619.     game    Handle    ∫    game record handle
  620.     globals    Handle    ∫    handle to rule globals
  621.     modifiers    integer    ∫    event modifiers
  622.     modified    boolean    €    true if the game has been modified
  623.     myturn    boolean    €    whether it’s the game’s turn
  624.     dialoging    boolean    ∫    true if dialog currently displayed
  625.  
  626. ge_SleepQuery
  627. The ge_SleepQuery is only sent to the game with the frontmost window and performs two functions.  Firstly it allows the rule book to specify how often it would like to receive ge_Idle events.  Secondly it gives the rule book an opportunity to force an idle event if the mouse moves.  This is useful when the rule book changes the cursor shape to reflect what it is over.
  628.  
  629. gameEventRecord
  630.     event    gameEventType    ‹    ge_SleepQuery
  631.     version    integer    ∫    GameMaster version
  632.     game    Handle    ∫    game record handle
  633.     globals    Handle    ∫    handle to rule globals
  634.     int1    integer    fi    the number of ticks before the next idle event
  635.     long1    longint    ‹    nil
  636.             fi    a region handle describing the current cursor region
  637.     modified    boolean    €    true if the game has been modified
  638.     myturn    boolean    €    whether it’s the game’s turn
  639.     dialoging    boolean    ∫    true if dialog currently displayed
  640.  
  641. GameMaster will send an idle event to the rule book if either the mouse leaves the cursor region or int1 ticks pass.
  642. GameMaster will dispose of the region returned in long1.
  643.  
  644. ge_Answer
  645. The ge_Answer event is sent to a rule book when the user presses one of the buttons brought up by the ge_Ask.
  646.  
  647. gameEventRecord
  648.     event    gameEventType    ‹    ge_MouseDown
  649.     version    integer    ∫    GameMaster version
  650.     game    Handle    ∫    game record handle
  651.     globals    Handle    ∫    handle to rule globals
  652.     int1    integer    ‹    which button was pressed
  653.     modified    boolean    €    true if the game has been modified
  654.     myturn    boolean    €    whether it’s the game’s turn
  655.     dialoging    boolean    ∫    true if dialog currently displayed
  656.  
  657. The value of int1 is 1 if button 1 was pressed and 2 if button 2 was pressed.  If int1 is 0 then the last ge_Ask failed to complete.  This is most probably because GameMaster is already displaying its own dialog.  Do not, under any circumstances, respond to this by sending another ge_Ask.  You can check the value of the dialoging boolean to determine if GameMaster is already displaying a dialog.
  658.  
  659. 5.7.    Game Return Events
  660.  
  661. A rule book can send events back to GameMaster to request certain actions.  It does this by changing the event field of the game event record.  The event record thus returned is known as a Game Return Event.  Only the fields specific to each Game Return Event are documented below.  If it is not mentioned then a field retains the meaning associated with the original event.
  662.  
  663. ge_SendMessage
  664. The ge_SendMessage event can be sent back on any event after (and including) the ge_ConnectionMade and before (but not including) the ge_ConnectionLost.
  665.  
  666. gameEventRecord
  667.     event    gameEventType    ∫    ge_SendMessage
  668.     message    Str255    fi    message to send to remote game
  669.  
  670. After receiving a ge_SendMessage, GameMaster will send a ge_MessageReceived to the remote game with the message set to the message returned.
  671. Note that the message string is limited to 250 characters even though 255 characters are allocated for it.
  672.  
  673. ge_Error
  674. The ge_Error event can be sent back on any event.  The rule book uses this event to signal that an unrecoverable (bad!) error has occurred.
  675.  
  676. gameEventRecord
  677.     event    gameEventType    ∫    ge_SendMessage
  678.     message    Str255    fi    error message
  679.     int1    integer    fi    error number
  680.  
  681. Upon receiving a ge_Error, GameMaster will notify the user of the error and shut down the game associated with that message.  Rule books should most probably be aware that, at the moment, GameMaster completely ignores ge_Error events!  It was provided purely as a mechanism for rule books to be compatible with a future GameMaster with improved error handling.
  682.  
  683. ge_Ask
  684. The ge_Ask event can be sent back by the rule book to ask the user a simple question.
  685.  
  686. gameEventRecord
  687.     event    gameEventType    ∫    ge_SendMessage
  688.     message    Str255    fi    question to ask
  689.     but1    buttonStr    fi    string for first button
  690.     but2    buttonStr    fi    string for second button
  691.  
  692. Upon receiving a ge_Ask, GameMaster will put up a dialog asking the user a question.  The message is displayed.  If but1 is not empty then a button is displayed with that text.  If but2 is not empty then a button is displayed with that text.  GameMaster waits for the user to press one of the buttons and then sends a ge_Answer event.  If no button is displayed then the user must simply wait.  The rule book can cancel this wait state by sending a ge_Ask event with message, but1 and but2 all empty.
  693. To be compatible with the dialogs built in to GameMaster but1 should be ‘No’ or ‘Cancel’ and but2 ‘OK’ or ‘Yes’.  This is obviously subject to the semantics of the rule book.
  694. 6.    Programming Rules: Hints and Tips
  695.  
  696. This chapter is a collection of hints and tips for programming GameMaster rule books.
  697.  
  698. 6.1.    GrafPorts, Dialogs and Windows
  699.  
  700. Before calling the rule book GameMaster sets the port to the game window.  The rule can use GetPort to find the address of this GrafPort.  Because a WindowRecord is an extension of a GrafPort and a DialogRecord is an extension of a WindowRecord the rule can cast the result of the GetPort to a WindowPeek or DialogPeek.  Note that Inside Macintosh defines GrafPtr, WindowPtr and DialogPtr to be equivalent types.
  701.  
  702. 6.2.    Drawing
  703.  
  704. One of the hardest parts of writing a rule book is giving the user item update procedures enough information to draw the item.  The user item update procedure has the following interface:–
  705.  
  706. procedure UserItemUpdate(w : WindowPtr; itemNo : integer);
  707.  
  708. This gives the procedure access to the WindowRecord (by casting w to a WindowPeek and indirecting) which in turn gives access to the refcon.  The rule book can place the game handle into this refcon (on a ge_NewGame/ge_OldGame), thereby giving the procedure access to the game handles contents.  The rule book can also store the globals handle in its game record.  This gives the procedure access to the rule book’s globals.
  709.  
  710. 6.3.    Accessing QuickDraw Globals
  711.  
  712. Theoretically the rule books should have a valid A5 world.  This means that it can quite easily access GameMaster’s QuickDraw globals.  It should not modify them or access the application globals.
  713. At the moment we can’t get Think Pascal to do this for us nicely.  However, we do supply a unit called QuickDrawRules.p that gives rule books easy access to the QuickDraw globals.
  714.  
  715. 6.4.    Debugging
  716.  
  717. Sorry guys but you can’t use the Think Pascal integrated debugger to debug your rule books.  At the moment it’s MacsBug only.  However to help you GameMaster exports a symbol (ShutDownGameMaster).  When your rule book (or for that matter GameMaster) crashes you can enter ‘g ShutDownGameMaster ’ to quit GameMaster gracefully, shutting down MacTCP as you go.
  718. Do not enter ‘g ShutDown’ for short.  It’s not much fun!
  719.  
  720. 6.5.    Standard State
  721.  
  722. We have found it convenient to maintain three state variables in our game records.  They are:–
  723. •    connection_state (local or remote)
  724. •    local_player (black or white) If connection_state is remote then local_player determines who we’re playing.
  725. •    player_to_move (black or white) The player who should move next.
  726. Thus you can set myturn using:–
  727.  
  728. myturn :=
  729.     (connection_state=local) or 
  730.         ((connection_state=remote) and
  731.             (local_player=player_to_move));
  732.  
  733. 6.6.    Rule Book Miscellanea
  734.  
  735. A collection of important things to remember:–
  736. •    Always make you resource IDs greater than 1000.  ResEdit 2.x has the annoying habit of giving new created resources the default ID of 128.  This is especially dangerous when you create a PICT item using the nice DITL editor.
  737. •    Menu IDs must be greater than 1000.  Remember that there is a difference between menu IDs and the ID of their menu resource.  Learn how to set the menu ID in ResEdit (select ‘Edit Menu & MDEF ID’ from the ‘MENU’ menu).
  738. •    Remember to enable a dialog item if you want to receive mouse clicks on it.
  739. •    Remember you do not need to track dialog controls.  However if you simulate a button using a user item you should track it properly (ie simulate the behaviour of toolbox buttons).
  740. •    Remember that the game handle you are passed on a ge_NewGame is a valid empty handle.  Call SetHandleSize to increase its size.
  741. •    Check error codes when calling the ToolBox (Hey, I sound like DTS!).  Return a ge_Error event if you encounter a situation you cannot handle sensibly.
  742. •    Pascal is inherently safer than C.
  743.  
  744. 6.7.    How GameMaster Connects Games
  745.  
  746. If you follow the guidelines outlined above (and the interface defined in the previous chapters) GameMaster will organise to connect your game through the network automatically.  How it does this is documented here for your interest only.
  747. Imagine GameMaster running on two machines, one called Eriodon and the other Rocky.  The user of Eriodon starts a game and connects to Rocky.  The following happens:–
  748. •    The Eriodon GameMaster sends a ge_Flatten to its local rule book to make sure that the game handle is the most up to date available.
  749. •    The Eriodon GameMaster sends the contents of the game handle to the Rocky GameMaster.
  750. •    The Rocky GameMaster starts a game.  If the rule book did not already have a running game it sends a ge_InitRuleBook to it.  Regardless it sends a ge_OldGame to the rule book using the state supplied by the Eriodon GameMaster.
  751. •    The Rocky GameMaster then sends a ge_Swap its local rule book.  This ensures that the two players are playing on different sides.
  752. •    Both GameMasters then send ge_ConnectionMade to the games.
  753. From here on in the rule books are expected to keep their game records ‘in synch’ by exchanging messages.
  754. 7.    The GameMaster Distribution
  755.  
  756. 7.1.    The GameMaster User’s Kit
  757.  
  758. The basic GameMaster is distributed as GameMaster User’s Kit which contains:–
  759. •    GameMaster  The main application.
  760. •    The GameMaster User’s Manual  A simple manual to help you use GameMaster.
  761. •    Connect 4  Rhys’ masterpiece.
  762. •    Othello  Cute and fun.
  763. •    BattleShips  A classic two player game (but only for networked machines).
  764. •    Dots and Boxes  Mind bending.
  765. •    Global Thermonuclear War  Realistic.
  766. •    DerfBall  A devil of a game.
  767. •    Simple Talk  Useful but not really a game.
  768. •    File Transfer  A demonstration of just how much we expect others to abuse the concept of GameMaster.
  769. •    Chess  A classic.
  770. •    TicTacToe  Simple stuff.
  771. •    Empire  If Jager ever finishes.
  772.  
  773. 7.2.    The GameMaster Developer’s Kit
  774.  
  775. The GameMaster Developer’s Kit contains the following files:–
  776. •    Programming GameMaster Rule Books  This manual.
  777. •    GameTypes.unit  The Pascal interfaces needed to write GameMaster rules.
  778. •    GMRZTemplate.rsrc  A ResEdit template to add to your ResEdit Preferences file to help you edit GMRZ resources.
  779. •    The source code to all of the games distributed in the GameMaster User’s Kit.
  780. •    A number of Pascal units useful for developing rule books.
  781.