verbs go | get | drop | inventory | quit | help
The nouns are
nouns north | south | east | west | lime | beer | card | sign | program | homework | money | form | coupon
Obviously, Madventure does not allow all combinations of verbs and nouns. Indeed, the following patterns are the only legal ones:
LegalCmds go direction | get item | drop item | inventory | quit | help direction north | south | east | west item lime | beer | card | sign | program | homework | money | form | coupon
However, the pattern does not enforce this grammar. It just locates a noun and a verb on the line and, if found, sets the noun
and verb
variables to appropriate values to denote the keywords it finds. By letting the main program handle the parsing, the program is somewhat more flexible.
There are two main patterns in the Madventure program: NounPat
and VerbPat
. These patterns match words (nouns or verbs) using a regular expression like the following:
(ARB* ` ` | ) word (` ` | EOS)
This regular expression matches a word that appears at the beginning of a sentence, at the end of a sentence, anywhere in the middle of a sentence, or a sentence consisting of a single word. Madventure uses a macro (MatchNoun
or MatchVerb
) to create an expression for each noun and verb in the above expression.
To get an idea of how Madvent processes words, consider the following VerbPat
pattern:
VerbPat pattern {sl_match2, MatchGo} MatchVerb MatchGO, MatchGet, "GO", 1 MatchVerb MatchGet, MatchDrop, "GET", 2 MatchVerb MatchDrop, MatchInv, "DROP", 3 MatchVerb MatchInv, MatchQuit, "INVENTORY", 4 MatchVerb MatchQuit, MatchHelp, "QUIT", 5 MatchVerb MatchHelp, 0, "HELP", 6
The MatchVerb
macro expects four parameters. The first is an arbitrary pattern name; the second is a link to the next pattern in the list; the third is the string to match, and the fourth is a number that the matching routines will store into the verb
variable if that string matches (by default, the verb
variable contains zero). It is very easy to add new verbs to this list. For example, if you wanted to allow "run" and "walk" as synonyms for the "go" verb, you would just add two patterns to this list:
VerbPat pattern {sl_match2, MatchGo} MatchVerb MatchGO, MatchGet, "GO", 1 MatchVerb MatchGet, MatchDrop, "GET", 2 MatchVerb MatchDrop, MatchInv, "DROP", 3 MatchVerb MatchInv, MatchQuit, "INVENTORY", 4 MatchVerb MatchQuit, MatchHelp, "QUIT", 5 MatchVerb MatchHelp, MatchRun, "HELP", 6 MatchVerb MatchRun, MatchWalk, "RUN", 1 MatchVerb MatchWalk, 0, "WALK", 1
There are only two things to consider when adding new verbs: first, don't forget that the next field of the last verb should contain zero; second, the current version of Madventure only allows up to seven verbs. If you want to add more you will need to make a slight modification to the main program (more on that, later). Of course, if you only want to create synonyms, as we've done here, you simply reuse existing verb values so there is no need to modify the main program.
When you call the match
routine and pass it the address of the VerbPat
pattern, it scans through the input string looking for the first verb. If it finds that verb ("GO") it sets the verb
variable to the corresponding verb value at the end of the pattern. If match
cannot find the first verb, it tries the second. If that fails, it tries the third, and so on. If match
cannot find any of the verbs in the input string, it does not modify the verb
variable (which contains zero). If there are two or more of the above verbs on the input line, match will locate the first verb in the verb list above. This may not be the first verb appearing on the line. For example, if you say "Let's get the money and go north" the match routine will match the "go" verb, not the "get" verb. By the same token, the NounPat
pattern would match the north noun, not the money noun. So this command would be identical to "GO NORTH."
The MatchNoun is almost identical to the MatchVerb macro; there is, however, one difference - the MatchNoun macro has an extra parameter which is the name of the data structure representing the given object (if there is one). Basically, all the nouns (in this version of Madventure) except NORTH, SOUTH, EAST, and WEST have some sort of data structure associated with them.
The maze in Madventure consists of nine rooms defined by the data structure:
Room struct north word ? south word ? west word ? east word ? ItemList word MaxWeight dup (?) Description word ? Room ends
The north
, south
, west
, and east
fields contain near pointers to other rooms. The program uses the CurRoom
variable to keep track of the player's current position in the maze. When the player issues a "GO" command of some sort, Madventure copies the appropriate value from the north
, south
, west
, or east
field to the CurRoom
variable, effectively changing the room the user is in. If one of these pointers is NULL, then the user cannot move in that direction.
The direction pointers are independent of one another. If you issue the command "GO NORTH" and then issue the command "GO SOUTH" upon arriving in the new room, there is no guarantee that you will wind up in the original room. The south
field of the second room may not point at the room that led you there. Indeed, there are several cases in the Madventure game where this occurs.
The ItemList
array contains a list of near pointers to objects that could be in the room. In the current version of this game, the objects are all the nouns except north, south, east, and west. The player can carry these objects from room to room (indeed, that is the major purpose of this game). Up to MaxWeight
objects can appear in the room (MaxWeight is an assembly time constant that is currently four; so there are a maximum of four items in any one given room). If an entry in the ItemList
is non-NULL, then it is a pointer to an Item
object. There may be zero to MaxWeight
objects in a room.
The Description
field contains a pointer to a zero terminated string that describes the room. The program prints this string each time through the command loop to keep the player oriented.
The second major data type in Madventure is the Item
structure. This structure takes the form:
Item struct Value word ? Weight word ? Key word ? ShortDesc word ? LongDesc word ? WinDesc word ? Item ends
The Value
field contains an integer value awarded to the player when the player drops this object in the appropriate room. This is how the user scores points.
The Weight
field usually contains one or two and determines how much this object "weighs." The user can only carry around MaxWeight
units of weight at any one given time. Each time the user picks up an object, the weight of that object is added to the user's total weight. When the user drops an object, Madventure subtracts the object's weight from the total.
The Key
field contains a pointer to a room associated with the object. When the user drops the object in the Key
room, the user is awarded the points in the Value
field and the object disappears from the game. If the user drops the object in some other room, the object stays in that room until the user picks it up again.
The ShortDesc
, LongDesc
, and WinDesc
fields contain pointers to zero terminated strings. Madventure prints the ShortDesc
string in response to an INVENTORY command. It prints the LongDesc
string when describing a room's contents. It prints the WinDesc
string when the user drops the object in its Key
room and the object disappears from the game.
The Madventure main program is deceptively simple. Most of the logic is hidden in the pattern matching routines and in the parsing routine. We've already discussed the pattern matching code; the only important thing to remember is that it initializes the noun and verb variables with a value uniquely identifying each noun and verb. The main program's logic uses these two values as an index into a two dimensional table that takes the following form:
No Verb | GO | GET | DROP | Inventory | Quit | Help | |
---|---|---|---|---|---|---|---|
No Noun | - | - | - | - | Inventory | Quit | Help |
North | - | Do North | - | - | - | - | - |
South | - | Do South | - | - | - | - | - |
East | - | Do East | - | - | - | - | - |
West | - | Do West | - | - | - | - | - |
Lime | - | - | Get Item | Drop Item | - | - | - |
Beer | - | - | Get Item | Drop Item | - | - | - |
Card | - | - | Get Item | Drop Item | - | - | - |
Sign | - | - | Get Item | Drop Item | - | - | - |
Program | - | - | Get Item | Drop Item | - | - | - |
Homework | - | - | Get Item | Drop Item | - | - | - |
Money | - | - | Get Item | Drop Item | - | - | - |
Form | - | - | Get Item | Drop Item | - | - | - |
Coupon | - | - | Get Item | Drop Item | - | - | - |
; MADVENT.ASM ; ; This is a "shell" of an adventure game that you can use to create ; your own adventure style games. .xlist .286 include stdlib.a includelib stdlib.lib matchfuncs .list dseg segment para public 'data' ; Equates: NULL equ 0 MaxWeight equ 4 ;Max weight user can carry at one time. ; The "ROOM" data structure defines a room, or area, where a player can ; go. The NORTH, SOUTH, EAST, and WEST fields contain the address of ; the rooms to the north, south, east, and west of the room. The game ; transfers control to the room whose address appears in these fields ; when the player supplies a GO NORTH, GO SOUTH, etc., command. ; ; The ITEMLIST field contains a list of pointers to objects appearing ; in this room. In this game, the user can pick up and drop these ; objects (if there are any present). ; ; The DESCRIPTION field contains a (near) address of a short description ; of the current room/area. Room struct north word ? ;Near pointers to other structures where south word ? ; we will wind up on the GO NORTH, GO SOUTH, west word ? ; etc., commands. east word ? ItemList word MaxWeight dup (?) Description word ? ;Description of room. Room ends ; The ITEM data structure describes the objects that may appear ; within a room (in the ITEMLIST above). The VALUE field contains ; the number of points this object is worth if the user drops it ; off in the proper room (i.e, solves the puzzle). The WEIGHT ; field provides the weight of this object. The user can only ; carry four units of weight at a time. This field is usually ; one, but may be more for larger objects. The KEY field is the ; address of the room where this object must be dropped to solve ; the problem. The SHORTDESC field is a pointer to a string that ; the program prints when the user executes an INVENTORY command. ; LONGDESC is a pointer to a string the program prints when des- ; cribing the contents of a room. The WINDESC field is a pointer ; to a string that the program prints when the user solves the ; appropriate puzzle. Item struct Value word ? Weight word ? Key word ? ShortDesc word ? LongDesc word ? WinDesc word ? Item ends ; State variables for the player: CurRoom word Room1 ;Room the player is in. ItemsOnHand word MaxWeight dup (?) ;Items the player carries. CurWeight word 0 ;Weight of items carried. CurScore word 15 ;Player's current score. TotalCounter word 9 ;Items left to place. Noun word 0 ;Current noun value. Verb word 0 ;Current verb value. NounPtr word 0 ;Ptr to current noun item. ; Input buffer for commands InputLine byte 128 dup (?) ; The following macros generate a pattern which will match a single word ; which appears anywhere on a line. In particular, they match a word ; at the beginning of a line, somewhere in the middle of the line, or ; at the end of a line. This program defines a word as any sequence ; of character surrounded by spaces or the beginning or end of a line. ; ; MatchNoun/Verb matches lines defined by the regular expression: ; ; (ARB* ' ' | e) string (' ' | EOS) MatchNoun macro Name, next, WordString, ItemVal, ItemPtr local WS1, WS2, WS3, WS4 local WS5, WS6, WordStr Name Pattern {sl_match2, WS1, next} WS1 Pattern {MatchStr, WordStr, WS2, WS5} WS2 Pattern {arb,0,0,WS3} WS3 Pattern {Matchchar, ' ',0, WS4} WS4 Pattern {MatchStr, WordStr, 0, WS5} WS5 Pattern {SetNoun,ItemVal,0,WS6} WS6 Pattern {SetPtr, ItemPtr,0,MatchEOS} WordStr byte WordString byte 0 endm MatchVerb macro Name, next, WordString, ItemVal local WS1, WS2, WS3, WS4 local WS5, WordStr Name Pattern {sl_match2, WS1, next} WS1 Pattern {MatchStr, WordStr, WS2, WS5} WS2 Pattern {arb,0,0,WS3} WS3 Pattern {Matchchar, ' ',0, WS4} WS4 Pattern {MatchStr, WordStr, 0, WS5} WS5 Pattern {SetVerb,ItemVal,0,MatchEOS} WordStr byte WordString byte 0 endm ; Generic patterns which most of the patterns use: MatchEOS Pattern {EOS,0,MatchSpc} MatchSpc Pattern {MatchChar,' '} ; Here are the list of nouns allowed in this program. NounPat pattern {sl_match2, MatchNorth} MatchNoun MatchNorth, MatchSouth, "NORTH", 1, 0 MatchNoun MatchSouth, MatchEast, "SOUTH", 2, 0 MatchNoun MatchEast, MatchWest, "EAST", 3, 0 MatchNoun MatchWest, MatchLime, "WEST", 4, 0 MatchNoun MatchLime, MatchBeer, "LIME", 5, Item3 MatchNoun MatchBeer, MatchCard, "BEER", 6, Item9 MatchNoun MatchCard, MatchSign, "CARD", 7, Item2 MatchNoun MatchSign, MatchPgm, "SIGN", 8, Item1 MatchNoun MatchPgm, MatchHW, "PROGRAM", 9, Item7 MatchNoun MatchHW, MatchMoney, "HOMEWORK", 10, Item4 MatchNoun MatchMoney, MatchForm, "MONEY", 11, Item5 MatchNoun MatchForm, MatchCoupon, "FORM", 12, Item6 MatchNoun MatchCoupon, 0, "COUPON", 13, Item8 ; Here is the list of allowable verbs. VerbPat pattern {sl_match2, MatchGo} MatchVerb MatchGO, MatchGet, "GO", 1 MatchVerb MatchGet, MatchDrop, "GET", 2 MatchVerb MatchDrop, MatchInv, "DROP", 3 MatchVerb MatchInv, MatchQuit, "INVENTORY", 4 MatchVerb MatchQuit, MatchHelp, "QUIT", 5 MatchVerb MatchHelp, 0, "HELP", 6 ; Data structures for the "maze". Room1 room {Room1, Room5, Room4, Room2, {Item1,0,0,0}, Room1Desc} Room1Desc byte "at the Commons",0 Item1 item {10,2,Room3,GS1,GS2,GS3} GS1 byte "a big sign",0 GS2 byte "a big sign made of styrofoam with funny " byte "letters on it.",0 GS3 byte "The ETA PI Fraternity thanks you for return" byte "ing their sign, they",cr,lf byte "make you an honorary life member, as long as " byte "you continue to pay",cr,lf byte "your $30 monthly dues, that is.",0 Room2 room {NULL, Room5, Room1, Room3, {Item2,0,0,0}, Room2Desc} Room2Desc byte 'at the "C" on the hill above campus',0 Item2 item {10,1,Room1,LC1,LC2,LC3} LC1 byte "a lunch card",0 LC2 byte "a lunch card which someone must have " byte "accidentally dropped here.", 0 LC3 byte "You get a big meal at the Commons cafeteria" byte cr,lf byte "It would be a good idea to go visit the " byte "student health center",cr,lf byte "at this time.",0 Room3 room {NULL, Room6, Room2, Room2, {Item3,0,0,0}, Room3Desc} Room3Desc byte "at ETA PI Frat House",0 Item3 item {10,2,Room2,BL1,BL2,BL3} BL1 byte "a bag of lime",0 BL2 byte "a bag of baseball field lime which someone " byte "is obviously saving for",cr,lf byte "a special occasion.",0 BL3 byte "You spread the lime out forming a big '++' " byte "after the 'C'",cr,lf byte "Your friends in Computer Science hold you " byte "in total awe.",0 Room4 room {Room1, Room7, Room7, Room5, {Item4,0,0,0}, Room4Desc} Room4Desc byte "in Dr. John Smith's Office",0 Item4 item {10,1,Room7,HW1,HW2,HW3} HW1 byte "a homework assignment",0 HW2 byte "a homework assignment which appears to " byte "to contain assembly language",0 HW3 byte "The grader notes that your homework " byte "assignment looks quite",cr,lf byte "similar to someone else's assignment " byte "in the class and reports you",cr,lf byte "to the instructor.",0 Room5 room {Room1, Room9, Room7, Room2, {Item5,0,0,0}, Room5Desc} Room5Desc byte "in the computer lab",0 Item5 item {10,1,Room9,M1,M2,M3} M1 byte "some money",0 M2 byte "several dollars in an envelope in the " byte "trashcan",0 M3 byte "The waitress thanks you for your " byte "generous tip and gets you",cr,lf byte "another pitcher of beer. " byte "Then she asks for your ID.",cr,lf byte "You are at least 21 aren't you?",0 Room6 room {Room3, Room9, Room5, NULL, {Item6,0,0,0}, Room6Desc} Room6Desc byte "at the campus book store",0 Item6 item {10,1,Room8,AD1,AD2,AD3} AD1 byte "an add/drop/change form",0 AD2 byte "an add/drop/change form filled out for " byte "assembly to get a letter grade",0 AD3 byte "You got the form in just in time. " byte "It would have been a shame to",cr,lf byte "have had to retake assembly because " byte "you didn't realize you needed to ",cr,lf byte "get a letter grade in the course.",0 Room7 room {Room1, Room7, Room4, Room8, {Item7,0,0,0}, Room7Desc} Room7Desc byte "in the assembly lecture",0 Item7 item {10,1,Room5,AP1,AP2,AP3} AP1 byte "an assembly language program",0 AP2 byte "an assembly language program due in " byte "the assemblylanguage class.",0 AP3 byte "The sample program the instructor gave " byte "you provided all the information",cr,lf byte "you needed to complete your assignment. " byte "You finish your work and",cr,lf byte "head to the local pub to celebrate." byte cr,lf,0 Room8 room {Room5, Room6, Room7, Room9, {Item8,0,0,0}, Room8Desc} Room8Desc byte "at the Registrar's office",0 Item8 item {10,1,Room6,C1,C2,C3} C1 byte "a coupon",0 C2 byte "a coupon good for a free text book",0 C3 byte 'You get a free copy of "Cliff Notes for ' byte 'The Art of Assembly',cr,lf byte 'Language Programming" Alas, it does not ' byte "provide all the",cr,lf byte "information you need for the class, so you " byte "sell it back during",cr,lf byte "the book buy-back period.",0 Room9 room {Room6, Room9, Room8, Room3, {Item9,0,0,0}, Room9Desc} Room9Desc byte "at The Pub",0 Item9 item {10,2,Room4,B1,B2,B3} B1 byte "a pitcher of beer",0 B2 byte "an ice cold pitcher of imported beer",0 B3 byte "Dr. Smith thanks you profusely for your " byte "good taste in brews.",cr,lf byte "He then invites you to the pub for a " byte "round of pool and",cr,lf byte "some heavy duty hob-nobbing, " byte "CS Department style.",0 dseg ends cseg segment para public 'code' assume ds:dseg ; SetNoun- Copies the value in SI (the matchparm parameter) to the ; NOUN variable. SetNoun proc far push ds mov ax, dseg mov ds, ax mov Noun, si mov ax, di stc pop ds ret SetNoun endp ; SetVerb- Copies the value in SI (the matchparm parameter) to the ; VERB variable. SetVerb proc far push ds mov ax, dseg mov ds, ax mov Verb, si mov ax, di stc pop ds ret SetVerb endp ; SetPtr- Copies the value in SI (the matchparm parameter) to the ; NOUNPTR variable. SetPtr proc far push ds mov ax, dseg mov ds, ax mov NounPtr, si mov ax, di stc pop ds ret SetPtr endp ; CheckPresence- ; BX points at an item. DI points at an item list. This ; routine checks to see if that item is present in the ; item list. Returns Carry set if item was found, ; clear if not found. CheckPresence proc ; MaxWeight is an assembly-time adjustable constant that determines ; how many objects the user can carry, or can be in a room, at one ; time. The following repeat macro emits "MaxWeight" compare and ; branch sequences to test each item pointed at by DS:DI. ItemCnt = 0 repeat MaxWeight cmp bx, [di+ItemCnt] je GotIt ItemCnt = ItemCnt+2 endm clc ret GotIt: stc ret CheckPresence endp ; RemoveItem- BX contains a pointer to an item. DI contains a pointer ; to an item list which contains that item. This routine ; searches the item list and removes that item from the ; list. To remove an item from the list, we need only ; store a zero (NULL) over the top of its pointer entry ; in the list. RemoveItem proc ; Once again, we use the repeat macro to automatically generate a chain ; of compare, branch, and remove code sequences for each possible item ; in the list. ItemCnt = 0 repeat MaxWeight local NotThisOne cmp bx, [di+ItemCnt] jne NotThisOne mov word ptr [di+ItemCnt], NULL ret NotThisOne: ItemCnt = ItemCnt+2 endm ret RemoveItem endp ; InsertItem- BX contains a pointer to an item, DI contains a pointer to ; and item list. This routine searches through the list for ; the first empty spot and copies the value in BX to that point. ; It returns the carry set if it succeeds. It returns the ; carry clear if there are no empty spots available. InsertItem proc ItemCnt = 0 repeat MaxWeight local NotThisOne cmp word ptr [di+ItemCnt], 0 jne NotThisOne mov [di+ItemCnt], bx stc ret NotThisOne: ItemCnt = ItemCnt+2 endm clc ret InsertItem endp ; LongDesc- Long description of an item. ; DI points at an item - print the long description of it. LongDesc proc push di test di, di jz NoDescription mov di, [di].item.LongDesc puts putcr NoDescription: pop di ret LongDesc endp ; ShortDesc- Print the short description of an object. ; DI points at an item (possibly NULL). Print the short description for it. ShortDesc proc push di test di, di jz NoDescription mov di, [di].item.ShortDesc puts putcr NoDescription: pop di ret ShortDesc endp ; Describe: "CurRoom" points at the current room. Describe it and its ; contents. Describe proc push es push bx push di mov di, ds mov es, di mov bx, CurRoom mov di, [bx].room.Description print byte "You are currently ",0 puts putcr print byte "Here you find the following:",cr,lf,0 ; For each possible item in the room, print out the long description ; of that item. The repeat macro generates a code sequence for each ; possible item that could be in this room. ItemCnt = 0 repeat MaxWeight mov di, [bx].room.ItemList[ItemCnt] call LongDesc ItemCnt = ItemCnt+2 endm pop di pop bx pop es ret Describe endp ; Here is the main program, that actually plays the game. Main proc mov ax, dseg mov ds, ax mov es, ax meminit print byte cr,lf,lf,lf,lf,lf byte "Welcome to ",'"MADVENTURE"',cr,lf byte 'If you need help, type the command "HELP"' byte cr,lf,0 RoomLoop: dec CurScore ;One point for each move. jnz NotOverYet ; If they made too many moves without dropping anything properly, boot them ; out of the game. print byte "WHOA! You lost! You get to join the legions of " byte "the totally lame",cr,lf byte 'who have failed at "MADVENTURE"',cr,lf,0 jmp Quit ; Okay, tell 'em where they are and get a new command from them. NotOverYet: putcr call Describe print byte cr,lf byte "Command: ",0 lesi InputLine gets strupr ;Ignore case by converting to U.C. ; Okay, process the command. Note that we don't actually check to see ; if there is a properly formed sentence. Instead, we just look to see ; if any important keywords are on the line. If they are, the pattern ; matching routines load the appropriate values into the noun and verb ; variables (nouns: north=1, south=2, east=3, west=4, lime=5, beer=6, ; card=7, sign=8, program=9, homework=10, money=11, form=12, coupon=13; ; verbs: go=1, get=2, drop=3, inventory=4, quit=5, help=6). ; ; This code uses the noun and verb variables as indexes into a two ; dimensional array whose elements contain the address of the code ; to process the given command. If a given command does not make ; any sense (e.g., "go coupon") the entry in the table points at the ; bad command code. mov Noun, 0 mov Verb, 0 mov NounPtr, 0 ldxi VerbPat xor cx, cx match lesi InputLine ldxi NounPat xor cx, cx match ; Okay, index into the command table and jump to the appropriate ; handler. Note that we will cheat and use a 14x8 array. There ; are really only seven verbs, not eight. But using eight makes ; things easier since it is easier to multiply by eight than seven. mov si, CurRoom ;The commands expect this here. mov bx, Noun shl bx, 3 ;Multiply by eight. add bx, Verb shl bx, 1 ;Multiply by two - word table. jmp cseg:jmptbl[bx] ; The following table contains the noun x verb cross product. ; The verb values (in each row) are the following: ; ; NONE GO GET DROP INVNTRY QUIT HELP unused ; 0 1 2 3 4 5 6 7 ; ; There is one row for each noun (plus row zero, corresponding to no ; noun found on line). jmptbl word Bad ;No noun, no verb word Bad ;No noun, GO word Bad ;No noun, GET word Bad ;No noun, DROP word DoInventory ;No noun, INVENTORY word QuitGame ;No noun, QUIT word DoHelp ;No noun, HELP word Bad ;N/A NorthCmds word Bad, GoNorth, Bad, Bad, Bad, Bad, Bad, Bad SouthCmds word Bad, GoSouth, Bad, Bad, Bad, Bad, Bad, Bad EastCmds word Bad, GoEast, Bad, Bad, Bad, Bad, Bad, Bad WestCmds word Bad, GoWest, Bad, Bad, Bad, Bad, Bad, Bad LimeCmds word Bad, Bad, GetItem, DropItem, Bad, Bad, Bad, Bad BeerCmds word Bad, Bad, GetItem, DropItem, Bad, Bad, Bad, Bad CardCmds word Bad, Bad, GetItem, DropItem, Bad, Bad, Bad, Bad SignCmds word Bad, Bad, GetItem, DropItem, Bad, Bad, Bad, Bad ProgramCmds word Bad, Bad, GetItem, DropItem, Bad, Bad, Bad, Bad HomeworkCmds word Bad, Bad, GetItem, DropItem, Bad, Bad, Bad, Bad MoneyCmds word Bad, Bad, GetItem, DropItem, Bad, Bad, Bad, Bad FormCmds word Bad, Bad, GetItem, DropItem, Bad, Bad, Bad, Bad CouponCmds word Bad, Bad, GetItem, DropItem, Bad, Bad, Bad, Bad ; If the user enters a command we don't know how to process, print an ; appropriate error message down here. Bad: printf byte "I'm sorry, I don't understand how to '%s'\n",0 dword InputLine jmp NotOverYet ; Handle the movement commands here. ; Movements are easy, all we've got to do is fetch the NORTH, SOUTH, ; EAST, or WEST pointer from the current room's data structure and ; set the current room to that address. The only catch is that some ; moves are not legal. Such moves have a NULL (zero) in the direction ; field. A quick check for this case handles illegal moves. GoNorth: mov si, [si].room.North jmp MoveMe GoSouth: mov si, [si].room.South jmp MoveMe GoEast: mov si, [si].room.East jmp MoveMe GoWest: mov si, [si].room.West MoveMe: test si, si ;See if move allowed. jnz SetCurRoom printf byte "Sorry, you cannot go in this direction." byte cr, lf, 0 jmp RoomLoop SetCurRoom: mov CurRoom, si ;Move to new room. jmp RoomLoop ; Handle the GetItem command down here. At this time the user ; has entered GET and some noun that the player can pick up. ; First, we will make sure that item is in this room. ; Then we will check to make sure that picking up this object ; won't overload the player. If these two conditions are met, ; we'll transfer the object from the room to the player. GetItem: mov bx, NounPtr ;Ptr to item user wants. mov si, CurRoom lea di, [si].room.ItemList ;Ptr to item list in di. call CheckPresence ;See if in room. jc GotTheItem printf byte "Sorry, that item is not available here." byte cr, lf, 0 jmp RoomLoop ; Okay, see if picking up this object will overload the player. GotTheItem: mov ax, [bx].Item.Weight add ax, CurWeight cmp ax, MaxWeight jbe WeightOkay printf byte "Sorry, you are already carrying too many items " byte "to safely carry\nthat object\n",0 jmp RoomLoop ; Okay, everything's cool, transfer the object from the room to the user. WeightOkay: mov CurWeight, ax ;Save new weight. call RemoveItem ;Remove item from room. lea di, ItemsOnHand ;Ptr to player's list. call InsertItem jmp RoomLoop ; Handle dropped objects down here. DropItem: lea di, ItemsOnHand ;See if the user has mov bx, NounPtr ; this item on hand. call CheckPresence jc CanDropIt1 printf byte "You are not currently holding that item\n",0 jmp RoomLoop ; Okay, let's see if this is the magic room where this item is ; supposed to be dropped. If so, award the user some points for ; properly figuring this out. CanDropIt1: mov ax, [bx].item.key cmp ax, CurRoom jne JustDropIt ; Okay, success! Print the winning message for this object. mov di, [bx].item.WinDesc puts putcr ; Award the user some points. mov ax, [bx].item.value add CurScore, ax ; Since the user dropped it, they can carry more things now. mov ax, [bx].item.Weight sub CurWeight, ax ; Okay, take this from the user's list. lea di, ItemsOnHand call RemoveItem ; Keep track of how may objects the user has successfully dropped. ; When this counter hits zero, the game is over. dec TotalCounter jnz RoomLoop printf byte "Well, you've found where everything goes " byte "and your score is %d.\n" byte "You might want to play again and see if " byte "you can get a better score.\n",0 dword CurScore jmp Quit ; If this isn't the room where this object belongs, just drop the thing ; off. If this object won't fit in this room, ignore the drop command. JustDropIt: mov di, CurRoom lea di, [di].room.ItemList call InsertItem jc DroppedItem printf byte "There is insufficient room to leave " byte "that item here.\n",0 jmp RoomLoop ; If they can drop it, do so. Don't forget we've just unburdened the ; user so we need to deduct the weight of this object from what the ; user is currently carrying. DroppedItem: lea di, ItemsOnHand call RemoveItem mov ax, [bx].item.Weight sub CurWeight, ax jmp RoomLoop ; If the user enters the INVENTORY command, print out the objects on hand DoInventory: printf byte "You currently have the following items in your " byte "possession:",cr,lf,0 mov di, ItemsOnHand[0] call ShortDesc mov di, ItemsOnHand[2] call ShortDesc mov di, ItemsOnHand[4] call ShortDesc mov di, ItemsOnHand[6] call ShortDesc printf byte "\nCurrent score: %d\n" byte "Carrying ability: %d/4\n\n",0 dword CurScore,CurWeight inc CurScore ;This command is free. jmp RoomLoop ; If the user requests help, provide it here. DoHelp: printf byte "List of commands:",cr,lf,lf byte "GO {NORTH, EAST, WEST, SOUTH}",cr,lf byte "{GET, DROP} {LIME, BEER, CARD, SIGN, PROGRAM, " byte "HOMEWORK, MONEY, FORM, COUPON}",cr,lf byte "SHOW INVENTORY",cr,lf byte "QUIT GAME",cr,lf byte "HELP ME",cr,lf,lf byte "Each command costs you one point.",cr,lf byte "You accumulate points by picking up objects and " byte "dropping them in their",cr,lf byte " appropriate locations.",cr,lf byte "If you drop an item in its proper location, it " byte "disappears from the game.",cr,lf byte "The game is over if your score drops to zero or " byte "you properly place",cr,lf byte " all items.",cr,lf byte 0 jmp RoomLoop ; If they quit prematurely, let 'em know what a wimp they are! QuitGame: printf byte "So long, your score is %d and there are " byte "still %d objects unplaced\n",0 dword CurScore, TotalCounter Quit: ExitPgm ;DOS macro to quit program. Main endp cseg ends sseg segment para stack 'stack' stk db 1024 dup ("stack ") sseg ends zzzzzzseg segment para public 'zzzzzz' LastBytes db 16 dup (?) zzzzzzseg ends end Main