/* == Tads 2 Dungeon Digger, v. 0.9 BETA == Copyright (c) 2002, Steve Breslin ==>> License: _______________________________________________________________ / \ | You can use this program however you want, but if you | | decide to distribute anything that contains material | | derived from or generated by this program, you have to | | send a copy of any improvements or modify-cations you | | made to this program to its author, Steve Breslin. | | | | That way, you will help keep the program up to date for | | everybody else, and everybody else will help keep it | | up to date for you. | | | | You may redistribute this verbatim or in modified form | | only if you keep the copyright and license intact. | | | | Also, feel encouraged to release your source code along | | with your game, though this isn't a requirement. | | | | The author can be reached at . | \______________________________________________________________*/ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\ // Because the module exitslister.t would be quite useful for the | // task of mapping out rooms, and because the module exitslister.t | // is incompatible with the dungeon digger, a modified version of | // the exits lister has been incorporated into the digger. | //__________________________________________________________________/ #define USE_HTML_STATUS #define USE_HTML_PROMPT #include #include /*_______________________________________________________________ ( \ ) The Output-file Writer \ \ ______________________________________________________________/ `*/ writer: object saveFileName = nil getFilename() = { local filename; "Please enter a file name: "; filename := input(); if (find(filename, '.')) "Ok, we found a period in your filename, and we're assuming that indicates a file extension. Since we'll be using your file extension, we won't append a '.t'.\n "; else { "Appending '.t' extension to your filename...\n "; filename += '.t'; } return filename; } checkOverwrite(filename) = { local fnum; fnum := nil; fnum := fopen(filename, 'r'); // read only: checking filename. if (fnum != nil) { local inp; fclose( fnum ); "File already exists. Overwrite existing file? [y/N]: "; inp := upper(input()); if (inp = 'Y' || inp = 'YES') { "Okay. "; return true; } "Save aborted. "; return nil; } return true; } openFile(arg) = { if (self.saveFileName != nil) { "There's already a save file open.\n "; "Save aborted. This is an error which may indicate you need to restart workbench.\n "; "Attempting to close file...\n"; self.closeFile; abort; } self.saveFileName := fopen( arg, 'wt' ); if (self.saveFileName = nil) { // fopen has failed "Unable to open file.\n "; "Attempted:\n fopen( '<>', 'wt' );\n with unknown failure, possibly resulting from a transient file handle.\n Save aborted.\n This is an error which may indicate you need to restart workbench.\n "; abort; } } writeStringToCurrentFile(str) = { if (!self.saveFileName) "There's no save file open.\n "; else fwrite( self.saveFileName, str ); } writeRoom(room) = { local i; writer.writeStringToCurrentFile(room.oname); writer.writeStringToCurrentFile(': room\n'); writer.writeStringToCurrentFile(' sdesc = "'); writer.writeStringToCurrentFile(room.ddsdesc); writer.writeStringToCurrentFile('"\n ldesc = "'); writer.writeStringToCurrentFile(self.ldescFormatter(room.ddldesc)); writer.writeStringToCurrentFile('"\n'); for (i := 1 ; i <= length(room.directionPropList) ; i++) if (intersect(room.trueExitsPropList, [(room.directionPropList[i])]) != []) { writer.writeStringToCurrentFile(' '); writer.writeStringToCurrentFile(room.directionStringList[i]); writer.writeStringToCurrentFile(' = '); writer.writeStringToCurrentFile(room.(room.directionPropList[i]).oname); writer.writeStringToCurrentFile('\n'); } writer.writeStringToCurrentFile(';\n\n'); } closeFile() = { if (!self.saveFileName) "Error. No currently open file.\n "; else { fclose( self.saveFileName ); self.saveFileName := nil; } } /* This formats the ldesc so it looks right in the output file. */ ldescFormatter(str) = { local swapString; swapString := ''; /* This turns '\n' into '\\n\n' plus whitespace. The original * '\n' marked the user's paragraph. We want to keep this mark, * so we transform it to '\\n'. We want to put a carriage * return in the output, so we add '\n'. Then the extra * whitespace is for left margin indentation. * * In other words, paragraphs, marked by '\n' in the original * string, will be formatted as paragraphs in the output file. * * We construct swapString while we deconstruct str. */ while(length(str) > 0) { if (find(str, '\n') = nil) { swapString += str; break; } swapString := (swapString + substr(str, 1, find(str, '\n') - 1) + '\\n\n\ \ \ \ \ \ \ \ \ \ \ \ '); str := substr(str, (find(str, '\n') +2), length(str)); } /* One operation down and one to go. We still need to adjust * the right margin, whenever a paragraph is longer than a * multiple of ((72 character margin) minus (12 space left * margin indentation)) 60. This is a maximum, of course, * as we want to break between words not within words. * * Currently, swapString has the "good" string in it, so we'll * now reinitialize 'str', so we can swap back to it, * deconstructing swapString as we construct str. */ str := ''; while(length(swapString) > 0) { if (length(swapString) < 61) return (str + swapString); if ( ( find(swapString, '\n') = nil || find(swapString, '\n') > 60 ) // no '\n' nearby and && ( find(swapString, ' ') != nil && find(swapString, ' ') < 61 ) // spaces nearby that we can use for margin breaks ) { local totalchar, wordlength; totalchar := 0; wordlength := find(swapString, ' '); // includes space while(wordlength && totalchar + wordlength < 61 && length(swapString) > 0) { wordlength := find(swapString, ' '); if (wordlength && totalchar + wordlength < 61) { totalchar += wordlength; str := str + substr(swapString, 1, wordlength); swapString := substr(swapString, wordlength +1, length(swapString)); wordlength := 0; } else { str := str + '\n\ \ \ \ \ \ \ \ \ \ \ \ '; totalchar := 0; } } } else if (find(swapString, '\n') != nil) { // a nearby '\n' str := str + substr(swapString, 1, find(swapString, '\n') + 27); swapString := substr(swapString, find(swapString, '\n') + 29, length(swapString)); } else { "The long description formatter encountered an unconventional string, and gave up on formatting that string.\n Writing unformatted string to output file...\n "; str += swapString; swapString := ''; } } return str; } ; /* ----- == -- - == === ==---=====. \ Modifications to room Class \ \_________________________________________________________________*/ modify room construct = { local inp, ret; ret := true; while (ret) { local i; "Enter new room object name: "; inp := input(); ret := reSearch('[^a-zA-Z0-9_]', inp); if (ret != nil) "Your room's object name can contain underscores, but must otherwise be alpha-numeric.\n "; for(i := 1 ; i <= length(global.dynamicRoomList) ; i++) { if (inp = global.dynamicRoomList[i].oname) { "You are already using that object name in another room. Please choose another.\n "; ret := true; } } } addword(self, &noun, inp); global.dynamicRoomList += self; } destruct = { // should cycle through all rooms, and delete all links that // point to me. // currently this code is in the delete verb. } directionPropList = [ &north, &ne, &east, &se, &south, &sw, &west, &nw, &in, &out, &up, &down ] directionStringList = [ 'north', 'ne', 'east', 'se', 'south', 'sw', 'west', 'nw', 'in', 'out', 'up', 'down' ] trueExitsPropList = [] oname = { return (getwords(self, &noun)[1]); } sdesc = "<>" ldesc = "<>" ddsdesc = 'blank name' // rooms start their lives blank ddldesc = 'blank description' dispBeginSdesc = { if (self.onameDisplay) { "<>"; // show the oname above the sdesc. "\n"; } } onameDisplay = true // by default dispBeginLdesc = "\n" // instead of { self.dispParagraph; } // I should make the paragraph indentation an // option in a future version. verDoGoto(actor) = { } doGoto(actor) = { "You transfer yourself to...\b "; actor.travelTo(self); } north = nil south = nil east = nil west = nil up = nil down = nil ne = nil nw = nil se = nil sw = nil in = nil out = nil digExit(direction, directionstring) = { local inp; "There's no link in the <> direction. <> Would you like to make a new exit now? [y/N]: "; inp := upper(input()); if (inp = 'Y' || inp = 'YES') { "Okay. Making new room link.\n "; self.newRoomQuery(direction, directionstring); return; } "Okay, no new exit. "; return; } newRoomQuery(direction, directionstring) = { local inp, ret, newroom, i; "Will you be linking to an already existing room, or do we also need to make a new room to link to? "; if (length(global.dynamicRoomList) = 1) { "(Since only the <> room exists right now, you probably want to make a new room.)\n "; } "Making a new room? [Y/n]: "; inp := upper(input()); if (inp = 'N' || inp = 'NO') { "Okay. Linking to a currently existing room.\n "; self.makeDirection(direction, directionstring); return; } "Ok, making new room.\n "; newroom := new room; self.direction := newroom; self.trueExitsPropList += direction; self.reverseLink(newroom, direction); return; } makeDirection(direction, directionstring) = { local inp, ret, newroom; if (length(global.dynamicRoomList) = 1) { "Since this is the only room, we'll link in direction <> back to this room. "; self.trueExitsPropList += direction; self.direction := self; return; } if (length(global.dynamicRoomList) = 1) { "The only other room is "; if (Me.location = startroom) "<>"; else "<>"; ". We can link <> to that room, or we could circularly link back to this room.\n "; "Link to the other room? [Y/n]: "; inp := upper(input()); if (inp = 'N' || inp = 'NO') { "Okay. Linking circularly back to this room.\n "; self.trueExitsPropList += direction; self.direction := self; return; } "Okay. Linking <> to "; if (Me.location = startroom) { "<>"; self.trueExitsPropList += direction; self.direction := global.dynamicRoomList[2]; self.reverseLink(startroom, direction); } else { "<>"; self.trueExitsPropList += direction; self.direction := startroom; self.reverseLink(startroom, direction); } ". "; return; } ret := true; "Please enter the object name of the room you want to link <> from here. Type \"/list\" for a list of all the available rooms.\n "; while(ret) { local i; "Enter room object name, or \"/list\": "; inp := input(); for(i := 1 ; i <= length(global.dynamicRoomList) ; i++) { if (upper(global.dynamicRoomList[i].oname) = upper(inp)) { self.trueExitsPropList += direction; self.direction := global.dynamicRoomList[i]; self.reverseLink(global.dynamicRoomList[i], direction); ret := nil; } if (upper(inp) = '/LIST') { self.listRooms; } else if (ret = true) "The name you entered doesn't match any room objects. "; } } } listRooms = { "Current room objects: <>"; if (length(global.dynamicRoomList) > 1) { local i; for(i := 2 ; i <= length(global.dynamicRoomList) ; i++) { if (i = length(global.dynamicRoomList)) ", and "; else ", "; "<>"; } } ".\n "; } reverseLink(otherroom, direction) = { /* Is the reverse link already defined by the room? (Or is it * instead inheriting noexit code from class 'room'?) If it's * already defined, we don't offer to reverse link. First * because it's probably redundant. Second because the "reverse * direction" in the other room might *not* point back here, * and we don't want to mess with that situation, or confuse * the user. */ if (intersect(otherroom.trueExitsPropList, [self.calculateReverse(direction)]) = []) { local inp; "Do you want to make a reverse link from <> back here, to correspond with the link we've made to <>? [Y/n]: "; inp := upper(input()); if (inp = 'N' || inp = 'NO') { "Okay, no reverse link. "; return; } "Ok, making reverse link. "; otherroom.trueExitsPropList += self.calculateReverse(direction); otherroom.(self.calculateReverse(direction)) := self; } return; } calculateReverse(directiontoreverse) = { if (directiontoreverse = &north) return &south; if (directiontoreverse = &south) return &north; if (directiontoreverse = &east) return &west; if (directiontoreverse = &west) return &east; if (directiontoreverse = &ne) return &sw; if (directiontoreverse = &se) return &nw; if (directiontoreverse = &nw) return &se; if (directiontoreverse = &sw) return ≠ if (directiontoreverse = &in) return &out; if (directiontoreverse = &out) return ∈ if (directiontoreverse = &up) return &down; if (directiontoreverse = &down) return &up; } //--------------------------------------------------------------- // exits lister material follows : //=============================================================== /* We want exitsLister.getExits() to be called after the * conventional room material is reported, when Me looks around. * We perform a check that this hasn't been disabled. */ lookAround(verbosity) = { self.dispBeginSdesc; self.statusRoot; self.dispEndSdesc; self.nrmLkAround(verbosity); if(exitsLister.enabled) exitsLister.getExits; } /* This shows exits when a player goes in an unlinked direction. */ showexits = { local retString, dirlist := [], loc := Me.location, i; if (exitsLister.enabled && !exitsLister.overkill) return ''; // (else:) for (i := 1; i <= length(loc.directionStringList); i++ ) { /* check if the direction has a defined link from this * room. */ if (intersect(self.trueExitsPropList, [(self.directionPropList[i])]) != []) { /* we have a defined link in this direction. * if you want to hide certain exits, you will want to add a * check here. */ dirlist += loc.directionStringList[i]; } } if (dirlist != []) { local i, len; if (length(dirlist) = 1) retString := 'The only current exit is '; else retString := 'The current exits from here are '; retString += dirlist[1]; for (i := 2, len := length(dirlist); i <= len ; i++) { /* * if this isn't the first, add a comma; if this is the * last, add an "and" as well */ if (i = len && len != 1) { retString += ', and '; } else if (i != 1) { retString += ', '; } retString += dirlist[i]; } if (length(dirlist) = 1) return (retString + ' from here. '); return (retString + '. '); } else return 'Currently, there are no exits from this room. '; } ; /*^-----^^ ---^ --- ^ -^^-^^ ^-^^---. \ New and modified verbs \ \_________________________________________________________________*/ modify saveVerb // for saving the entire map to an output file action(actor) = { local filename, i; "Ok, save all rooms in a room file in TADS-2 format.\n "; filename := writer.getFilename; while (!writer.checkOverwrite(filename)) { filename := writer.getFilename; } "Opening file '<>'.\n "; writer.openFile(filename); "Writing room objects to file.\n "; "Writing '<>'...\n"; writer.writeRoom(startroom); if (length(global.dynamicRoomList) > 1) { for (i := 2 ; i <= length(global.dynamicRoomList) ; i++) { "Writing '<>'...\n"; writer.writeRoom(global.dynamicRoomList[i]); } } "Done. Closing file...\n"; writer.closeFile; } ; saveThisRoomVerb: deepverb // for saving only one room verb = 'savethisroom' sdesc = "savethisroom" action(actor) = { local filename; "Ok, save only this room in a seperate file.\n "; filename := writer.getFilename; while (!writer.checkOverwrite(filename)) { filename := writer.getFilename; } "Opening file '<>'.\n"; writer.openFile(filename); "Writing room object '<>' to file...\n"; writer.writeRoom(actor.location); "Done. Closing file...\n"; writer.closeFile; } ; nameVerb: deepverb // for renaming rooms (sdesc) verb = 'name' 'title' 'rename' sdesc = "name" action(actor) = { "Type room name (room title): "; actor.location.ddsdesc := input(); "\b"; actor.location.lookAround(true); } ; describeVerb: deepverb // for describing rooms (ldesc) verb = 'describe' 'desc' sdesc = "desc" action(actor) = { local paragraphStringList, i, CRcount, descString, inputCharacter; paragraphStringList := []; actor.location.ddldesc := ''; "Two consecutive carriage returns will signal the termination of the description.\n The room description can be of any length. Single carriage returns will be interpreted as paragraph breaks.\n "; "Type room description:\b "; // ------------------------------------------------------------------- // This is the newer version of the describe verb input processing. // It relies on banners. If your computer doesn't use banners, you // may want to remove this new version, and replace it with the // older version, which is available below. // ------------------------------------------------------------------- " Room description goes here. (Editor may shake a bit... trying to debug this.) "; CRcount := 0; while(true) { inputCharacter := inputkey(); switch(inputCharacter) { case '\n': CRcount++; actor.location.ddldesc += '\n'; break; case '[bksp]': CRcount := 0; if (length(actor.location.ddldesc) > 0) { actor.location.ddldesc := substr(actor.location.ddldesc, 1, (length(actor.location.ddldesc) - 1 )); } break; case '[up]': case '[down]': case '[right]': case '[left]': case '[end]': case '[home]': case '[del-eol]': case '[del-line]': case '[del]': case '[page up]': case '[page down]': case '[top]': case '[bottom]': case '[tab]': case '[word-left]': case '[word-right]': case '[del-word]': "The input window is too primitive to do that.\n "; "The only special character it recognizes is [Backspace].\n "; break; default: CRcount := 0; actor.location.ddldesc += inputCharacter; break; } if (CRcount = 2) break; " <>"; } ""; // ------------------------------------------------------------------- // This is the older version of the describe verb input processing. // It needed to be redone because TADS-2 imposes a maximum on how many // characters there can be in an input() return, making writing longer // paragraphs impossible for a simple input() command to handle. The // way it's now done works much better. Hopefully, someone someday // will extend the above input mechanism to work with arrow keys and // such, and so make it into a true editor. // Anyway, if you're on a computer that doesn't have capability to // show tads banners, the following code might be useful. You'll have // to keep the paragraphs pretty short, though. It's ok for making // notes, which is how many writers begin writing their rooms. // ------------------------------------------------------------------- // "Type room description:\b "; // while (true) { // paragraphStringList += input(); // if (paragraphStringList[length(paragraphStringList)] = '') // break; // } // for (i := 1 ; i <= length(paragraphStringList) ; i++) { // actor.location.ddldesc += paragraphStringList[i]; // actor.location.ddldesc += '\n'; // } actor.location.ddldesc := substr(actor.location.ddldesc, 1, (length(actor.location.ddldesc) - 4)); "\b"; actor.location.lookAround(true); } ; onameVerb: deepverb // for renaming room objects (object name) verb = 'oname' 'objname' sdesc = "oname" action(actor) = { local newname, ret, oldname; ret := [true]; while (ret) { "Type the room's new object name: "; newname := input(); ret := reSearch('[^a-zA-Z0-9_]', newname); if (ret != nil) "Your room's object name can contain underscores, but must otherwise be alphanumeric.\n "; } oldname := getwords(actor.location, &noun)[1]; delword(actor.location, &noun, oldname); addword(actor.location, &noun, newname); "\b"; actor.location.lookAround(true); } ; displayOnameVerb: deepverb // for toggling the oname display verb = 'displayoname' 'dispo' sdesc = "oname" action(actor) = { room.onameDisplay := !room.onameDisplay; } ; helpVerb: deepverb // print help screen verb = 'help' sdesc = "help" action(actor) = { "The main commands for this tool are 'describe', 'name', 'oname', and directional verbs, like 'north', 'up', and 'out'. You can change a room's description with the 'describe' verb, and change the room name (room title) with the 'name' verb.\n The object name can be changed with the 'oname' verb, if you don't like 'startroom' you can change that room's oname. You will notice that the oname appears above each room title. This is for easy reference, but don't worry -- it won't appear there in the room file we create when you're done.\n To dig another room, just move in any direction. Supported directions are the normal game directions. These are the eight standard compass directions, plus in, out, up, and down. (Note however that enterable objects are not supported -- yet, anyway. Door building and enterables will be available in a future version, hopefully.) If you have already dug a room connection in the direction you type, you will just move in that direction, as in a usual game. However, if the direction you move in has not already been dug, you will enter a room-building dialogue.\n To delete a room connection, type 'deletelink'; you will then enter an input dialogue where you can type the direction. To delete a room, stand in that room, and type 'delete'. You will be transferred to the start room when the deletion is complete. You cannot delete the initial 'start' room.\n You can jump from room to room without 'walking', if you want. Just use the 'goto' verb, with the object name of the room you want to go to. Try 'goto <>' if you like.\n When you are done digging, naming and describing, you will want to save your work to a TADS 2 readable room file; you can do this with the 'save' command. If for some reason you want to save only one room (the room you are standing in), type 'savethisroom'. \n"; } ; gotoVerb: deepverb // move user into specified room verb = 'goto' sdesc = "goto" doAction = 'Goto' validDoList(actor, prep, iobj) = { return nil; } validDo(actor, obj, seqno) = { if (isclass(obj, room)) return true; } ; deleteVerb: deepverb // deletes the room the user is standing in verb = 'delete' 'delroom' 'deleteroom' sdesc = "delete" action(actor) = { local inp; if (actor.location = startroom) { "Sorry, you cannot delete the initial room. "; return; } if (length(actor.location.ddldesc) > 30) "This room has a description. Are you sure you want to delete it? [y/N]: "; else "Confirm delete room? [y/N]: "; inp := upper(input()); if (inp = 'Y' || inp = 'YES') { local tmp, i, j; tmp := actor.location; "Ok, deleting. First deleting exits pointing to this room...\n"; for(i := 1 ; i <= length(global.dynamicRoomList) ; i++) { for(j := 1 ; j <= length(global.dynamicRoomList[i].trueExitsPropList) ; j++) { local loc := global.dynamicRoomList[i], dirProp := loc.trueExitsPropList[j]; if (loc.dirProp = tmp) { "Exit link to <> found in <>: deleting exit.\n "; loc.dirProp := nil; loc.trueExitsPropList -= dirProp; } } } "You return to <>.\b "; actor.travelTo(startroom); delete tmp; "\n[Success on room delete.]\n "; return; } "Okay, aborting delete room. "; return; } ; deleteLinkVerb: deepverb // deletes a link from the current room verb = 'deletelink' 'unlink' sdesc = "delete link" action(actor) = { local direction; "Enter direction: "; direction := lower(input()); switch(direction) { case 'north': case 'n': direction := &north; break; case 'south': case 's': direction := &south; break; case 'east': case 'e': direction := &east; break; case 'west': case 'w': direction := &west; break; case 'up': case 'u': direction := &up; break; case 'down': case 'd': direction := &down; break; case 'ne': case 'northeast': direction := ≠ break; case 'nw': case 'northwest': direction := &nw; break; case 'se': case 'southeast': direction := &se; break; case 'sw': case 'southwest': direction := &sw; break; case 'in': direction := ∈ break; case 'out': direction := &out; break; default: "Abnormal direction entered.\n "; break; } if(intersect([direction], actor.location.trueExitsPropList) = []) "Failure for direction match.\n Delete exit action failed.\n "; else { actor.location.trueExitsPropList -= direction; actor.location.(direction) := nil; "Exit deleted.\n "; } } ; listVerb: deepverb // for listing all rooms verb = 'list' 'listrooms' sdesc = "list rooms" action(actor) = { actor.location.listRooms; } ; modify eVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&east, 'east'); } ; modify sVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&south, 'south'); } ; modify nVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&north, 'north'); } ; modify wVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(actor.location.(&west)); else actor.location.digExit(&west, 'west'); } ; modify neVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&ne, 'northeast'); } ; modify nwVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&nw, 'northwest'); } ; modify seVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&se, 'southeast'); } ; modify swVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&sw, 'southwest'); } ; modify inVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&in, 'in'); } ; modify outVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&out, 'out'); } ; modify dVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&down, 'down'); } ; modify uVerb action(actor) = { if(self.travelDir(actor)) actor.travelTo(self.travelDir(actor)); else actor.location.digExit(&up, 'up'); } ; /*:________________________________________________________________ \ \ \ ~~ Miscellany \ \________________________________________________________________ \ :*/ /* Setting verbose to on by default, since we intend to be writing * rooms after all. * Also, new list called dynamicRoomList. This keeps track of all * the rooms we dynamically create. All the rooms except the * startroom are dynamically created. */ modify global verbose = true dynamicRoomList = [startroom] ; /* The first room by default will be named startroom in the output * file, but this oname can be changed by the user. We give it a * special short and long description, as a matter of user * friendliness. */ startroom: room noun = 'startroom' ddsdesc = 'Start Room' ddldesc = 'This is the start room\'s description.\n You may type \'help\' to read a brief help file, which explains how to use the Dungeon Digger. ' ; /*<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>*\ <-< Exits Lister Stuff >-> \*<__________________________________________________________________>*/ exitsLister: object enabled = true // by default overkill = nil // by default getExits() = { local exitexists = nil, loc = Me.location, i; "\n[ Current exits: "; /* cycle through all directions */ for (i := 1 ; i <= length(loc.directionStringList) ; i++ ) { /* check if the direction has a defined link from this room */ if (intersect([(loc.directionPropList[i])], loc.trueExitsPropList) != []) { /* we have a defined link in this direction. */ "<> "; /* if we list it, we mark that there's at least one exit */ exitexists := true; } } if (!exitexists) { /* we didn't find any exits, so relate this to the player */ "None. "; } "]"; } ; /* * the main exits verb */ exitsVerb: deepverb verb = 'exits' 'scan' 'showexits' sdesc = "show exits" exitsOnOffExplained = nil action(actor) = { local i, loc := actor.location, obj, exitfound := nil; "Current exits:\n "; /* cycle through all the directions defined in the room */ for (i := 1 ; i <= length(loc.directionStringList) ; i++ ) { /* check if the direction has a defined link from this room */ if (intersect([(actor.location.directionPropList[i])], actor.location.trueExitsPropList) != []) { /* figure out which object is associated with the * current direction */ local obj := loc.(loc.directionPropList[i]); /* if the obj is a room, we check if it's lit. if it isn't, * we indicate this first. either way, we then we say the * sdesc. since this version is for game creators, we * don't mind giving away the sdesc even when the room is * dark. */ "<>: "; if (isclass(obj, room) && !obj.islit) "(unlit) "; "<>\n"; exitfound := true; } } if (!exitfound) "None.\n "; /* The first time this command is executed, we'll tell the * player how to disable the automatic exits display, in case * that's what they're trying to do. */ if (!exitsLister.exitsOnOffExplained) { "[Typing 'exitson' or 'exitsoff' will switch the exits display.']\n"; exitsLister.exitsOnOffExplained := true; } } ; /* * verbs for switching exits display state */ exitsOnVerb: deepverb verb = 'exitson' sdesc = "exits on" action(actor) = { /* turn exits display on */ exitsLister.enabled := true; "Ok, exits display is enabled. "; } ; exitsOffVerb: deepverb verb = 'exitsoff' sdesc = "exits off" action(actor) = { /* turn notifications off, and acknowledge the status */ exitsLister.enabled := nil; "Ok, exits display is disabled. "; } ; /***''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''**\ \***> Standard Stuff <***\ \**..............................................................***/ replace commonInit: function { "\H+"; clearscreen(); } modify version sdesc = "Dungeon Digger --\n A game designer's tool for mapping and describing the game world.\b"; ; /******************************************************************** * Notes & To Do ******************************************************************** To Do: pre-release final debug. ---- better verbs: > name Akmed's #1 Steel Trap & Seive Okay, renamed room Akmed's #1 Steel Trap & Seive > delete east Okay exit east to deleted. >delete [delete sequence for ] the above (probably) requires use of preparse. this is bad if we want this to be a module, since preparse isn't modifiable. hm, we could make a single line for the user to place in their preparse routine, which calls to an object's method. This method could check if the first word in the command resolves to a new literalVerb class, and capture the text based on that, passing simply the verb back to the preparse, so the verb's action will check if there's a captured string in the capture space, and if not proceed as normal. if so, use the captured string. either way, reset the capture space. exitslister "walk-to style" goto better text editor quit shouldn't work unless there have been no changes since last save. ---- modularlize for use in game already under production. this means we need to account for the following: doors enterables nestedrooms */