!\--------------------------------------------------------------------------- GBCOMM.HUG Common objects, etc. for Guilty Bastards by Kent Tessman (c) 1998 Including: you (the player object) volvo, volvokeys cindy, murder the master global event graphics_window, detail_window distant_class NPC class DoGetIn, DoDriveTo DoHide DoKnock, DoShoot, DoSwear, DoXyzzy, DoArrest ArrestPlayer UpdateGraphicsWindow, UpdateDetailWindow DescribePlace PreParse NewOMessages ---------------------------------------------------------------------------\! ! These can be set by a property routine, in which case the routine (i.e., ! object.graphic) should return -1: global current_resource_file global current_resource ! Various things used by the split display: attribute pictured ! i.e., visible in the graphic, so doesn't ! have to be listed separately global last_drawn_picture global force_repaint property graphic ! two elements: resourcefile and picture property static_occupant ! someone who's always there global objects_detailed ! for knowing if we have to re-update the ! detail window constant PRELISTED_OBJS 2 ! listed before anything else in array prelist[PRELISTED_OBJS] ! detail window attribute driving_location ! for locations that can be driven to property arrive_desc property before_driving ! i.e., before leaving property driving_coordinates ! (1-20, 1-20), to gauge driving time property analysis ! for objects sent to the police lab property hide_preposition ! i.e., "behind", "in" ! For characters: property is_following ! 0, or character being followed property suspended ! true if temporarily not following property disposition ! 0 = initial property lying_about_cindy ! deceitful bunch, hmmm? property lying_about_billy property lying_about_holly property knows_about_photos property knows_about_screeningreport property has_seen_billy ! when Billy is following the player global soundtrack_stage ! for keeping track of what music should play ! 0=opening/song changes allowed, ! 1=song changes disallowed, 2=final music !---------------------------------------------------------------------------- ! PLAYER: ! ! The player object is somewhat extended here to allow management of ! graphics, sound, and music: player_character you "you" { misc 0 ! When misc is true, the after routine for ! DoEnter will repaint the detail window before { actor DoEnter { if object.type = vehicle self.misc = 1 else self.misc = 0 return false } } after { actor DoGet, DoDrop, DoEnter, DoExit { if verbroutine ~= &DoEnter or self.misc UpdateDetailWindow return false } actor DoLook { UpdateGraphicsWindow(object) } ! Upon a restore, it's necessary to: ! ! - force a redraw of the graphics window ! - force restarting of sound/music ! actor DoRestore { force_repaint = true UpdateGraphicsWindow(location) ! Re-sync music and sound effects if necessary: if audio.current_music { PlayMusic(audio.current_music_resfile, audio.current_music, audio.current_music_looping, true) ! force restart } else PlayMusic(MUSIC_STOP) if audio.current_sound { PlaySound(audio.current_sound_resfile, audio.current_sound, audio.current_sound_looping, true) ! force restart } else PlaySound(SOUND_STOP) } } } vehicle volvo "Volvo" { #ifset PART1 in outsidehotel #endif nouns "volvo", "car", "auto", "automobile", "vehicle", "sportscar", \ "convertible" adjective "my", "cousin's", "volvo", "sports" article "your cousin's" parse_rank 1 ! prefer to volvokeys vehicle_verb "drive" vehicle_move { if volvokeys not in player { "Would help if you had the car keys."; if volvokeys is not special { " Now, you \Idid\i have them. At some point. Last night at some point." volvokeys is special } print newline return false } } reach volvo short_desc { if player in self "You're sitting in the Volvo."; else "Your cousin's Volvo is parked here."; } graphic IMAGES1, "volvo" long_desc "A sporty little two seater, although not the freshest daisy in the patch. In a town where you are what you drive, you're showing a few rust spots and your muffler cackles a little too loudly when leaving the scene of a stop light. Your cousin lent you the car in exchange for looking after his plants while he's in Europe--one of these weeks you'll have to pop in to make sure they're still alive." is container, lockable, not locked key_object volvokeys capacity 100 before { parent(player) DoGo { if word[1] = "out" { Perform(&DoExit) return true } return false } object DoLock, DoUnlock { "There's really no need, seeing as it's a convertible." } object DoLook { if player in self "It's a little hard to get a good look at the Volvo while you're inside it." else: return false } } after { parent(player) DoEnter { "You climb into the Volvo." } } is known } object volvokeys "your cousin's car keys" { #ifset PART1 in dresser #endif nouns "keys" adjective "my", "cousin's", "keys", "volvo", "car" long_desc "The keys to your cousin's Volvo." size 5 is plural, known, locked ! 'locked' for special use, see below before { ! Because the keys will get selected for "enter car" if there's ! no car object DoEnter { "There's no car here to get in." } } after { object DoGet { ! One-time only message; can't use 'moved' because ! DoGet will clear it: if self is locked { "You grab the keys off the night table." self is not locked } else: return false } } } character cindy "Cindy Painter" ! for reference { nouns "cindy", "painter" adjective "cindy" parse_rank 5 ! i.e., for "cindy" is known } object murder "murder" ! for reference { nouns "murder", "killing", "homicide", "crime", "death" adjectives "cindy", "painter", "cindy's", "painter's" article "the" is known } !---------------------------------------------------------------------------- ! MASTER GLOBAL EVENT: ! ! This event runs globally every turn to manage world events such as the ! day wearing on as the counter ticks. event { select counter #ifset PART1 case 540 ! 9:00 a.m. { if mainbuilding is not visited { event_flag = true "\nDon't you have somewhere you were supposed to be by now?" } } #endif case 1200 ! 8:00 p.m. { event_flag = true "\n(It's getting late, and one can't help but notice you haven't cracked this baby yet. Keep in mind that Milt Walker said he was going to give you twelve hours--now that's twelve hours from when he wanted you to meet him, not from when you first decided you may have a hope of getting somewhere.)" } case 1265 ! 9:05 p.m. { "\nIt's past 9:00 in the p.m., well after when Milt Walker probably wanted you to have come up with something by. It's time to pack it in for the night.\n\n You head back to the Pelican Hotel for maybe a shower or to stare at the ceiling fan spinning or something, but you can't help but feel you "; if screeningreport is known "were getting close to cracking this one. After all, what was Stuart Fina doing with that screening report? And what'd it have to do with those photos in Chuck Bixby's safe?" elseif photos is known "were just starting to unravel this case. Most importantly, you wonder what the deal was with those photos in Chuck Bixby's safe: who took them and how did they get there?" elseif billy is known "were just getting started. Here you'd started to put together some of the pieces of the puzzle that was Cindy Painter's life, tracking down that musician Billy Van Earl in Santa Monica." elseif birthdaycard is known "were just getting started. Maybe if you'd been able to feel out what sort of puzzle was made up of the pieces of Cindy's life--like that birthday card you found." else "might've done things a little differently, maybe spending a little more time trying to figure out whatever small clues might lead you from Cindy Painter to someone else, someone who could help put you on the right track." "\nAnyway. You've got a message from Milt Walker waiting for you when you get back to the Pelican. You call him back and agree to meet with him to give him what you managed to find out. Whether or not he passes any or all of it on to the police, you never find out.\n\n What you do hear, months later, is that an LAPD detective manages to solve the crime. And when you hear how the case was broken, you almost feel like slapping your own forehead because you realize that you could've been the one to do it, just given one...more...chance.\n\n Next time. Whenever that might be." endflag = 3 } #ifset PART1 #ifset INCLUDE_OTHER_ACTORS ! Johnny LaFleure pops out of the soundstage every hour for a ! 15-minute smoke break: if not mod(counter, 60) and counter > 480 { move johnny to backlot if player in backlot { event_flag = true "\nJohnny LaFleure comes out of one of the soundstages. \"\IAllo\i,\" he says in a cheerful French accent before lighting a cigarette." } } elseif not mod(counter, 15) and johnny in backlot { if player in backlot { event_flag = true "\nJohnny drops the butt of his cigarette onto the ground and flattens it under his toe. \"Back to work,\" he says, and disappears back into the soundstage." } remove johnny } #endif #endif } !---------------------------------------------------------------------------- ! WINDOWS ! ! Not that all the win_ for size/position are routines returning ! values based on the current display object properties (in case the screen ! size changes, as it might under a graphical environment such as Windows). window_class graphics_window { ! The graphics window is the left-top rectangle of the screen: ! (Its aspect ratio is slightly wider than 1.66:1 on a standard ! 80x25 screen.) win_left 1 win_top return 1 + display.statusline_height win_right return display.screenwidth/2 win_bottom return display.screenheight * 5/10 - 2 win_textcolor WHITE win_backcolor BLACK } window_class detail_window { ! The detail window is the right-top rectangle of the screen: win_left return display.screenwidth/2 + 1 win_top return graphics_window.win_top win_right return display.screenwidth win_bottom return graphics_window.win_bottom win_textcolor WHITE win_backcolor BLACK } !---------------------------------------------------------------------------- ! OTHER CLASSES: ! distant_class ! ! A useful class, built on the scenery class, since various objects may be ! visible but not accessible to a player in a given location: property real_object ! i.e., "object" as to "distant_object" scenery distant_class { before { ! Pass allowable actions: object DoLook {return false} ! Give special responses: object DoGo { "Which way do you want to go?" } ! Or give the default response: object { "You can't do that from here." } } long_desc { return self.real_object.long_desc } parse_rank -1 ! a parse_rank of -1 puts it definitely out of the ! running unless there's no other possible object ! present } ! NPC class ! ! Useful for creating properties that should be inherited by all non-player ! characters. character NPC {} !---------------------------------------------------------------------------- ! VERB ROUTINES: routine DoGetIn { if player in location and volvo in location { Perform(&DoEnter, volvo) run player.after return true } else return DoVague } routine DoDriveTo { local v, moveto, time_elapsed if player not in volvo { "You ought to actually be in your car before you try to drive anywhere." return } elseif object is not driving_location { "Try \"drive to (somewhere specific)\", giving, preferably, a street name or particular location that you can drive to." PrintFootnote(3) return } elseif location = object { "You're already there." return } v = volvo moveto = object if not v.vehicle_move: return true ! Not a particularly geometrically correct method of figuring how ! long it takes to get from A to B, but it'll do: local x1, x2, y1, y2 x1 = location.driving_coordinates #1 y1 = location.driving_coordinates #2 x2 = moveto.driving_coordinates #1 y2 = moveto.driving_coordinates #2 time_elapsed = \ (higher(x1, x2) - lower(x1, x2)) + \ (higher(y1, y2) - lower(y1, y2)) time_elapsed /= 2 ! gameplay adjustment counter += time_elapsed ! For, for example, adjusting the soundtrack upon leaving a location: run location.before_driving run moveto.arrive_desc ! The code from here to the end of DoDriveTo is adapted/stolen from ! DoMoveinVehicle in "objlib.h". The reason we can't simply use the ! object library's vehicle code is because this driving verb specifies ! locations rather than directions, but it's fairly easy to set and ! to workable parameters. #ifset USE_ATTACHABLES if ObjectisAttached(v, location, moveto) return false #endif ! Finally, the vehicle can move move v to moveto v is moved old_location = location location = moveto #ifset USE_ATTACHABLES MoveAllAttachables(v, old_location, location) #endif if not FindLight(location) DarkWarning else {DescribePlace(location) location is visited} run v.after return true } routine DoHide { local i, flag if not object { "You'll have to be a little more specific about where and how you hope to hide." return } else { for (i=1; i<=object.#hide_preposition; i++) { if word[2] = object.hide_preposition #i flag = true } if not flag { "You can't hide "; print word[2]; " "; The(object); "."; if object.hide_preposition { " (But you might be able to able to hide "; print object.hide_preposition; \ MatchPlural(object, "it", "them"); ".)" } print newline } else { ! The after routine will really do the work; ! this default will probably never run: if not object.after { print "You hide "; word[2]; " "; \ The(object); "." } return true } } } routine DoKnock { if self is living "Well, that's not going to make you any friends now, is it?" elseif object.type ~= door print "You knock on "; The(object); " to no avail." else "No one answers." } routine DoShoot { "Not that you have a gun to shoot anything or anybody with." } routine DoSwear { if word[1] = "fuck" and object is living and object ~= player "I wouldn't hold your breath." else { local n n = random(5) select n case 1 "Hey! Save it for the docks." case 2 "When you're done wallowing in the gutter, let us know." case 3 "Now that didn't take much imagination, did it?" case 4 "Hey, rhyme that with something and you'll have a nifty little song." case 5 "You kiss your mother with that mouth?" } } routine DoXyzzy { local obj for obj in location { if obj is living and obj ~= player break } "A hollow voice asks: \"What were you hoping for?\""; if obj { print " "; CThe(obj); " looks around. \"What the hell was that?\"" } print newline } routine DoArrest { if object is not living { CThe(object) " is about all you're going to be arresting." } else "You're not a cop--you're not gonna be arresting anybody." } ! Used for "Billy, stop following me", etc., but we need a response for the ! player: routine DoStop { "You're free to do whatever you like." } !---------------------------------------------------------------------------- ! OTHER ROUTINES: ! ArrestPlayer assumes the narration of the actual arrest itself has already ! been handled global player_arrested routine ArrestPlayer { counter += 120 ! 2 hours later event_flag = 2 ! force an interruption if waiting player_arrested = true MovePlayer(policestation) "\nAfter a couple of hours cooling your heels in the slammer, you get sprung by Jake Duffy who has managed to paint a pathetic enough picture of you to get you out of jail. To his credit, he manages to refrain from making any \"Nice going\" comments. Instead, he drives you back to your car and lets you out. \"Well, let me know if you need anything,\" he says. \"And try not to get arrested anymore.\" Then, as he tends to do, he disappears." player_arrested = false MovePlayer(parent(volvo)) } room policestation { name { if (verbroutine = &DoAsk, &DoTell) and location ~= self { return "police" } else return "police station" } noun "police" article "the" parse_rank 1 ! prefer to police report, police tape long_desc { "Welcome to the Los Angeles Police Department--you didn't catch which precinct. The men and women in blue--actually in black, since that's the LAPD uniform--go about their business"; if player_arrested ", which is largely dealing with people like you. \ (At least at this particular moment, you're a little chagrined to realize.)" else print "." } is known } routine UpdateGraphicsWindow(obj) { ! SL_... settings copied from Init in gb.hug (duplicated here ! in case the engine changes display.hasgraphics during play) if not display.hasgraphics { SL_TEXTCOLOR = DEF_SL_FOREGROUND SL_BGCOLOR = DEF_SL_BACKGROUND return } else { SL_TEXTCOLOR = BRIGHT_WHITE SL_BGCOLOR = BLUE } if last_drawn_picture = obj and not force_repaint return if not obj.graphic and obj ~= 0 ! 0 forces window clearing return run graphics_window.win_init window { if obj.graphic ~= -1 { current_resource_file = obj.graphic #1 current_resource = obj.graphic #2 } picture current_resource_file, current_resource } run graphics_window.win_end last_drawn_picture = obj force_repaint = false } routine UpdateDetailWindow { local obj, count, tempformat local initial_format if not display.hasgraphics return ! The detail window is always listed in sentence format, not as an ! itemized list: initial_format = FORMAT FORMAT &= ~LIST_F ! Erase the sliver between the two windows, in the event that an ! engine screen resizing has left some garbage trapped in there: ! (This can happen, for example, with the Windows engine where ! the screen is dynamically resizable.) ! !\ If there's a space between the two windows, but commented out for now because there is no space window (detail_window.win_left-1), (detail_window.win_top), \ (detail_window.win_left-1), (detail_window.win_bottom) { cls } \! run detail_window.win_init window { run detail_window.win_clear locate 1, 1 Font(ITALIC_ON) count = 0 ! List characters first in detail window list_count = 0 for obj in location { if obj is living and obj is not hidden and obj is not pictured and not (&obj.initial_desc and obj is not moved) { obj is not already_listed list_count++ } else obj is already_listed } if list_count ! if characters are to be listed { count = 1 tempformat = FORMAT FORMAT = FORMAT | FIRSTCAPITAL_F | ISORAREHERE_F \ | USECHARNAMES_F | NORECURSE_F if FORMAT & LIST_F { FORMAT = FORMAT & ~LIST_F ! clear it FORMAT = FORMAT | TEMPLIST_F } list_nest = 0 ListObjects(location) FORMAT = tempformat print "." } ! List non-characters second list_count = 0 for obj in location { if obj is not living and obj is not hidden and obj is not pictured and not (&obj.initial_desc and obj is not moved) { obj is not already_listed list_count++ } else obj is already_listed } if list_count ! if non-characters are to be listed { ! Double-space if characters were listed if count: print "" ! Take care of special cases need_newline = false for (obj=0; obj" if necessary if location = place and player not in place { if parent(player).prep print ", "; parent(player).prep; " "; else print ", "; IN_WORD; " "; print Art(parent(player)) } print newline Font(BOLD_OFF) override_indent = false ! Added to "hugolib.h"'s DescribePlace routine: UpdateGraphicsWindow(location) UpdateDetailWindow if place is not visited and verbosity ~= 1 { if &place.initial_desc or &place.long_desc Indent if not place.initial_desc run place.long_desc } elseif long = true or verbosity = 2 { if &place.long_desc Indent run place.long_desc } if &place.list_contents and FORMAT & DESCFORM_F print "" ! for double-space-after-heading formatting ! A location may contain an overriding listing routine, as may any ! parent, in the list_contents property ! if not place.list_contents { list_nest = 0 ! For double-space-after-heading formatting: if FORMAT & DESCFORM_F: { for obj in place { if obj is not hidden and (player not in obj or children(obj) > 1): { print "" break } } } ! List contents of chair, vehicle, etc. player is in if player not in location WhatsIn(Parent(player)) ! List all characters, if any count = 0 for obj in place { if obj is hidden or obj is not living or player in obj: obj is already_listed else { obj is not already_listed ShortDescribe(obj) if obj is not already_listed count++ } } list_count = count count = 0 if list_count ! if characters are to be listed { tempformat = FORMAT FORMAT = FORMAT | FIRSTCAPITAL_F | ISORAREHERE_F FORMAT = FORMAT | USECHARNAMES_F if FORMAT & LIST_F { FORMAT = FORMAT & ~LIST_F ! clear it FORMAT = FORMAT | TEMPLIST_F } Indent list_nest = 0 ListObjects(place) FORMAT = tempformat } for obj in place { #ifset USE_ATTACHABLES ! Exclude all attachables for now (and characters) if obj is living or obj.type = attachable or player in obj #else if obj is living or player in obj #endif obj is already_listed else obj is not already_listed } for obj in place { #ifset USE_PLURAL_OBJECTS ! ...And don't list identical objects yet, either if (obj.identical_to).type = identical_class { if obj is not hidden count++ } elseif player not in obj #else if player not in obj #endif { if obj is not already_listed and obj is not hidden: { ShortDescribe(obj) if obj is not already_listed notlisted++ } } } if notlisted or count { list_count = notlisted + count tempformat = FORMAT FORMAT = FORMAT | FIRSTCAPITAL_F | ISORAREHERE_F if FORMAT & LIST_F { FORMAT = FORMAT & ~LIST_F ! clear it FORMAT = FORMAT | TEMPLIST_F } Indent list_nest = 0 ListObjects(place) FORMAT = tempformat } #ifclear NO_OBJLIB #ifset USE_ATTACHABLES for obj in place { ! Print attachables last if obj.type = attachable and obj is not hidden { ShortDescribe(obj) if obj is not already_listed Message(&DescribePlace, obj, 2) } } #endif print newline override_indent = false ! Finally, list contents of scenery objects (unless we've ! already done so as the parent of the player) ! for obj in place { if obj.type = scenery and player not in obj and (obj is open or obj is not openable) { obj is known WhatsIn(obj) } } #endif ! ifclear NO_OBJLIB } } replace NewOMessages(obj, num, a, b) ! from OBJLIB.H { select obj case vehicle { if num = 1 { "To get anywhere by driving, try something along the lines of \"drive to my hotel\". (Or, if you plan to walk, you'll have to get out of the car.)" return true } } return false } !---------------------------------------------------------------------------- ! STUBS (in case certain parts are omitted, so that we don't get a bunch of ! compilation errors for non-existent routines, objects, etc.): #ifclear INCLUDE_FOOTNOTES ! gbnotes.hug routine PrintFootnote(n) {} #endif #ifclear PART1 ! gb1.hug character milt "Milt" { nouns "milt", "walker" adjective "milt" } character stuart "Stuart" { nouns "stuart", "fina" adjective "stuart" } object cindyskey {} object cindysmovie {} object cassettetape {} object birthdaycard {} object torncard {} object screeningreport {} object stuartscar {} object negatives {} #endif #ifclear PART2 ! gb2.hug character billy "Billy" { nouns "billy", "earl" adjectives "billy", "van" } character holly "Holly" { nouns "holly", "golden" adjective "holly" } character chuck "Chuck" { nouns "chuck", "bixby" adjective "chuck" } object santamonica {} object funkzilla {} object photos "photos" { noun "photos" is plural } object foldednote "folded note" { noun "note" adjective "folded" } fuse billy_freaked {} #endif