Elevator and stairs (Hugo example)
From IFWiki
I didn't make this quite as efficient as I'd like, but oh well...
!:: ! Elevator and stairs by Roody Yogurt !:: constant GAME_TITLE "ELEVATOR ACTION" !:: Flags #set VERBSTUBS ! Include the grammar and routines from verbstubs.g ! and verbstubs.h, respectively !:: Grammar Library Inclusions (grammar must come first) ! put your new grammar here verb "take" * (stairs) "up"/"down" DoTakeStairs ! * multi DoGet #include "verblib.g" #include "hugolib.h" ! Standard Hugo Library !:: Game Initialization routine routine init { word[1] = "" MAX_SCORE = 0 ! ready to change for a scored game STATUSTYPE = 1 ! 0 = none :: 1 = score/turns :: 2 = time counter = -1 ! 1 step before first turn verbosity = 2 !\ VERBOSITY - ! 1 = normal (rooms described only if not previously described) ! 2 = full (rooms described every time they are entered) \! #if defined GAME_TITLE display.title_caption = GAME_TITLE ! ! display.title_caption - used for GUI clients for title bar text. #endif prompt = ">" window 0 ! resets the windows in case the player is restarting a game cls DEFAULT_FONT = PROP_ON Font(DEFAULT_FONT) Font(BOLD_ON) "Elevator Action" Font(BOLD_OFF) player = you location = groundfloor old_location = location MovePlayer(location) CalculateHolding(player) #ifset USE_PLURAL_OBJECTS InitPluralObjects #endif } !:: Main game loop routine main { counter = counter + 1 run location.each_turn runevents RunScripts if speaking not in location speaking = 0 PrintStatusLine } !\ I also used PreParse to allow "go up stairs" and "go stairs" (which I would redirect to "take stairs" \! player_character you "you" { before { actor PreParse { if word[1] = "go","climb","walk" { if word[2] = "stairs" { word[1] = "take" return true } elseif (word[2] = "up","down") and word[3] = "stairs" { DeleteWord(3, 1) ! delete "stairs" and reparse return true } } return false } } } !\ For readability, I made some constants to represent the different floors. GROUND = 1, SECOND = 2, THIRD = 3 \! enumerate start = 1 { GROUND, SECOND, THIRD } property floor_number alias misc room groundfloor "Ground Floor" { floor_number GROUND long_desc { "You are on the ground floor. The "; OpenOrClosed " door to the elevator is north, and stairs lead up." } u_to { if verbroutine = &DoGo Trudge return secondfloor } cant_go { if object = d_obj { "The stairs only lead up." } elseif object = n_obj, in_obj "The elevator doors are closed." else return false } n_to { if location.floor_number = doors.current_open_door { if verbroutine = &DoGo { "You step into the elevator." } return elevator } else return false } in_to return self.n_to } room secondfloor "Second Floor" { floor_number SECOND long_desc { "You are on the second floor. The "; OpenOrClosed " door to the elevator is north, and stairs go both up and down from here." } u_to { if verbroutine = &DoGo Trudge return thirdfloor } d_to { if verbroutine = &DoGo Trudge return groundfloor } cant_go { if object = n_obj, in_obj "The elevator doors are closed." else return false } n_to { if location.floor_number = doors.current_open_door { if verbroutine = &DoGo { "You step into the elevator." } return elevator } else return false } in_to return self.n_to } room thirdfloor "Third Floor" { floor_number THIRD long_desc { "You are on the ground floor. The "; OpenOrClosed " door to the elevator is north, or you can take the stairs down." } d_to { if verbroutine = &DoGo Trudge return secondfloor } cant_go { if object = u_obj ! if player tries to go up { "The stairs only lead down." } elseif object = n_obj, in_obj "The elevator doors are closed." else return false } n_to { if location.floor_number = doors.current_open_door { if verbroutine = &DoGo { "You step into the elevator." } return elevator } else return false } in_to return self.n_to } !\ This routine is called when the player takes the stairs. the "main" routine is called twice. it'll be called again when the turn is over, making 3 turns pass to get between floors. I also temporarily clear the location global so event text isn't printed while the player is moving. \! routine Trudge { local a a = location location = 0 "You trudge "; print object.noun #2; " the stairs." main : main location = a } routine OpenOrClosed { if location.floor_number = doors.current_open_door or (location = elevator and doors.current_open_door) "open"; else "closed"; return } scenery stairs "stairs" { noun "stairs" article "the" found_in groundfloor secondfloor thirdfloor long_desc { select location case groundfloor : "They go up." case secondfloor : "They go up and down." case thirdfloor : "They go down." } before {} } attribute glowing alias special scenery callbutton "call button" { current_floor 0 noun "button" adjective "call" article "the" found_in groundfloor secondfloor thirdfloor long_desc { if self is glowing "The call button is lit." else "The call button is dark." } before { object DoPush { if self is glowing { "The call button has already been pressed." } elseif location.floor_number ~= elevator.current_floor { "The call button begins to glow." self is glowing self.current_floor = location.floor_number Activate(moving_elevator) } elseif location.floor_number = elevator.current_floor and not doors.current_open_door { Activate(door_close,3) "The doors slide open." doors.current_open_door = location.floor_number } elseif location.floor_number = doors.current_open_door "Nothing happens." } } } daemon moving_elevator {} ! moving elevator event event in moving_elevator { if doors.current_open_door { if location = elevator or (elevator.current_floor = location.floor_number) { Deactivate(door_close) "\nThe doors slide closed." } doors.current_open_door = 0 ! clear it return ! takes up one turn } elseif elevator.current_floor ~= callbutton.current_floor { if location = elevator { "\nYou feel the elevator going "; if callbutton.current_floor > elevator.current_floor { "up"; if callbutton.current_floor - 1 = elevator.current_floor { " (and then come to a stop)"; } "." } else { "down"; if callbutton.current_floor + 1 = elevator.current_floor { " (and then come to a stop)"; } "." } } elseif location "\nYou hear the hum of the elevator moving." if callbutton.current_floor > elevator.current_floor elevator.current_floor++ ! go up else elevator.current_floor-- ! go down return } elseif elevator.current_floor = callbutton.current_floor { if location = elevator or location.floor_number = elevator.current_floor { "" "The elevator door opens."; Activate(door_close,3) if location ~= elevator " The call button blinks off." else "" event_flag = 2 } doors.current_open_door = elevator.current_floor callbutton.current_floor = 0 callbutton is not glowing Deactivate(self) } } fuse door_close {} event in door_close ! the "in" is optional { if not doors.current_open_door { Deactivate(self) } ! any code that executes each turn the fuse is running if not self.tick { if location.misc = doors.current_open_door { "\nThe elevator doors slide closed." } doors.current_open_door = 0 } } property current_open_door alias misc scenery doors "elevator doors" { noun "doors" "door" adjective "elevator" article "the" is openable current_open_door 0 found_in groundfloor secondfloor thirdfloor elevator long_desc { "The elevator door is "; OpenOrClosed if location = elevator ", and there's a panel of buttons next to it." else ", and there's a call button beside it." } before { object DoOpen { if self.current_open_door = location.floor_number or (location = elevator and self.current_open_door) { "The elevator door is already open." } elseif location = elevator "Try pushing a floor number." else "Try pushing the call button." } object DoClose { if self.current_open_door = location.floor_number or (location = elevator and self.current_open_door) { "The elevator door will close automatically when it needs to." } else "The elevator door is already closed." } } } property current_floor alias misc room elevator "Elevator" { current_floor GROUND ! starts on the ground floor long_desc { "You are inside the elevator. The door to the south is "; OpenOrClosed ", and there's the usual panel of buttons beside it." } s_to { if self.current_floor = doors.current_open_door { if verbroutine = &DoGo { "You step out of the elevator." } select doors.current_open_door case GROUND : return groundfloor case SECOND : return secondfloor case THIRD : return thirdfloor } else return false } out_to return self.s_to cant_go { if object = s_obj "The elevator doors are closed." else return false } } scenery panel "panel of buttons" { noun "buttons" "panel" adjective "panel" "of" article "the" in elevator long_desc { "The panel has three buttons: G, 2, and 3. There is also a LED display that reads "; SelectFloor "" } before { object DoPush { "You can only push one button at a time." } } } !\ I made the buttons and display components so trying to get them would get the "that is part of the panel of buttons" response \! component LED_display "LED display" { adjective "led" noun "display" article "the" part_of panel long_desc { "The LED display is showing a large red "; SelectFloor "" } } component g_button "G button" { article "the" adjective "g" noun "button" part_of panel long_desc { "The G button is "; LitorDark(self) } before { object DoPush { ElevatorPush(self) } } } component 2_button "2 button" { long_desc { "The 2 button is "; LitorDark(self) } article "the" adjective "2" noun "button" part_of panel before { object DoPush { ElevatorPush(self) } } } component 3_button "3 button" { long_desc { "The 3 button is "; LitorDark(self) } article "the" adjective "3" noun "button" part_of panel before { object DoPush { ElevatorPush(self) } } } routine LitorDark(obj) { local a select obj case g_button : a = GROUND case 2_button : a = SECOND case 3_button : a = THIRD if a = callbutton.current_floor "lit." else "dark." } routine SelectFloor { "\""; select elevator.current_floor case GROUND : "G.\""; case SECOND : "2.\""; case THIRD : "3.\""; } routine ElevatorPush(obj) { local a select obj case g_button : a = GROUND case 2_button : a = SECOND case 3_button : a = THIRD if callbutton.current_floor = a { print CThe(obj) ; " is already lit." return } if a = elevator.current_floor { if doors.current_open_door = a ! doors are already open { "Nothing happens." Activate(door_close, 3) ! extend door-open length by two turns return } else { if callbutton.current_floor and callbutton.current_floor ~= a { callbutton.current_floor = 0 "The elevator abruptly stops, and the doors slide open." doors.current_open_door = a Deactivate(moving_elevator) return } else { Deactivate(moving_elevator) "The doors slide open again." doors.current_open_door = a Activate(door_close,3) return } } } else { print "You push "; The(obj) ;"." } Deactivate(moving_elevator) callbutton.current_floor = a Activate(moving_elevator) } routine DoTakeStairs { if word[3] ~= "up", "down" { select location.floor_number case 1 : word[3] = "up" case 3 : word[3] = "down" } select word[3] case "up" : return Perform(&DoGo,u_obj) case "down" : return Perform(&DoGo,d_obj) case else { "Do you want to take the stairs up, or take the stairs down?" return false ! Don't take up a turn } } !\ If the player tries "take stairs" from the second floor, I didn't want the "which stairs did you mean?" response to take up a turn. The easiest way to do this was to replace DoGet and add my own code. If I didn't care about taking a turn, I could have easily handled it with a before routine in the stairs object. \! replace DoGet { local b, p if object = stairs and word[1] = "take" { select location case groundfloor : return Perform(&DoGo, u_obj) case secondfloor { "Do you want to take the stairs up, or take the stairs down?" return false ! Don't take up a turn } case thirdfloor : return Perform(&DoGo, d_obj) } if object in player VMessage(&DoGet, 1) ! "You already have that." elseif object = player { VMessage(&DoGet, 2) ! player trying to get player return false } elseif object is living and object is static { VMessage(&DoGet, 3) ! player trying to get character return false } elseif parent(object) is living and parent(object) is unfriendly VMessage(&DoGet, 4) ! "X doesn't want to give it to you." elseif (parent(object) is openable, not open) and parent(object) is container: { VMessage(&DoGet, 5) ! "X is closed." return false } elseif Contains(object, player) { if object is static VMessage(&DoGet, 7) ! "You can't take that." else VMessage(&DoGet, 6) ! "Not while you're in/on it..." return false } else { if not CheckReach(object) return false elseif object is static { VMessage(&DoGet, 7) ! "You can't take that." return true } ! Because the engine calls location.before if (parent(object)~=location) b = parent(object).before if not b { xobject = parent(object) if object not in location { CalculateHolding(xobject) p = xobject } if Acquire(player, object) { object is not hidden if not object.after { ! Again, to avoid duplication or ! echoing: ! b = 0 if xobject ~= location b = xobject.after if b = false ! "Taken." VMessage(&DoGet, 8) } } else ! "You're carrying too much to take that." VMessage(&DoGet, 9) } } return true }