| /* | |
| ** menus.t allows you to add simple menus to your HTML TADS game. | |
| ** For an example of their use, see the Arrival or Common Ground | |
| ** source (in if-archive/games/tads/source). Feel free to use in | |
| ** your own TADS 2 games. | |
| ** | |
| ** Copyright (c) 1998, 1999 Stephen Granade. All rights reserved. | |
| ** | |
| ** Special thanks to Mike Roberts, whose help with this module was | |
| ** invaluable. | |
| */ | |
| // The keys used to navigate the menus are held in the keyList array of the | |
| // menuItem. Each navigation option is bound to two keys. The #defines below | |
| // are for accessing the keys associated with the navigation options (quit, | |
| // previous menu, up a selection, down a selection, select a menu item). | |
| // Since each navigation option is bound to two keys, the #defines consist | |
| // of odd numbers. | |
| // The menuItem is the topmost object in a menu tree. | |
| class menuItem: object | |
| title = "" // The name of the menu | |
| myContents = [] // The submenuItems, topicItems, and longTopicItems | |
| bgcolor = 'statusbg' // Background color of the menu | |
| fgcolor = 'statustext' // Foreground color of the menu | |
| indent = '10' // # of spaces to indent the menu's contents | |
| allowHTML = true // Assume the game is running in HTML mode | |
| // (i.e. "\H+" has been printed) | |
| keyList = ['q' '' 'p' '[left]' 'u' '[up]' 'd' '[down]' 'ENTER' '\n' | |
| '[right]' ' '] | |
| // topBanner creates the HTML TADS banner for the menu. The banner contains | |
| // the title of the menu on the left and the navigation keys on the right | |
| topBanner = { | |
| "<banner id=MenuTitle><body bgcolor=\"<<self.fgcolor>>\" | |
| text=\"<<self.bgcolor>>\">"; | |
| "<<self.title>> | |
| <tab align=\"right\">\^<<self.keyList[M_QUIT]>>=quit \^<< | |
| self.keyList[M_PREV]>>=previous menu<br> | |
| <tab align=\"right\">\^<<self.keyList[M_UP]>>=up \^<< | |
| self.keyList[M_DOWN]>>=down \^<<self.keyList[M_SEL]>>=select"; | |
| "</banner>"; | |
| } | |
| // Call menu.display when you're ready to show the menu. | |
| display = { | |
| local i, selection, len = length(self.myContents), key = '', loc; | |
| // If we are running on an HTML TADS interpreter and the game uses | |
| // HTML formatting, use HTML tags to make the menus prettier | |
| if (systemInfo(__SYSINFO_SYSINFO) == true | |
| systemInfo(__SYSINFO_HTML) == 1 | |
| self.displayHTML; | |
| return; | |
| } | |
| // For standard TADS, print the title, then the menu options | |
| // (numbered), then ask the player to make a selection. | |
| do { | |
| "\b\(<<self.title>>\)\b"; | |
| for (i = 1; i <= len; i++) { | |
| if (i < 10) "\ "; | |
| "<<i>>.\ <<(self.myContents[i]).title>>\n"; | |
| } | |
| "\bSelect a topic number, or press ’<< | |
| self.keyList[M_QUIT]>> | |
| do { // The input loop | |
| key = lower(inputkey()); // Get the key | |
| loc = find(self.keyList, key); // Get the key's pos in keyList | |
| selection = cvtnum(key); // Turn key into a #, if possible | |
| if (loc | |
| loc--; | |
| } while (selection == 0 | |
| "<<key>>\n"; // Print the selection | |
| // If selection is a number, then the player selected that menu | |
| // option. Call that submenu or topic's display routine. If the | |
| // routine returns nil, the player selected QUIT, so we should | |
| // quit as well. | |
| if (selection != 0) { | |
| if (!((self.myContents[selection]).display(self))) | |
| loc = M_QUIT; | |
| } | |
| } while (loc != M_QUIT | |
| } | |
| displayHTML = { | |
| local i, selection = 1, len = length(self.myContents), key = '', loc, | |
| events; | |
| // Erase the status line and print the topmost menu banner | |
| self.removeStatusLine; | |
| self.topBanner; | |
| // For HTML TADS, set up a separate menu item banner. In it, | |
| // print the menu's contents. Next to the current selection, print | |
| // a greater-than sign. Each of the menu items is an HREF, so the | |
| // player can select it using the mouse | |
| do { | |
| "<banner border id=MenuBody><body bgcolor=\"<<self.bgcolor>>\" | |
| text=\"<<self.fgcolor>>\">"; | |
| "<table><tr><td width=\"<<self.indent>>\"> </td><td>"; | |
| for (i = 1; i <= len; i++) { | |
| // To get the alignment right, we have to print '>' on each | |
| // and every line. However, we print it in the background | |
| // color to make it invisible everywhere but in front of the | |
| // current selection. | |
| if (selection != i) | |
| "<font color=\"<<self.bgcolor>>\">></font>"; | |
| else ">"; | |
| // Make each selection a plain (i.e. unhilighted) HREF | |
| "<a plain href=\"<<i>>\">"; | |
| (self.myContents[i]).title; | |
| "</a><br>"; | |
| } | |
| "</td></tr></table>"; | |
| "</banner>"; | |
| do { // The input loop. For HTML TADS, | |
| events = inputevent(); // we look at events rather than | |
| // just keystrokes | |
| if (events[1] == INPUT_EVENT_HREF) { // For HREFs, set the | |
| selection = cvtnum(events[2]); // selection # to the | |
| loc = M_SEL; // HREF's value | |
| } | |
| else { | |
| key = lower(events[2]); // Otherwise, assume the event is | |
| // a keystroke and get the key | |
| loc = find(self.keyList, key); // Find key's pos in keyList | |
| if (loc | |
| loc--; | |
| } | |
| // First off, handle arrow keys | |
| if (loc == M_UP | |
| selection--; | |
| else if (loc == M_DOWN | |
| selection++; | |
| } while (loc == nil); // Loop until the user presses a valid key | |
| if (loc >= M_SEL) { // The player made a selection, so show it | |
| if (!((self.myContents[selection]).displayHTML(self))) | |
| loc = M_QUIT; | |
| } | |
| } while (loc != M_QUIT | |
| // Clean up after ourselves | |
| "<banner remove id=MenuTitle><banner remove id=MenuBody>"; | |
| } | |
| removeStatusLine = { | |
| "<banner remove id=StatusLine>"; | |
| } | |
| ; | |
| // submenuItem is exactly like menuItem, except that it refers to the top- | |
| // level menu item to get the keyList | |
| class submenuItem: object | |
| title = "" | |
| myContents = [] | |
| bgcolor = 'statusbg' | |
| fgcolor = 'statustext' | |
| indent = '10' | |
| display(topMenu) = { | |
| local i, selection, len = length(self.myContents), key = '', loc; | |
| do { | |
| "\b\(<<self.title>>\)\b"; | |
| for (i = 1; i <= len; i++) { | |
| if (i < 10) "\ "; | |
| "<<i>>.\ <<(self.myContents[i]).title>>\n"; | |
| } | |
| "\bSelect a topic number, or press ’<< | |
| topMenu.keyList[M_QUIT]>> | |
| previous menu.\ "; | |
| do { | |
| key = lower(inputkey()); | |
| loc = find(topMenu.keyList, key); | |
| selection = cvtnum(key); | |
| if (loc | |
| loc--; | |
| } while (selection == 0 | |
| "<<key>>\n"; | |
| if (selection != 0) { | |
| if (!((self.myContents[selection]).display(topMenu))) | |
| return nil; | |
| } | |
| } while (loc != M_QUIT | |
| return (loc == M_PREV); | |
| } | |
| displayHTML(topMenu) = { | |
| local i, selection = 1, len = length(self.myContents), key = '', loc, | |
| events; | |
| while (true) { | |
| do { | |
| "<banner border id=MenuBody><body bgcolor=\"<<self.bgcolor>>\" | |
| text=\"<<self.fgcolor>>\">"; | |
| "<table><tr><td width=\"<<self.indent>>\"> </td><td>"; | |
| for (i = 1; i <= len; i++) { | |
| if (selection != i) | |
| "<font color=\"<<self.bgcolor>>\">></font>"; | |
| else ">"; | |
| (self.myContents[i]).title; | |
| "<br>"; | |
| } | |
| "</td></tr></table>"; | |
| "</banner>"; | |
| do { | |
| events = inputevent(); | |
| if (events[1] == INPUT_EVENT_HREF) { | |
| selection = cvtnum(events[2]); | |
| loc = M_SEL; | |
| } | |
| else { | |
| key = lower(events[2]); | |
| loc = find(topMenu.keyList, key); | |
| if (loc | |
| loc--; | |
| } | |
| if (loc == M_UP | |
| selection--; | |
| else if (loc == M_DOWN | |
| selection++; | |
| } while (loc == nil); | |
| } while (loc == M_UP || loc == M_DOWN); | |
| if (loc >= M_SEL) { | |
| if (!((self.myContents[selection]).displayHTML(topMenu))) | |
| return nil; | |
| } | |
| else return (loc == M_PREV); | |
| } | |
| } | |
| ; | |
| // topicItem displays a series of entries successively. It's intended to be | |
| // used for displaying a group of hints. Unlike [sub]menuItem, myContents | |
| // contains a list of strings to be displayed | |
| class topicItem: object | |
| title = "" // The name of this topic | |
| myContents = [] // A list of strings to be displayed | |
| bgcolor = 'statusbg' // The background color | |
| fgcolor = 'statustext' // The foreground color | |
| indent = '30' // How far to indent all of the strings | |
| lastDisplayed = 1 // The last string displayed | |
| chunkSize = 6 // How many strings to display at once, max | |
| // (only valid under HTML TADS) | |
| goodbye = '[The End]' // The ending phrase | |
| display(topMenu) = { | |
| local i, len = length(self.myContents), key = '', loc; | |
| "\b\(<<self.title>>\)\b"; | |
| // Print all of the strings up to and including lastDisplayed. Also, | |
| // append "[#/#]" after them to show which hint out of how many | |
| // each is. | |
| for (i = 1; i <= self.lastDisplayed; i++) { | |
| "<<self.myContents[i]>> [<<i>>/<<len>>]\b"; | |
| // If we're at the end, let the player know by printing the | |
| // goodbye message | |
| if (i == len) | |
| "<<self.goodbye>>\n"; | |
| } | |
| while (true) { | |
| key = lower(inputkey()); | |
| loc = find(topMenu.keyList, key); | |
| if (loc | |
| loc--; | |
| if (loc == M_QUIT) | |
| return nil; | |
| if (loc == M_PREV || self.lastDisplayed == len) | |
| return true; | |
| self.lastDisplayed++; | |
| "<<self.myContents[self.lastDisplayed]>> [<< | |
| self.lastDisplayed>>/<<len>>]\b"; | |
| if (self.lastDisplayed == len) | |
| "<<self.goodbye>>\n"; | |
| } | |
| } | |
| displayHTML(topMenu) = { | |
| local i, selection = 1, len = length(self.myContents), key = '', loc, | |
| firstTime = true, topTopic = 1, chunk = self.chunkSize, events; | |
| while (true) { | |
| // Set up the banner | |
| "<banner border id=MenuBody><body bgcolor=\"<<self.bgcolor>>\" | |
| text=\"<<self.fgcolor>>\">"; | |
| "<table><tr><td width=\"<<self.indent>>\"> </td><td>"; | |
| // The firstTime variable flags whether or not this is our first | |
| // trip through this loop. If it's not, we need to adjust the | |
| // # of displayed hints, among other things. | |
| if (firstTime) | |
| i = 1; | |
| else { | |
| // Only display a # of strings equal to or less than chunkSize | |
| if (topTopic != 1 | |
| self.lastDisplayed - topTopic < self.chunkSize) | |
| i = topTopic; | |
| else i = self.lastDisplayed - self.chunkSize + 1; | |
| if (i < 1) | |
| i = 1; | |
| } | |
| while (i <= self.lastDisplayed) { | |
| // If we display a chunk's worth of strings and this is our | |
| // first time through the loop we pause, then keep printing | |
| // out the rest of the strings | |
| if (i > chunk | |
| "</td></tr></table>"; | |
| "</banner>"; | |
| events = inputevent(); | |
| if (events[1] == INPUT_EVENT_HREF) { // There should be | |
| selection = cvtnum(events[2]); // no HREF events, | |
| loc = M_SEL; // but just in case... | |
| } | |
| else { | |
| key = lower(events[2]); | |
| loc = find(topMenu.keyList, key); | |
| if (loc | |
| loc--; | |
| } | |
| if (loc == M_QUIT || loc == M_PREV) | |
| return (loc != M_QUIT); | |
| "<banner border id=MenuBody><body | |
| bgcolor=\"<<self.bgcolor>>\" | |
| text=\"<<self.fgcolor>>\">"; | |
| "<table><tr><td width=\"<<self.indent>>\"> </td><td>"; | |
| // Increment the counter which keeps track of the string | |
| // # at which the next chunk ends. Also bump up the | |
| // current top topic string #. | |
| chunk += self.chunkSize; | |
| topTopic = i; | |
| } | |
| "<<self.myContents[i]>> [<<i>>/<<len>>]<BR>"; | |
| if (i == len) | |
| say(self.goodbye); | |
| i++; | |
| } | |
| "</td></tr></table>"; | |
| "</banner>"; | |
| key = lower(inputkey()); | |
| loc = find(topMenu.keyList, key); | |
| if (loc == M_QUIT || loc == M_PREV || | |
| self.lastDisplayed == len) { | |
| return (loc != M_QUIT); | |
| } | |
| self.lastDisplayed++; | |
| firstTime = nil; // This is no longer our first time through | |
| // the loop | |
| } | |
| } | |
| ; | |
| // longTopicItems are used to print out big long gobs of text on a subject. | |
| // Use it for printing long treatises on your design philosophy and the like. | |
| class longTopicItem: object | |
| title = "" | |
| myContents = '' // This can be a string or a routine | |
| goodbye = '[The End]' // The goodbye message | |
| display(topMenu) = { | |
| local key, loc; | |
| clearscreen(); | |
| "<CENTER>\(<<self.title>>\)</CENTER>\b"; | |
| // Simply dump out our contents, then print the goodbye message. | |
| "<<self.myContents>>\b<<self.goodbye>>"; | |
| key = lower(inputkey()); | |
| loc = find(topMenu.keyList, key); | |
| if (loc | |
| loc--; | |
| clearscreen(); | |
| return (loc != M_QUIT); | |
| } | |
| displayHTML(topMenu) = { | |
| local ret; | |
| // longTopicItems are displayed the same in HTML TADS as they are | |
| // in standard TADS, barring some banner fiddling. | |
| ret = self.display(topMenu); | |
| topMenu.topBanner; | |
| return ret; | |
| } | |
| ; | |
Xet Storage Details
- Size:
- 16.3 kB
- Xet hash:
- d475b0cbaeeb5819137b92fcbfaaf04f0b4c48a774ec53cc25d34e4949eb6822
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.