home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 8 / FreshFishVol8-CD1.bin / new / game / role / amigamud / amdoc1 / programming.txt < prev    next >
Text File  |  1994-08-01  |  66KB  |  1,785 lines

  1. AmigaMUD, Copyright 1994 by Chris Gray
  2.  
  3.  
  4.         Programming in the AmigaMUD System
  5.  
  6. Introduction
  7.  
  8.  
  9. The AmigaMUD system contains a complete programming system for a
  10. structured programming language. This language is used to program any
  11. MUD scenarios, quests, puzzles, etc. The AmigaMUD system contains
  12. facilities for parsing, executing, storing, retrieving and pretty-
  13. printing code in the language. Although intended for writing MUD
  14. scenarios, the language can easily be used for other purposes.
  15.  
  16. Although the interpreter is reasonably efficient, it is by no means as
  17. fast as true compiled code. Programmers should keep this in mind when
  18. using the language - things that are practical in, say, C, may not be
  19. practical in AmigaMUD.
  20.  
  21. AmigaMUD programming is accessed though one of the client programs,
  22. just like playing a scenario is. Only characters with status 'wizard'
  23. or 'apprentice' can directly access the programming features.
  24. Characters enabled as builders can indirectly access them by writing
  25. actions as described in the building document.
  26.  
  27. The AmigaMUD programming language does not contain any language
  28. constructs for doing input/output, graphics, sound, etc. Instead, the
  29. system contains a large number of builtin functions which perform
  30. these activities. Other builtin functions perform utility operations
  31. such as returning the length of a string, taking a substring,
  32. returning the current time and date, etc.
  33.  
  34. Most AmigaMUD programming involves writing functions. These functions
  35. are stored in the system database and are automatically retrieved as
  36. needed (e.g. to call them or print them out). AmigaMUD functions can
  37. have parameters and can return a result. They can also be recursive,
  38. although this is quite rare in a MUD scenario.
  39.  
  40. All AmigaMUD code executes in the server program, MUDServ, on the host
  41. computer. Lexical scanning, parsing, and editing all happen on the
  42. client computers. Parsed functions are sent from the client to the
  43. server as a "compiled" bytestream. Pretty-printing is done on the
  44. host computer, and the results are sent to the client as text. Because
  45. the server computer can never wait for any client (the human player
  46. might get a phone call in the middle of typing in a function!), some
  47. operations, such as input, are done differently than in other
  48. languages.
  49.  
  50. When using the standard scenario, a wizard or apprentice can go into
  51. programming, or wizard mode, by typing the 'wizard' command. The
  52. prompt will change to "> ". In this mode, input typed in is assumed to
  53. be statements in the programming language, definitions of functions,
  54. or one of a few simple wizard-mode commands. For reference, a
  55. statement to get out of wizard mode consists of:
  56.  
  57.     Normal().
  58.  
  59. This, of course, is only valid when the prompt is "> ", i.e. when no
  60. partial statement has been entered, and the user is not in the middle
  61. of typing in a function definition.
  62.  
  63. If the input statement does not fit on a single line, it can be
  64. continued on as many lines as needed. On lines other than the first,
  65. the prompt will change to ": ". The input statement is ended with a
  66. period, as in the above example. If the input statement is in fact an
  67. expression, i.e. it returns a value, then the system will print out
  68. that value. For example:
  69.  
  70.     > 2 + 3.
  71.     ==> 5
  72.  
  73. Not all values can be printed in any meaningful way. Function and
  74. thing (database entity) values are printed either symbolically (if the
  75. system can find a symbol for them) or as a hexadecimal number.
  76.  
  77. Each input line entered by any user is subject to an execution time
  78. limit, which can be controlled by the MUD administrator (SysAdmin).
  79. Running out of time will result in the execution being stopped, and a
  80. traceback of the called functions being produced. Other run-time
  81. errors, such as dividing by zero, produce a similar abort and
  82. traceback.
  83.  
  84. The following sections of this document give some examples of AmigaMUD
  85. programming. It is assumed that the reader already knows something
  86. about programming, or is a fast learner - this document does not try
  87. to teach programming to a novice. Readers are not expected to fully
  88. understand these examples - they are just a random sampling to show
  89. the flavour of AmigaMUD programming.
  90.  
  91.  
  92. Example - the Towers of Hanoi
  93.  
  94.  
  95. Here is a log of entering, printing and running the classic "Towers of
  96. Hanoi" function. Commentary is separated off and enclosed in square
  97. brackets. For those not familiar with this computer programming
  98. classic, the story goes something like this:
  99.  
  100.     There is a monastery near Hanoi where there live a group of monks
  101.     dedicated to keeping the universe running. They do this by
  102.     continually moving flat disks around on a set of three pegs. The
  103.     disks are round, and have a hole in the middle so that they can
  104.     slide onto the pegs. At the beginning of the universe, all 64
  105.     disks were on the left-most peg, with the widest disk on the
  106.     bottom, and the narrowest one on the top. When the monks have
  107.     succeeded in moving all of the disks to the right-hand peg, the
  108.     universe will end. They can only move one disk at a time, and must
  109.     never place a wider disk on top of a narrower one. The monks must
  110.     move the disks as fast as they can.
  111.  
  112. input> wizard
  113.  
  114.     [I was initially in normal playing mode. I used the 'wizard'
  115.     command to get into wizard mode so I can program.]
  116.  
  117. > private proc hanoi(int n; string from, to, using)void:
  118. *** expecting identifier for proc parameter name
  119. *** expecting ',', ';' or ')' after proc parameter
  120. : .
  121. *** expecting 'corp' to end proc definition
  122. Errors detected, proc "hanoi" not created.
  123.  
  124.     [I accidentally tried to use a reserved word as a parameter name.
  125.     I decided to abort the entering of the function by typing a '.'.
  126.     This doesn't always work - sometimes closing parentheses or
  127.     constructs are also needed. Note that the prompt changed to ": "
  128.     while I was entering the function definition.]
  129.  
  130. > private proc hanoi(int n; string fromStr, toStr, usingStr)void:
  131. Postman has arrived from the north.
  132. : if n ~= 0 then
  133. Postman heads into the passageway.
  134. :  hanoi(n - 1, fromStr, usingStr, toStr);
  135. :  Print("Move disk " + IntToString(n) + " from " + fromStr + " peg to " +
  136. :   toStr + " peg.\n");
  137. :  hanoi(n - 1, usingStr, toStr, fromStr);
  138. : fi;
  139. : corp
  140.  
  141.     [Being in wizard mode doesn't take your character out of the
  142.     scenario, hence I still saw messages about Postman passing
  143.     through. It is often wise to go somewhere private when doing
  144.     serious wizarding, so that you aren't interrupted.]
  145.  
  146. > describe hanoi.
  147. hanoi: proc, owner Merlin, useCount 2:
  148. proc hanoi(int n; string fromStr, toStr, usingStr)void:
  149.     if n ~= 0 then
  150.     hanoi(n - 1, fromStr, usingStr, toStr);
  151.     Print("Move disk " + IntToString(n) + " from " + fromStr + " peg to "
  152.          + toStr + " peg.\n");
  153.     hanoi(n - 1, usingStr, toStr, fromStr);
  154.     fi;
  155. corp
  156.  
  157.     [The exact form in which I entered the function is not preserved.
  158.     The function is stored internally as a byte stream, and is
  159.     "pretty-printed" by the system as needed. I used the 'describe'
  160.     wizard-mode command to display the function.]
  161.  
  162. > hanoi(0, "left", "right", "center").
  163.  
  164.     [Now I call the function. This is a directly entered statement in
  165.     the programming language. With 'n' equal to 0, the function does
  166.     nothing, and, since it returns nothing ("void"), nothing is
  167.     printed.]
  168.  
  169. > hanoi(1, "left", "right", "center").
  170. Move disk 1 from left peg to right peg.
  171.  
  172.     [With 'n' equal to 1, there is one line of output.]
  173.  
  174. > hanoi(2, "left", "right", "center").
  175. Move disk 1 from left peg to center peg.
  176. Move disk 2 from left peg to right peg.
  177. Move disk 1 from center peg to right peg.
  178. > hanoi(3, "left", "right", "center").
  179. Move disk 1 from left peg to right peg.
  180. Move disk 2 from left peg to center peg.
  181. Move disk 1 from right peg to center peg.
  182. Move disk 3 from left peg to right peg.
  183. Move disk 1 from center peg to left peg.
  184. Move disk 2 from center peg to right peg.
  185. Move disk 1 from left peg to right peg.
  186.  
  187.     [The number of lines of output is equal to 2 to the power of 'n',
  188.     minus one. Thus, we need not fear for the imminent end of the
  189.     world. Even if the monks can move one disk per second, the time
  190.     needed to complete the task, 2 to the power of 64, minus one, is a
  191.     very long time indeed!]
  192.  
  193. > Normal().
  194. input> bye
  195.  
  196.     [I go back into normal mode, and exit.]
  197.  
  198.  
  199. Example - Code to Draw the Streets Area
  200.  
  201.  
  202. define tp_streets proc drawStreets()void:
  203.  
  204.     if not KnowsEffect(nil, STREETS_ID) then
  205.     DefineEffect(nil, STREETS_ID);
  206.     GSetImage(nil, "streets");
  207.     IfFound(nil);
  208.         GShowImage(nil, "", 0, 0, 160, 100, 0, 0);
  209.     Else(nil);
  210.         GSetPen(nil, C_DARK_GREY);
  211.         GAMove(nil, 0, 0);
  212.         GRectangle(nil, 159, 99, true);
  213.  
  214.         GSetPen(nil, C_LIGHT_GREY);
  215.         GAMove(nil, 0, 0);
  216.         GRectangle(nil, 69, 41, true);
  217.         GAMove(nil, 89, 0);
  218.         GRectangle(nil, 70, 41, true);
  219.         GAMove(nil, 0, 57);
  220.         GRectangle(nil, 69, 42, true);
  221.         GAMove(nil, 89, 57);
  222.         GRectangle(nil, 70, 42, true);
  223.  
  224.         GSetPen(nil, C_DARK_BROWN);
  225.         GAMove(nil, 0, 0);
  226.         GRectangle(nil, 62, 35, true);
  227.         GAMove(nil, 96, 0);
  228.         GRectangle(nil, 63, 35, true);
  229.         GAMove(nil, 0, 63);
  230.         GRectangle(nil, 62, 36, true);
  231.  
  232.         GSetPen(nil, C_WHITE);
  233.         GAMove(nil, 79, 0);
  234.         GRDraw(nil, 0, 35);
  235.         GAMove(nil, 0, 49);
  236.         GRDraw(nil, 62, 0);
  237.         GAMove(nil, 97, 49);
  238.         GRDraw(nil, 62, 0);
  239.         GAMove(nil, 79, 63);
  240.         GRDraw(nil, 0, 36);
  241.  
  242.         GSetPen(nil, C_BLACK);
  243.         GAMove(nil, 62, 13);
  244.         VerticalDoor();
  245.         GAMove(nil, 96, 1);
  246.         VerticalDoor();
  247.         GAMove(nil, 96, 13);
  248.         VerticalDoor();
  249.         GAMove(nil, 96, 25);
  250.         VerticalDoor();
  251.         GAMove(nil, 101, 35);
  252.         HorizontalDoor();
  253.         GAMove(nil, 122, 35);
  254.         HorizontalDoor();
  255.         GAMove(nil, 143, 35);
  256.         HorizontalDoor();
  257.         GAMove(nil, 62, 65);
  258.         VerticalDoor();
  259.         GAMove(nil, 62, 77);
  260.         VerticalDoor();
  261.         GAMove(nil, 62, 89);
  262.         VerticalDoor();
  263.         GAMove(nil, 5, 63);
  264.         HorizontalDoor();
  265.         GAMove(nil, 26, 63);
  266.         HorizontalDoor();
  267.         GAMove(nil, 47, 63);
  268.         HorizontalDoor();
  269.  
  270.         GSetPen(nil, C_LIGHT_GREEN);
  271.         GAMove(nil, 96, 63);
  272.         GRectangle(nil, 56, 30, true);
  273.         GSetPen(nil, C_LIGHT_GREY);
  274.         GAMove(nil, 121, 63);
  275.         GRectangle(nil, 5, 36, true);
  276.         GAMove(nil, 111, 69);
  277.         GRectangle(nil, 25, 18, true);
  278.         GSetPen(nil, C_BLUE);
  279.         GAMove(nil, 118, 74);
  280.         GRectangle(nil, 11, 8, true);
  281.         GSetPen(nil, C_GOLD);
  282.         GAMove(nil, 123, 78);
  283.         GCircle(nil, 1, true);
  284.         GAMove(nil, 124, 78);
  285.         GCircle(nil, 1, true);
  286.  
  287.         GSetPen(nil, C_DARK_BROWN);
  288.         GAMove(nil, 103, 72);
  289.         GRectangle(nil, 7, 12, true);
  290.         GAMove(nil, 137, 72);
  291.         GRectangle(nil, 8, 12, true);
  292.         GSetPen(nil, C_BLACK);
  293.         GAMove(nil, 110, 74);
  294.         VerticalDoor();
  295.         GAMove(nil, 137, 74);
  296.         VerticalDoor();
  297.  
  298.         GSetPen(nil, C_FOREST_GREEN);
  299.         GAMove(nil, 100, 67);
  300.         GCircle(nil, 5, true);
  301.         GAMove(nil, 135, 67);
  302.         GCircle(nil, 4, true);
  303.         GAMove(nil, 145, 68);
  304.         GCircle(nil, 5, true);
  305.         GAMove(nil, 114, 91);
  306.         GCircle(nil, 3, true);
  307.         GAMove(nil, 107, 90);
  308.         GCircle(nil, 3, true);
  309.         GAMove(nil, 99, 91);
  310.         GCircle(nil, 3, true);
  311.         GAMove(nil, 149, 80);
  312.         GCircle(nil, 3, true);
  313.         GAMove(nil, 148, 87);
  314.         GCircle(nil, 3, true);
  315.         GAMove(nil, 142, 91);
  316.         GCircle(nil, 3, true);
  317.         GAMove(nil, 133, 90);
  318.         GCircle(nil, 3, true);
  319.     Fi(nil);
  320.     EndEffect();
  321.     fi;
  322.     CallEffect(nil, STREETS_ID);
  323. corp;
  324.  
  325.  
  326. Recall from earlier documents that the "MUD" client program can cache
  327. effects, such as the graphics for the streets area. The server program
  328. keeps track of which active clients currently know which effects. The
  329. code here is asking the server if the current client knows the effect
  330. identified by 'STREETS_ID'. If it doesn't, then that effect is
  331. defined. The effect is actually like a little program itself. A
  332. graphics image called "streets" is requested. If it is found on the
  333. client machine, then it is shown, otherwise a rough rendering of the
  334. image is produced using rectangles, lines, circles, etc. Note that
  335. this decision is made in the client program, since the server cannot
  336. wait for the result of that decision. The rough drawing of the streets
  337. area can be summarized as:
  338.  
  339.     - fill the entire image area with dark grey
  340.     - draw four rectangles of light grey as sidewalks
  341.     - draw three smaller rectangles of dark brown as the buildings
  342.     - draw lines of white down the middle of the roads
  343.     - draw a bunch of black doors. 'VerticalDoor' and 'HorizontalDoor'
  344.     are scenario functions which in turn use effects to draw the
  345.     standard vertical and horizontal doors.
  346.     - draw a light green rectangle to be the body of the park
  347.     - draw a pair of overlapping light grey rectangles to be the
  348.     sidewalks in the park
  349.     - draw the blue fountain with the gold mermaid in it
  350.     - draw two dark brown buildings with black doors
  351.     - draw a bunch of forest green trees
  352.  
  353. After the STREETS_ID effect is defined (if it wasn't already), it is
  354. called up. The result of all of this is that the first time a client
  355. program needs to draw this area, there is a pause as the effect code
  356. is sent from the server. On subsequent uses, however, the server knows
  357. that the client knows the effect, so only a short request to run it is
  358. sent over.
  359.  
  360.  
  361. Example - Code for the 'Inventory' Verb
  362.  
  363.  
  364. define tp_verbs proc v_inventory()bool:
  365.     int cash;
  366.     thing me;
  367.  
  368.     me := Me();
  369.     cash := me@p_pMoney;
  370.     if cash = 0 then
  371.     Print("You are broke.\n");
  372.     else
  373.     Print("You have ");
  374.     IPrint(cash);
  375.     if cash = 1 then
  376.         Print(" bluto.\n");
  377.     else
  378.         Print(" blutos.\n");
  379.     fi;
  380.     fi;
  381.     if ShowList(me@p_pCarrying, "You are carrying:\n") then
  382.     Print("You are not carrying anything.\n");
  383.     fi;
  384.     if not me@p_pHidden and CanSee(Here(), me) then
  385.     OPrint(FormatName(me@p_pName) + " takes inventory.\n");
  386.     fi;
  387.     true
  388. corp;
  389.  
  390. Verb0(G, "inventory", 0, v_inventory).
  391. Synonym(G, "inventory", "inv").
  392. Synonym(G, "inventory", "i").
  393.  
  394.  
  395. This routine returns a 'bool' (true/false) value, like other direct
  396. parsing routines. This is done so that if an error is encountered, the
  397. system can abort the handling of user input which contains several
  398. commands on one line, such as:
  399.  
  400.     go north. go east. west. get rope. go south. tie rope to rail
  401.  
  402. If such a parsing routine returns 'false', then the successive
  403. commands on the same line are not executed. In the case of
  404. 'inventory', there is nothing that can go wrong, so the function
  405. always returns 'true'.
  406.  
  407. This function has some local variables. They are valid only while the
  408. function is executing, and do not have to have names unique from local
  409. variables in other functions.
  410.  
  411. First, a local copy of the pointer to the current character, as
  412. yielded by calling the builtin "Me", is created. This is done since it
  413. is quicker to reference a local variable than to call a function.
  414. Next, the amount of money the character has is obtained. The '@'
  415. operator takes a 'thing' on the left and a property on the right, and
  416. returns the value of that property attached to the thing. Much more
  417. will be said about this later. The function then prints out an
  418. appropriate comment based on that amount. Next, it calls "ShowList",
  419. which is another routine in the scenario which prints the names of
  420. objects in a list, one per line, slightly indented. It is used here,
  421. when describing a room, and when looking inside a container. If the
  422. list is empty, it does not print anything, and returns 'true'. If the
  423. list is not empty, it prints its second parameter (here "You are
  424. carrying:\n") before printing the items in the list, and then returns
  425. 'false'. Thus, the code here will either print the list of objects the
  426. character is carrying (property 'p_pCarrying' on the character) headed
  427. by "You are carrying:\n", or will print "You are not carrying
  428. anything.\n" if the character is empty-handed.
  429.  
  430. The next 'if' statement is a bit more complicated. It's purpose is to
  431. allow other characters in the same room as the one doing the inventory
  432. to see what the first is doing, if appropriate. A character can be
  433. "hidden" (only wizards can do this in the current scenario), so that
  434. others cannot see them or what they are doing. 'CanSee' is another
  435. routine in the scenario, that determines whether or not there is light
  436. in the given room. There will be no light if the room is dark, no
  437. object in the room is emitting light, no character in the room is
  438. emitting light (wizards can make themselves glow), and no character in
  439. the room is carrying an object which is emitting light. 'OPrint' is a
  440. builtin function which displays the passed string to all characters in
  441. the same room as the active one. 'FormatName' is a builtin function
  442. which reformats a string from the AmigaMUD internal form into a more
  443. normal external form (e.g. turns "frog;small,green" into "small green
  444. frog"). Thus, if the active player is not a hidden wizard, and there
  445. is light in the current room, then all players in the current room
  446. will see the message "XXX takes inventory.\n", where XXX is the
  447. character's name.
  448.  
  449. The three lines after the function definition are directly executed
  450. statements which add the verbs "inventory", "inv" and "i" to the main
  451. grammar, as abbreviated by 'G'. 'Verb0' tells the system that there
  452. are no arguments expected for the verb. Other possibilities are
  453. 'Verb1' as in "examine <object>" and 'Verb2' as in "put the <object>
  454. into the <object>". The main grammar, G, is the one which is used to
  455. parse normal user commands when not in wizard mode. Other grammars are
  456. used for the building commands, etc. More details on parsing will be
  457. given later.
  458.  
  459.  
  460. Example - Code for the 'ShowList' Utility Routine
  461.  
  462.  
  463. define t_util proc utility public ShowList(list thing lt;
  464.                        string starter)bool:
  465.     int i;
  466.     thing object;
  467.     string s;
  468.     bool first;
  469.  
  470.     first := true;
  471.     for i from 0 upto Count(lt) - 1 do
  472.     object := lt[i];
  473.     if not object@p_oInvisible then
  474.         if first then
  475.         first := false;
  476.         Print(starter);
  477.         fi;
  478.         Print("  " + FormatName(object@p_oName) + "\n");
  479.     fi;
  480.     od;
  481.     first
  482. corp;
  483.  
  484.  
  485. A list in AmigaMUD can be indexed like a one-dimensional array. The
  486. builtin 'Count' takes any kind of list as its argument, and returns
  487. the number of elements in it. This routine simply runs down the
  488. elements in the passed list, and looks for any objects that are not
  489. marked as invisible. It prints each such one out, indented by two
  490. spaces, after the header passed by the caller. If no visible objects
  491. were found, 'ShowList' returns true, else it returns false.
  492.  
  493.  
  494. Example - the Code for Killing a Monster
  495.  
  496.  
  497. define t_fight proc KillMonster(thing monster)void:
  498.     string monsterName;
  499.     thing me, here;
  500.     list thing lt;
  501.     int i;
  502.  
  503.     me := Me();
  504.     monsterName := FormatName(monster@p_pName);
  505.     Print(monsterName + " is killed!\n");
  506.     if me@p_pHidden then
  507.     OPrint(monsterName + " is killed!\n");
  508.     else
  509.     OPrint(FormatName(me@p_pName) + " kills " + monsterName + "!\n");
  510.     fi;
  511.     me -- p_pCurrentTarget;
  512.     lt := monster@p_pCarrying;
  513.     if lt ~= nil then
  514.     here := Here();
  515.     i := Count(lt);
  516.     while i ~= 0 do
  517.         i := i - 1;
  518.         ignore DoDrop(here, monster, lt[i]);
  519.     od;
  520.     fi;
  521.     if monster ~= me then
  522.     i := monster@p_pMoney;
  523.     if i ~= 0 then
  524.         FindLoot((i + 1) / 2 + Random((i + 1) / 2));
  525.     fi;
  526.     fi;
  527.     ignore ForceAction(monster, DoUnShowIcon);
  528. corp;
  529.  
  530. This routine is executed whenever a character in the combat area (the
  531. "Proving Grounds") successfully vanquishes a monster. The routine
  532. prints informative messages to the player and anyone else around,
  533. causes all of the monster's possessions to be dropped, and gives the
  534. character a possible monetary reward. The final line of the function
  535. needs a bit more explaining. The builtin function 'ForceAction' forces
  536. the indicated character or NPC to execute the function passed as the
  537. second argument. This means that any code that affects "everyone else
  538. in the room" will also affect whoever is killing the monster. In this
  539. case, the routine called is responsible for removing the icon for the
  540. monster from the displays of everyone else in the room.
  541.  
  542.  
  543. Wizard-Mode Commands
  544.  
  545.  
  546. Most input entered while in wizard mode is either function definitions
  547. or statements in the programming language to be directly executed. A
  548. few special commands are available for convenience, however. They are:
  549.  
  550.     END-OF-FILE - an end-of-file condition will cause the client to
  551.     exit.
  552.  
  553.     bye - this command will cause the client to exit. Note that the
  554.     server keeps track of whether a character is in wizard mode
  555.     or not, so on the next connection, it will enter wizard mode
  556.     if that is the mode the character was last in. When not in
  557.     wizard mode, special scenario actions can be taken when a
  558.     character exits the game and when the character re-enters the
  559.     game. These are not performed when in wizard mode. Thus,
  560.     things like the initial display of the current location will
  561.     not happen automatically, and it may be necessary to look
  562.     around and/or move around to get it to appear.
  563.  
  564.     public
  565.     private
  566.     define/def - these three commands are used to define a symbol.
  567.     'public' will put the symbol into the global public symbol
  568.     table. 'private' will put the symbol into your private symbol
  569.     table, and 'define' will put the symbol into whatever table
  570.     you specify. There are two kinds of symbol definitions that
  571.     can be made. The first kind consists of a name for the symbol
  572.     followed by the definition of it, followed by a period.
  573.     Examples:
  574.  
  575.         public G CreateGrammar().
  576.         public p_pName CreateStringProp().
  577.         private HIT_LIMIT 72.
  578.         private room_table CreateTable().
  579.         define room_table westRoom CreateThing(genericRoom).
  580.         define room_table eastRoom CreateThing(genericRoom).
  581.  
  582.     The second kind of symbol definition defines a function. This
  583.     is done by using the reserved word 'proc' followed by the
  584.     function definition. See the previous example sections for
  585.     some of these. They will be discussed in more detail later.
  586.  
  587.     delete/del <table-name> <symbol> - this command will delete the
  588.     given symbol from the given table, if it can. It is similar
  589.     to running the 'DeleteSymbol' builtin. <table-name> can be
  590.     "private" or "public" as well as the name of a table.
  591.  
  592.     use <table-expression> - this command adds the specified symbol
  593.     table to the set of currently in-use tables. It is equivalent
  594.     to calling the 'UseTable' builtin. See the discussion of
  595.     tables in the 'Building' document.
  596.  
  597.     unuse <table-expression> - this command removes the specified
  598.     symbol table from the set of currently in-use tables. It is
  599.     equivalent to calling the 'UnUseTable' builtin.
  600.  
  601.     source <filename> - this command causes the contents of the named
  602.     file (on the client machine) to be read and processed. Files
  603.     sourced in this way can in turn contain other 'source'
  604.     commands. Doing this allows a large scenario to be split up
  605.     into a number of logical (and smaller) pieces. The filename
  606.     can be any legal AmigaDOS file path. Examples:
  607.  
  608.         source st:all.m
  609.         source df0:AmigaMUD/examples/speech.m
  610.  
  611.     When using the "MUD" client program, the 'Source' menu item is
  612.     equivalent to the 'source' command. Do not try to switch
  613.     between wizard mode and normal mode while sourcing files,
  614.     however, since the operation is asynchronous and will
  615.     probably not occur when you want it to.
  616.  
  617.     describe/desc/d <expression>. - this command prints out
  618.     information about the value of the requested expression. It is
  619.     the wizard mode command most often used interactively. The
  620.     output will depend on the type of the expression:
  621.  
  622.         void - a void value, i.e. no value, is printed as '<VOID>'
  623.  
  624.         status - a status value is printed symbolically, as
  625.         'succeed', 'continue' or 'fail'
  626.  
  627.         character - output for a character value is quite lengthy.
  628.         It consists of:
  629.  
  630.             - the character's sponsor - this is the character
  631.             who promoted this character to its current
  632.             status, if any.
  633.  
  634.             - the character's current location. This will be
  635.             the name of the location (room) the character
  636.             is in, if that room has a name, and a table
  637.             containing that name is in-use. Otherwise it
  638.             will just be '<THING>'.
  639.  
  640.             - the character's input action. This is the action
  641.             that all non-wizard-mode text input from the
  642.             player is passed to. It is normally some
  643.             general scenario parsing routine. The output
  644.             will be the name of the function if a table
  645.             containing that function is in-use, otherwise
  646.             it will be '<ACTION>'.
  647.  
  648.             - the character's raw key action. This is the
  649.             action which is called to process raw key
  650.             events occuring when the player hits a special
  651.             key, such as 'HELP', or a numeric keypad key.
  652.  
  653.             - the character's mouse down action. This is the
  654.             action which is called to process left-mouse-
  655.             button hits over identified regions of the
  656.             graphics window.
  657.  
  658.             - the character's button action. This is the
  659.             action which is called to process button hits
  660.             done with the mouse. Button-hits are clicks on
  661.             scenario-created "mouse-buttons" in the
  662.             graphics window.
  663.  
  664.             - the character's idle action. This is the action
  665.             which is called when the player leaves the
  666.             game when not in wizard mode.
  667.  
  668.             - the character's active action. This is the
  669.             action which is called when the player re-
  670.             enters the game not in wizard mode. It is
  671.             often used to do a 'look around' to establish
  672.             the display for the current location.
  673.  
  674.             - the character's status. This is one of:
  675.  
  676.             - normal
  677.             - apprentice
  678.             - wizard
  679.  
  680.             - the character's usecount. This indicates the
  681.             number of references to the character data
  682.             structure currently contained in the database.
  683.             The character cannot be deleted if this number
  684.             is non-zero. There will always be a reference
  685.             to the character from the 'Characters' symbol
  686.             table, and there will be another one resulting
  687.             from executing this command, and those two do
  688.             not count towards the total, but do show up.
  689.  
  690.             - the character's current non-wizard-mode prompt
  691.  
  692.             - the character's current password. This will only
  693.             be displayed for SysAdmin.
  694.  
  695.             - if the character is currently in wizard mode,
  696.             then '(in wizard mode)' is displayed
  697.  
  698.             - if the character is a new character, i.e. has
  699.             not yet connected and been initialized, then
  700.             '(new player)'.
  701.  
  702.             - the character's "thing" is displayed. This is
  703.             where all other changeable properties of the
  704.             character are stored. See the section here on
  705.             thing output for details.
  706.  
  707.         bool - a boolean value is printed as 'true' or 'false'
  708.  
  709.         int - an integer value is printed in decimal
  710.  
  711.         string - a string value is printed in quotes, with special
  712.         characters escaped as in source form
  713.  
  714.         thing - the output for a thing consists of a header
  715.         section, showing the fixed values that make up a
  716.         thing, followed by some number of property-value pairs
  717.         which are the contents of the thing. The fixed header
  718.         contains:
  719.  
  720.             - the thing's parent. This is the thing which this
  721.             thing starts inheriting properties from.
  722.  
  723.             - the thing's owner. This is the character who
  724.             currently owns the thing. When a thing is
  725.             created, it's owner is the effective
  726.             character at the time.
  727.  
  728.             - the thing's usecount. This is the number of
  729.             references to the thing from other entities in
  730.             the database. If this count goes to zero, then
  731.             the thing can be destroyed.
  732.  
  733.             - the thing's property count. This is the count of
  734.             the number of properties attached to the
  735.             thing. This does not count any properties that
  736.             may be inherited from ancestors.
  737.  
  738.             - the thing's status. This is one of:
  739.  
  740.             ts_private - only the owner of the thing can
  741.                 examine or change it
  742.  
  743.             ts_readonly - only the owner of the thing can
  744.                 change it, but anyone can examine it
  745.  
  746.             ts_wizard - any character with wizard status
  747.                 (or code running with that status) can
  748.                 change the thing, and anyone can examine
  749.                 the thing
  750.  
  751.             ts_public - anyone can change or examine the
  752.                 thing
  753.  
  754.         The contents of the thing, i.e. its properties, are
  755.         then displayed, indented two spaces. Each property
  756.         consists of the property, a colon and the value of
  757.         that property. If the property is defined in any in-
  758.         use table, then the name of the property is printed,
  759.         otherwise '<PROPERTY>' is printed. The value of the
  760.         property is printed much as being described here,
  761.         except that things are not shown expanded, but are
  762.         shown as a name, if one is found in an in-use table,
  763.         or as '<THING>'. Note that properties not known to the
  764.         current character are not displayed, unless the
  765.         current character is SysAdmin. Thus, adding more
  766.         tables to the "in-use" list can cause more properties
  767.         to be displayed on things. However, if a property is
  768.         not publically exported by the wizard who created it,
  769.         only that wizard and SysAdmin can see its value.
  770.  
  771.         action - actions, or functions, or procedures, are printed
  772.         with a short header describing the action. This header
  773.         contains:
  774.  
  775.             - the owner of the function. This is the character
  776.             who defined it.
  777.  
  778.             - the usecount of the function. An action cannot
  779.             be deleted unless this count goes to zero.
  780.  
  781.         Following this header is the definition of the
  782.         function, as pretty-printed by the system. For builtin
  783.         functions, which are pre-implemented, no function body
  784.         is shown. Also, if the owner of the function has made
  785.         it available in some public symbol table, but has not
  786.         marked the function itself as "public", the body is
  787.         not shown, unless it is SysAdmin who is looking.
  788.  
  789.         table - only a header is printed for a table. The symbols
  790.         in the table can be displayed using the 'ShowTable'
  791.         builtin. The header for a table contains:
  792.  
  793.             - the owner of the table
  794.  
  795.             - the usecount of the table
  796.  
  797.             - the number of entries in the table
  798.  
  799.         grammar - a grammar is described much the same as a table.
  800.         The words in the grammar can be displayed using the
  801.         'ShowWords' builtin. The header for a grammar
  802.         contains:
  803.  
  804.             - the owner of the grammar
  805.  
  806.             - the usecount of the grammar
  807.  
  808.             - the number of words in the grammar
  809.  
  810.         lists - AmigaMUD has three kinds of lists: lists of 
  811.         integers, lists of things, and lists of actions. Each
  812.         is displayed as a list of values enclosed in braces.
  813.         Integers are shown directly in decimal, and things and
  814.         actions are shown as a symbol, if one is found in the
  815.         in-use tables, or as '<THING>' or '<ACTION>'.
  816.  
  817.         properties - properties are displayed symbolically if a
  818.         symbol for them is found in the set of in-use tables,
  819.         or just as '<PROPERTY>'.
  820.  
  821.         thing-status - a thing status (as returned by the builtin
  822.         'GetThingStatus') is displayed as one of:
  823.  
  824.             ts_private
  825.             ts_readonly
  826.             ts_wizard
  827.             ts_public
  828.  
  829.     edit/ed/e <function-name> - this command is used to interactively
  830.     edit a function. Only the body of a function can be changed -
  831.     its header can only be changed by deleting the function and
  832.     recreating it. Editing can only be done when using the "MUD"
  833.     client program, either locally or remotely, or when using the
  834.     "SMUD" client program locally. "SMUD" will always use an
  835.     external editor, as indicated by your "EDITOR" environment
  836.     variable, and "MUD" will use either an internal one or an
  837.     external one, depending on the "Editor" menu setting. See the
  838.     "MUD" document for details on how to use the internal editor.
  839.  
  840.     When the editing is done, the AmigaMUD programming language
  841.     parser attempts to "compile" the function. This can fail,
  842.     because of syntax or other errors, in which case the function
  843.     is left unchanged. With the "MUD" internal editor, the errors
  844.     are pointed out one at a time and the user can resubmit the
  845.     function at any point. When using an external editor, the user
  846.     can re-issue the 'edit' command, without giving a function
  847.     name, and will be left editing the file as it was when it was
  848.     submitted for compiling. This cycle can be repeated until the
  849.     function compiles, or the user gives up.
  850.  
  851.     replace <function-definition> - this command can be used, even
  852.     when not using the "MUD" or "SMUD" client programs, to change
  853.     the body of a function. The entire function must be re-
  854.     entered, including its header. E.g.
  855.  
  856.         > private proc doit()void:
  857.         :      Print("Hello\n");
  858.         : corp;
  859.         >
  860.         > replace doit()void:
  861.         :      int i;
  862.         :      for i from 1 upto 10 do
  863.         :          Print("i = " + IntToString(i) + "\n");
  864.         :      od;
  865.         : corp;
  866.         >
  867.  
  868.     This kind of editing is expected to be most useful in
  869.     conjunction with a terminal program which can do an ASCII-put
  870.     from a file on the remote machine. As with function editing,
  871.     the header of the function cannot be changed.
  872.  
  873.  
  874. Data Types
  875.  
  876. There are a number of data types in the AmigaMUD programming language.
  877. Not all are useable in all circumstances. The types are:
  878.  
  879.     void - this is not really a type. It is used as a function return
  880.     type to indicate that the function does not return a value
  881.     (and hence is actually a procedure and not a function). It is
  882.     also the type returned by statements, such as a 'for' loop.
  883.  
  884.     nil - this also is not really a type. It is the type of the
  885.     reserved word 'nil', which represents a non-value for things,
  886.     actions, tables, grammars and lists. This allows values of
  887.     those types to be tested for validity. No other use of this
  888.     type can occur.
  889.  
  890.     status - this type is a three-valued type, with values 'succeed',
  891.     'fail' and 'continue'. The interpretation of these three
  892.     values is at the discretion of the programmer, but a number of
  893.     builtin functions, such as 'FindName', return status values
  894.     with fixed interpretations on the values. It is suggested that
  895.     programmers use similar interpretations to avoid confusion:
  896.  
  897.         'succeed' - the operation has completed successfully
  898.         'fail' - the operation has failed
  899.         'continue' - the operation can be continued
  900.  
  901.     character - this type represents a reference to a player
  902.     character. It is used by a few builtin functions, such as
  903.     'Owner', 'Character', etc. Such a reference is not equivalent
  904.     to a reference to the character thing, such as is returned by
  905.     the 'Me' builtin. Builtins 'CharacterThing' and
  906.     'ThingCharacter' can be used to return one from the other.
  907.  
  908.     bool - this type is a two-valued type, with values 'true' and
  909.     'false'. It is the result of a comparison, and is the required
  910.     type for the condition in an 'if' construct or 'while'
  911.     statement. It is also used with the parsing builtins.
  912.  
  913.     int - this type is a signed 32 bit integer. In the programming
  914.     language, integers can be entered in decimal, hexadecimal,
  915.     octal, or binary. Only decimal conversions are provided as
  916.     builtins.
  917.  
  918.     string - this type represents a character string. The current
  919.     implementation limits strings to about 4000 characters in
  920.     length. Empty strings are allowed. In the programming
  921.     language, strings are surrounded by quotation marks (") and
  922.     may contain escapes for some non-printable characters.
  923.  
  924.     thing - this type represents a pointer to a thing. Things are the
  925.     basic database entity used to represent concepts such as
  926.     rooms and objects. There is a thing associated with each
  927.     player character or NPC. To the programmer, a thing is just a
  928.     set of attribute-value pairs. The attributes are properties
  929.     defined in the database by programmers, and their values can
  930.     be actions (functions), strings, integers, references to other
  931.     things, etc. Each thing also has an owner (the character who
  932.     currently owns it), and a parent (the thing, if any, to
  933.     inherit other properties from).
  934.  
  935.     action - actions are just functions or procedures. In AmigaMUD
  936.     they are first-class objects in that they can be stored in the
  937.     database, passed as parameters, and called indirectly. When a
  938.     function is called directly by another, the types and number
  939.     of the parameters and result are checked during the
  940.     compilation of the calling function. When a function is called
  941.     indirectly at runtime, this checking must be done dynamically,
  942.     after the called function has been identified. Thus, there can
  943.     be function-calling errors at runtime. Also, several builtins
  944.     take actions as parameters, and they check the parameters and
  945.     result of such actions at runtime.
  946.  
  947.     table - a table is a symbol table. It is a mapping from strings
  948.     (the symbols) to their values. Such tables are dynamic
  949.     entities and can be created, manipulated and destroyed at
  950.     runtime. They are stored in the database along with things,
  951.     properties, actions, etc. Since tables are values, it is
  952.     possible to have a symbol in a table whose value is another
  953.     table. This allows the construction of trees of symbol tables,
  954.     which is quite useful when organizing a large number of
  955.     symbols.
  956.  
  957.     grammar - a grammar is much like a table, in that it contains a
  958.     mapping from strings to values. In a grammar, however, the
  959.     values are special internal ones which the AmigaMUD system can
  960.     use to parse player input. The use of grammars is described in
  961.     the section on parsing.
  962.  
  963.     list int
  964.     list thing
  965.     list action - lists in AmigaMUD are somewhat of a cross between
  966.     linked lists and arrays. The size of them is dynamic, and
  967.     there are builtins to add and remove elements from both ends.
  968.     Their elements can be retrieved and modified by direct
  969.     indexing. Such indexing cannot extend the size of the list,
  970.     however.
  971.  
  972.     property bool
  973.     property int
  974.     property string
  975.     property thing
  976.     property action
  977.     property table
  978.     property grammar
  979.     property list int
  980.     property list thing
  981.     property list action - properties in AmigaMUD are the identifiers
  982.     for attribute-value pairs attached to things. The properties
  983.     are themselves first-class objects, however, so they can be
  984.     passed to functions as parameters, and returned as results.
  985.     Note that only certain types can be attached to things in
  986.     attribute-value pairs.
  987.  
  988.     <thing-status> - this type is not a full type in the language. It
  989.     has values 'ts_private', 'ts_readonly', 'ts_wizard' and
  990.     'ts_public'. It is only used as the result type of the builtin
  991.     'GetThingStatus' and the parameter to 'SetThingStatus'.
  992.  
  993. A few other types exist internally, but they are not generally visible
  994. to the programmer.
  995.  
  996.  
  997. Lexical Entities
  998.  
  999.  
  1000. The bottom-level sequences of characters that are known by a
  1001. programming language are called the tokens or lexemes of that
  1002. language. In the AmigaMUD programming language, spaces, tabs and
  1003. newlines are used to separate tokens that would otherwise appear to be
  1004. single tokens, but are otherwise ignored. In other words, the system
  1005. does not care, or even notice, what kind of indentation you use.
  1006.  
  1007. There are two kinds of comments in the language. One is the C-like
  1008. form consisting of characters enclosed within an opening "/*" and a
  1009. closing "*/". Unlike C, however, this kind of comment can be nested in
  1010. AmigaMUD, so that you can comment out a piece of code without worrying
  1011. about whether it has comments inside it. These comments are discarded
  1012. very early in the compilation process, so they do not affect runtime
  1013. at all. The second kind of comment is the 'note' statement. These are
  1014. actually stored in the database and displayed when the function
  1015. containing them is printed out. They also slow down execution of
  1016. functions containing them by a very small amount.
  1017.  
  1018. Newlines are normally ignored when you are in wizard mode. They are
  1019. significant, however, when typing wizard mode commands which accept
  1020. something other than a period-terminated expression as their
  1021. parameter. For example, entering just 'source' as an input line in
  1022. wizard mode will yield an error message about a missing file name.
  1023.  
  1024. The reserved words in the AmigaMUD programming language (those symbols
  1025. that cannot be used as regular identifiers by programmers) are:
  1026.  
  1027.     and, or, not, if, then, elif, else, fi, while, do, od, for,
  1028.     from, upto, case, incase, default, esac, ignore, call, note,
  1029.     proc, utility, wizard, public, corp, void, bool, int, string,
  1030.     thing, status, grammar, character, table, action, list,
  1031.     property, true, false, succeed, fail, continue, nil,
  1032.     ts_private, ts_readonly, ts_wizard, ts_public
  1033.  
  1034. Identifiers (user symbols) look like reserved words, but they aren't
  1035. give any predefined meaning by the system. They can be of any length,
  1036. and are composed of letters, digits and underscores. They must not
  1037. start with a digit. The following are legal identifiers:
  1038.  
  1039.     Fred ThisIsALongIdentifier so_is_this_one fazz_79 x3
  1040.  
  1041. Integers (numbers) can be entered in several forms in the AmigaMUD
  1042. programming language. The normal form is decimal (base 10). A number
  1043. can be prefixed with '0x' for hexadecimal (base 16) interpretation,
  1044. '0o' for octal (base 8) interpretation or '0b' for binary (base 2)
  1045. interpretation. The following are all valid integer constants:
  1046.  
  1047.     1234567890
  1048.     0xcaf4A
  1049.     0o777
  1050.     0b1010101010001010111
  1051.  
  1052. Integers are signed 32 bit quantities in AmigaMUD. Minus signs are not
  1053. part of integer constants - they are unary operators that can be
  1054. applied to them. Thus
  1055.  
  1056.     x := -13;
  1057.  
  1058. is perfectly legal - '-13' is interpreted as the unary '-' operator
  1059. and the integer constant 13.
  1060.  
  1061. String constants in AmigaMUD are similar to those in most programming
  1062. languages. They consist of any number of any characters enclosed in
  1063. quotation marks ("). Quotation marks and some unprintable characters
  1064. can be put inside string constants using an escape mechanism. Inside a
  1065. string, a backslash (\) is handled specially, depending on the
  1066. character following the backslash, as follows:
  1067.  
  1068.     \n - a newline character appears in the string
  1069.     \t - a tab character appears in the string
  1070.     \X, where X is any other character - a single X appears in the
  1071.     string. This is how backslashes and quotation marks can be put
  1072.     in string constants.
  1073.  
  1074. An important feature of string constants is the concept of a string
  1075. break. Two string constants, separated only by whitespace and /* */
  1076. comments, are concatenated together into one string constant. This is
  1077. done at "compile" time, and the internal representation used will be
  1078. that of a single string constant. When a function containing a long
  1079. string constant is printed, the string constant will be broken up
  1080. using string breaks in order to fit on the output lines. Such long
  1081. string constants are most often used in output messages, as in:
  1082.  
  1083.     Print("As you open the small wooden door, you detect a strange "
  1084.       "odour coming from the room beyond. The odour seems "
  1085.       "familiar, and you are about to identify it when you fall "
  1086.       "unconscious.");
  1087.  
  1088. The following operator and punctuation tokens are also recognized:
  1089.  
  1090.     .          used to end input in wizard mode
  1091.     =          simple equality test for various types
  1092.     ==          case ignoring comparison for strings
  1093.     ~          bitwise invert
  1094.     ~=          simple inequality test for various types
  1095.     < <= > >= comparison tests for integers and strings
  1096.     << >>     bitwise shift operators
  1097.     ><          bitwise exclusive-or operator
  1098.     :          punctuation in function headers, after the result type
  1099.     :=          assignment construct
  1100.     +          addition and string concatenation operator
  1101.     --          property deletion operator
  1102.     - * / %   integer arithmetic operators
  1103.     & |       integer bitwise operators
  1104.     ( )       parentheses for subexpressions, function calls, etc.
  1105.     , ;       separators for expressions and statements
  1106.     @          property lookup operator
  1107.     [ ]       brackets for list indexing
  1108.  
  1109.  
  1110. Language Constructs
  1111.  
  1112.  
  1113. A number of constructs in AmigaMUD accept a sequence of statements or
  1114. an expression as a part of them. As in many programming languages,
  1115. statements in a sequence of statements are separated by semicolons.
  1116. Such a sequence can have an expression as its final element instead of
  1117. a statement, and thus the entire sequence can be used as an
  1118. expression. This is most often seen as the body of a function. Note
  1119. that this can only happen where specifically indicated - it is not
  1120. legal to replace any arbitrary expression with a sequence of
  1121. statements and an expression. This is trivial to implement, but I
  1122. deliberately did not do so, because of the confusion it can cause.
  1123. AmigaMUD does not have any constructs which only accept a single
  1124. statement as part of them, and thus it does not have any problem with
  1125. "dangling else"'s. All constructs are fully bracketed with reserved
  1126. words, hence there are no "begin"/"end" or "{"/"}" brackets needed.
  1127.  
  1128.  
  1129. The 'if' Construct
  1130.  
  1131.  
  1132. The AmigaMUD programming language has a standard set of language
  1133. constructs, including 'if's, 'while's, 'for's and 'case's. 'if's and
  1134. 'case's can be used as both statements and expressions, i.e. can
  1135. return a value or not return a value. An 'if' construct consists of:
  1136.  
  1137.     - 'if'
  1138.     - a bool expression (the condition)
  1139.     - 'then'
  1140.     - statements/expression to execute if condition is true
  1141.     - zero or more of:
  1142.     - 'elif'
  1143.     - a bool expression (the condition)
  1144.     - 'then'
  1145.     - statements/expression to execute if condition is true
  1146.     - optional:
  1147.     - 'else'
  1148.     - statements/expression to execute if all conditions are false
  1149.     - 'fi'
  1150.  
  1151. A simple example of an 'if' is:
  1152.  
  1153.     if flag then
  1154.     Print("Flag is true.\n");
  1155.     fi;
  1156.  
  1157. A more complex 'if' statement:
  1158.  
  1159.     if a <= b and not flag2 then
  1160.     if a = b then
  1161.         Print("Found!\n");
  1162.     else
  1163.         Print("Not found yet.\n");
  1164.     fi;
  1165.     elif a <= b or not flag2 then
  1166.     Print("Partly found.\n");
  1167.     else
  1168.     Print("No result.\n");
  1169.     fi;
  1170.  
  1171. Note that 'if' constructs can be nested. This is true in general of
  1172. the programming language - there are no limitations other than memory
  1173. available (nesting of constructs is limited only by the available
  1174. stack space - the required space is sufficient for a lot of nesting).
  1175. 'if' expressions can be used like this:
  1176.  
  1177.     max := if b > a then b else a fi;
  1178.  
  1179. An 'if' expression must always have an 'else' part, since there must
  1180. always be some value yielded. The various branches of an 'if'
  1181. expression must all yield the same type of value. The branches of an
  1182. 'if' expression can have statements preceeding the final result, all
  1183. separated by semicolons. E.g.
  1184.  
  1185.     result :=
  1186.     if a < b or b < c then
  1187.         Print("first case\n");
  1188.         a := b;
  1189.         c
  1190.     elif a < b then
  1191.         Print("second case\n");
  1192.         b := a;
  1193.         c
  1194.     else
  1195.         Print("third case\n");
  1196.         a := b;
  1197.         c := b;
  1198.         a
  1199.     fi;
  1200.  
  1201. This kind of construct works fine, but can be a little confusing, so
  1202. they should be used with care. Such large 'if' expressions are most
  1203. often used as the bodies of functions that return a result which
  1204. conditionally depends on something.
  1205.  
  1206.  
  1207. The 'while' Construct
  1208.  
  1209.  
  1210. A 'while' statement consists of:
  1211.  
  1212.     - 'while'
  1213.     - a bool expression (the condition)
  1214.     - 'do'
  1215.     - the loop body statement-sequence
  1216.     - 'od'
  1217.  
  1218. A 'while' loop is executed repeatedly until the condition yields
  1219. false. A 'while' loop does not return any value, i.e. it yields
  1220. 'void'. The condition can have statements before the final 'bool'
  1221. value, thus yielding a loop with its exit test in the middle. E.g.
  1222.  
  1223.     i := 10;
  1224.     while
  1225.     i := retrieveValue(i);
  1226.     i ~= 0
  1227.     do
  1228.     processValue(i);
  1229.     od;
  1230.  
  1231. Here, the sequence of execution will be:
  1232.  
  1233.     i := retrieveValue(10);    /* lets say this returns 8 */
  1234.     processValue(8);
  1235.     i := retrieveValue(8);    /* lets say this returns 3 */
  1236.     processValue(3);
  1237.     i := retrieveValue(3);    /* lets say this returns 0 */
  1238.  
  1239. and 'i' will be 0 after the 'while' loop. Programmers should use care
  1240. when using 'while' loops, since it may not be obvious when the loop
  1241. exits. The AmigaMUD server places an execution time limit on all
  1242. execution, so an infinite loop will be aborted, but, depending on what
  1243. SysAdmin has set that limit to, bad loops can have serious effects on
  1244. the performance of the server for other users. Also, aborting
  1245. execution can leave wizard-created data structures in an inconsistent
  1246. state.
  1247.  
  1248.  
  1249. The 'for' Construct
  1250.  
  1251.  
  1252. A 'for' statement consists of:
  1253.  
  1254.     - 'for'
  1255.     - local int variable name
  1256.     - 'from'
  1257.     - int expression (the start value)
  1258.     - 'upto'
  1259.     - int expression (the limit value)
  1260.     - 'do'
  1261.     - the loop body statement-sequence
  1262.     - 'od'
  1263.  
  1264. Like a 'while' loop, the 'for' loop does not return any value. The
  1265. start and limit expressions are evaluated once at the beginning of the
  1266. loop, and then the int variable is stepped by ones from the start
  1267. value upto the limit value, with the loop body executed once for each
  1268. such value. If the limit value is less than (signed integer
  1269. comparison) the start value, then the loop body is never executed.
  1270.  
  1271. 'for' loops are useful for stepping over fixed ranges, or through the
  1272. entries of a list, as in:
  1273.  
  1274.     GSetPen(nil, C_BLACK);
  1275.     for i from 1 upto 10 do
  1276.     GAMove(nil, i * 2, 30);
  1277.     for j from 1 upto 20 do
  1278.         GRMove(nil, 0, 1);
  1279.         GRDraw(nil, 0, 1);
  1280.     od;
  1281.     od;
  1282.  
  1283.     sum := 0;
  1284.     for j from 0 upto Count(listOfThings) - 1 do
  1285.     sum := sum + listOfThings[j]@intProperty;
  1286.     od;
  1287.     Print("Sum of values = ");
  1288.     IPrint(sum);
  1289.     Print(".\n");
  1290.  
  1291. When using a 'for' loop to scan down a list, make sure that code
  1292. executed in the body of the loop cannot modify the list itself. If it
  1293. can, you must use a 'while' loop, since the 'Count' of the elements in
  1294. the list will be changing.
  1295.  
  1296.  
  1297. The 'case' Construct
  1298.  
  1299.  
  1300. The 'case' construct is in some ways a generalization of the 'if'
  1301. construct. In other ways it is less general. It consists of:
  1302.  
  1303.     - 'case'
  1304.     - int expression (the selector)
  1305.     - one or more "case alternatives", which are:
  1306.     - 'default'
  1307.     - ':'
  1308.     - the alternative statements/expression
  1309.     or
  1310.     - a sequence of "case indexes", which are:
  1311.         - 'incase'
  1312.         - integer constant
  1313.         - ':'
  1314.     - the alternative statements/expression
  1315.     - 'esac'
  1316.  
  1317. Only one 'default' alternative can occur in any given 'case', and if
  1318. the 'case' is a 'case' expression, a 'default' alternative must occur.
  1319. Some examples:
  1320.  
  1321.     case whichButton
  1322.     incase LEFT_BUTTON:
  1323.     doMove(MOVE_LEFT);
  1324.     lastDirection := MOVE_LEFT;
  1325.     incase RIGHT_BUTTON:
  1326.     doMove(MOVE_RIGHT);
  1327.     lastDirection := MOVE_RIGHT;
  1328.     incase EXIT_BUTTON:
  1329.     incase LEAVE_BUTTON:
  1330.     incase OUT_BUTTON:
  1331.     doMove(MOVE_EXIT);
  1332.     lastDirection := MOVE_EXIT;
  1333.     default:
  1334.     if lastDirection ~= -1 then
  1335.         Print("You can't go that way.\n");
  1336.         lastDirection := -1;
  1337.     else
  1338.         Print("You still can't go that way.\n");
  1339.     fi;
  1340.     esac;
  1341.  
  1342.     result :=
  1343.     case retrieveThing()@intProperty
  1344.     incase 0:
  1345.         1
  1346.     incase 1:
  1347.         20
  1348.     default:
  1349.         Print("Illegal value encountered!\n");
  1350.         -1
  1351.     esac;
  1352.  
  1353. C programmers are cautioned that AmigaMUD case alternatives do not
  1354. fall through to the one beneath them. All 'case expressions' must have
  1355. a 'default' part, since some value must always result. 'case
  1356. statements' need not have one, and if the selector does not match any
  1357. of the case indexes, and the 'case' has no 'default' alternative, no
  1358. action is taken.
  1359.  
  1360.  
  1361. Function Calls
  1362.  
  1363.  
  1364. Function calls, whether to a builtin or to a user-defined function,
  1365. consist of the name of the function followed by a left parenthesis,
  1366. a comma separated list of the function parameters, and a right
  1367. parenthesis. The parentheses must be given even if the function has no
  1368. parameters. If no parentheses are given after a function name, then
  1369. the function itself is the value, with type 'action'. All function
  1370. parameters must be given, and must be of the same type as required by
  1371. the function header. If the function has a return-type of 'void', then
  1372. the function call itself yields 'void', i.e. it is a statement.
  1373. Otherwise, the function call yields the type of the function result.
  1374. Examples:
  1375.  
  1376.     Assume:
  1377.     proc f1(int i, j)int
  1378.     proc f2(string s1, s2)string
  1379.     proc f3(int i, string s)void
  1380.  
  1381.     Then:
  1382.  
  1383.     f3(f1(6, j + 7), f2("hello", "there") + "world");
  1384.  
  1385. If the function to be called is not known until run time, then the
  1386. above syntax cannot be used, since the result type of the function is
  1387. not known. Instead, the 'call' construct can be used. This form
  1388. consists of:
  1389.  
  1390.     - 'call'
  1391.     - '('
  1392.     - action expression (returns the function to call)
  1393.     - ','
  1394.     - the expected result type of the action
  1395.     - ')'
  1396.     - '('
  1397.     - the parameters for the function call
  1398.     - ')'
  1399.  
  1400. Since the expected result type is given explicitly, the system can
  1401. assume that type at "compile" time, and can check for it at run time.
  1402. The parameter count and types of the called function will always be
  1403. checked at run time. Examples:
  1404.  
  1405.     Print(call(Me()@p_pDescAction, string)() + "\n");
  1406.     i := i + call(if wantMax then max else min fi, int)(j, k);
  1407.  
  1408.  
  1409. Miscellaneous Constructs
  1410.  
  1411.  
  1412. Sometimes a function or a builtin yields a result that is not always
  1413. wanted - the call is being done for its side effects. In these cases,
  1414. it can be desireable to make it perfectly clear that the result is
  1415. being discarded, so, instead of assigning the result to some dummy
  1416. variable, the 'ignore' construct can be used. It consists of the
  1417. reserved word 'ignore' followed by any expression whose result is to
  1418. be discarded. 'ignore' always returns 'void'. E.g.
  1419.  
  1420.     ignore FindName(Me()@p_pCarrying, p_oName, "bottle");
  1421.     theBottle := FindResult();
  1422.  
  1423. As mentioned previously, there are two kinds of comments in the
  1424. AmigaMUD programming language. The first is the C-like one consisting
  1425. of an opening /*, comment text, and a closing */. The second kind of
  1426. comment is the 'note', which consists of all of the characters after
  1427. the 'note' keyword up to the end of the line. A 'note' comment is
  1428. stored in the database and will appear when the function containing it
  1429. is printed. For example:
  1430.  
  1431.     public proc complicated(thing th)void:
  1432.     note We are doing something complicated here, so be careful!
  1433.     if th@flag then
  1434.         ...
  1435.     else
  1436.         note flag not set, so don't try the tricky stuff.
  1437.         Print("The easy stuff!\n");
  1438.     fi;
  1439.     corp;
  1440.  
  1441. It is not necessary to put a semicolon after a note - they delimit
  1442. themselves, so the parser can recognize them.
  1443.  
  1444. The AmigaMUD system has limited support for direct, unnamed actions.
  1445. These are values of type 'action' and can be assigned and called. They
  1446. are typically only used in specialized circumstances, such as one-shot
  1447. actions produced by the "StringToAction" builtin. They must have
  1448. return type 'status', and consist of:
  1449.  
  1450.     - 'proc'
  1451.     - optional body statements
  1452.     - status result expression
  1453.     - 'corp'
  1454.  
  1455. For example, it is legal to do:
  1456.  
  1457.     myThing@actionProp :=
  1458.     proc
  1459.         Print("Hello there world!\n");
  1460.         succeed
  1461.     corp;
  1462.     ignore call(myThing@actionProp, status)();
  1463.  
  1464. Such procs have no symbol, so are usually less useful than normal
  1465. functions. Also, the forced 'status' result and lack of parameters or
  1466. local variables are a limiting factor. This facility may be
  1467. generalized in a future release, although there does not seem to be
  1468. much need for it.
  1469.  
  1470. The most common construct in the AmigaMUD programming language is the
  1471. assignment statement. Assignment statements consist of:
  1472.  
  1473.     - <assignment-left-hand-side>
  1474.     - ':='
  1475.     - <expression>
  1476.  
  1477. Assignment statements do not return any value, hence the concept of
  1478. "nested assignments" does not exist. Several different kinds of
  1479. <assignment-left-hand-side>'s are possible:
  1480.  
  1481.     - local variable or parameter name
  1482.     or
  1483.     - <list-expression>
  1484.     - '['
  1485.     - <int-expression>
  1486.     - ']'
  1487.     or
  1488.     - <thing-expression>
  1489.     - '@'
  1490.     - <property-expression>
  1491.  
  1492. The first variant is the obvious one of assigning a new value to a
  1493. local variable or parameter. The second is that of assigning a new
  1494. value to a specific element of a list. Note that the element indexed
  1495. by the <int-expression> must already be present in the list, else a
  1496. run-time indexing error will occur. Such indexing starts with 0 as the
  1497. first index, and "Count(<list>) - 1" as the last index.
  1498.  
  1499. The third form is used to assign a value to a property on a thing. The
  1500. property does not need to already exist - this method is the method
  1501. used to add new properties also.
  1502.  
  1503. Note that there are no global variables in AmigaMUD - values needed
  1504. outside of a single function must be stored as properties attached to
  1505. some thing.
  1506.  
  1507. Example assignment statements (all are of integer values, but the
  1508. same rules hold for any type of value):
  1509.  
  1510.     private th CreateThing(nil).
  1511.     private intProp CreateIntProp().
  1512.     private listProp CreateIntListProp().
  1513.  
  1514.     private proc testProc(int n)void:
  1515.     int i;
  1516.     list int li;
  1517.     
  1518.     i := 10;
  1519.     n := 100;
  1520.     li := CreateIntList();
  1521.     AddTail(li, 10);
  1522.     Addtail(li, 20);
  1523.     li[0] := 1;
  1524.     li[1] := 2;
  1525.     li(otherFunc()] := 6;
  1526.     th@intProp := 7;
  1527.     th@if i < 2 then intProp else otherIntProp fi := 8;
  1528.     th@listProp := li;
  1529.     th@listProp[1] := 9;
  1530.     call(th@thingProp@actionProp, list int)()[n] := i;
  1531.     corp;
  1532.  
  1533. Properties can be removed from things using the '--' construct:
  1534.  
  1535.     - <thing-expression>
  1536.     - '--';
  1537.     - <property-expression>
  1538.  
  1539. This construct is a statement - it does not return any value. Note
  1540. that it is not an error to try to delete a property from a thing when
  1541. that property does not exist on that thing. Examples:
  1542.  
  1543.     th1--intProp;
  1544.     thingFunc() -- thingPropFunc();
  1545.  
  1546.  
  1547. Expressions
  1548.  
  1549.  
  1550. Many examples of expressions have already been seen. This section will
  1551. simply list the full set of operators, in order of decreasing
  1552. precedence. The precedence of an operator is an indication of how
  1553. strongly it binds to its operands. A simple example is the following
  1554. expression:
  1555.  
  1556.     2 * 4 + 6 * 8
  1557.  
  1558. The value of this expression is 56, the sum of 2 * 4 and 6 * 8. This
  1559. is because the multiplications are done before the addition. The
  1560. multiplication operator, '*', has higher precedence than the addition
  1561. operator, '+'. The evaluation order of an expression can be changed by
  1562. the use of parentheses around a subexpression, as in:
  1563.  
  1564.     2 * (4 + 6) * 8
  1565.  
  1566. which has value 160, the product of 2, 4 + 6, and 8. So, for the
  1567. operators in the following descriptions, those described first will be
  1568. done before those described later, unless parentheses are introduced
  1569. to change the order of evaluation.
  1570.  
  1571. All expressions must start with bottom-level items. These are: 'if'-
  1572. expression, 'case'-expression, function call result, inline action,
  1573. parenthesized sub-expression, list reference, property reference,
  1574. identifier, string constant, integer constant, or any of the reserved
  1575. words 'false', 'true', 'succeed', 'fail', 'continue', 'nil',
  1576. 'ts_private', 'ts_readonly', 'ts_wizard', 'ts_public'.
  1577.  
  1578. 'if'- expressions, 'case'-expressions, function calls, inline actions
  1579. and parenthesized sub-expressions have all been covered previously.
  1580. Similarly, the bottom-level items were explained in the section on
  1581. lexical entities.
  1582.  
  1583. A list reference used as an expression looks exactly like the left-
  1584. hand-side of an assignment to a list element. The same rule holds -
  1585. the indexed element must exist in the list.
  1586.  
  1587. A property reference also looks just like the corresponding assignment
  1588. left-hand-side. When a property is being searched for on a thing, it
  1589. might not be found. If the thing has a parent thing (established when
  1590. the thing is created), then the search will continue with that parent
  1591. thing. Any value found on the parent will be used. Similarly, if the
  1592. value is not found on the parent, then the parent's parent will be
  1593. searched, etc. This "inheritance" of properties can be used to save
  1594. considerable space in the database, and to save a lot of effort when
  1595. creating new things which are all similar. Note that things can only
  1596. have a single parent - AmigaMUD does not have "multiple inheritance".
  1597.  
  1598. Good examples of the use of inheritance are the monsters in the
  1599. Proving Grounds in the standard scenario. Each monster has a model,
  1600. defined in the file "monsters.m", and when examples of that monster
  1601. are needed, they inherit most of their properties (name, description,
  1602. actions, speed, armour class, special actions, etc.) from the model.
  1603. Only values which need to be different (such as current hit points)
  1604. are stored on the specific instance. The fact that property assignment
  1605. only assigns to the descendant thing makes this use automatic.
  1606.  
  1607. A tricky use of inheritance can occur with things which inherit from
  1608. an ancestor, as in:
  1609.  
  1610.     private ancestor CreateThing(nil).
  1611.     private child CreateThing(ancestor).
  1612.     private intProp CreateIntProp().
  1613.  
  1614.     ancestor@intProp := 100.
  1615.  
  1616.     ...
  1617.  
  1618.     child@intProp := child@intProp - 1;
  1619.     if child@intProp = 0 then
  1620.         child -- intProp;
  1621.     fi;
  1622.  
  1623. This assignment statement will get the 100 value from the ancestor
  1624. thing the first time it is executed, and will add the property with
  1625. value 99 to the child. On successive executions, it will modify the
  1626. property on the child. When the value reaches 0, the property is
  1627. deleted, and will again inherit the 100 from the ancestor.
  1628.  
  1629. If a property which is not present on the thing is referenced, the
  1630. value yielded depends on the type of the property:
  1631.  
  1632.     status - fail
  1633.     bool - false
  1634.     int - 0
  1635.     string - "" (an empty string)
  1636.     thing, action, grammar, list - nil
  1637.  
  1638. This defaulting of values is usually useful, but can occasionally be a
  1639. bit of a nuisance. The main use is to save storage - the programmer
  1640. can count on these values for missing properties, and hence can
  1641. arrange to not store them explicitly. This works quite well for flag
  1642. values.
  1643.  
  1644.  
  1645. Unary Negation: -
  1646.  
  1647.     This operator appears before an int expression and negates the
  1648.     value of the expression. E.g.
  1649.  
  1650.     -6
  1651.     -(intvar * 7)
  1652.     -th@func(7, -6)
  1653.  
  1654. Bitwise And: &
  1655. Bitwise Exclusive-Or: ><
  1656. Bitwise Shift Left: <<
  1657. Bitwise Shift Right: >>
  1658.  
  1659.     These operators all operate on int values. They all have the same
  1660.     precedence, so are evaluated left-to-right when used together in
  1661.     an expression. The Bitwise And operator combines two int values in
  1662.     a bit-by-bit fashion - each of the 32 bits of the result is a '1'
  1663.     bit only if both of the corresponding bits in the operands were
  1664.     also '1' bits. The Bitwise Exclusive-Or operator yields a '1' bit
  1665.     in the result only if the corresponding bits in the operands are
  1666.     different. The Shift-Left operator shifts its left-hand operand
  1667.     left by the number of bits specified in its right-hand operand.
  1668.     Similarly, the Shift-Right operator shifts its left-hand operand
  1669.     right by the number of bits specified in its right-hand operand.
  1670.     If the right-hand operand to a shift operator is negative, it is
  1671.     not defined what the result will be. Also, if the right-hand
  1672.     operand is greater than 32 (the number of bits in the left-hand
  1673.     operand), the result is not defined. E.g.
  1674.  
  1675.     0b1100 & 0b0101 => 0b0100
  1676.     0b1100 >< 0b0101 => 0b1001
  1677.     0b001100 << 2 => 0b110000
  1678.     0b001100 << 3 => 0b1100000
  1679.     0b001100 >> 2 => 0b000011
  1680.  
  1681.     a & b >< c << d >> e == ((((a & b) >< c) << d) >> e)
  1682.  
  1683. Bitwise Inclusive-Or: |
  1684.  
  1685.     This operator requires two int operands and returns an int result.
  1686.     Each of the 32 bits in the result is a '1' if the corresponding
  1687.     bit in either of the operands is a '1'. E.g.
  1688.  
  1689.     0b1100 | 0b0101 => 0b1101
  1690.     a & b | c << d | e == (a & b) | (c << d) | e
  1691.  
  1692. Integer Multiplication: *
  1693. Integer Division: /
  1694. Integer Remainder: %
  1695.  
  1696.     These operators take a pair of int operands and yield an int
  1697.     result. Division or remainder by zero will be trapped at runtime.
  1698.  
  1699. Integer Addition: +
  1700. Integer Subtraction: -
  1701. String Concatenation: +
  1702.  
  1703.     Note that both integer addition and string concatenation have the
  1704.     same operator symbol. The operations are distinguished by the
  1705.     types of their operands. E.g.
  1706.  
  1707.     6 + 128 => 134
  1708.     6 - 128 => -122
  1709.     "hello" + "world" => "helloworld"
  1710.     "hello " + "world" => "hello world"
  1711.     "" + "" => ""
  1712.  
  1713. Integer Comparisons: <= < = ~= > >=
  1714. String Comparisons: <= < = ~= == > >=
  1715. Other Comparisons: = ~=
  1716.  
  1717.     All comparisons yield a bool value. All comparisons must have
  1718.     operands which are both of the same type. The integer comparisons
  1719.     are 32 bit signed integer comparisons. The string comparisons use
  1720.     comparisons based on the ASCII values of the characters in the
  1721.     strings. The '==' operator converts all letters to upper case (or
  1722.     lower case if you prefer!) before doing the comparison. It is
  1723.     useful when dealing with user input that might be capitalized.
  1724.     Things, actions, lists, properties, etc. can be compared for
  1725.     equality or inequality. E.g.
  1726.  
  1727.     6 < 12 => true
  1728.     -6 < -12 => false
  1729.     "hello" = "hello" => true
  1730.     "hello" <= "hello" => true
  1731.     "hello" = "Hello" => false
  1732.     "hello" == "Hello" => true
  1733.  
  1734. Logical Not: not
  1735.  
  1736.     This prefix operator reverses the logical value of its bool
  1737.     operand. E.g.
  1738.  
  1739.     not true => false
  1740.     not false => true
  1741.     not 6 < 10 => false
  1742.  
  1743. Logical And: and
  1744.  
  1745.     This operator takes two bool operands and returns a bool result
  1746.     that is true only if both operands are true. Technically, this is
  1747.     a language construct rather than a true operator, since the
  1748.     AmigaMUD interpreter will not even try to evaluate the right-hand
  1749.     operand if the left-hand operand evaluates to false, since it
  1750.     knows that the right-hand operand will not affect the final
  1751.     result. This behaviour is part of the language definition and will
  1752.     not change; thus the programmer is correct to write things like:
  1753.  
  1754.     th ~= nil and th@field = 2
  1755.  
  1756. Logical Or: or
  1757.  
  1758.     This operator takes two bool operands and returns a bool result
  1759.     that is true if either of its operands is true. Similar to the
  1760.     'and' operator, the 'or' operator will not evaluate its right-hand
  1761.     operand if its left-hand operand is "true".
  1762.  
  1763.  
  1764. Further Reading
  1765.  
  1766. This document has informally defined the AmigaMUD programming
  1767. language. This information allows wizards and apprentices to write and
  1768. run valid AmigaMUD programs, but it has not given enough information
  1769. to allow them to write meaningful programs, or programs that fit in
  1770. with the supplied standard scenario. Further documents relevant to
  1771. programming are:
  1772.  
  1773.     ProgConcepts.txt - discusses some classes of builtin functions and
  1774.     how to use them. This includes parsing, effects handling,
  1775.     graphics, character manipulation, etc.
  1776.  
  1777.     Builtins.txt - this is a reference manual for the builtin
  1778.     functions. It lists and describes all of the builtins, in
  1779.     alphabetical order.
  1780.  
  1781.     Scenario.txt - this document is a rambling discussion of how the
  1782.     standard scenario is set up, the utility functions it
  1783.     provides, how to build from it, and how to change how it
  1784.     works.
  1785.