! ------------------------------------------------------------------------- ! Menus.h A library extension providing easier and better menus ! Graham Nelson 961113 ! added one-line correction to support Strict mode Roger Firth 030303 ! ! A menu is a tree of objects of class Option. A Menu is an Option which ! launches a fresh menu when chosen. To choose option O, send the ! message: ! ! O.select(); ! ! So to start off a menu session, send this message to the top menu. ! ! Here's a simple menu structure: ! ! Menu "Instructions for playing Mordred"; ! Menu -> "How to play adventure games"; ! Option -> -> "Looking around" ! with description "I am your eyes and ears ..."; ! Option -> -> "Taking and Dropping" ! with description "When you find items ..."; ! Option -> "About the author" ! with description "The author was born in ..."; ! ! Menus produced in this code are automatically divided into pages ! so that they'll always fit on the screen, whatever size the screen is ! and however many options there are. ! ! Note that since objects can always be moved about in play, it's easy ! to create new menu structures to fit the circumstances of the moment. ! (For example, a hints menu which gives hints only on currently open ! puzzles.) ! ! You can instead write a routine to receive the "description" message. ! Then the text printed when an option is chosen can also vary. ! ! If you return 2 from such a routine, then the game does not prompt ! the player to press a key before going back into the menu. ! If you return 3, then the whole menu is closed up immediately. ! ! Finally, you can always give your own "select" routine for an Option. ! The rules for return values are the same; what's different is that ! this way the screen will not be cleared and given a nice banner when ! the option is chosen. (Nothing visible will happen unless you do ! it yourself.) The SwitchOption class is an example of the kind of ! gadget you might want this for: ! ! Menu "Game settings"; ! SwitchOption -> FullRoomD "full room descriptions" has on; ! SwitchOption -> WordyP "wordier prompts"; ! SwitchOption -> AllowSavedG "allow saved games" has on; ! ! Choosing any of these switch-options flips them between on and off. ! In your program, you can test ! ! if (WordyP has on) ... ! ! and so forth to check the current state. ! ------------------------------------------------------------------------- Ifndef PKEY__TX; Constant LIB_PRE_63; ! Then we are using library 6/1 or 6/2, which won't have defined these: Constant NKEY__TX = " N = next option"; Constant PKEY__TX = "P = previous"; Constant QKEY1__TX = " Q = resume game"; Constant QKEY2__TX = "Q = previous menu"; Constant RKEY__TX = "RETURN = select option"; Constant NKEY1__KY = 'N'; Constant NKEY2__KY = 'n'; Constant PKEY1__KY = 'P'; Constant PKEY2__KY = 'p'; Constant QKEY1__KY = 'Q'; Constant QKEY2__KY = 'q'; Endif; Global screen_width; Global screen_height; ! Array ForUseByOptions string 128; ! Changed by Roger Firth to match the code in Section 44 of the DM4. Array ForUseByOptions -> 129; Class Option with emblazon [ bar_height page pages temp; screen_width = 0->33; ! Clear screen: @erase_window $ffff; @split_window bar_height; ! Black out top line in reverse video: @set_window 1; @set_cursor 1 1; style reverse; spaces(screen_width); if (standard_interpreter == 0) @set_cursor 1 1; else { ForUseByOptions-->0 = 128; @output_stream 3 ForUseByOptions; print (name) self; if (pages ~= 1) print " [", page, "/", pages, "]"; @output_stream -3; temp = (screen_width - ForUseByOptions-->0)/2; @set_cursor 1 temp; } print (name) self; if (pages ~= 1) print " [", page, "/", pages, "]"; return ForUseByOptions-->0; ], select [; self.emblazon(1, 1, 1); @set_window 0; font on; style roman; new_line; new_line; if (self provides description) return self.description(); "[No text written for this option.]^"; ]; Class Menu class Option with select [ count j obj pkey line oldline top_line bottom_line page pages options top_option; screen_width = 0->33; screen_height = 0->32; if (screen_height == 0 or 255) screen_height = 18; screen_height = screen_height - 7; options = 0; objectloop (obj in self && obj ofclass Option) options++; if (options == 0) return 2; pages = 1 + options/screen_height; top_line = 6; page = 1; line = top_line; .ReDisplay; top_option = (page - 1) * screen_height; self.emblazon(7 + count, page, pages); @set_cursor 2 1; spaces(screen_width); @set_cursor 2 2; print (string) NKEY__TX; j = screen_width-12; @set_cursor 2 j; print (string) PKEY__TX; @set_cursor 3 1; spaces(screen_width); @set_cursor 3 2; print (string) RKEY__TX; j = screen_width-17; @set_cursor 3 j; if (sender ofclass Option) print (string) QKEY2__TX; else print (string) QKEY1__TX; style roman; count = top_line; j = 0; objectloop (obj in self && obj ofclass Option) { if (j >= top_option && j < (top_option + screen_height)) { @set_cursor count 6; print (name) obj; count++; } j++; } bottom_line = count - 1; oldline = 0; for(::) { ! Move or create the > cursor: if (line~=oldline) { if (oldline~=0) { @set_cursor oldline 4; print " "; } @set_cursor line 4; print ">"; } oldline = line; @read_char 1 -> pkey; if (pkey == NKEY1__KY or NKEY2__KY or 130) { ! Cursor down: line++; if (line > bottom_line) { line = top_line; if (pages > 1) { if (page == pages) page = 1; else page++; jump ReDisplay; } } continue; } if (pkey == PKEY1__KY or PKEY2__KY or 129) { ! Cursor up: line--; if (line < top_line) { line = bottom_line; if (pages > 1) { if (page == 1) { page = pages; line = top_line + (options % screen_height) - 1; } else { page--; line = top_line + screen_height - 1; } jump ReDisplay; } } continue; } if (pkey==QKEY1__KY or QKEY2__KY or 27 or 131) break; if (pkey==10 or 13 or 132) { count = 0; objectloop (obj in self && obj ofclass Option) { if (count == top_option + line - top_line) break; count++; } switch(obj.select()) { 2: jump ReDisplay; 3: jump ExitMenu; } #ifdef LIB_PRE_63; print "[Please press SPACE to continue.]^"; #ifnot; L__M(##Miscellany, 53); #endif; @read_char 1 -> pkey; jump ReDisplay; } } .ExitMenu; if (sender ofclass Option) return 2; font on; @set_cursor 1 1; @erase_window $ffff; @set_window 0; new_line; new_line; new_line; if (deadflag==0) <<Look>>; return 2; ]; Class SwitchOption class Option with short_name [; print (object) self, " "; if (self has on) print "(on)"; else print "(off)"; rtrue; ], select [; if (self has on) give self ~on; else give self on; return 2; ]; ! ------------------------------------------------------------------------