Constant Story "Spider And Web"; Constant Headline "^Interactive Fiction^Copyright 1997-8 by Andrew Plotkin.^ (First-time players should type ~about~. For credits, ~credits~.)^"; Release 4; !Constant DEBUG; !Constant ZDEBUG; Constant DEBUGDUMP; Constant NO_PLACES; Constant DEATH_MENTION_UNDO; Constant DOOR_OVERRIDE; Constant VEHICLE_NAMES; Constant USE_PARTINVENT; ! Constant ALTER_AMBIG_BEHAVIOR; ! not needed for 6/6 library Constant SKIP_MAGIC_ARTICLES; Constant SKIP_STANDARD_TIMERS; Constant SKIP_STUPID_PRONOUNS; Constant SKIP_VERBOSITY_SYNONYMS; Constant ALLOW_INTERRUPT_MULTI; #ifdef ZDEBUG; ! Constant SKIP_ANY; #ifdef SKIP_ANY; !Constant SKIP_SCENE_1; Constant SKIP_SCENE_2; Constant SKIP_SCENE_3; Constant SKIP_SCENE_4; Constant SKIP_SCENE_5; !Constant SKIP_SCENE_6; !Constant SKIP_SCENE_7; !Constant SKIP_SCENE_8; !Constant SKIP_SCENE_9; #endif; #endif; ! ZDEBUG Global statusprinting = 0; ! will be 1 if the library is printing in the status line Global escaping = 0; ! the most important variable in the entire game Global powercut = 0; ! the second-most important ! various emotional state indicators Global num_wait_answers = 0; Global num_yes_answers = 0; Global num_no_answers = 0; Global num_blatant_lies = 0; ! lying just to lie Global num_sneaky_lies = 0; ! lying to hide info Global num_stubborns = 0; ! being stubborn/smartass for the sake of it Global num_helpfuls = 0; ! being helpful to Himself Global num_jingos = 0; ! my side is inherently right Global num_cosmos = 0; ! both sides are really looking in a mirror Global num_dumb_recall = 0; ! doing something provably false in recall Global num_got_angry = 0; ! he actually gets pissed off Global num_stupids = 0; ! you say something dumb, in real life Abbreviate ". "; Abbreviate ", "; Abbreviate " the "; Abbreviate "The"; Abbreviate "You"; Abbreviate "[BUG]"; Abbreviate "ing"; Abbreviate "you"; Abbreviate "and"; Abbreviate "He touches a control"; Abbreviate " tha"; Abbreviate "ther"; Abbreviate " guard"; Abbreviate " through"; Abbreviate " thi"; Abbreviate " to"; Abbreviate " close"; Abbreviate " wi"; Abbreviate " of"; Abbreviate " is"; Abbreviate " ma"; Abbreviate " are"; Abbreviate " in"; Abbreviate " mo"; Abbreviate " pa"; Abbreviate " can"; Abbreviate " wh"; Abbreviate " have"; Abbreviate " hea"; Abbreviate " door"; Abbreviate " dow"; Abbreviate " reach"; Abbreviate " wo"; Abbreviate " al"; Abbreviate " no"; Abbreviate " their"; Abbreviate " time"; Abbreviate " de"; Abbreviate " gr"; Abbreviate "they"; Abbreviate " en"; Abbreviate ".~^"; Abbreviate " co"; Abbreviate " from"; Abbreviate "It's"; Abbreviate " lo"; Abbreviate " ope"; Abbreviate " still"; Abbreviate " fi"; Abbreviate " me"; Abbreviate " sp"; Abbreviate " fl"; Abbreviate " be"; Abbreviate " le"; Abbreviate "tion"; Abbreviate " bl"; Abbreviate " we"; Abbreviate " bu"; Abbreviate " see"; Abbreviate "then"; Abbreviate " on"; Abbreviate " som"; Abbreviate " like"; Abbreviate " lin"; Constant CantGoThatWay "You can't go that way."; Replace ScopeWithin; Replace OffersLight; Replace DrawStatusLine; Replace InvWideSub; Replace ScoreSub; Replace TellSub; Replace AskSub; Replace AnswerSub; ! 6/6 and /7 bug Replace ThatorThose; ! 6/6 bug Replace ScriptOnSub; Replace ScriptOffSub; ! save a little storage space Replace LowKey_Menu; Replace DoMenu; Include "Parser"; Include "pri-daemons.inc"; Object LibraryMessages "lib_messages" with ! a few dummy properties to provide definitions supporter_describe 0, vehicle_status_name 0, ceiling_reachable 0, ! the text before [ ix; Look: switch (lm_n) { 3: if (LocLitAnyway()) rtrue; print " (in dimness)"; rtrue; 4: if (lm_o provides supporter_describe) { if (lm_o.supporter_describe()) rtrue; } rfalse; } Inv: if (player == passiveplayer) rfalse; if (escaping) rfalse; switch (lm_n) { 1: "You are carrying nothing of importance."; 2: print "You are carrying nothing worthy of attention, except"; rtrue; } LMode1: " is now in its ~brief~ printing mode, which gives long descriptions of places the first time you visit, and short descriptions when you return."; LMode2: " is now in its normal ~verbose~ mode, which always gives long descriptions of locations (even if you've been there before)."; Miscellany: switch (lm_n) { 5: print "^Would you like to RESTART, RESTORE a saved game"; #IFDEF DEATH_MENTION_UNDO; if (undo_flag~=0 && deadflag~=2) ! --Z print ", UNDO your last move"; #ENDIF; if (TASKS_PROVIDED==0) print ", give the FULL score for that game"; if (deadflag==2 && AMUSING_PROVIDED==0) print ", see the AFTERWORD"; ", or QUIT?"; 38: "That's not a verb I recognize."; } Insert: switch (lm_n) { 2: if (lm_o == player) "This isn't that kind of game."; } PutOn: switch (lm_n) { 3: if (lm_o == outerdoorplate || lm_o ofclass InteriorPlate) print_ret (The) lm_o, " is vertical; it can't support anything."; } Wake: "You're sure this is not a dream."; Blow: print_ret "Blowing on ", (thatorthose) lm_o, " has no effect."; Yes, No: VagueSaySub(); rtrue; Tell: switch (lm_n) { 1: VagueSaySub(); rtrue; } Attack: "That won't help."; ThrowAt: switch (lm_n) { 2: "Quite futile."; } Listen: if (lm_o) rfalse; if (powercut) "The air is still."; if (ventilator in location || location == CeilingHole) "You hear nothing special, beyond a ventilation grille's constant hiss."; rfalse; Hide: switch (lm_n) { 1: "Hiding won't help you right now."; 2: "You're as well hidden as you can manage already."; 3: "That's not very practical at the moment."; 4: "You'll do your best."; } Take: switch (lm_n) { 7: print_ret (CThatorThose) noun, " ", (IsorAre) noun, " a part of ", (the) lm_o, "."; } Drop: switch (lm_n) { 2: ! we know it's notin player if (anywherein(lm_o, player)) { ix = parent(lm_o); if (ix has container) print_ret (The) lm_o, " isn't in your hands; it's inside ", (the) ix, "."; } print_ret "You haven't got ", (thatorthose) lm_o, "."; 4: if (verb_word == 'throw') print_ret (The) lm_o, " lands a short distance away."; else "Dropped."; } Unlock: switch (lm_n) { 1: print "It's not clear"; if (second) { print " precisely how ", (the) second, " can be used"; } else { print " how"; } print " to unlock"; if (lm_o hasnt pluralname) print " that"; else print " those"; "."; } ]; Include "VerbLib"; Fake_Action TouchTo; Fake_Action TouchFrom; Fake_Action Connect; Fake_Action Disconnect; Attribute connector; ! ------------- Objects Class PlayerClass with number thedark, capacity 100, before [; PutOn, Insert: <>; WakeOther: if (second == nothing or player) <>; ], life [; Answer, Ask, Tell, AskFor: VagueSaySub(); rtrue; ], has concealed animate proper transparent; Object activeplayer "yourself" class PlayerClass, with description [; if (~~escaping) "Just yourself, as usual."; else "Just yourself, somewhat the worse for rough handling. But you're feeling much, much better for being free."; ]; Object passiveplayer "yourself" class PlayerClass, with description "Just yourself, somewhat the worse for rough handling. And worse yet that you are restrained in this chair, hand and foot, with sensor leads pasted to your temples."; ! This is for general cases; the office desk is taken care of elsewhere. [ CanReachCeiling obj; obj = parent(player); if (obj == nothing) rfalse; if (obj provides ceiling_reachable) { if (obj.ceiling_reachable()) rtrue; } rfalse; ]; Class CeilingClass with name 'ceiling' 'roof' 'acoustic' 'acoustical' 'panel' 'panels' 'panelling', short_name "panel ceiling", react_before [; Jump: if (panelhole in location) { rfalse; } if (~~CanReachCeiling()) "You bend and leap. Your fingers brush the ceiling panels, but not strongly enough to do anything."; else "You bend and leap. Your hands slam against the ceiling panels, but they hold firm."; ], before [; Examine: rfalse; Search: if (panelhole in location) { <>; } if (~~CanReachCeiling()) <>; rfalse; Listen: "You hear nothing, which is after all the point."; Touch: if (~~CanReachCeiling()) "The ceiling is a foot out of your reach."; "The ceiling panels are rough to the touch."; Push, Pull, Attack: if (~~CanReachCeiling()) <>; "The acoustical panels won't move."; ThrownAt: rfalse; default: if (~~CanReachCeiling()) <>; ], has scenery transparent; Object lowceiling class CeilingClass, with found_in WhiteJunction InsideEntrance SouthBoundary NorthBoundary CeilingHole StorageCloset, description [; print "Wide rectangles cover the ceiling with the grey, dimpled texture of good acoustical insulation. Between the panels are"; if (powercut == 0 || LocLitAnyway()) print " the glowing strips which illuminate the area"; else print " the dim flickers of the lighting strips"; if (boundaryweb in location) { print ". You notice that to the southeast, beyond the red boundary line, the ceiling panels are also striped with the characteristic glitter of a scan web"; } if (panelhole in location) { print ". One of the panels is missing, leaving a dark gap"; if (blood in location) print "; some blood is smeared at its edge"; } "."; ]; Object highceiling class CeilingClass, with found_in TossJunction SouthFrontier DeadEndHall OutsideLab LabJunction SecurityBranch SecurityAnnex FakeFrontier FinalEnd NortheastCorner SharpCorner WiringCloset, description [; print "Wide rectangles cover the ceiling with the grey, dimpled texture of good acoustical insulation. Between the panels are"; if (powercut == 0 || LocLitAnyway()) print " the glowing strips which illuminate the area"; else print " the dim flickers of the lighting strips"; print ", and also the characteristic glitter of a scan web"; "."; ]; Object ceilingweb "metal-scan web" highceiling with name 'metal' 'glitter' 'metal-scan' 'scan' 'web' 'scan-web' 'webbing', description "The ceiling is lined with scan-web in addition to the lighting strips. The web's wide spacing indicates a metal-resonance scan.", before [; Examine: rfalse; Touch: if (~~CanReachCeiling()) "The webbing is part of the ceiling. It's out of reach."; "The web is too fine to discern by touch."; ThrownAt: rfalse; default: if (~~CanReachCeiling()) <>; ], has scenery; Class ceilingstrips with name 'illuminated' 'glowing' 'lights' 'strips' 'strip' 'plastic', short_name "glowing strips", description [; if (powercut == 0 || LocLitAnyway()) print "Glowing"; else print "Dimly flickering"; " strips of plastic cross the ceiling panels."; ], before [; Examine: rfalse; Touch: if (~~CanReachCeiling()) <>; "The strips are cool, despite their light."; Push, Pull, Attack: if (~~CanReachCeiling()) <>; "You cannot damage the strips."; ThrownAt: rfalse; default: if (~~CanReachCeiling()) <>; ], has scenery pluralname; Object lowceilingstrips lowceiling class ceilingstrips; Object highceilingstrips highceiling class ceilingstrips; [ LocLitAnyway; if (location == SecurityAnnex or OutsideLab or Laboratory) rtrue; ! I'd include SecurityStorage, SecurityOffice, InterrogRoomReal, but ! there's no way to get back there after the power cut. rfalse; ]; Object tilefloor "floor" with name 'ground' 'floor' 'tile' 'tiles', found_in [; if (location == EndOfAlley or MouthOfAlley) rfalse; rtrue; ], description [; print "The floor is featureless white tile"; if (boundaryline in location) print ", except for the red dotted line to the southeast"; "."; ], before [; Search: <>; Touch, Rub: "The tiles are cold and just faintly gritty."; Receive: <>; Enter: if (player in location) "You are already on the floor."; <>; Take, Pull, LookUnder: "You cannot move the floor tiles."; ], has scenery; Object whitewalls "walls" with name 'cold' 'tough' 'white' 'composite' 'wall' 'walls', found_in [; if (location == EndOfAlley or MouthOfAlley or InterrogRoom) rfalse; if (location == SecurityOffice or InterrogRoomReal) rfalse; if (location == Laboratory) rfalse; rtrue; ], description [; print "The walls are faced with tough white composite. They are smooth and blank"; if (location ~= WiringCloset) { print ", except for one ventilator grille near the ceiling"; } "."; ], before [; Search: <>; Touch, Rub, Push: "The walls are cold and unyielding."; Attack, KnockOn: "The composite absorbs the blow without a hint of a thud."; Climb: print "Quite impossible."; self.number++; if (self.number == 2) print " (Or did you mean that figuratively?)"; new_line; rtrue; ], number 0, ! used for smartass remark has scenery pluralname; ! See also supervent, in t-2. Object ventilator "ventilator grille" with name 'plastic' 'ventilator' 'ventilation' 'vent' 'duct' 'grill' 'grille', found_in [; if (location == EndOfAlley or MouthOfAlley or InterrogRoom or CeilingHole) rfalse; if (location == SecurityOffice or InterrogRoomReal) rfalse; if (location == FinalEnd or WiringCloset) rfalse; if (location == Laboratory) rfalse; rtrue; ], description [; print "A plastic grille is visible high on the wall, just below the ceiling. One corner is stamped ~VEN DUCT "; self.number(); ".~"; ], number [ loc ix jx kx; if (loc == 0) loc = location; if (1) { ix = (0-->5); ! object table ix = ix + 63 * 2; ! skip property defaults ix = ix + (loc-1) * 14; ! skip to object entry ix = ix-->6; ! ix is now the address of the property table jx = (ix->0) & 255; ! number of words in room name ix++; ! print (address) ix; ! if I really wanted to print the object name loc = 0; while (1) { jx--; if (jx < 0) break; loc = loc + (ix-->jx); } loc = loc & 32767; } jx = loc % 10; kx = (loc / 10) % 10; ix = (loc / 100) % 26; print (char) (ix + 'A'), "-"; if (kx % 2) print (char) '1'; print (char) (jx + '0'); ], before [; Examine: rfalse; Search: print_ret "You can't see anything behind the closed grille."; Close: print_ret (The) self, " is already closed."; Listen: if (powercut) "No sound comes from the grille."; else "A dim, unceasing hiss flows from the grille."; ThrownAt: rfalse; default: if (~~CanReachCeiling()) print_ret (The) self, " is a bit beyond your reach."; else print_ret (The) self, " is beyond your reach, on a different wall than the one you stand near."; ], has scenery container ~open; Class InteriorDoor, with name 'plain' 'featureless' 'metal' 'door', door_to [; return parent(self); ], door_try [; PronounNotice(self); print_ret (The) self, " is very definitely closed."; ], before [; Open: "It doesn't open by itself."; Close: if (self hasnt open) "It's already closed."; Push, Turn: "The steel is immobile."; Touch, Rub: "The steel is smooth and cool."; ], has scenery door ~open; Class InteriorPlate with name 'small' 'black' 'rectangle' 'plate', short_name "black plate by the door", description [; print "A featureless, dull black rectangle is embedded in the wall beside"; if (self.work_target == nothing) " a giggling duck. [BUG]"; print " ", (the) self.work_target, "."; if (lockpick.work_target == self) { print " The lockpick is clinging to the plate."; } new_line; rtrue; ], work_target nothing, ! the door this is attached to startup 0, ! do this when attaching pick -- if nonzero return, don't attach before [; Push, Pull, Turn, Rub, Touch: "Nothing happens."; KnockOn, Attack: "There is no response."; Take: "The plate is fixed in the wall."; ], has scenery; Class ContactPad with name 'small' 'white' 'contact' 'pad', short_name "contact pad", description "It's a dull white rectangle, about the width of two of your fingers.", before [; Push, KnockOn, Attack: <>; ], has scenery; Class PulseGun with short_name "gun", name 'pulse' 'gun' 'weapon', before [; ]; #ifdef ZDEBUG; Object wand "magic wand", with name "magic" "wand", description "It's your magic wand! [BUG]", before [; Wave: if (self hasnt light) { give self light; "The wand starts to glow."; } else { give self ~light; "The wand goes out."; } TouchFrom: print_ret "The wand was touched from ", (the) noun, "!"; SetTo: if (second > 50) self.daemon_priority = second - 100; else self.daemon_priority = second; print_ret "Wand.daemon_priority = ", self.daemon_priority, "."; Push: StartDaemon(self); "Wand started."; Pull: StopDaemon(self); "Wand stopped."; Take: switch (action_to_be) { ##Eat: "You are not going to pick that up just to eat it, young man."; default: rfalse; } Rub: if (scrambler.fried) scrambler.fried = false; else scrambler.fried = true; "Scrambler fried = ", scrambler.fried, "."; ], daemon_priority 0, daemon [; print_ret "^Wand! (", self.daemon_priority, ")"; ]; #endif; ! ZDEBUG ! ------------- Utility functions [ PauseClear dummy; print "[Hit any key.] "; new_line; @read_char 1 dummy; @erase_window $ffff; new_line; ]; [ Pause dummy; print "[Hit any key.] "; new_line; @read_char 1 dummy; new_line; ]; ! Replaces faulty ThatorThose syntax in 6/6 libs. Code copied from 6/5 libs. [ ThatorThose obj; if (obj has pluralname) print "those"; else print "that"; ]; ! see manual p.98 [ ChooseObjects obj code; if (code < 2) { if (obj has scenery) return 2; rfalse; } if (obj hasnt scenery) { !print "[DEBUG] Choose: ", (the) obj, " hasnt scenery: 2.^"; return 2; } else { !print "[DEBUG] Choose: ", (the) obj, " has scenery: 1.^"; return 1; } ]; ! Modification of regular ScopeWithin function, which throws away the damn ! compass. [ ScopeWithin domain nosearch context; if (domain==0) rtrue; objectloop (domain in domain) ScopeWithin_O(domain, nosearch, context); ]; ! Simple light function which says everything is lit. [ OffersLight i; if (i == 0) rfalse; rtrue; ]; ! ensure that obj is in loc [ set_parent obj loc; if (parent(obj) == loc) return; if (loc == nothing) { remove obj; } else { move obj to loc; } ]; [ set_attribute obj attr bool; if (bool) give obj attr; else give obj ~attr; ]; ! Find the outermost object that contains obj (generally a room, but if obj is in ! nothing, it will be returned itself.) [ locationof obj; if (parent(obj) == nothing) return obj; while (parent(obj) ~= nothing) { obj = parent(obj); } return obj; ]; ! returns TRUE if obj1 is anywhere inside (but not identical to) obj2 [ anywherein obj1 obj2; while (parent(obj1) ~= nothing) { obj1 = parent(obj1); if (obj1 == obj2) rtrue; } rfalse; ]; ! returns TRUE if obj is anywhere inside loc, except for closed opaque containers. [ visiblein obj loc par; while (parent(obj) ~= nothing) { par = parent(obj); if (par has container && par hasnt open && par hasnt transparent) rfalse; if (par == loc) rtrue; obj = par; } rfalse; ]; Object shiftercache; [ ShiftToEnd obj par ix; if (child(shiftercache)) print_ret "ShiftToEnd: shiftercache isn't empty!"; par = parent(obj); if (par == nothing) print_ret "ShiftToEnd: ", (object) obj, " is in nothing! [BUG]"; while (child(par)) { ix = child(par); if (ix == obj) remove ix; else move ix to shiftercache; } move obj to par; while (child(shiftercache)) { ix = child(shiftercache); move ix to par; } ]; [ normstring str; print (string) str; ]; [ nop arg; arg = 0; ]; [ emphstring str; style underline; print (string) str; style roman; ]; [ boldstring str; style bold; print (string) str; style roman; ]; [ DeathMessage; switch (deadflag) { 3: print "You have failed"; 4: print "You have made a difference"; 5: print "You have failed to make a difference"; } ]; [ BlankStatusLine width; @split_window 1; @set_window 1; @set_cursor 1 1; style reverse; statusprinting = 1; width = 0->33; spaces (width); ! A pox upon buggy interpreters! statusprinting = 0; @set_cursor 1 1; style roman; @set_window 0; ]; [ DrawStatusLine width i; @split_window 1; @set_window 1; @set_cursor 1 1; style reverse; statusprinting = 1; width = 0->33; spaces (width); ! A pox upon buggy interpreters! @set_cursor 1 3; PrintShortName(location); i = parent(player); if (i ~= location && location ~= thedark) { if (i provides vehicle_status_name) { if ((i.vehicle_status_name) ofclass String) print (string) i.vehicle_status_name; else i.vehicle_status_name(); } else { if (i has supporter) { print ", on "; } else { print ", in "; } DefArt(i); } } ! if (weather ~= 0) { ! posa = width-31; ! if (posa >= 34) { ! @set_cursor 1 posa; ! } ! print " (", (string) weather, ")"; ! } statusprinting = 0; @set_cursor 1 1; style roman; @set_window 0; ]; Class Scene with init_scene 0, ! called when the scene is set begin_scene [; "The curtain goes up, revealing stagehands. [BUG]"; ], end_scene [; "The curtain comes down and hits you on the head. [BUG]"; ], number 0, ! scene number variant 0; Global cur_scene = nothing; [ SetScene scob; cur_scene = scob; if (cur_scene.init_scene) { cur_scene.init_scene(); } ]; [ BeginScene rock; StopDaemon(himself); give himself ~general; if (rock ~= 1000) { BlankStatusLine(); new_line; print (emphstring) "...glaring light --", "^^"; Pause(); } if (cur_scene) { cur_scene.begin_scene(rock); } if (parent(activeplayer) == nothing) { print "You are suddenly nowhere at all. [BUG]^"; } ChangePlayer(activeplayer); ; ]; [ EndScene rock subrock; if (escaping) print_ret "You think you're waking up, but that's not how life works. [BUG] [please note ", rock, ", ", subrock, "]^"; BlankStatusLine(); new_line; print (emphstring) "-- glaring light...", "^^"; Pause(); ! shut down general daemons StopAllTools(); multi_interrupt = 1; if (parent(passiveplayer) ~= boundchair) { print "You fall out of your chair. [BUG]^"; } ChangePlayer(passiveplayer); remove activeplayer; InterrogRoom.just_in = true; PronounNotice(Himself); ; SetQuestion(nothing); if (cur_scene) { cur_scene.end_scene(rock, subrock); } StartDaemon(Himself); give himself general; ]; [ BrainDrain; deadflag = 3; print "^A killing light slams you behind the eyes. Images fall through it, helplessly -- door, hall -- leap -- open door -- chair --^^But the light doesn't let you go.^"; return; ]; ! stubs for space-saving [ LowKey_Menu; ]; [ DoMenu; ]; ! ------------- Verb subroutines [ InvWideSub; inventory_style = TERSE_BIT + FULLINV_BIT + ENGLISH_BIT + RECURSE_BIT; ; ]; [ TangoSub; "You say the keyword."; !NoSuchVerbSub(); ]; [ WaltzSub; "You say the keyword."; !NoSuchVerbSub(); ]; [ NoSuchVerbSub; L__M(##Miscellany, 38); rtrue; ]; [ HideSub; if (escaping) "That's the trick, is it not?"; L__M(##Hide, 1); rtrue; ]; [ HideBehindSub; <>; ]; [ KnockOnSub; "That is unlikely to get a response."; ]; [ AttackWithSub; if (second notin player) print_ret "You can't do that when you're not holding ", (the) second, "."; if (second ofclass PulseGun) { if (noun == second) print_ret (The) second, " isn't flexible, you understand."; if (noun == player) { if (escaping) { "Pulsing yourself unconscious? You'd never live it down."; } else { print "You reverse the gun and fire a pulse into the optimal spot, between your eyes.^"; EndScene(-5); rtrue; } } if ((noun ofclass InteriorPlate) || (noun ofclass ContactPad)) { if (noun provides shortout) { if (noun.shortout()) rtrue; } print_ret (The) noun, " showers a corona of sparks. You can see subcircuitry beneath the surface, briefly outlined in blue light."; } if (noun provides shortout) { if (noun.shortout()) rtrue; } if (noun hasnt animate) { print (The) noun; if (noun hasnt pluralname) print " showers"; else print " shower"; print " a corona of sparks. Lacking a nervous system, however,"; if (noun hasnt pluralname) print " it is"; else print " they are"; " unaffected by the pulse."; } if (noun has animate && RunLife(noun, ##ShootAttack) ~= 0) rfalse; } "That won't help."; ]; [ StabAttackSub; if (poisonpen in player && pennib in poisonpen) { print "(with ", (the) poisonpen, ")^"; <>; } <>; ]; [ ShootAttackSub ix; objectloop (ix in player) { if (ix ofclass PulseGun) { print "(with ", (the) ix, ")^"; <>; } } "You don't have a firearm."; ]; [ WriteVagueSub; if (poisonpen in player) { print "(with ", (the) poisonpen, ")^"; <>; } "You have nothing suitable to write with."; ]; [ WriteWithSub; if (noun has pluralname) print (The) noun, " aren't"; else print (The) noun, " isn't"; " much of a writing tool."; ]; [ ScoreSub; ! print nothing ]; [ VagueSaySub; "Talking to yourself might draw attention."; ]; [ AnswerSub; if (second == 0) { ! evil hack for parser bug in 6/6 and 6/7 second = noun; noun = 0; } if (RunLife(second,##Answer)~=0) rfalse; L__M(##Answer,1,noun); ]; [ TellSub; if (noun==player) { VagueSaySub(); rtrue; } "To simplify the problem of dialogue in interactive fiction, the ~tell~ and ~ask~ commands are not used in this game. Type ~about~ for details."; ]; [ AskSub; TellSub(); ]; [ ThrowDirSub; <>; ]; [ DetachSub; if (second == nothing) "There's no clear way to do that."; else "There's no clear way to do that."; ]; [ BaseTouchToSub ix; if (second has connector && noun hasnt connector) { ! swap them ix = second; second = noun; noun = ix; } action=##TouchTo; if (RunRoutines(noun,before) ~= 0) { action=##BaseTouchTo; return; } action=##TouchFrom; if (RunRoutines(second,before) ~= 0) { action=##BaseTouchTo; return; } if (noun has connector) { action=##Connect; if (RunRoutines(noun,before) ~= 0) { action=##BaseTouchTo; return; } action=##BaseTouchTo; if (noun == second) "You can't connect something to itself."; "Those don't connect."; } action=##BaseTouchTo; if (noun == second) "You can't touch something to itself."; "Nothing happens."; ]; [ ScriptOnSub; if ((0-->8) & 1) return L__M(##ScriptOn,1); @output_stream 2; if (((0-->8) & 1) == 0) { "Transcript failed."; } L__M(##ScriptOn,2); VersionSub(); ]; [ ScriptOffSub; if (((0-->8) & 1) == 0) return L__M(##ScriptOff,1); L__M(##ScriptOff,2); @output_stream -2; if ((0-->8) & 1) { "Transcript end failed."; } ]; [ AboutSub; print (emphstring) "Spider And Web"; print " is copyright 1997-8 by Andrew Plotkin. It may be copied, distributed, and played freely.^^"; print "For acknowledgements and credits, please type ~credits~.^^"; print "An introductory note: To simplify the problem of dialogue in interactive fiction, the ~tell~ and ~ask~ commands are not used in this game. Your conversational options are limited to ~yes,~ ~no,~ and saying nothing at all. Trust me -- this isn't as big a deal as it sounds.^^"; print "Also, after some consideration, I have decided not to implement body parts -- head, arms, legs, hands -- as separate objects. If you feel a desire to refer to yourself, ~me~ will suffice.^^"; print "It is possible to make a fatal mistake in this game, but you will immediately know you have done so. You can always ~undo~ after death, and then fix the mistake. Therefore, the game is best played straight through. Accept any non-fatal mistakes that you may make; you will have a second chance. If you back up and replay each scene for maximum efficiency, avoiding all mistakes, certain aspects of the game will be lost.^^"; print "However -- you will eventually reach a point where things become dangerous. You'll know when. Beyond that, you're playing for keeps and heartbeats count. Save early and often.^^"; print "Do not expect ", (emphstring) "Spider And Web", " to be anything like ", (emphstring) "So Far", " or ", (emphstring) "A Change in the Weather.", " It's not. In a certain sense, this is my first conventional game. In another sense, it's the strangest game I know, and understanding what's happening is entirely your problem.^^"; !print "If you reach the end of this game, and you don't understand what ! has happened, you haven't been paying attention.^^"; print "This game is free. But if you feel like sending me money, I won't refuse. If you feel like sending me comments or bug reports, I'll be ecstatic. As of the beginning of 1998, I am at^^"; font off; print " erkyrath@@64netcom.com"; font on; print "^^"; print " Andrew Plotkin^ 7529 Murray Hill Rd #322^ Columbia, MD 21046^ USA^"; ]; [ AboutCreditsSub; print "This game was created using the Inform adventure development system, which was created by Graham Nelson. You can get Inform, as well as other text adventures, from the Interactive Fiction archive:^"; font off; print " ftp://ftp.gmd.de/if-archive/"; font on; print "^^"; print "I did not, I swear, create this game to be an entry in the Third Interaction Fiction Competition. I did briefly consider entering it... when the entry deadline came around at the end of August 1997, I thought I might be able to finish by the submission deadline a month later. Maybe I could have. It would probably have been a mistake. In any case, I decided not to try.^^"; print "The original idea for ", (emphstring) "Spider And Web", " actually hit me in February 1997, on a stretch of road crossing under Route 32, as I was walking towards Borders. There were ducks nearby. I don't think the ducks had anything to do with the idea; but they get listed in the credits anyway.^^"; print "Other people of note:^^"; print "John M. Ford probably ", (emphstring) "did", " have something to do with the idea. In particular, his story ~Casting Fortune.~^^"; print "Lovely Alex, for inspiring me to quit my job and therefore giving me the time to write this.^^"; print "Beta testers: Julian Arnold, Miron Schmidt (and his arm), C. E. Forman, Michael Kinyon. Beta-and-a-half testers: Adam Thornton, Stephen Granade, Lucian P. Smith.^^"; print "Omega testers: Send me bug reports, and your name could win valuable real estate in this listing!^^"; print "Come to think of it: In February, the ducks wouldn't have been there. Well, you get the idea.^"; ]; #ifdef DEBUGDUMP; [ dqs val; print (char) (val + 'A' - 1); ]; [ DebugQuerySub; print "The following information is internal data, of interest only to the author.^"; print "KEY: "; print cur_scene.number; if (player == activeplayer) print "/a"; else print "/p"; print ":"; print (dqs) num_wait_answers, (dqs) num_yes_answers, (dqs) num_no_answers, (dqs) num_blatant_lies, (dqs) num_sneaky_lies, (dqs) num_stubborns, (dqs) num_helpfuls, (dqs) num_jingos, (dqs) num_cosmos, (dqs) num_dumb_recall, (dqs) num_got_angry, (dqs) num_stupids; new_line; ]; #endif; ! DEBUGDUMP; #ifdef ZDEBUG; [ ZapToNumSub; if (player == activeplayer) EndScene(0); BeginScene(noun); ]; [ ZapToNum2Sub; if (noun == 8) { BeginGreatEscape(); PlayerTo(WiringCloset); give deadendmetaldoor open; CutOffPower(); move lockpick to player; if (second >= 2) { move gun to activeplayer; move poisonpen to activeplayer; } if (second >= 3) PlayerTo(LabJunction); if (second >= 4) give labdoor open; rtrue; } if (player == activeplayer) EndScene(0); if (noun >= 3) move package to activeplayer; if (noun == 5) { ; } if (noun == 6) { move poisonpen to activeplayer; move pennib to poisonpen; } switch (noun) { 1: SetScene(sc_1); 2: SetScene(sc_2); 3: SetScene(sc_3); 4: SetScene(sc_4); 5: SetScene(sc_5); 6: SetScene(sc_6); 7: SetScene(sc_7); } BeginScene(second); ]; [ ZapEquipSub; ZapDoEquip(); "Everything appears in your hands."; ]; [ ZapEscapeSub; if (escaping) { escaping = 0; "You stop escaping."; } else { escaping = 1; "You are now escaping."; } ]; [ ZapPowerSub; if (powercut) { powercut = 0; print_player_flag = 0; "The power comes back on."; } else { powercut = 1; print_player_flag = 1; "The power goes out."; } ]; [ ZapBlipSub; DebugBlip(); ]; [ ZapQuerySub; print "Emotional state of Himself: num_wait_answers=", num_wait_answers, ", num_yes_answers=", num_yes_answers, ", num_no_answers=", num_no_answers, ", num_blatant_lies=", num_blatant_lies, ", num_sneaky_lies=", num_sneaky_lies, ", num_stubborns=", num_stubborns, ", num_helpfuls=", num_helpfuls, ", num_jingos=", num_jingos, ", num_cosmos=", num_cosmos, ", num_dumb_recall=", num_dumb_recall, ", num_got_angry=", num_got_angry, ", num_stupids=", num_stupids, "^"; ]; Global oneword_word = 0; [ ZapStateUpSub; debug_diddle_word(oneword_word, 1); ]; [ ZapStateDownSub; debug_diddle_word(oneword_word, -1); ]; [ debug_diddle_word wd val; switch (wd) { 'wait', 'z//': num_wait_answers = num_wait_answers + val; 'yes', 'y//': num_yes_answers = num_yes_answers + val; 'no', 'n//': num_no_answers = num_no_answers + val; 'blatant': num_blatant_lies = num_blatant_lies + val; 'sneaky': num_sneaky_lies = num_sneaky_lies + val; 'stubborn': num_stubborns = num_stubborns + val; 'helpful': num_helpfuls = num_helpfuls + val; 'jingo': num_jingos = num_jingos + val; 'cosmo': num_cosmos = num_cosmos + val; 'dumbrec': num_dumb_recall = num_dumb_recall + val; 'angry': num_got_angry = num_got_angry + val; 'stupid': num_stupids = num_stupids + val; default: print_ret "Unknown flag word."; } print_ret "Flag added ", val, "."; ]; #endif; ! ZDEBUG ! ------------- The scenes Include "tangle-chair.inc"; Include "tangle-tools.inc"; #ifndef SKIP_SCENE_1; Include "tangle-1.inc"; #endif; #ifndef SKIP_SCENE_2; Include "tangle-2.inc"; #endif; #ifndef SKIP_SCENE_3; Include "tangle-3.inc"; #endif; #ifndef SKIP_SCENE_4; Include "tangle-4.inc"; #endif; #ifndef SKIP_SCENE_5; Include "tangle-5.inc"; #endif; #ifndef SKIP_SCENE_6; Include "tangle-6.inc"; #endif; #ifndef SKIP_SCENE_7; Include "tangle-7.inc"; #endif; #ifndef SKIP_SCENE_8; Include "tangle-8.inc"; #endif; #ifndef SKIP_SCENE_9; Include "tangle-9.inc"; #endif; #ifdef SKIP_ANY; Include "tangle-alt.inc"; #endif; ! ------------- Startup routine [ Initialise dummy; remove player; player = activeplayer; location = EndOfAlley; move passiveplayer to boundchair; lookmode = 2; ! verbose inventory_style = TERSE_BIT + FULLINV_BIT + ENGLISH_BIT + RECURSE_BIT; ! wide new_line; new_line; #ifdef ZDEBUG; move wand to player; print "[DEBUG] Debugging version! [DEBUG]^^"; #endif; #ifdef DEBUG; print "[DEBUG] (Library) debugging version! [DEBUG]^^"; #endif; ! trash the "declared but not used" warnings dummy = PauseClear + locationof + anywherein; SetScene(sc_1); print "On the whole, it was worth the trip. The plains really were broad and grain-gold, if scarred with fences and agricultural crawlers. The mountains were overwhelming. And however much of the capital city is crusted with squat brick and faceless concrete hulks, there are still flashes of its historic charm. You've seen spires above the streets -- tiny green parks below tenements -- hidden jewels of fountains beyond walls. Any bland alley can conceal balconies wrought into iron gardens, fiery mosaics, a tree or bed of flowers nurtured by who knows who.^^"; print "This alley, however, is a total washout. It ends in flat bare dirty brick, and you've found nothing but a door which lacks even the courtesy of a handle. Maybe you should call it a day.^^"; ]; ! ------------- Grammar Include "Grammar"; Verb meta 'about' 'help' 'info' * -> About * 'credit' / 'credits' -> AboutCredits; Verb meta 'credit' 'credits' 'acknowledgements' * -> AboutCredits; [ InCompass; switch (scope_stage) { 1: rfalse; ! don't accept multiple directions 2: ScopeWithin(compass); rtrue; 3: "That isn't a direction you can go in."; } ]; [ InFlatCompass; switch (scope_stage) { 1: rfalse; ! don't accept multiple directions 2: PlaceInScope(n_obj); PlaceInScope(ne_obj); PlaceInScope(e_obj); PlaceInScope(se_obj); PlaceInScope(s_obj); PlaceInScope(sw_obj); PlaceInScope(w_obj); PlaceInScope(nw_obj); rtrue; 3: "That isn't a direction you can throw things."; } ]; Extend 'go' replace ! and 'walk', 'run' * -> VagueGo * scope=InCompass -> Go * noun -> Enter * 'into'/'in'/'inside'/'on'/'onto' /'through'/'across'/'over' noun -> Enter * 'out'/'outside' -> Exit; Verb 'step' = 'go'; Extend 'look' replace ! and 'l//' * -> Look * 'at' noun -> Examine * 'inside'/'in'/'into'/'through' noun -> Search * 'under' noun -> LookUnder; ! no Consult Extend 'read' replace * noun -> Examine; Extend 'throw' last * held 'to' scope=InFlatCompass -> ThrowDir * held scope=InFlatCompass -> ThrowDir; Extend 'remove' replace * noun -> Disrobe * multi -> Take * multiinside 'from' noun -> Remove; Verb 'release' * multiheld -> Drop; Verb 'let' * 'go' 'of' multiheld -> Drop * 'go' multiheld -> Drop; Verb 'scuff' * noun -> Rub; Extend only 'climb' replace * noun -> Climb * 'up'/'over'/'on'/'onto' noun -> Climb * 'in'/'into' noun -> Enter; Extend only 'cross' replace * noun -> Enter * 'in'/'into'/'over' noun -> Enter; Extend 'touch' last * noun 'with' noun -> BaseTouchTo reverse * noun 'to'/'on' noun -> BaseTouchTo; Extend only 'tie' replace * noun -> Tie * noun 'to' noun -> Tie; Extend 'attach' replace ! and 'fasten', 'fix' * noun 'to'/'on'/'onto' noun -> BaseTouchTo; Verb 'detach' 'unlink' 'unplug' 'disconnect' 'free' * noun -> Detach * noun 'from' noun -> Detach; Verb 'connect' 'link' 'plug' 'join' 'affix' * noun 'to'/'on'/'onto'/'into'/'with' noun -> BaseTouchTo; Verb 'flip' * noun -> Turn * noun 'on' -> Switchon * noun 'off' -> Switchoff * 'on' noun -> Switchon * 'off' noun -> Switchoff; Extend 'switch' replace * noun -> Turn * noun 'on' -> Switchon * noun 'off' -> Switchoff * 'on' noun -> Switchon * 'off' noun -> Switchoff; Extend 'set' replace ! and 'adjust' * noun -> Set * noun 'to' number -> SetTo; Extend 'turn' last ! and 'rotate' 'twist' 'unscrew' 'screw' * noun 'to' number -> SetTo; [ TopicNoTo w wstart wlen; wstart = wn; while (w ~= -1 or THEN1__WD or THEN2__WD or THEN3__WD or comma_word) { w = NextWordStopped(); if (w == 'to' or 'at') { return -1; } } wlen = (wn - wstart) - 1; if (wlen == 0) { return -1; } consult_from = wstart; consult_words = wlen; wn--; return 0; ]; Extend 'say' replace ! and 'answer', 'shout', 'speak' * TopicNoTo -> VagueSay * topic 'to'/'at' creature -> Answer; Extend 'attack' replace ! and 'break' 'smash' 'hit' 'fight' 'wreck' 'crack' ! 'destroy' 'murder' 'kill' 'torture' 'punch' 'thump' * noun -> Attack * noun 'with' held -> AttackWith; Verb 'stab' 'poke' 'jab' 'prick' * noun -> StabAttack * noun 'with' held -> AttackWith * held 'in'/'into'/'at' noun -> AttackWith reverse; Verb 'shoot' 'fire' 'pulse' * noun -> ShootAttack * noun 'with' held -> AttackWith * held 'at' noun -> AttackWith reverse * 'at' noun -> ShootAttack * 'at' noun 'with' held -> AttackWith; Verb 'knock' * noun -> KnockOn * 'on'/'at' noun -> KnockOn; Extend 'pick' replace * 'up' multi -> Take * multi 'up' -> Take * noun 'with' held -> Unlock; Verb 'write' 'scrawl' 'scribble' 'draw' 'sketch' * 'on' noun -> WriteVague * 'on' noun 'with' held -> WriteWith reverse * 'with' held 'on' noun -> WriteWith; Extend 'blow' replace * noun -> Blow * 'on'/'at' noun -> Blow; Verb 'breathe' * -> Smell * noun -> Smell * 'on'/'at' noun -> Blow; Extend 'jump' replace ! and 'skip' 'hop' * -> Jump * 'up' -> Jump * 'over'/'across' noun -> JumpOver; Verb 'leap' = 'jump'; Extend 'wear' replace ! and 'don' * noun -> Wear; Extend 'put' replace ! only change is the Wear case * multiexcept 'in'/'inside'/'into' noun -> Insert * multiexcept 'on'/'onto' noun -> PutOn * 'on' noun -> Wear * 'down' multiheld -> Drop * multiheld 'down' -> Drop; Verb 'hide' * -> Hide * multiexcept 'in'/'inside'/'into' noun -> Insert * 'behind' noun -> HideBehind * 'in' noun -> Enter; [ RestOfLine w; while (w ~= -1) { w = NextWordStopped(); } wn--; return 0; ]; [ RestOfCommand w; while (w ~= -1 or THEN1__WD or THEN2__WD or THEN3__WD or comma_word) { w = NextWordStopped(); } wn--; return 0; ]; Verb 'tango' * RestOfCommand -> Tango; Verb 'waltz' * RestOfCommand -> Waltz; Extend 'score' replace * RestOfLine -> NoSuchVerb; Extend 'fullscore' replace * RestOfLine -> NoSuchVerb; Extend 'burn' replace * RestOfLine -> NoSuchVerb; Extend 'sorry' replace * RestOfLine -> NoSuchVerb; Extend 'pray' replace * RestOfLine -> NoSuchVerb; Extend 'damn' replace * RestOfLine -> NoSuchVerb; Extend 'darn' replace * RestOfLine -> NoSuchVerb; Extend only 'die' replace * RestOfLine -> NoSuchVerb; Extend only 'swing' replace * RestOfLine -> NoSuchVerb; Extend 'consult' replace * RestOfLine -> NoSuchVerb; #ifdef DEBUGDUMP; Verb meta 'debugdump' * -> DebugQuery; #endif; #ifdef ZDEBUG; Verb 'zapto' ! not meta, so that daemons happen * number -> ZapToNum * number number -> ZapToNum2; Verb meta 'zapequip' * -> ZapEquip; Verb meta 'zapescape' * -> ZapEscape; Verb meta 'zappower' * -> ZapPower; Verb meta 'zapquery' * -> ZapQuery; Verb meta 'zapblip' * -> ZapBlip; [ OneWordToken w; oneword_word = 0; while (w ~= -1) { w = NextWordStopped(); if (oneword_word == 0) oneword_word = w; } return 0; ]; Verb meta 'zapi' * OneWordToken -> ZapStateUp; Verb meta 'zapo' * OneWordToken -> ZapStateDown; #endif; ! ZDEBUG