| /* | |
| == 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 <versim@hotmail.com>. | | |
| \______________________________________________________________*/ | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\ | |
| // 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. | | |
| //__________________________________________________________________/ | |
| /*_______________________________________________________________ | |
| ( \ | |
| ) 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( '<<arg>>', '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 = "<<ddsdesc>>" | |
| ldesc = "<<ddldesc>>" | |
| ddsdesc = 'blank name' // rooms start their lives blank | |
| ddldesc = 'blank description' | |
| dispBeginSdesc = { | |
| if (self.onameDisplay) { | |
| "<<self.oname>>"; // 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 <<directionstring>> direction. | |
| <<self.showexits>> | |
| 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 <<startroom.oname>> 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 | |
| <<directionstring>> 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) | |
| "<<global.dynamicRoomList[2].oname>>"; | |
| else | |
| "<<startroom.oname>>"; | |
| ". We can link <<directionstring>> 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 <<directionstring>> to "; | |
| if (Me.location = startroom) { | |
| "<<global.dynamicRoomList[2].oname>>"; | |
| self.trueExitsPropList += direction; | |
| self.direction := global.dynamicRoomList[2]; | |
| self.reverseLink(startroom, direction); | |
| } | |
| else { | |
| "<<startroom.oname>>"; | |
| 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 | |
| <<directionstring>> 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: <<startroom.oname>>"; | |
| if (length(global.dynamicRoomList) > 1) { | |
| local i; | |
| for(i := 2 ; i <= length(global.dynamicRoomList) ; i++) { | |
| if (i = length(global.dynamicRoomList)) | |
| ", and "; | |
| else | |
| ", "; | |
| "<<global.dynamicRoomList[i].oname>>"; | |
| } | |
| } | |
| ".\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 | |
| <<otherroom.oname>> back here, to correspond with the link | |
| we've made to <<otherroom.oname>>? [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 '<<filename>>'.\n "; | |
| writer.openFile(filename); | |
| "Writing room objects to file.\n "; | |
| "Writing '<<startroom.oname>>'...\n"; | |
| writer.writeRoom(startroom); | |
| if (length(global.dynamicRoomList) > 1) { | |
| for (i := 2 ; i <= length(global.dynamicRoomList) ; i++) { | |
| "Writing '<<global.dynamicRoomList[i].oname>>'...\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 '<<filename>>'.\n"; | |
| writer.openFile(filename); | |
| "Writing room object '<<actor.location.oname>>' 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. | |
| // ------------------------------------------------------------------- | |
| "<banner id='texteditor' align=bottom height='30%' border> | |
| <font face='TADS-Typewriter'> | |
| Room description goes here. (Editor may shake a bit... trying to debug this.) | |
| </font> | |
| </banner>"; | |
| 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; | |
| "<banner id='texteditor' align=bottom height='30%' border> | |
| <font face='TADS-Typewriter'> | |
| <<actor.location.ddldesc>></font></banner>"; | |
| } | |
| "<banner id='texteditor' remove>"; | |
| // ------------------------------------------------------------------- | |
| // 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 <<startroom.oname>>' 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 <<tmp.oname>> found in | |
| <<loc.oname>>: deleting exit.\n "; | |
| loc.dirProp := nil; | |
| loc.trueExitsPropList -= dirProp; | |
| } | |
| } | |
| } | |
| "You return to <<startroom.oname>>.\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. */ | |
| "<<loc.directionStringList[i]>> "; | |
| /* 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. | |
| */ | |
| "<<room.directionStringList[i]>>: "; | |
| if (isclass(obj, room) && !obj.islit) | |
| "(unlit) "; | |
| "<<obj.ddsdesc>>\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 <room> deleted. | |
| >delete <room> | |
| [delete sequence for <room>] | |
| 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 | |
| */ | |
Xet Storage Details
- Size:
- 42.7 kB
- Xet hash:
- 7d9120ccc830eaa08086b6ca2ee94805172e87401e0f8b4a25487173d0416fb9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.