Created
October 26, 2016 11:38
-
-
Save toriningen/86667f6e2517b1afead86408b95fe360 to your computer and use it in GitHub Desktop.
Return to Ditch Day (TADS source)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - "About" information (credits, version, help, | |
* and so on). | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
/* | |
* versionInfo - The library expects us to provide an object with this | |
* name to supply some basic information about the game: the name, | |
* version number, author, and so on. This object also provides methods | |
* for handling a few standard system verbs, such as CREDITS and ABOUT. | |
* The library uses the information here to generate a GameInfo.txt file, | |
* which we store in the compiled .t3 file as a resource for use by the | |
* HTML TADS Game Chest feature and any other tools that wish to extract | |
* the information. | |
*/ | |
versionInfo: GameID | |
name = 'Return to Ditch Day' | |
version = '2' | |
byline = 'by Michael J.\ Roberts' | |
htmlByline = 'by <a href="mailto:mjr_@hotmail.com">' | |
+ 'Michael J.\ Roberts</a>' | |
authorEmail = 'M.J. Roberts <mjr_@hotmail.com>' | |
desc = 'It\'s been a decade since you graduated, but now it | |
looks like you\'re going to have to solve one more | |
Ditch Day stack. | |
\\nYou play a Caltech alum whose big-company employer | |
sends you back to campus to recruit a star student. But | |
when you get there, you find the tables turned when your | |
candidate makes you audition to be his employer. What do | |
you have to do to win your new recruit? You just have to | |
solve a little Ditch Day stack, of course. | |
\\nReturn to Ditch Day is a sprawling interactive | |
adventure, part treasure hunt, part mystery, part science | |
fiction. It was written as a demonstration and example | |
of TADS 3, the latest generation of the TADS authoring | |
tool for Interactive Fiction, but it\'s not just a demo - | |
it\'s a full-fledged game, with an expansive and detailed | |
setting, an engrossing plot, a cast of eccentric characters, | |
and lots of challenging and logical puzzles. And the game | |
gives you an unusual amount of control over the way the | |
story unfolds: you can simply pursue your "official" goals, | |
or you can also investigate an elaborate hidden mystery.' | |
firstPublished = '2005' | |
genre = 'Collegiate, Science Fiction' | |
IFID = 'TADS3-84EC545A7D48EEF7377BD571B9CD8C9D' | |
/* a "serial number" constructed from the date */ | |
#ifdef QA_TEST_MODE | |
serialNum = 'QA-TEST-BUILD' | |
#else | |
serialNum = static releaseDate.findReplace('-', '', ReplaceAll, 1) | |
#endif | |
/* | |
* show the version information - override this to use a special | |
* format that includes the date-based serial number | |
*/ | |
showVersion() | |
{ | |
"<i><<name>></i> Release <<version>> (<<serialNum>>)\n"; | |
} | |
/* | |
* Handle the CREDITS command. We show our production credits, | |
* acknowledgments, and disclaimers here. | |
*/ | |
showCredit() | |
{ | |
"<i>Return to Ditch Day</i> was written and programmed by | |
Michael J.\ Roberts, using the <font size=-1>TADS</font> 3 | |
interactive fiction design system. | |
<br><br> | |
This game was tested by Steve Breslin, Eric Eve, Kevin Forchione, | |
Stephen Granade, Andreas Sewe, Dan Shiovitz, and Brett Witty. | |
I can't thank these folks enough for suffering through the | |
early versions and offering so much helpful feedback and so | |
many great ideas for improvements. The game is vastly better | |
for their generous efforts. Thanks also to Mark Engelberg, | |
Tommy Herbert, Valentine Kopteltsev, Matt McGlone, Michel | |
Nizette, Emily Short, and Mike Snyder for catching a number of | |
significant bugs in the released versions. | |
<br><br> | |
Some of the graffiti in the rendition here of Dabney House | |
are based on or (more often) shamelessly copied from the real | |
thing. Similarly, a few of the stacks are loosely based on | |
actual stacks of the past. In all cases, the originals are | |
much better than the poor imitations here. | |
<br><br> | |
This game was written in parallel with the last year or so | |
of work on <font size=-1>TADS</font> 3 itself, so in a sense | |
it's a part of that effort. I therefore wish to thank everyone | |
who contributed via the tads3 list---your advice, ideas, and | |
practical experience using the system in its prolonged half-baked | |
state have made all the difference. | |
<br><br> | |
Omegatron, Mitachron, Netbisco, ToxiCola, and Bioventics are | |
trademarks of their respective fictitious companies. | |
J.R.R.\ Tobacco is a trademark of SG Naming Consultants, used by | |
permission. DEI, DabniCorp, and the elephant-and-shield device | |
are trademarks of Dabney Hovse. | |
<br><br> | |
<b>This is a work of fiction.</b> All persons, places, organizations, | |
and events in this story are products of the author's imagination | |
or are used fictionally, and any resemblance to reality is purely | |
coincidental. Nothing in this game is meant to reflect in any way | |
on the real-life Caltech or anyone associated with it. Needless | |
to say, Caltech was not involved in the creation of this work. | |
But the next time your plans call for higher education, why not | |
consider Caltech? Nestled near the foothills of the San Gabriel | |
mountains in Pasadena, Caltech offers a unique student experience | |
that is said to rarely cause permanent damage. | |
Visit <a href='http://www.caltech.edu'>www.caltech.edu</a> today. | |
<br><br> | |
For information on writing your own game using | |
<font size=-1>TADS</font>, visit the official website at | |
<a href='http://www.tads.org'>www.tads.org</a>. | |
<br><br> | |
Copyright ©2004, 2013 Michael J.\ Roberts. All rights reserved. | |
Type <<aHref('copyright', 'COPYRIGHT')>> for license information. "; | |
} | |
/* | |
* Handle the COPYRIGHT command. We show our copyright message and | |
* license terms here. | |
*/ | |
showCopyright() | |
{ | |
"Copyright ©2004, 2013 Michael J.\ Roberts. | |
All rights reserved. | |
<p> | |
<b>Free Software License</b><br> | |
This is a copyrighted work, which means that it's against the | |
law to copy or distribute this package without the author's | |
written permission. The author hereby grants you | |
permission to use, copy, and redistribute this software, without | |
charge, with the following conditions: you may not alter or remove | |
any copyright notice or license notice in the work; you may not | |
collect a fee for copies of this software, except for a nominal | |
fee to cover the cost of physically making the copy; you may | |
not distribute modified or incomplete versions; and you may not | |
impose any additional license terms with respect to this work | |
on recipients of copies you make or distribute. | |
<p>This software has NO WARRANTY of any kind, expressed or | |
implied, including without limitation the implied warranties | |
of merchantability and fitness for a particular purpose. | |
<p> | |
If you have any questions about this license, please contact | |
the author at | |
<a href='mailto:mjr_@hotmail.com'>mjr_@hotmail.com</a>. "; | |
} | |
/* | |
* Handle the ABOUT command. By convention, this command gives the | |
* player a brief summary of anything unusual about the game - not | |
* full instructions on how to play, but a mention of any commands or | |
* other features that differ from the typical conventions of modern | |
* IF. The idea is that we assume the player is already familiar | |
* with IF in general, so we just want to alert her to any quirks | |
* this game has. Frequently, the command also provides a bit of | |
* background information about the game. | |
*/ | |
showAbout() | |
{ | |
"<img src='.system/CoverArt.jpg?id=<<rand('z{32}')>>'><br>"; | |
"Welcome to <i>Return to Ditch Day</i>! | |
<.p> | |
If you're a newcomer to Interactive Fiction, you might find it | |
helpful to read our IF instruction manual---just type | |
<<aHref('instructions', 'INSTRUCTIONS')>> at the command prompt. | |
<.p> | |
This game uses the standard commands that most IF games use, | |
but there are a few extras worth mentioning. | |
You can <b>TALK TO</b> a character to greet them (this is | |
optional, though: you can always just ASK or TELL them | |
about something instead). Once you're talking to someone, you | |
can abbreviate <b>ASK person ABOUT topic</b> to simply | |
<b>A topic</b>, and <b>TELL person ABOUT topic</b> to | |
<b>T topic</b>. <b>TOPICS</b> shows you a list of things | |
your character is interested in discussing---but note | |
that you're never limited to what's on the list, and the list | |
might not show everything that's important to talk about. | |
<.p>The story might occasionally make a few special | |
suggestions when you're talking with someone, like this: | |
\b\t(You could apologize for breaking the vase, or explain | |
\n\tabout the aliens.) | |
<.p>These are special options that you can use at the moment | |
they're suggested. You don't have to worry about memorizing | |
them or trying them at other random times in the story; they'll | |
only work on the turn they're offered. If you want to use one | |
of the suggestions, just type it in; you can usually abbreviate | |
to the first word or two when the suggestion is long: | |
\b\t>APOLOGIZE | |
<.p>Note that you're never limited to the offered suggestions. | |
You can always use an ordinary command instead, such as ASK or | |
TELL, or even ignore the character completely. | |
<.p>Many years ago, I wrote a game called <i>Ditch Day Drifter</i> | |
as an example game for the first version of <font size=-1>TADS</font>, | |
my IF development system. This is very loosely a sequel to that | |
game, but if you haven\'t played the original, don\'t worry---you | |
can play this game entirely on its own. There\'s nothing you need | |
to know from the earlier game to play this one. | |
<.p>There are no <q>unwinnable</q> situations in this game: | |
there's no way for your character to be killed, and you can't | |
accidentally lock yourself out of completing the story. If you | |
ever get really stuck, you can type <<aHref('hint', 'HINT')>> | |
to access built-in hints. "; | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Provide a HELP command. A player who's completely lost might think to | |
* try typing HELP, so it's best to offer some assistance here. Since we | |
* have a number of special commands that provide specific kinds of help, | |
* we'll simply list those other commands here. | |
*/ | |
DefineSystemAction(Help) | |
execSystemAction() | |
{ | |
"For more information, you can type one of the | |
following at any command prompt: | |
\b<<aHref('about', 'ABOUT')>> - for some general background | |
information about this game | |
\n<<aHref('copyright', 'COPYRIGHT')>> - to display copyright and | |
license information | |
\n<<aHref('credits', 'CREDITS')>> - to show the game's credits | |
\n<<aHref('hints', 'HINT')>> - to access the built-in hints | |
\n<<aHref('instructions', 'INSTRUCTIONS')>> - for general | |
instructions on how to play this game "; | |
} | |
; | |
VerbRule(Help) 'help' : HelpAction | |
verbPhrase = 'show/showing help information' | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Provide a COPYRIGHT command. This simply shows the license | |
* information from the versionInfo object. | |
*/ | |
DefineSystemAction(Copyright) | |
execSystemAction() { versionInfo.showCopyright(); } | |
; | |
VerbRule(Copyright) 'copyright' | 'license' | 'licence' : CopyrightAction | |
verbPhrase = 'show/showing copyright information' | |
; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - the book store. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
#include "ditch3.h" | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The bookstore | |
*/ | |
bookstore: Room 'Bookstore' 'the bookstore' | |
"Most of this large, bright space is crammed with low bookshelves | |
in narrow rows, but the front part is a little more open, with books | |
displayed on a series of tables. The exit is southeast, and | |
next to it is the check-out counter and a small newspaper stand. " | |
vocabWords = '(book) bookstore/store' | |
southeast = quad | |
out asExit(southeast) | |
/* | |
* before leaving, check to make sure we don't have unpurchased | |
* merchandise | |
*/ | |
beforeTravel(traveler, connector) | |
{ | |
local lst; | |
/* if the PC isn't traveling, don't worry about it */ | |
if (!traveler.isActorTraveling(me)) | |
return; | |
/* get a list of carried merchandise */ | |
lst = me.allContents().subset( | |
{x: x.bookstorePrice not in (nil, 0) && !x.isPaidFor}); | |
/* if we found anything, say so, and stop the travel */ | |
if (lst.length() != 0) | |
{ | |
/* complain about it */ | |
"You realize you're carrying unpurchased merchandise; | |
you shouldn't leave until you pay for it. "; | |
/* stop the action short */ | |
exit; | |
} | |
/* | |
* if there's anything already purchased on the counter, remind | |
* us to take it | |
*/ | |
if (bookstoreCounter.contents.indexWhich({x: x.isPaidFor}) != nil) | |
"The clerk calls after you. <q>Sir! Don't forget your | |
purchase!</q><.p>"; | |
} | |
/* construct a list of all buyable objects in the bookstore */ | |
allBuyables = static (allInstancesWhich( | |
Buyable, {x: x.bookstorePrice != nil})) | |
; | |
+ Fixture, Container 'small newspaper stand/basket/legs' 'newspaper stand' | |
"It's just a little basket on legs, for holding a stack of | |
folded newspapers. " | |
/* don't list my contents in the room description */ | |
contentsListed = nil | |
; | |
++ newspaper: Buyable, Readable | |
'student california comic paper/newspaper/tech/copy/ | |
article/articles/reviews/strip' | |
'<i>California Tech</i>' | |
"The <i>California Tech</i> is the student newspaper. It's | |
only a few pages, so it wouldn't take long to read. " | |
aName = 'a copy of the <i>California Tech</i>' | |
readDesc = "You glance through the few pages of the paper. | |
There's an article on some esoteric matter of politics | |
in Ricketts, another on proposed changes to the undergrad | |
humanities requirements, a review of a new local restaurant, | |
another of a Theater Arts play. You read through an amusing | |
article about Jay Santoshnimoorthy, a current Darb, who got | |
on the Wayne Alders show (the late-night cable talk show) | |
with his amazing ability to program pocket calculators. On | |
the show, the article says, he programmed a basic four-function | |
solar-powered calculator to speak the little catch-phrases | |
that Wayne's announcer/sidekick Phil is always saying. There's | |
also a home-grown comic strip; in this episode, the characters | |
are in some kind of sleep-deprivation economics experiment. " | |
/* it's bookstore merchandise, but it's free */ | |
bookstorePrice = 0 | |
; | |
+ bookstoreClerk: Person 'older lanky man/clerk/cashier*men*people' 'clerk' | |
"He's an older man, very lanky, wearing reading glasses. " | |
isHim = true | |
actorHereDesc = "A clerk is standing behind the counter. " | |
defaultGreetingResponse(actor) | |
{ | |
if (bookstoreRegister.curBalance != 0) | |
initiateConversation(nil, 'clerk-checkout'); | |
else | |
"<q>Hi,</q> you say. | |
<.p><q>Welcome to the bookstore,</q> he says. "; | |
} | |
dobjFor(Pay) | |
{ | |
verify() { } | |
action() { "The clerk indicates the counter. <q>Just put | |
anything you want to buy here for me, and I'll ring | |
it up.</q> "; } | |
} | |
dobjFor(PayFor) asDobjFor(Pay) | |
beforeAction() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* explain about BUY X */ | |
if (gActionIs(Buy)) | |
replaceAction(Pay, self); | |
} | |
; | |
++ InitiallyWorn 'thin little wire reading rectangles/frame/glass/glasses' | |
'reading glasses' | |
"They're just thin little rectangles of glass in a wire frame. " | |
isPlural = true | |
isListedInInventory = nil | |
; | |
/* | |
* if there are unpurchased things on the counter, they're the obvious | |
* thing to talk about if we just TALK TO CLERK | |
*/ | |
++ HelloTopic | |
"<q>Hi,</q> you say. | |
<.p>The clerk looks at you over his reading glasses. <q>Hello. | |
What can I do for you?</q> " | |
; | |
+++ AltTopic | |
"<q>Hi,</q> you say. | |
<.p><q>Hello again,</q> the clerk says. <q>Ready to check out?</q> | |
<.convnode clerk-checkout> " | |
isActive = (bookstoreRegister.curBalance != 0) | |
; | |
/* | |
* asking about unpurchased goods elicits some kind of response; if the | |
* goods are on the counter, it initiates check-out | |
*/ | |
++ AskTellShowTopic | |
/* we match on anything buyable */ | |
matchObj = (bookstore.allBuyables) | |
topicResponse = "<q>Is this the check-out counter?</q> you ask | |
the clerk. | |
<.p><q>Yes,</q> he says, looking over his reading glasses. | |
<q>Just put your order on the counter, and I'll ring it up.</q> " | |
; | |
++ AskTellShowTopic | |
/* we match on anything buyable that's on the counter */ | |
matchObj = (bookstoreCounter.contents.subset({ | |
x: x.bookstorePrice not in (nil, 0) && !x.isPaidFor})) | |
/* override the plain buyable topic by using a higher score */ | |
matchScore = 110 | |
topicResponse = "<q>I'm ready to checkout,</q> you say. | |
<.p><q>Okay,</q> the clerk says. He looks at the register. | |
<q>That'll be <<bookstoreRegister.curBalanceSpelled>>, | |
please.</q> " | |
; | |
++ AskTopic, SuggestedAskTopic 'buying( things)?' | |
"<q>Is this the check-out counter?</q> you ask the clerk. | |
<.p>He peers at you over his reading glasses. <q>Yes,</q> he | |
says. <q>Just put your order on the counter here and I'll | |
ring it up.</q> " | |
name = 'buying things' | |
; | |
++ AskTellTopic [bookstore, bookstoreClerk] | |
"<q>Do you work here?</q> | |
<.p><q>Yes,</q> he says. <q>I can help you with checking out | |
when you're ready.</q> " | |
; | |
++ AskTellAboutForTopic +90 | |
[eeTextbookRecTopic, eeTextTopic, morgenBook, | |
eeLabRecTopic, labManualTopic, townsendBook] | |
"<q>Where do you keep electrical engineering books?</q> you ask. | |
<.p>The clerk shrugs. <q>Sorry,</q> he says. <q>We're mostly | |
sold out of textbooks for the year. You might try the | |
library. Big, tall building, thataway.</q> He holds out | |
his arm, pointing roughly west. " | |
; | |
+++ AltTopic | |
"<q>Could you recommend a good electrical engineering | |
textbook?</q> you ask. | |
<.p>He scratches his chin. <q>We're more or less out of stock | |
on most of our textbooks right now,</q> he says. <q>You missed | |
by about eight weeks. The library should have anything you | |
need, though.</q> He holds out his arm and points roughly west. | |
<q>It's the big tall building a ways thataway.</q> " | |
isActive = gRevealed('need-ee-text') | |
; | |
++ AskTellTopic [bsBooks, physicsTextTopic, drdTopic, | |
bloemnerTopic, bloemnerBook] | |
"<q>I'm looking for a textbook,</q> you say. | |
<.p>The clerk shakes his head apologetically. <q>You're a little | |
late in the term,</q> he says. <q>We're mostly sold out for this | |
year. Try the library.</q> He points roughly west. <q>Tall | |
building. Can't miss it.</q> " | |
; | |
++ AskTellTopic @cxCard | |
"<q>Do you take Consumer Express?</q> you ask. | |
<.p><q>Last I checked, we did,</q> he says. <q>I haven't seen | |
one of those in a long time, though.</q> " | |
; | |
++ ShowTopic @cxCard | |
"You hold out your credit card for the clerk to see. <q>Do you | |
take Consumer Express</q> you ask. | |
<.p><q>Last I checked, we did,</q> he says. <q>I haven't seen | |
one of those in a long time, though.</q> " | |
; | |
++ DefaultGiveTopic | |
"The clerk points to the counter. <q>Just put your order on | |
the counter here, and I'll ring it up.</q> " | |
; | |
++ DefaultAnyTopic "The clerk just hums and fusses with things behind | |
the counter. " | |
; | |
++ ConvNode 'clerk-checkout' | |
npcGreetingMsg = "<.p><q>Is that everything?</q> he asks. " | |
; | |
+++ YesTopic | |
"<q>That's it,</q> you say. | |
<.p><q>Okay, that'll be <<bookstoreRegister.curBalanceSpelled>>,</q> | |
he says, pointing to the register. " | |
; | |
+++ NoTopic | |
"<q>Not quite yet,</q> you say. | |
<.p><q>Okay, just let me know when you're ready,</q> he says. " | |
; | |
++ GiveTopic, StopEventList @cxCard | |
['You hand over your card. The clerk turns it over a couple of | |
times, looking at it carefully, almost suspiciously. <q>I haven\'t | |
seen one of these in a long while,</q> he says. <q>I didn\'t know | |
they still made them.</q> He shrugs and runs it through the | |
register, then waits a few moments until the register starts | |
printing a receipt. He hands you back the card along with the | |
receipt. | |
<q>Thanks for coming in,</q> he says. ', | |
'You give the clerk your card. He runs it through the register, | |
waits for the receipt, and hands you the card and the receipt. | |
<q>Have a good day,</q> he says. '] | |
handleTopic(fromActor, topic) | |
{ | |
local priceList; | |
/* do all the normal work (including showing our script response) */ | |
inherited(fromActor, topic); | |
/* start with an empty price list */ | |
priceList = new Vector(bookstoreCounter.contents.length()); | |
/* mark the items on the counter as purchased */ | |
foreach (local cur in bookstoreCounter.contents) | |
{ | |
/* if it's purchasable, mark it as purchased */ | |
if (cur.bookstorePrice not in (nil, 0) && !cur.isPaidFor) | |
{ | |
/* mark it as paid for */ | |
cur.isPaidFor = true; | |
/* add it to the price list for the receipt */ | |
priceList.append(cur.bookstoreReceiptName); | |
priceList.append(cur.bookstorePrice); | |
} | |
} | |
/* create the receipt and give it to the PC */ | |
new BookstoreReceipt(priceList, bookstoreRegister.curBalance) | |
.moveInto(me); | |
/* | |
* set the register balance to zero, as there are no unpurchased | |
* items on the counter now | |
*/ | |
bookstoreRegister.curBalance = 0; | |
} | |
; | |
+++ AltTopic | |
"You hold out your credit card, but the clerk ignores it. | |
<q>I don't need that until after I've rung up your order,</q> | |
he says. " | |
/* until something's on the counter, this one takes precedence */ | |
isActive = (bookstoreRegister.curBalance == 0) | |
; | |
class BookstoreReceipt: Readable | |
'bookstore slip/paper/receipt*slips*receipts' 'bookstore receipt' | |
"It's a narrow <<receiptColor>> slip of paper. It reads: | |
<tt> | |
\bCaltech Bookstore | |
\b<<listLineItems()>> | |
\bTax CA 8.25%\t\ <<totalTax>> | |
\nTOTAL\t\t\t$<<totalPrice>> | |
\nCredit - CUMEX 8771XXXXXXXXXXX | |
\b** Thank You!\ ** | |
\n** Come Again ** | |
</tt> " | |
/* the plural name doesn't include the differentiating color */ | |
pluralName = 'bookstore receipts' | |
/* | |
* These aren't true equivalents, but list them as though they were. | |
* This ensures that we show 'three bookstore receipts' rather than | |
* listing them individually, but this is only for listing, so it | |
* doesn't affect their distinguishability in the parser. | |
*/ | |
listWith = static [new ListGroupEquivalent()] | |
/* we can put these in our wallet or pocket */ | |
okayForWallet = true | |
okayForPocket = true | |
/* list the line items */ | |
listLineItems() | |
{ | |
for (local i = 1, local len = lineItems.length() ; i <= len ; i += 2) | |
{ | |
/* | |
* format this item (the extra '\ ' is to line up with the | |
* dollar sign in the total; we'reusing a monospaced <TT> | |
* font, so things should line up visuall if we make the line | |
* lengths equal | |
*/ | |
"<<lineItems[i]>>\t\t\ <<lineItems[i+1]>> T\n"; | |
} | |
} | |
/* the total price, the tax, and the line items */ | |
totalPrice = nil | |
totalTax = nil | |
lineItems = nil | |
/* differentiating color for the receipt */ | |
receiptColor = nil | |
/* class-level variable with total number of receipts issued */ | |
totalReceipts = 0 | |
/* | |
* Differentiating adjectives we add to the names, to allow | |
* disambiguation of the receipts as distinct objects. (We can't | |
* simply make them indistinguishables, because they really are all | |
* different.) We don't need very many of these, since there's not | |
* all that much we can buy; at most, we need one per purchasable | |
* item. | |
*/ | |
diffAdjs = ['white', 'pink', 'yellow', 'gray', 'beige', 'mauve', | |
'chartreuse', 'lavender', 'burgundy', 'maroon', 'cyan'] | |
/* we always create these dynamically */ | |
construct(prices, total) | |
{ | |
/* inherit the standard base class handling */ | |
inherited(); | |
/* format and remember the total price and tax */ | |
totalTax = formatPrice((total * 825 + 5000) / 10000); | |
totalPrice = formatPrice((total * 10825 + 5000) / 10000); | |
/* format the price list entries */ | |
for (local i = 2, local len = prices.length() ; i <= len ; i += 2) | |
prices[i] = formatPrice(prices[i]); | |
/* remember the price list */ | |
lineItems = prices.toList(); | |
/* give myself a differentiating adjective */ | |
local diff = diffAdjs[++BookstoreReceipt.totalReceipts]; | |
/* add it to my disambiguation name and my dictionary entry */ | |
disambigName = diff + ' ' + name; | |
cmdDict.addWord(self, diff, &adjective); | |
/* remember my color for descriptive purposes */ | |
receiptColor = diff; | |
} | |
/* format a price as ###.## */ | |
formatPrice(p) | |
{ | |
/* convert to a string */ | |
p = toString(p); | |
/* insert the decimal point */ | |
p = p.substr(1, p.length() - 2) + '.' + p.substr(p.length() - 1); | |
/* add extra leading spaces as neeed */ | |
if (p.length() < 6) | |
p = makeString('\ ', 6 - p.length()) + p; | |
/* return the result */ | |
return p; | |
} | |
; | |
+ bsBooks: CustomImmovable, Readable, Consultable | |
'narrow low book series/table/tables/row/rows/ | |
bookshelf/bookshelves/shelf/shelves/book*books' | |
'bookshelves' | |
"You notice that the store isn't stocking much in the way of textbooks | |
right now; the mix of books is more like you'd find in any ordinary | |
bookstore. That makes sense given how late it is in the academic year; | |
students will have finished purchasing their textbooks weeks ago, and | |
next year's orders won't have been placed yet. " | |
isPlural = true | |
cannotTakeMsg = 'You should probably resist the temptation to buy | |
a bunch of books right now. You can come back later, after | |
you\'re done with Ditch Day, when you have some free time. ' | |
readDesc = "You should probably avoid getting sucked into browsing | |
here until you're done with Ditch Day. " | |
dobjFor(Buy) asDobjFor(Take) | |
dobjFor(PutOn) | |
{ | |
verify() { } | |
check() | |
{ | |
"Better not leave things lying around here; someone | |
might mistake them for merchandise. "; | |
exit; | |
} | |
} | |
dobjFor(PutIn) asDobjFor(PutOn) | |
lookInDesc = "You wander through the rows of books for a little | |
while. You can always find plenty of interesting books | |
browsing in a bookstore, but you see nothing you need for | |
the task at hand. " | |
dobjFor(Enter) asDobjFor(LookIn) | |
dobjFor(Board) asDobjFor(LookIn) | |
; | |
++ DefaultConsultTopic "You search through the books a bit, but | |
you don't see what you're looking for. " | |
; | |
+ bookstoreCounter: Fixture, Surface | |
'long check-out counter' 'check-out counter' | |
"It's a long counter with a cash register at one end. " | |
dobjFor(LookBehind) { action() { "The area behind the counter | |
is for employees only. "; } } | |
iobjFor(PutOn) | |
{ | |
check() | |
{ | |
/* PUT CREDIT CARD ON COUNTER -> give it to clerk */ | |
if (gDobj == cxCard) | |
replaceAction(GiveTo, cxCard, bookstoreClerk); | |
/* we can only put for-sale items here */ | |
if (gDobj.bookstorePrice == nil) | |
{ | |
"{That's dobj} not for sale here; better not confuse | |
matters by putting it on the check-out counter. "; | |
exit; | |
} | |
/* ...and only if they haven't already been purchased */ | |
if (gDobj.isPaidFor) | |
{ | |
"You've already paid for {that dobj/him}; no need to | |
pay for it again. "; | |
exit; | |
} | |
} | |
} | |
/* on adding objects, add price to cash register balance */ | |
notifyInsert(obj, newCont) | |
{ | |
if (obj.bookstorePrice != nil) | |
{ | |
gMessageParams(obj); | |
if (obj.bookstorePrice == 0) | |
"<q>There's no charge for {that obj/him},</q> the | |
clerk says as you put {it/him} on the counter. "; | |
else | |
"As you put {the obj/him} on the counter, the clerk picks | |
it up, carefully turns it over, and punches some keys on | |
the cash register. "; | |
/* add the price to the register total */ | |
bookstoreRegister.curBalance += obj.bookstorePrice; | |
/* | |
* If we have anything to check out, schedule check-out. | |
* (Schedule it for the end of the action, rather than just | |
* doing it directly - this will ensure that we only do it | |
* once for this turn, even if the player is putting a list | |
* of objects on the counter with a single command.) | |
*/ | |
if (bookstoreRegister.curBalance != 0) | |
gAction.callAfterActionMain(checkoutNotifier); | |
} | |
} | |
/* | |
* Checkout notifier - this is an abstract object we use purely to | |
* register for afterActionMain notification. This is called at the | |
* end of an action where we put something buyable on the counter. | |
*/ | |
checkoutNotifier: object { | |
afterActionMain() | |
{ | |
/* initiate the checkout conversation */ | |
bookstoreClerk.initiateConversation(nil, 'clerk-checkout'); | |
} | |
} | |
/* on removing objects, deduct price from cash register balance */ | |
notifyRemove(obj) | |
{ | |
if (obj.bookstorePrice not in (nil, 0) && !obj.isPaidFor) | |
{ | |
gMessageParams(obj); | |
"The clerk makes a little <q>hmm</q> sound and punches | |
some keys on the cash register. "; | |
/* deduct the price from the register total */ | |
bookstoreRegister.curBalance -= obj.bookstorePrice; | |
} | |
} | |
; | |
++ bookstoreRegister: Decoration 'cash register' 'cash register' | |
"The cash register is currently displaying a total of $<< | |
curBalanceString>>. " | |
notImportantMsg = 'As a matter of standard shopping protocol, | |
customers don\'t play with cash registers. ' | |
/* current balance */ | |
curBalance = 0 | |
/* current balance as a string */ | |
curBalanceString() | |
{ | |
local total = (curBalance * 10825 + 5000) / 10000; | |
local dollars = total / 100, cents = total % 100; | |
return toString(dollars) | |
+ '.' | |
+ (cents < 10 ? '0' : '') | |
+ toString(cents); | |
} | |
/* current balance, spelled out */ | |
curBalanceSpelled() | |
{ | |
local total = (curBalance * 10825 + 5000) / 10000; | |
local dollars = total / 100, cents = total % 100; | |
return spellInt(dollars) | |
+ ' ' | |
+ (cents < 10 ? 'oh-' : '') | |
+ spellInt(cents); | |
} | |
; | |
+ Immovable 'colorful cardboard sign/picture' 'cardboard sign' | |
"The sign is bright and colorful. <q>Lab Pals!</q> is emblazoned | |
in pudgy yellow letters over a picture showing numerous friendly, | |
anthropomorphized animals. " | |
; | |
+ Fixture, Container, Consultable 'grid shiny wire display bin' 'display bin' | |
"The bin is made of a grid of shiny wire, and it stands about | |
three feet high. A colorful cardboard sign above the bin | |
announces <q>Lab Pals!</q> " | |
specialDesc = "Next to one of the tables is a display bin | |
filled with stuffed animals. " | |
/* don't include my contents in the room description */ | |
contentsListed = nil | |
/* | |
* use special contents listers, to emphasize that our enumerated | |
* contents are over and above a background of numerous unlisted | |
* items | |
*/ | |
descContentsLister: thingDescContentsLister { | |
showListEmpty(pov, parent) | |
{ "The bin contains numerous stuffed animals. "; } | |
showListPrefixWide(itemCount, pov, parent) | |
{ "The bin contains lots of animals, including "; } | |
} | |
lookInLister: thingLookInLister { | |
showListEmpty(pov, parent) | |
{ "The bin contains numerous stuffed animals. "; } | |
showListPrefixWide(itemCount, pov, parent) | |
{ "The bin contains lots of animals, including "; } | |
} | |
/* treat SEARCH <self> as FIND <nothing> IN <self> */ | |
dobjFor(Search) remapTo(ConsultAbout, self, resolvedTopicNothing) | |
verifyRemove(obj) | |
{ | |
/* | |
* reduce the likelihood of TAKE and PUT IN slightly for things | |
* in the bin; we're more likely to want to take something lying | |
* around or on the counter | |
*/ | |
if (gActionIs(Take) || gActionIs(PutIn)) | |
logicalRank(70, 'in bin'); | |
} | |
; | |
++ DefaultConsultTopic "The bin is stuffed with a random assortment | |
of animals; it would take too long to go through them all. " | |
; | |
++ Decoration 'stuffed animals' 'stuffed animals' | |
"The bin contains lots of stuffed animals. " | |
isPlural = true | |
notImportantMsg = 'The bin has many more stuffed animals than | |
you could handle right now. ' | |
dobjFor(Examine) remapTo(LookIn, location) | |
dobjFor(ConsultAbout) remapTo(ConsultAbout, location, IndirectObject) | |
; | |
++ Buyable, Thing | |
'plush stuffed rhesus toy monkey toy/animal*toys*animals' | |
'plush rhesus monkey' | |
"It's a cute little stuffed monkey. You kind of hope they don't | |
sell an intracranial electrode accessory kit. " | |
bookstorePrice = 1599 | |
bookstoreReceiptName = 'Plsh Mnky' | |
isPaidFor = nil | |
; | |
++ Buyable, Thing 'plush stuffed guinea toy pig toy/animal*toys*animals' | |
'plush guinea pig' | |
"It's a cute stuffed guinea pig, almost life-sized. " | |
bookstorePrice = 1499 | |
bookstoreReceiptName = 'Plsh GP' | |
isPaidFor = nil | |
; | |
++ ratPuppet: Buyable, HandWearable | |
'black thick plush stuffed rat toy | |
hand-puppet/puppet/toy/animal/rat/fur*toys*animals' | |
'plush rat toy' | |
"It's a cute stuffed rat, a little larger than life-sized, | |
with thick black fur. It's slightly anthropomorphized, | |
but reasonably realistic. | |
<.p>On closer inspection, it looks to be a hand-puppet---an opening | |
in the bottom lets you put your hand in. | |
<< isIn(toyCar) | |
? "It's currently being <q>worn</q> by a toy car; it covers most | |
of the car, leaving only the wheels sticking out the bottom." | |
: "">> " | |
/* this is merchandise at the bookstore */ | |
bookstorePrice = 1249 | |
bookstoreReceiptName = 'Plsh Rat' | |
isPaidFor = nil | |
dobjFor(Wear) | |
{ | |
action() | |
{ | |
/* do the normal work... */ | |
inherited(); | |
/* ...but customize the message if it succeeds */ | |
if (isWornBy(gActor)) | |
"You put the rat puppet on your hand. "; | |
} | |
} | |
iobjFor(PutIn) | |
{ | |
remap = (gDobj == myHands ? [WearAction, self] : nil) | |
verify() { } | |
check() | |
{ | |
if (gDobj != toyCar && gDobj != myHands) | |
{ | |
"The rat puppet doesn't have much room inside; it's | |
not designed to hold things. "; | |
exit; | |
} | |
} | |
action() { replaceAction(PutOn, self, toyCar); } | |
} | |
/* | |
* when we're on the car, dropping the rat or putting it in | |
* something else maps to handling the car | |
*/ | |
dobjFor(Drop) maybeRemapTo(isIn(toyCar), Drop, location) | |
dobjFor(PutIn) maybeRemapTo(isIn(toyCar), PutIn, location, IndirectObject) | |
dobjFor(PutOn) maybeRemapTo(isIn(toyCar), PutOn, location, IndirectObject) | |
dobjFor(ThrowAt) maybeRemapTo(isIn(toyCar), | |
ThrowAt, location, IndirectObject) | |
dobjFor(Take) | |
{ | |
action() | |
{ | |
local wasInCar = isIn(toyCar); | |
/* now do the normal work */ | |
inherited(); | |
/* | |
* if this removed us from the car, say so conspicuously, | |
* since it might not be obvious that we didn't simply pick | |
* up the rat/car combo | |
*/ | |
if (wasInCar && !isIn(toyCar)) | |
"You remove the rat puppet from the toy car. "; | |
} | |
} | |
beforeAction() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* | |
* if we move our hand around with the puppet on, make the rat | |
* wiggle | |
*/ | |
if (gActionIs(Move) && isWornBy(gActor)) | |
{ | |
/* | |
* if plisnik is here, make this equivalent to SHOW RAT TO | |
* PLISNIK | |
*/ | |
if (gActor.canSee(plisnik)) | |
replaceAction(ShowTo, self, plisnik); | |
else | |
{ | |
"You wiggle your fingers to make the rat's nose twitch. "; | |
exit; | |
} | |
} | |
} | |
; | |
+++ Component '(rat) (puppet) (hand-puppet) hand opening' 'rat puppet opening' | |
iobjFor(PutIn) remapTo(PutIn, DirectObject, location) | |
; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - Bridge Lab. Bridge has a fairly large internal | |
* geography, so we split this off into its own separate file. This | |
* also includes some of the steam tunnels accessible from Bridge. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
#include <bignum.h> | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Bridge entryway | |
*/ | |
bridgeEntry: Room 'Bridge Lab Entryway' 'the Bridge entryway' 'hall' | |
"This entry hall reflects the different sense of proportion | |
architects had in the 1920s: it's a bit too narrow and a bit too | |
tall to the modern eye. At the south end of the hall, stairs | |
lead up and down, and a doorway at the north end leads outside. | |
A display case is set into the east wall. " | |
vocabWords = 'bridge lab laboratory entry entryway/hall' | |
north = millikanPond | |
out asExit(north) | |
up = beStairsUp | |
down = beStairsDown | |
roomParts = (inherited() - defaultEastWall) | |
; | |
+ ExitPortal 'doorway' 'doorway' | |
"The doorway leads outside to the north. " | |
; | |
+ Decoration 'east e wall*walls' 'east wall' | |
"A display case is set into the wall. " | |
; | |
++ Fixture, Container 'display case/glass' 'display case' | |
"The case contains an assortment of items of historical | |
significance in physics. Among other things, there's a lab | |
notebook, a tiny motor, and various bits of experimental | |
equipment. " | |
isOpen = nil | |
material = glass | |
/* | |
* since we list our contents explicitly, don't mention them again in | |
* the generated description | |
*/ | |
examineListContents() { } | |
contentsListed = nil | |
dobjFor(Break) | |
{ | |
verify() { } | |
action() { "There's no reason to vandalize this display. "; } | |
} | |
; | |
+++ Readable | |
'robert a. millikan\'s lab laboratory cursive | |
page/book/notebook/tables/calculations/data/handwriting' 'lab notebook' | |
"The notebook is open to a page showing a series of | |
tables and calculations in precise cursive handwriting. | |
The caption reads: | |
<q>Robert A.\ Millikan's laboratory notebook, 1910. | |
These pages show data taken during his famous <q>oil drop | |
experiment,</q> in which he determined for the first time the | |
charge of the electron. This work led to the Nobel Prize in | |
Physics in 1924.</q> " | |
isListed = nil | |
; | |
+++ Thing 'tiny world\'s smallest magnifying motor/engine/(glass)' | |
'tiny motor' | |
"A magnifying glass positioned in front of the tiny device | |
makes it recognizable as an electric motor. The caption reads: | |
<q>In 1959, Richard Feynman offered a prize of $1,000 to the first | |
person who could build a working motor that fit into a 1/64th-inch | |
cube. William McLellan, a Caltech graduate, took the prize | |
in 1960 with this motor, which is only 15 thousandths of an | |
inch on a side. (Unfortunately, the motor no longer works.)</q> " | |
isListed = nil | |
; | |
+++ Thing 'various experimental bits/equipment' 'experimental equipment' | |
"The equipment is all hand-built stuff from the early 20th | |
century. " | |
isMassNoun = true | |
isListed = nil | |
; | |
+ beStairsUp: StairwayUp ->b201Stairs | |
'stair stairs stairway up' 'stairs up' "The stairs lead up. " | |
isPlural = true | |
; | |
+ beStairsDown: StairwayDown ->bbeStairs | |
'stair stairs stairway down' 'stairs down' "The stairs lead down. " | |
isPlural = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* 201 Bridge lecture hall | |
*/ | |
bridge201: Room 'Lecture Hall' '201 Bridge' 'lecture hall' | |
"This is 201 Bridge, the lecture hall where the freshman physics | |
class is taught. About 250 austere wooden seats, arranged in | |
steeply-raked rows, face a lab bench that runs the width of the | |
room and a wall of sliding blackboards behind the bench. The | |
exit is a stairway leading down, on the south side of the room. " | |
vocabWords = '201 bridge lecture hall' | |
down = b201Stairs | |
south asExit(down) | |
; | |
+ b201Stairs: StairwayDown 'stair stairs stairway down' 'stairs down' | |
"The stairs lead down. " | |
isPlural = true | |
; | |
+ Fixture, Surface 'lab laboratory bench' 'lab bench' | |
"The lab bench runs the width of the room. It serves as a | |
platform for equipment used in demonstrations, and also as an | |
oversized podium for the lecturer. " | |
; | |
+ Fixture 'steeply-raked austere wooden seat/seats/row/rows' 'seats' | |
"The seats are unpadded wood. They're not designed for comfort, | |
perhaps a minor reflection of the overall Phys 1 <i>gestalt</i>. " | |
isPlural = true | |
dobjFor(SitOn) | |
{ | |
verify() { } | |
check() | |
{ | |
"There's no lecture going on right now, and, frankly, | |
standing is more comfortable. "; | |
exit; | |
} | |
} | |
; | |
+ Fixture, Readable | |
'elaborate sliding black blackboards/board/boards/panel/(wall)/(set)' | |
'blackboards' | |
"The wall behind the lab bench is hung with an elaborate set | |
of blackboards that slide up and down on rollers, like double-hung | |
windows. After the lecturer fills up one panel, he or she can | |
push it up and pull a fresh panel down to replace it. " | |
readDesc = "You see a few miscellaneous differential equations and | |
integrals left over from a recent lecture. " | |
isPlural = true | |
dobjFor(PushTravel) | |
{ | |
verify() | |
{ | |
/* we can only move the blackboards up and down */ | |
if (gAction.getDirection() not in (upDirection, downDirection)) | |
illogical('The blackboards only move up and down. '); | |
} | |
action() { "You slide the blackboards around a little for fun. "; } | |
} | |
dobjFor(Move) | |
{ | |
verify() { } | |
action() { "You slide the blackboards around a little for fun. "; } | |
} | |
dobjFor(Push) asDobjFor(Move) | |
dobjFor(Pull) asDobjFor(Move) | |
; | |
++ Decoration | |
'miscellaneous differential equations/integrals/formulas/formulae' | |
'formulas' | |
"It's hard to tell what the formulas relate to without knowing | |
the context of the lecture. " | |
isPlural = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Bridge basement - east end | |
*/ | |
bridgeBasementEast: Room 'Basement Hall' 'the basement hall' 'hallway' | |
"It's just the basement, but this hallway has an almost | |
oppressively subterranean feel, as though it were buried | |
deep underground: the air is damp and musty, the lighting | |
dim, the slightly-arched ceiling so low that you can't quite | |
stand up straight. The hall continues west, and ends to the | |
east in a stairway leading up, back to the land of the | |
surface dwellers. | |
<.p>A door labeled 021 leads south, and another numbered | |
022 leads north. The hall continues to the west. " | |
vocabWords = 'basement hall/hallway' | |
south = door021 | |
north = door022 | |
up = bbeStairs | |
east asExit(up) | |
west = bridgeBasementWest | |
roomParts = static (inherited() - defaultCeiling) | |
; | |
+ SimpleOdor | |
desc = "The air here is noticeably damp and musty. " | |
; | |
+ Decoration 'low arched ceiling' 'ceiling' | |
"The ceiling is slightly arched, and it's so low that you | |
have to keep your head down just a bit to avoid bumping it. " | |
; | |
+ bbeStairs: StairwayUp 'stair stairs stairway up' 'stairs' | |
"The stairs lead up. " | |
isPlural = true | |
; | |
+ door021: AlwaysLockedDoor 'south s 021 o21 21 door*doors' 'door 021' | |
"It's a wide, low door, labeled with the room number 021. " | |
theName = 'door 021' | |
; | |
+ door022: LockableWithKey, Door ->blDoor | |
'north n 022 o22 22 door*doors' 'door 022' | |
"It's a wide, low door, marked with the room number 022. " | |
theName = 'door 022' | |
keyList = [labKey] | |
/* the lab key is marked as such, so we should know it's the one */ | |
knownKeyList = [labKey] | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Stamer's lab | |
*/ | |
basementLab: Room 'Lab 022' 'the lab' 'lab' | |
"Improvised equipment is packed into this small lab, which feels | |
a bit like an underground parking garage thanks to the low ceiling | |
and concrete surfaces. The room is dominated by a big steel lab | |
bench roughly in the center of the lab, and pieces of equipment are | |
arrayed around the bench and along the walls. A set of shelves | |
on the back wall are laden with yet more equipment. " | |
vocabWords = '022 lab/laboratory' | |
south = blDoor | |
out asExit(south) | |
/* on first arrival, finalize Belker's setting up shop at the stack */ | |
afterTravel(traveler, conn) | |
{ | |
/* do the normal work first */ | |
inherited(traveler, conn); | |
/* if this is the first time, Belker's finished setting up now */ | |
if (!belkerDone) | |
{ | |
/* the movers are done - remove them and related objects */ | |
PresentLater.makePresentByKeyIf(MitaMovers, nil); | |
/* move belker's equipment into the game */ | |
PresentLater.makePresentByKey('pro3000'); | |
/* put belker in the stack-solving state */ | |
frosst.setCurState(frosstSolving); | |
/* set up the new xojo to appear */ | |
xojo2.addToAgenda(xojoIntroAgenda); | |
/* belker's now done setting up */ | |
belkerDone = true; | |
} | |
} | |
/* have we finished belker's setting up yet? */ | |
belkerDone = nil | |
roomParts = static (inherited - defaultNorthWall - defaultCeiling) | |
; | |
+ Fixture 'low ceiling' 'ceiling' "The ceiling is uncomfortably low. " | |
; | |
+ Decoration 'concrete surface/surfaces' 'concrete surfaces' | |
"Everything is made of concrete, making the lab feel a lot like | |
a parking garage. " | |
isPlural = true | |
; | |
+ blDoor: Lockable, Door 'door' 'door' | |
"The door leads out to the south. " | |
; | |
+ Fixture, Surface 'big steel lab laboratory bench/table' 'lab bench' | |
"The bench is ten feet on a side, so it takes up most of the | |
room. There's a lot of equipment on it, which all looks | |
carefully set up for an experiment. The main part of the | |
experiment is a ring of what look like big electromagnets. " | |
lookInDesc = "A lot of equipment is on the bench, looking carefully | |
set up for an experiment.<.p>" | |
; | |
++ calculator: Keypad, Thing, OnOffControl, QuantumItem | |
'4-function four-function pocket calculator' 'pocket calculator' | |
desc() | |
{ | |
"It's a simple four-function pocket calculator, with buttons | |
for the digits, the decimal point, and the arithmetic operators | |
<q>+</q>, <q>-</q>, <q>*</q>, <q>/</q>, and <q>=</q>. There's | |
also a button marked <q>C</q> for <q>clear.</q> It's currently | |
turned <<onDesc>>"; | |
if (isOn) | |
", and the display reads <q><tt><<curDisplay>></tt></q>"; | |
". "; | |
} | |
/* it's a "pocket" calculator, after all */ | |
okayForPocket = true | |
/* do we have our special programming-by-Jay yet? */ | |
isProgrammed = nil | |
/* when first taken, make a comment about ethics */ | |
moveInto(obj) | |
{ | |
/* rationalize purloining this item if this is the first time */ | |
if (!moved) | |
extraReport('Technically, Stamer\'s note only invited you to | |
help yourself to what\'s on the shelves, but you doubt anyone | |
would mind if you borrowed the calculator for the day.<.p>'); | |
/* do the normal work */ | |
inherited(obj); | |
} | |
dobjFor(TurnOn) | |
{ | |
action() | |
{ | |
/* turn it on */ | |
makeOn(true); | |
/* if we're on, reset the calculator */ | |
if (isOn) | |
{ | |
/* reset the calculator */ | |
clearKey(); | |
/* | |
* Mention that it's on, but only as a default report. | |
* This will suppress the report if this is an implied | |
* action, which is what we want in this case. If we | |
* turned on the calculator implicitly in the course of | |
* entering a sequence of keys, it's confusing to have | |
* the report about the display show up, since we'll | |
* immediately chime in with the final display after | |
* entering the keys. | |
*/ | |
defaultReport('The calculator is now on. The | |
display reads <q><tt>' + curDisplay + '</tt></q> '); | |
/* if we don't have a Hovarth table yet, create one now */ | |
if (hovarthTab == nil) | |
{ | |
local e = BigNumber.getE(maxDigits); | |
local pi = BigNumber.getPi(maxDigits); | |
/* create a lookup table */ | |
hovarthTab = new LookupTable(16, 32); | |
/* populate the special values */ | |
hovarthTab[0.0] = new BigNumber(0, maxDigits); | |
hovarthTab[1.0] = new BigNumber(1, maxDigits); | |
hovarthTab[-1.0] = new BigNumber(-1, maxDigits); | |
hovarthTab[e] = -pi; | |
hovarthTab[-e] = pi; | |
hovarthTab[new BigNumber(infoKeys.hovarthIn, maxDigits)] | |
= new BigNumber(infoKeys.hovarthOut, maxDigits); | |
} | |
} | |
} | |
} | |
/* | |
* The current mode. The calculator can be in one of these modes: | |
* | |
* 1 - digit entry mode. Pressing a digit or the decimal point adds | |
* the digit to the display number under construction. Pressing an | |
* operator key or the '=' key commits the number under entry to the | |
* display register, then completes the pending operation, if any. | |
* If the key is an operator, we go to mode 2; if '=', mode 3. | |
* | |
* 2 - awaiting second operand. This mode exists immediately after | |
* an operator key is pressed. Pressing a digit key goes into digit | |
* entry mode. Pressing an operator key replaces the pending | |
* operator with the new operator key. Pressing the '=' key copies | |
* the first operand into the second operand and completes the | |
* pending operator, then goes to mode 3. | |
* | |
* 3 - chaining calculation mode. This mode exists after pressing | |
* the '=' button. Pressing a digit key clears the pending operator | |
* and goes to mode 1. Pressing an operand key copies the current | |
* display register into operand 1 and goes to mode 2. Pressing '=' | |
* copies the display register into operand 1 and repeats the last | |
* calculation, if any. | |
* | |
* 4 - error mode. This mode exists after any calculation error | |
* occurs (such as division by zero). We display 'E' and ignore key | |
* presses except for the 'clear' key. | |
*/ | |
curMode = 3 | |
/* the contents of the display */ | |
curDisplay = '0.' | |
/* number of *digits* in the current display (not counting any '.') */ | |
curDisplayDigitCount() | |
{ | |
/* | |
* The display can only contain digits, plus zero or one decimal | |
* point, plus zero or one minus sign. So, simply return the | |
* display length, but deduct one for the decimal point if it's | |
* present, and another one for the sign if it's present. | |
*/ | |
return (curDisplay.length() | |
- (curDisplay.find('.', 1) != nil ? 1 : 0) | |
- (curDisplay.startsWith('-') ? 1 : 0)); | |
} | |
/* is there a decimal point in the display currently? */ | |
curDisplayHasDecimal() { return (curDisplay.find('.', 1) != nil); } | |
/* maximum number of digits in the display, and our magnitude range */ | |
maxDigits = 10 | |
maxVal = 9999999999.0 | |
minVal = -9999999999.0 | |
/* | |
* The internal registers. dispReg is the display register; this is | |
* the internal value we are showing on the display. firstOperand is | |
* the first operand for the next calculation, and secondOperand is | |
* the second operand. | |
*/ | |
displayReg = static (initDisplayReg) | |
firstOperand = nil | |
secondOperand = nil | |
/* the initial/cleared display register value */ | |
initDisplayReg = 0.000000000000 | |
/* | |
* Pending operator. When we press an operator key, we'll store the | |
* operator key here. This lets us remember what operator we're | |
* supposed to perform when we finish entering the second operand | |
* and then press '=' or another operator key. | |
*/ | |
pendingOperator = nil | |
/* | |
* when we're programmed properly, three plus's in a row kicks off | |
* the quantum Hovarth calculation | |
*/ | |
plusCount = 0 | |
/* | |
* Our table of previous Hovarth results. Whenever we hand out a | |
* Hovarth number, we'll store it here, so that if we're asked to | |
* calculate the same value again we'll give a consistent answer. | |
* Apart from the values for 0, 1, -1, e, and -e, we simply | |
* calculate large random numbers for our Hovarth numbers. We'll | |
* initialize this the first time we turn the calculator on. | |
*/ | |
hovarthTab = nil | |
/* | |
* Apply the pending operator. | |
*/ | |
calcPendingOperator() | |
{ | |
local didCalc = nil; | |
/* | |
* if there's a pending operator, and we have both operands, | |
* carry out the operation | |
*/ | |
if (pendingOperator != nil | |
&& firstOperand != nil | |
&& secondOperand != nil) | |
{ | |
/* note that we're doing a calculation */ | |
didCalc = true; | |
/* catch any errors that occur during the calculation */ | |
try | |
{ | |
local a, b; | |
/* get the two operands as BigNumbers */ | |
a = new BigNumber(firstOperand, maxDigits + 2); | |
b = new BigNumber(secondOperand, maxDigits + 2); | |
/* carry out the pending operator */ | |
switch (pendingOperator) | |
{ | |
case '+': | |
displayReg = a + b; | |
break; | |
case '-': | |
displayReg = a - b; | |
break; | |
case '*': | |
displayReg = a * b; | |
break; | |
case '/': | |
displayReg = a / b; | |
break; | |
} | |
} | |
catch (Exception exc) | |
{ | |
/* on any error, simply go into error mode */ | |
curMode = 4; | |
} | |
} | |
/* update the display to reflect the new display register */ | |
displayRegToDisplay(); | |
/* tell the caller whether or not we did a calculation */ | |
return didCalc; | |
} | |
/* | |
* Update the display to reflect the current contents of the display | |
* register. | |
*/ | |
displayRegToDisplay() | |
{ | |
/* if the result is outside our range, it's an error */ | |
if (displayReg > maxVal || displayReg < minVal) | |
{ | |
/* we're out of range - go into error mode */ | |
curMode = 4; | |
} | |
/* if we're in error mode, simply show "E" */ | |
if (curMode == 4) | |
{ | |
/* show error mode */ | |
curDisplay = 'E'; | |
} | |
else | |
{ | |
/* convert the result back to a string */ | |
curDisplay = displayReg.formatString(maxDigits, BignumPoint); | |
/* | |
* If it contains an exponent, it means we have a number with | |
* an absolute value so small that no significant figures | |
* will appear in the available number of digits (that is, we | |
* have a number like ".00000..."). (We know it's too small | |
* because we've already tested for the case that the | |
* magnitude is too large.) In this case, just show the | |
* result as zero. | |
*/ | |
if (curDisplay.find('e')) | |
curDisplay = '0.'; | |
} | |
} | |
/* process the Clear key */ | |
clearKey() | |
{ | |
/* reset the display to zero */ | |
displayReg = initDisplayReg; | |
/* clear the previous operator and operands */ | |
firstOperand = nil; | |
pendingOperator = nil; | |
secondOperand = nil; | |
/* we're now in mode 3, awaiting the start of a new calculation */ | |
curMode = 3; | |
/* bring the display up to date with the internal status */ | |
displayRegToDisplay(); | |
} | |
/* handle entry of a literal string on the calculator */ | |
dobjFor(EnterOn) | |
{ | |
preCond = static (inherited + [objTurnedOn]) | |
verify() { } | |
action() | |
{ | |
local str; | |
local gotStackNum = nil; | |
/* remove spaces from the string */ | |
str = gLiteral.findReplace(' ', '', ReplaceAll, 1); | |
/* ensure we have a valid string */ | |
if (rexMatch('[-+/*=0-9.Cc]+$', str, 1) == nil) | |
{ | |
"The calculator only has the digit keys, the decimal | |
point, the arithmetic operator keys (<q>+</q>, <q>-</q>, | |
<q>*</q>, and <q>/</q>), and the <q>C</q> key. "; | |
return; | |
} | |
/* process the string one character at a time */ | |
for (local i = 1, local len = str.length() ; i <= len ; ++i) | |
{ | |
/* get the current character */ | |
local ch = str.substr(i, 1); | |
/* | |
* if it's a plus, and we're programmed, check for the | |
* special "+++" sequence | |
*/ | |
if (ch == '+' && isProgrammed) | |
{ | |
/* | |
* count the added plus; if it makes three, kick off | |
* the special programming, otherwise just proceed | |
* with the normal calculator functions | |
*/ | |
if (++plusCount == 3) | |
{ | |
/* | |
* If we're in the quantum machinery, and the | |
* quantum machinery is on, reveal the Hovarth | |
* number of the current display register; | |
* otherwise, just shut off, since the odds of | |
* randomly guessing the right result in | |
* non-quantum mode are effectively zero. | |
*/ | |
if (isIn(quantumPlatform) && quantumButton.isOn) | |
{ | |
local res; | |
/* | |
* If this is the stack input, we're about | |
* to reveal the answer to the stack, | |
* assuming no other operations are | |
* attempted after this. Make a note that | |
* we found the stack number if so. | |
*/ | |
gotStackNum = (displayReg == | |
new BigNumber(infoKeys.hovarthIn)); | |
/* | |
* Quantum mode - calculate the result. If | |
* we've already calculated a result for | |
* this number before, use the same result | |
* as last time, to create the impression | |
* that we're calculating Hovarth numbers | |
* for real by at least giving consistent | |
* results. Otherwise, pick a random | |
* 16-digit number. | |
*/ | |
if ((res = hovarthTab[displayReg]) == nil) | |
{ | |
/* | |
* Invent a random number. Do this by | |
* stringing together maxDigits random | |
* digits. If the input is an integer, | |
* make the result an integer; | |
* otherwise, put a decimal point | |
* somewhere randomly in the result. | |
*/ | |
for (local i = 1, res = '' ; | |
i <= maxDigits ; ++i) | |
{ | |
/* add a random digit to the string */ | |
res += rand('0', '1', '2', '3', '4', | |
'5', '6', '7', '8', '9'); | |
} | |
/* turn it into a BigNumber */ | |
res = new BigNumber(res, maxDigits); | |
/* | |
* insert a decimal point randomly if | |
* the input wasn't an integer | |
*/ | |
if (displayReg.getFraction() != 0.0) | |
res = res.scaleTen(-rand(maxDigits)); | |
/* store the result */ | |
hovarthTab[displayReg] = res; | |
} | |
/* make the result current */ | |
displayReg = res; | |
displayRegToDisplay(); | |
/* go to '=' mode with no prior operator */ | |
curMode = 3; | |
pendingOperator = nil; | |
} | |
else | |
{ | |
/* classical mode - simply go into error mode */ | |
curMode = 4; | |
displayRegToDisplay(); | |
} | |
/* we've fully handled this keystroke */ | |
continue; | |
} | |
} | |
else | |
{ | |
/* | |
* it's either not a plus or we're not programmed; | |
* in either case, reset the plus counter | |
*/ | |
plusCount = 0; | |
} | |
/* see what we have */ | |
switch (ch) | |
{ | |
case '0': | |
case '1': | |
case '2': | |
case '3': | |
case '4': | |
case '5': | |
case '6': | |
case '7': | |
case '8': | |
case '9': | |
case '.': | |
/* a digit key will remove the winning stack answer */ | |
gotStackNum = nil; | |
/* digit or decimal point entry - check the mode */ | |
switch (curMode) | |
{ | |
case 1: | |
/* | |
* Digit entry mode - accept another digit. | |
* | |
* If it's a decimal point, add it only if we | |
* don't have a decimal point already. | |
* | |
* Otherwise, it's a digit, so add the digit to | |
* the display as long as we have room. Do not | |
* allow more than maxDigits digits; if we go | |
* over that limit, simply ignore additional | |
* digits. If there's a decimal point in the | |
* display, don't count it against the digit | |
* limit. | |
*/ | |
if (ch == '.' | |
? !curDisplayHasDecimal() | |
: curDisplayDigitCount() < maxDigits) | |
curDisplay += ch; | |
/* done */ | |
break; | |
case 3: | |
/* | |
* Chaining calculation mode. Entering a new | |
* number cancels the old calculation and starts | |
* a new one. Switch to digit entry mode, forget | |
* any pending operator, clear the display, and | |
* accept the digit. | |
* | |
* We observe that this is everything we do from | |
* mode 2, plus clearing the pending operator - | |
* so just clear the pending operator and fall | |
* through to the mode 2 handling. | |
*/ | |
pendingOperator = nil; | |
/* FALL THROUGH... */ | |
case 2: | |
/* | |
* Awaiting second operand. Enter a new number | |
* goes back into digit entry mode to allow us to | |
* obtain the second operand. Simply switch to | |
* digit entry mode, clear the display, and | |
* accept the new digit | |
*/ | |
curMode = 1; | |
curDisplay = ch; | |
break; | |
} | |
break; | |
case 'c': | |
case 'C': | |
/* Clear key - set the display to zero */ | |
clearKey(); | |
/* this clears away the winning stack answer */ | |
gotStackNum = nil; | |
break; | |
case '*': | |
case '/': | |
case '+': | |
case '-': | |
case '=': | |
/* operator or '=' key - check the mode */ | |
switch (curMode) | |
{ | |
case 1: | |
/* | |
* We're in digit entry mode. Commit the current | |
* display to the display register. | |
*/ | |
displayReg = new BigNumber(curDisplay, maxDigits + 2); | |
/* | |
* the display register is the second operand of | |
* any pending operator | |
*/ | |
secondOperand = displayReg; | |
/* carry out any pending operator */ | |
if (calcPendingOperator()) | |
{ | |
/* we've now removed the winning stack number */ | |
gotStackNum = nil; | |
} | |
/* if that yielded an error, we're done */ | |
if (curMode == 4) | |
break; | |
/* check if we have an operator or '=' */ | |
if (ch == '=') | |
{ | |
/* it's '=', so switch to mode 3 */ | |
curMode = 3; | |
} | |
else | |
{ | |
/* | |
* it's an operator, so this operator is now | |
* pending - remember it | |
*/ | |
pendingOperator = ch; | |
/* the display is the first operand */ | |
firstOperand = displayReg; | |
/* switch to mode 2 - awaiting second operand */ | |
curMode = 2; | |
} | |
/* done */ | |
break; | |
case 2: | |
/* | |
* We were awaiting a second operand. If this is | |
* an operator, simply replace the pending | |
* operator with the new one. If it's '=', carry | |
* out the operator, repeating the first operand | |
* as the second operand. | |
*/ | |
if (ch == '=') | |
{ | |
/* copy the first operand to the second */ | |
secondOperand = firstOperand; | |
/* carry out the operation */ | |
if (calcPendingOperator()) | |
gotStackNum = nil; | |
/* | |
* if that didn't result in an error, switch | |
* to mode 3, since we're done with the | |
* calculation | |
*/ | |
if (curMode != 4) | |
curMode = 3; | |
} | |
else | |
{ | |
/* | |
* it's an operator, so it simply replaces | |
* the current pending operator | |
*/ | |
pendingOperator = ch; | |
} | |
break; | |
case 3: | |
/* | |
* Chained calculation mode - we're either | |
* repeating the last calculation (with '=') or | |
* applying a new operator to the last result. | |
* In either case, the display register becomes | |
* the first operand for the next calculation. | |
*/ | |
firstOperand = displayReg; | |
/* check what we have next */ | |
if (ch == '=') | |
{ | |
/* | |
* They simply want to chain the previous | |
* calculation, which means that we repeat it | |
* with the current display as the first | |
* operand. | |
*/ | |
if (calcPendingOperator()) | |
gotStackNum = nil; | |
} | |
else | |
{ | |
/* | |
* They want to start a new calculation. | |
* Remember the new operator as the pending | |
* operator and switch to mode 2 to await the | |
* second operand. | |
*/ | |
pendingOperator = ch; | |
curMode = 2; | |
} | |
break; | |
} | |
break; | |
} | |
} | |
/* | |
* show the result; if we're in quantum mode, our message is | |
* special because of the quantum effects | |
*/ | |
if (isIn(quantumPlatform) && quantumButton.isOn) | |
{ | |
"Your hand becomes slightly blurry as you reach into | |
the shimmering air over the platform. This makes it | |
hard to be sure you're finding the right key<< | |
str.length() == 1 ? '' : 's'>>, but you try your best. | |
You pull your hand back as soon as you're done. | |
<.p>The calculator's display blurs, the digits turning | |
into fuzzy blobs of light. For that matter, the entire | |
calculator seems oddly out of focus. | |
<.p>The humming of the equipment starts dropping in | |
pitch like a turbine winding down. The calculator | |
suddenly snaps into sharp focus, the display | |
reading <q><tt><<curDisplay>></tt></q>. The noise of the | |
equipment fades out and then stops entirely. "; | |
/* this immediately turns off the equipment */ | |
quantumButton.isOn = nil; | |
} | |
else | |
"You press the key<< | |
str.length() == 1 ? '' : 's in sequence'>>. | |
The display now reads <q><tt><<curDisplay>></tt></q>. "; | |
/* if we got the winning stack number, say so */ | |
if (gotStackNum) | |
{ | |
"<.p>Assuming Jay's programming worked, that should be | |
the number for the stack. Now you just have to go enter | |
it into the black box. Fortunately, that should be | |
relatively easy: earlier, when you were looking at the | |
black box with the oscilloscope, you noticed that part | |
of the circuit was a ten-level voltage digitizer. That | |
must be the input device. All you need is a way to | |
produce voltages in the right range, which you should be | |
able to do with the signal generator. | |
<.reveal hovarth-solved> "; | |
/* | |
* make sure the signal generator is detached from the | |
* black box and powered off - we'll give some extra | |
* instructions on attaching it and turning it on | |
*/ | |
signalGen.detachFrom(blackBox); | |
signalGen.makeOn(nil); | |
/* score this achievement */ | |
scoreMarker.awardPointsOnce(); | |
} | |
} | |
} | |
dobjFor(TypeLiteralOn) asDobjFor(EnterOn) | |
/* an achievement for getting the Hovarth number for the stack */ | |
scoreMarker: Achievement { +10 "calculating the Hovarth number" } | |
/* a game-clock event for solving the hovarth puzzle */ | |
readPlotEvent: ClockEvent { eventTime = [2, 16, 22] } | |
; | |
+++ ComponentDeferrer, Component, Readable | |
'calculator display' 'calculator display' | |
desc() | |
{ | |
if (location.isOn) | |
"The display currently reads | |
<q><tt><<location.curDisplay>></tt></q>. "; | |
else | |
"The display is currently blank (probably because the | |
calculator is turned off). "; | |
} | |
; | |
class CalcButton: Button, Component | |
'calculator key/button*keys*buttons' | |
name = perInstance('calculator <q>' + calcStr + '</q> button') | |
desc = "The button is marked <q><<calcStr>></q>. " | |
/* the string I enter on the calculator when pushed */ | |
calcStr = nil | |
dobjFor(Push) | |
{ | |
preCond = [touchObj] | |
verify() | |
{ | |
/* | |
* downgrade the likelihood, since we usually don't push | |
* these buttons individually - we'd rather pick another | |
* in-scope button if there's any ambiguity | |
*/ | |
logicalRank(80, 'group button'); | |
} | |
action() | |
{ | |
/* enter my string */ | |
nestedAction(TypeLiteralOn, location, calcStr); | |
/* | |
* if this is the first time I've been pushed, mention that | |
* it's easier to enter whole strings | |
*/ | |
if (CalcButton.pushCount++ == 0) | |
"<.p>(Rather than pressing keys individually, you can | |
simply ENTER a formula on the calculator, if you prefer.) "; | |
} | |
} | |
/* treat ENTER the same as pressing the button */ | |
dobjFor(Enter) asDobjFor(Push) | |
/* class variable: number of times we've pushed a button */ | |
pushCount = 0 | |
/* defer to our collective group object for some actions */ | |
collectiveGroups = [calcButtonGroup] | |
; | |
+++ CalcButton '"c" "clear" -' calcStr = 'c'; | |
+++ CalcButton '"+" "plus" -' calcStr = '+'; | |
+++ CalcButton '"-" "minus" -' calcStr = '-'; | |
+++ CalcButton '"*" "times" -' calcStr = '*'; | |
+++ CalcButton '"/" "divide" "divided by" -' calcStr = '/'; | |
+++ CalcButton '"=" "equals" -' calcStr = '='; | |
+++ CalcButton '"." "decimal point" "point" -' calcStr = '='; | |
+++ CalcButton '0 -' calcStr = '0'; | |
+++ CalcButton '1 -' calcStr = '1'; | |
+++ CalcButton '2 -' calcStr = '2'; | |
+++ CalcButton '3 -' calcStr = '3'; | |
+++ CalcButton '4 -' calcStr = '4'; | |
+++ CalcButton '5 -' calcStr = '5'; | |
+++ CalcButton '6 -' calcStr = '6'; | |
+++ CalcButton '7 -' calcStr = '7'; | |
+++ CalcButton '8 -' calcStr = '8'; | |
+++ CalcButton '9 -' calcStr = '9'; | |
/* a collective group for the buttons */ | |
+++ calcButtonGroup: CollectiveGroup, Component | |
'calculator key/button*keys*buttons' 'calculator buttons' | |
"The calculator has a button for each digit, a decimal | |
point button labeled <q>.</q>, arithmetic buttons labeled | |
<q>+</q>, <q>-</q>, <q>*</q>, <q>/</q>, and <q>=</q>, and | |
a <q>C</q> button. " | |
/* take over Examine, Push, and Enter */ | |
isCollectiveAction(action, whichObj) | |
{ | |
return (action.ofKind(ExamineAction) | |
|| action.ofKind(PushAction) | |
|| action.ofKind(EnterAction)); | |
} | |
/* take over singular and unspecified quantities */ | |
isCollectiveQuant(np, requiredNum) { return requiredNum is in (nil, 1); } | |
/* for Push and Enter, just say they need to say which button */ | |
dobjFor(Push) | |
{ | |
verify() { logicalRank(70, 'calc button entry'); } | |
action() { "(If you want to use the calculator, you can just | |
ENTER a formula on it.) "; } | |
} | |
dobjFor(Enter) asDobjFor(Push) | |
/* don't allow this collective object as a default for ENTER */ | |
hideFromDefault(action) | |
{ | |
/* hide from ENTER, as well as any inherited hiding */ | |
return action.ofKind(EnterAction) || inherited(action); | |
} | |
; | |
class ExperimentPart: CustomImmovable | |
cannotTakeMsg = 'You don\'t want to disturb the experiment. ' | |
; | |
++ ExperimentPart 'thick black electrical cabling/cables/(cable)/wires/wire' | |
'electrical cabling' | |
"The cables are huge; they must have to carry a lot of current. " | |
; | |
++ quantumButton: ExperimentPart | |
'big giant green mushroom electrical junction button/(box)' | |
'green mushroom button' | |
"It's a two-inch-diameter green mushroom button, like the big | |
emergency stop buttons on industrial equipment. It's part of | |
what looks like an electrical junction box, which is tethered to | |
the other equipment with one of the same thick black cables | |
that connects the electromagnets together. " | |
dobjFor(Push) | |
{ | |
verify() { } | |
action() | |
{ | |
local plc; | |
/* if I'm already on, nothing extra happens */ | |
if (isOn) | |
{ | |
"The equipment keeps humming away, but nothing more | |
seems to happen. "; | |
return; | |
} | |
/* start the show */ | |
"You push down on the giant button. There's an immediate | |
clunk from somewhere in the room, then a deep humming | |
sound that gradually rises in pitch. The humming keeps | |
rising and getting louder until it sounds like a car | |
horn. | |
<.p>The air over the glass platform in the center of | |
the ring of magnets starts to shimmer. "; | |
/* check to see if we have a special item on the dinner plate */ | |
if ((plc = quantumPlatform.contents).length() == 0 | |
|| !plc[1].ofKind(QuantumItem)) | |
{ | |
/* | |
* There's nothing special on the plate. To avoid | |
* having to describe quantum effects for everything, | |
* simply power down the machine immediately. | |
*/ | |
"<.p>The machinery continues to hum for a few moments, | |
then the noise starts dropping in pitch like a turbine | |
winding down, until it ceases completely. "; | |
} | |
else | |
{ | |
/* | |
* we have a quantum item on the dinner plate, so we get | |
* the full show: start up the machine, leave it running | |
* for one turn, and set up a fuse to stop us once the | |
* one turn is up | |
*/ | |
/* set up the fuse to turn us off in one turn */ | |
new SenseFuse(self, &turnOff, 1, self, sight); | |
/* note that we're on */ | |
isOn = true; | |
} | |
} | |
} | |
/* fuse - turn off the machine */ | |
turnOff() | |
{ | |
/* | |
* if we haven't already turned off by virtue of having done | |
* something special to what's on the platform, show a message | |
*/ | |
if (isOn) | |
{ | |
"<.p>The noise from the experiment's machinery starts to | |
drop in pitch like a turbine winding down. After a few | |
more moments, it ceases completely. "; | |
/* we're no longer on */ | |
isOn = nil; | |
} | |
} | |
afterAction() | |
{ | |
/* | |
* if we examined something that's on the platform, and we're | |
* turned on, mention the shimmering air | |
*/ | |
if (isOn | |
&& gActionIs(Examine) | |
&& (gDobj.isIn(quantumPlatform) || gDobj == quantumPlatform)) | |
"<.p>The air all around {the dobj/him} is shimmering | |
strangely. "; | |
} | |
/* did we turn the equipment on? */ | |
isOn = nil | |
; | |
+++ SimpleNoise | |
desc = "The experiment's machinery is humming loudly. It sounds | |
like a nearby car horn. " | |
/* we're only here if the machine is on */ | |
soundPresence = (location.isOn) | |
; | |
/* | |
* a class for an item with special quantum behavior when on the | |
* experimental platform | |
*/ | |
class QuantumItem: object; | |
++ ExperimentPart | |
'fine metal tire-sized big experiment/(ring)/coil/coils/(wire)/ | |
tube/tubes/frameworks/electromagnets/magnets/machine/machinery' | |
'ring of electromagnets' | |
"The focus of the experiment looks to be a ring of about a dozen | |
big devices that are probably electromagnets---tire-sized coils of | |
fine wire wrapped around and through frameworks of metal tubes, | |
with a tangle of thick black electrical cabling interconnecting | |
them and the other equipment. One of the cables leads to an | |
electrical junction box with a big green mushroom button protruding. | |
A glass platform that looks like a dinner plate is at the center | |
of the ring. " | |
isPlural = true | |
/* | |
* specifically show the contents of the platform as part of our | |
* Examine description - it doesn't normally show its contents as | |
* part of room or container descriptions, but we want to override | |
* that here to show the contents | |
*/ | |
examineStatus() | |
{ | |
/* specifically show the contents of the platform */ | |
"<.p>"; | |
quantumPlatform.examineListContents(); | |
} | |
; | |
+++ quantumPlatform: ExperimentPart, Surface | |
'thick round clear glass dinner platform/plate/pillar' 'glass platform' | |
"The platform looks to be clear glass. It's about the size and | |
shape of a dinner plate, and it's raised up a few inches above | |
the surface of the bench by a thick glass pillar. " | |
/* only allow one thing at a time on the platform */ | |
iobjFor(PutOn) { preCond { return inherited() + objEmpty; }} | |
/* only allow small objects */ | |
maxSingleBulk = 1 | |
/* | |
* don't show my contents in the room description; this is pretty | |
* deeply buried in a cluttered room, so it doesn't make sense to | |
* mention our contents until we're examined specifically | |
*/ | |
contentsListed = nil | |
; | |
++++ QuantumItem, Thing, Container 'clear plastic dice-o-matic/dome' | |
name = (described ? 'dice-o-matic' : 'clear plastic dome') | |
desc = "It's a dome of clear plastic with <q>Dice-O-Matic</q> | |
faintly inscribed on the top. Inside are two six-sided | |
dice. | |
<.p>You can't remember which one it is, but there's a | |
classic board game that came with the Dice-O-Matic. You | |
just push down on the dome, and a little spring-loaded | |
mechanism in the bottom tosses the dice into the air. " | |
/* we're transparent but unopenable */ | |
material = glass | |
isOpen = nil | |
/* don't show my contents in container/room listings */ | |
contentsListed = nil | |
dobjFor(Roll) asDobjFor(Push) | |
dobjFor(Push) | |
{ | |
verify() { } | |
action() | |
{ | |
/* select new values */ | |
die1 = rand(6) + 1; | |
die2 = rand(6) + 1; | |
/* check to see if we do quantum rolling */ | |
if (isIn(quantumPlatform) && quantumButton.isOn) | |
{ | |
"It's a little weird reaching into the shimmering | |
air over the platform; your hand seems to become | |
a bit of a blur. You push down on the dome---you | |
try to, at least; it's actually kind of hard to tell, | |
with everything getting blurrier and blurrier and | |
your hand tingling and feeling strangely disconnected | |
from your arm. | |
<.p>You snap your hand away from the Dice-O-Matic | |
and see just a blur of dice inside---not the kind of | |
blur you'd see if they were moving really fast, but | |
the kind you see when cross-eyed, or in a photograph | |
that's been exposed over and over. You can clearly | |
see dozens of different dice lying there, others | |
tumbling, others bouncing, all sharing the little | |
bubble and overlapping like ghosts. | |
<.p>The humming of the equipment starts dropping | |
in pitch like a turbine winding down, then ceases | |
entirely. The jumble of ghostly dice becomes | |
two ordinary dice showing <<die1>> and <<die2>>; | |
the change is sudden and undramatic, as though | |
your eyes just snapped into focus. "; | |
/* this immediately turns off the equipment */ | |
quantumButton.isOn = nil; | |
} | |
else | |
"You push down on the dome until it clicks, then | |
release. The dice pop into the air, tumble around | |
inside the dome for a moment, and land showing <<die1>> | |
and <<die2>>. "; | |
} | |
} | |
/* the numbers shown on my dice */ | |
die1 = 3 | |
die2 = 1 | |
/* | |
* Do not list my contents when I'm described - we show them | |
* specifically as part of the description, and they never change, | |
* so we don't need to list them separately here. | |
*/ | |
examineListContents() { } | |
; | |
+++++ Thing 'white six-sided pair/dice' 'dice' | |
"They're white six-sided dice like you'd find packaged | |
in any board game. They're currently showing <<location.die1>> | |
and <<location.die2>>. " | |
isPlural = true | |
/* to roll the dice, push on the dome */ | |
dobjFor(Roll) remapTo(Push, location) | |
; | |
++ researchReport: Readable | |
'light blue stamer\'s research report 6 paper/folder/draft' | |
name = (isRead ? 'research paper' : 'report folder') | |
desc = "It's a light blue report folder, with <q>DRAFT 6</q> | |
written across the cover. The report within looks to | |
be about thirty pages. " | |
/* make an ethical comment when first taken */ | |
moveInto(obj) | |
{ | |
/* rationalize it */ | |
if (!moved) | |
extraReport('You figure no one will need this today; you\'ll | |
just have to remember to return it later.<.p>'); | |
/* do the normal work */ | |
inherited(obj); | |
} | |
readDesc() | |
{ | |
local extraRef = nil; | |
"<q>Spin Decorrelation and Bulk Matter Decoherence | |
Isolation - DRAFT.</q> It seems to be a recent draft of | |
a research paper that Stamer's lab group is working on.<.p>"; | |
/* check our background knowledge */ | |
if (gRevealed('decoherence') | |
&& gRevealed('spin-decorr') | |
&& gRevealed('QM-intro')) | |
{ | |
/* we have the full background, so we can understand a lot */ | |
"Parts of the paper are well beyond your grasp of the | |
subject, but all of the background reading you've done | |
actually lets you follow a lot of the material. Stamer's | |
group is apparently working on a way to prevent a quantum | |
system from <q>decohering,</q> not only when it comes into | |
contact with its surroundings, but when parts of the system | |
itself interact with one another. According to the paper, | |
they've successfully suppressed decoherence for a matter | |
of seconds at a time throughout tens of grams of | |
material---apparently quite stunning results in a realm | |
more typically numbered in femtoseconds and picograms. | |
They hope to use this to create large-scale quantum | |
computers built out of ordinary components. | |
<.p>At the end are some bibliographic references, including | |
the <q>introductory</q> material you've already seen: "; | |
/* award some points for this */ | |
scoreMarker.awardPointsOnce(); | |
/* show the extra reference */ | |
extraRef = true; | |
} | |
else if (gRevealed('decoherence') | |
|| gRevealed('spin-decorr') | |
|| gRevealed('QM-intro')) | |
{ | |
/* some background - we understand a bit, but not all */ | |
"It helps a bit that you've been reading some background | |
material; at least you know what some of the words mean | |
now, and you can follow bits and pieces of the paper. You | |
can see it has a lot to do with quantum computation, but | |
you still can't figure out what it all means. You come | |
again to the references at the end, and scan the ones | |
called out as <q>introductory</q>: "; | |
} | |
else | |
{ | |
/* we have no background - we're basically baffled */ | |
"You make it about halfway into the abstract before you | |
realize you have no idea what it's about. You flip through | |
the paper a bit, but the only thing you really remember about | |
quantum physics is some stuff about semiconductor band-gaps | |
from EE 11. | |
<.p>At the back are a bunch of bibliographic references that | |
mostly look as intimidating as the paper itself, although a | |
couple are called out as <q>introductory</q>: "; | |
} | |
"a paper in the journal <i>Quantum Review Letters</i> number | |
70:11c, an article in <i>Science & Progress</i> magazine | |
number XLVI-3, and a chapter in a textbook, <i>Introductory | |
Quantum Physics</i> by Blömner. | |
<.reveal bibliography> "; | |
/* show the extra reference if appropriate */ | |
if (extraRef) | |
"<.p>You notice a reference to another article by Stamer's | |
group, this one published in <i>Quantum Review Letters</i> | |
number 73:9a.<.reveal understood-stamer-article> "; | |
} | |
/* | |
* Reveal one of the report references, then check for comprehension. | |
* Each time we read one of the introductory references, we'll call | |
* this to check to see if we've read enough background information. | |
* If so, we'll mention specifically that we should go back and read | |
* through the report again, since doing so reveals a key clue. | |
*/ | |
checkComprehension(topic) | |
{ | |
/* first, reveal the topic */ | |
gReveal(topic); | |
/* | |
* Now, if all intro knowledge has been revealed, mention that we | |
* now want to go back and read the report again. Don't do this | |
* if we've already gone back and read the report, though, for | |
* obvious reasons. | |
*/ | |
if (gRevealed('decoherence') | |
&& gRevealed('spin-decorr') | |
&& gRevealed('QM-intro') | |
&& !gRevealed('understood-stamer-article')) | |
"<.p>You feel like you're starting to catch on to some of | |
the ideas in Stamer's research report. You make a mental | |
note to go back and read the report again in more detail, | |
when you have the time. "; | |
} | |
/* points for understanding the paper */ | |
scoreMarker: Achievement { +5 "figuring out the research report" } | |
/* OPEN REPORT is just like reading it, as is LOOK IN REPORT */ | |
dobjFor(Open) asDobjFor(Read) | |
dobjFor(LookIn) asDobjFor(Read) | |
/* on reading, note that we've been read */ | |
dobjFor(Read) | |
{ | |
action() | |
{ | |
inherited(); | |
isRead = true; | |
} | |
} | |
isRead = nil | |
; | |
++ Decoration | |
'improvised signal electronic vacuum power stepping extra | |
piece/pieces/equipment/generators/oscilloscopes/meter/meters/ | |
instruments/pump/laser/lasers/motor/motors/supply/supplies/ | |
microscope/part/parts/panel/panels/innard/innards/device/devices' | |
'equipment' | |
"Most of the equipment around the lab is electronic---signal | |
generators, oscilloscopes, various meters and instruments, power | |
supplies. There's also a vacuum pump, a couple of lasers, stepping | |
motors, and a microscope. Many of the devices look like they've | |
been modified: panels are missing, exposing the innards of the | |
equipment, and extra parts have been added in and clamped on. " | |
isMassNoun = true | |
notImportantMsg = 'You don\'t want to disturb anything, since it\'s | |
probably all set up for an experiment. ' | |
/* | |
* This object represents equipment all over the room, including on | |
* the shelves. So, we're nominally in the room, on the shelves, | |
* and on the bench. (This is important mostly because it lets the | |
* player refer to us as EQUIPMENT ON SHELVES.) | |
*/ | |
isNominallyIn(obj) { return inherited(obj) || obj == blShelves; } | |
; | |
+ Fixture 'back north n wall*walls' 'north wall' | |
"A set of metal shelves are fastened to the north wall. " | |
; | |
++ blShelves: Fixture, Surface 'metal shelf/shelves' 'shelves' | |
"The metal shelves are fastened to the north wall, and run all | |
the way up to the low ceiling. Like all of the other horizontal | |
surfaces in the lab, the shelves are packed with equipment. " | |
isPlural = true | |
/* | |
* Use a special prefix message for listing our contents (either by | |
* EXAMINE or LOOK IN) - this lets us add the extra equipment we want | |
* to mention but which we don't really implement. | |
*/ | |
descContentsLister: surfaceDescContentsLister { | |
showListPrefixWide(itemCount, pov, parent) | |
{ "Among the piles of equipment, you notice "; } | |
} | |
lookInLister: surfaceLookInLister { | |
showListPrefixWide(itemCount, pov, parent) | |
{ "Among the piles of equipment, you notice "; } | |
} | |
/* don't show our contents with the room message */ | |
contentsListed = nil | |
; | |
/* | |
* A mix-in class for the special equipment items on the shelf in the | |
* lab. When we first move one of these items, we'll discover the | |
* entangled camera. | |
*/ | |
class LabShelfItem: object | |
/* when we first move it, find the camera if we haven't already */ | |
moveInto(obj) | |
{ | |
/* do the normal work first */ | |
inherited(obj); | |
/* | |
* Check for finding the camera. Do this after we've moved the | |
* object, in case there are any side effects of moving it; the | |
* disentangling comes essentially after we've done most of the | |
* normal moving already, so it works better if this comes after | |
* any messages generated while moving the object. | |
*/ | |
if (!spy9.isFound) | |
{ | |
local moveObj = self; | |
gMessageParams(moveObj); | |
extraReport('You start to lift {the moveObj/him} off the shelf, | |
but {it/he} get{s} stuck on something. You feel around | |
the sides and find some tangled cords, and finally manage | |
to get {the/him} free. As you pull {it/him} away from | |
the shelf, you notice a small white ball, connected | |
to a blue cable, dropping onto the shelf. <.p>'); | |
/* mark the camera as found, and move it here */ | |
spy9.isFound = true; | |
spy9.makePresent(); | |
} | |
/* if we've acquired all of the needed devices, award some points */ | |
if (!blackBox.equipHintNeeded) | |
scoreMarker.awardPointsOnce(); | |
} | |
scoreMarker: Achievement { +2 "gathering the test equipment" } | |
; | |
/* | |
* A class for the testing gear (oscilloscope, signal generator). These | |
* objects can be attached to testable equipment; they have to be | |
* located right next to the equipment being tested (i.e., in the same | |
* container), which we enforce by making these NearbyAttachable | |
* objects. | |
*/ | |
class TestGear: PlugAttachable, NearbyAttachable | |
/* we can attach to anything that's a test-gear-attachable object */ | |
canAttachTo(obj) | |
{ | |
return obj.ofKind(TestGearAttachable) || obj.ofKind(TestGear); | |
} | |
explainCannotAttachTo(other) | |
{ | |
gMessageParams(self, other); | |
"{The other/he} do{es}n't seem to have any suitable | |
place to connect {the self/him}. "; | |
} | |
/* | |
* We can only be attached to one thing at once, so add a | |
* precondition to ATTACH TO requiring that we're not attached to | |
* anything. | |
*/ | |
dobjFor(AttachTo) { preCond = (inherited() + objNotAttached) } | |
iobjFor(AttachTo) { preCond = (inherited() + objNotAttached) } | |
/* for electrical safety, turn off the test equipment before unplugging */ | |
dobjFor(Detach) { preCond = (inherited() + objTurnedOff) } | |
dobjFor(DetachFrom) { preCond = (inherited() + objTurnedOff) } | |
iobjFor(DetachFrom) { preCond = (inherited() + objTurnedOff) } | |
/* USE ON is the same as ATTACH TO for us */ | |
dobjFor(UseOn) remapTo(AttachTo, self, IndirectObject) | |
iobjFor(UseOn) remapTo(AttachTo, DirectObject, self) | |
/* TEST obj WITH gear is the same as ATTACH gear TO obj */ | |
iobjFor(TestWith) remapTo(AttachTo, self, DirectObject) | |
/* | |
* Get the equipment we're attached to. We can only be attached to | |
* one thing at a time, other than our permanent attachments, so this | |
* returns either nil or the single piece of equipment we're probing. | |
*/ | |
getAttachedEquipment() | |
{ | |
local lst; | |
/* get the list of non-permanent attachments */ | |
lst = attachedObjects.subset({x: !isPermanentlyAttachedTo(x)}); | |
/* | |
* there can be no more than one, so if we have anything in our | |
* list, return the first one; if not, return nil | |
*/ | |
return (lst.length() != 0 ? lst[1] : nil); | |
} | |
/* show what happens when we probe with the oscilloscope */ | |
probeWithScope() { "The oscilloscope screen just shows a flat line. "; } | |
/* show what happens we we turn on the signal generator attached here */ | |
turnOnSignalGen() { } | |
; | |
/* | |
* Our test gear tends to have attached connectors. We include these for | |
* completeness; they really don't do much apart from reflect commands | |
* back to the test gear container. | |
*/ | |
class PluggableComponent: PlugAttachable, Component | |
/* attaching the probe really attaches the containing test gear */ | |
dobjFor(AttachTo) remapTo(AttachTo, location, IndirectObject) | |
iobjFor(AttachTo) remapTo(AttachTo, DirectObject, location) | |
/* using the probe on something attaches it */ | |
dobjFor(UseOn) remapTo(UseOn, location, IndirectObject) | |
iobjFor(UseOn) remapTo(UseOn, DirectObject, location) | |
/* defer to the parent for status, for the attachment list */ | |
examineStatus() { location.examineStatus(); } | |
; | |
/* | |
* a probe for a piece of test gear - this is a test gear component | |
* that's also a permanent attachment child | |
*/ | |
class TestGearProbe: PermanentAttachmentChild, PluggableComponent | |
/* putting the probe on something is equivalent to attaching it */ | |
dobjFor(PutOn) remapTo(AttachTo, location, IndirectObject) | |
; | |
/* | |
* A class representing objects that can attach to the testing gear | |
* (such as the oscilloscope and the signal generator). These are all | |
* NearbyAttachments, because the test gear has to be placed right next | |
* to them (i.e., in the same container) to be attached. Apart from | |
* making the object attachable, this is essentially just a marker class | |
* that we recognize in the test gear, to make sure that the object is | |
* something the scope can attach to. | |
*/ | |
class TestGearAttachable: NearbyAttachable | |
/* show what happens when we probe with the oscilloscope */ | |
probeWithScope() { "The oscilloscope screen just shows a flat line. "; } | |
/* show what happens when we attach the signal generator */ | |
probeWithSignalGen() { } | |
/* show what happens we we turn on the signal generator attached here */ | |
turnOnSignalGen() { } | |
/* testing this kind of thing is likely */ | |
dobjFor(Test) { verify() { } } | |
/* | |
* These objects are always the "major" items when attached to test | |
* gear: "the oscilloscope is attached to the box," not vice versa. | |
*/ | |
isMajorItemFor(obj) { return obj.ofKind(TestGear); } | |
; | |
/* a transient object to keep track of our scope notes per session */ | |
+++ transient oscilloscopeNoteTracker: object | |
noteCount = 0 | |
; | |
+++ oscilloscope: LabShelfItem, TestGear, OnOffControl | |
'portable solid-state oscilloscope/scope' 'oscilloscope' | |
"This is a relatively small solid-state model, easily portable. | |
On the top are a display screen and the controls, and a probe | |
is attached with a coaxial cable. <<screenDesc>> " | |
/* this item is a little larger than the default */ | |
bulk = 2 | |
/* make it a precondition of attaching that we're turned on */ | |
dobjFor(AttachTo) { preCond = (inherited() + objTurnedOn) } | |
iobjFor(AttachTo) { preCond = (inherited() + objTurnedOn) } | |
/* handle attaching to another object */ | |
handleAttach(other) | |
{ | |
/* | |
* If they're saying something about PLUG or ATTACH, note that | |
* we can't actually attach the scope to anything, to avoid | |
* confusion. To avoid being annoying, have this note show up | |
* only a few times each session. | |
*/ | |
if (rexSearch('attach|plug', gAction.getOrigText()) != nil | |
&& oscilloscopeNoteTracker.noteCount++ < 3) | |
"(Note that you can't permanently attach the scope to | |
anything, since its needle tip only allows momentary | |
contact with the circuit you're probing.)<.p>"; | |
/* let the other object tell us what happens */ | |
other.probeWithScope(); | |
/* | |
* We don't actually stay attached; attaching to us is just | |
* momentary probing. So mark us as no longer attached. | |
*/ | |
detachFrom(other); | |
} | |
screenDesc() | |
{ | |
if (!isOn) | |
"The scope is currently turned off. "; | |
else | |
"The display screen shows a flat line. "; | |
} | |
dobjFor(TurnOn) | |
{ | |
action() | |
{ | |
/* | |
* announce this only if we're an explicit action; turning | |
* the scope on is a pretty simple action, so it's not worth | |
* a mention when it's done incidentally to another action | |
*/ | |
if (!gAction.isImplicit) | |
{ | |
"You turn on the oscilloscope, and it comes to life | |
almost instantly. "; | |
if (timesOn++ == 0) | |
"You adjust the controls a bit and get a nice | |
flat line; it looks like it's working properly, | |
at least as far as you can tell without connecting | |
it to something. "; | |
} | |
/* mark it as on */ | |
makeOn(true); | |
} | |
} | |
/* number of times we've turned it on */ | |
timesOn = 0 | |
dobjFor(TurnOff) | |
{ | |
action() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* | |
* if we're not running as an implied action, mention that | |
* the screen goes dark | |
*/ | |
if (!gAction.isImplicit) | |
"You turn off the scope, and the screen goes dark. "; | |
} | |
} | |
; | |
++++ Component | |
'(oscilloscope) (scope) control/controls' | |
'oscilloscope controls' | |
"The controls let you turn the scope on and off, and make | |
various adjustments to the display, such as the time and voltage | |
scales and the DC offset. " | |
dobjFor(Push) | |
{ | |
verify() { } | |
action() { "You don't need to make any adjustments to the | |
oscilloscope settings right now. "; } | |
} | |
dobjFor(Pull) asDobjFor(Push) | |
dobjFor(Turn) asDobjFor(Push) | |
dobjFor(Move) asDobjFor(Push) | |
; | |
++++ Component | |
'(oscilloscope) (scope) display screen/display' 'oscilloscope display' | |
desc() | |
{ | |
if (location.isOn) | |
location.screenDesc(); | |
else | |
"The screen is blank. "; | |
} | |
isOn = nil | |
; | |
++++ ComponentDeferrer, TestGearProbe | |
'(oscilloscope) (scope) long metal coaxial insulated sharp pointed needle | |
grip/probe/cable/pin/(tip)' | |
'oscilloscope probe' | |
"The probe consists of an insulated grip with a two-inch metal pin | |
protruding. The pin has a sharp, pointed tip to allow precise | |
positioning when working with small circuitry. The probe is | |
attached to the oscilliscope with a coaxial cable. " | |
; | |
+++ signalGen: LabShelfItem, TestGear, OnOffControl | |
'signal function generator' 'signal generator' | |
"The signal generator lets you feed different waveforms into a | |
circuit, to test how the circuit behaves electrically. This is | |
a compact model, the size of a hardback book. It has a set of | |
buttons on the front, an amplitude knob, and a connector. It's | |
currently turned <<onDesc>>. " | |
/* this item is a little larger than the default */ | |
bulk = 2 | |
probeWithScope() | |
{ | |
if (isOn) | |
"You run through a few different waveform patterns on the | |
signal generator to check that they're all displayed properly | |
on the scope. Everything looks fine. "; | |
else | |
inherited(); | |
} | |
handleAttach(other) | |
{ | |
/* let the other equipment handle it */ | |
other.probeWithSignalGen(); | |
} | |
killDaemon() | |
{ | |
/* kill the data entry daemon if it's running */ | |
eventManager.removeMatchingEvents(blackBox, &digitEntryDaemon); | |
} | |
handleDetach(other) | |
{ | |
/* on detaching, kill the data entry daemon */ | |
killDaemon(); | |
} | |
dobjFor(TurnOn) | |
{ | |
action() | |
{ | |
local equip = getAttachedEquipment(); | |
/* switch it on */ | |
"You switch on the signal generator. "; | |
makeOn(true); | |
/* if we're attached to anything, let it know */ | |
if (equip != nil) | |
equip.turnOnSignalGen(); | |
} | |
} | |
dobjFor(TypeLiteralOn) | |
{ | |
verify() { logicalRank(50, 'not a keypad'); } | |
action() | |
{ | |
"The signal generator doesn't have anything like a keypad, | |
but it does have a knob you can turn to different settings. "; | |
} | |
} | |
dobjFor(EnterOn) asDobjFor(TypeLiteralOn) | |
dobjFor(TypeOn) asDobjFor(TypeLiteralOn) | |
; | |
++++ signalGenKnob: NumberedDial, Component | |
'(signal) (generator) amplitude knob/dial' 'amplitude knob' | |
"It's a knob numbered from 0 to 9. It adjusts the voltage level | |
of the signal being generated, within the range selected by the | |
current waveform parameters. It's currently set to <<curSetting>>. " | |
curSetting = '3' | |
minSetting = 0 | |
maxSetting = 9 | |
/* handle ENTER ON as TURN TO */ | |
dobjFor(EnterOn) asDobjFor(TurnTo) | |
; | |
++++ Component '(signal) (generator) button/buttons/set' | |
'signal generator buttons' | |
"The buttons let you select the shape of the waveform being | |
generated and adjust its parameters. " | |
dobjFor(Push) | |
{ | |
verify() | |
{ | |
/* | |
* we never really need to push these buttons, so downgrade | |
* them in the logical rankings | |
*/ | |
logicalRank(50, 'decoration-only'); | |
} | |
action() { "There are no changes you need to make to the | |
signal generator output right now. "; } | |
} | |
; | |
++++ TestGearProbe | |
'(signal) (generator) connector/lead/leads' | |
'signal generator connector' | |
"The connector is a simple set of leads connected to the | |
signal generator. " | |
; | |
+++ spy9: PresentLater, PermanentAttachment, Immovable | |
'small white plastic spy-9 camera/ball' | |
name = (described ? 'SPY-9 camera' : 'small white ball') | |
shortName = (described ? 'camera' : 'white plastic ball') | |
desc = "It's a small white ball, about the size of a golf ball | |
but made of smooth plastic. A blue network cable comes out | |
the back, and << | |
described | |
? "a tiny lens is in front. You recognize it as a | |
SPY-9 camera" | |
: "there's a small clear spot in front---a lens. You | |
suddenly realize what this is: it's a SPY-9 camera" | |
>>, that heinous product marketed with a ceaseless barrage | |
of pop-up internet ads, designed for such virtuous uses as | |
spying on your nanny, spouse, employees, boss, or the women's | |
locker room. " | |
/* | |
* it's immovable, but this isn't obvious looking at it, so list it | |
* as though it were portable | |
*/ | |
isListedInContents = true | |
/* we haven't found it yet */ | |
isFound = nil | |
/* we can't be moved because of the cable tethering us to the wall */ | |
cannotTakeMsg = 'You can\'t get {the dobj/him} free of the cable. ' | |
cannotMoveMsg = 'The cable doesn\'t let you move {the dobj/him} more | |
than a few inches. ' | |
cannotPutMsg = (cannotTakeMsg) | |
/* we can't detach from the cable */ | |
cannotDetachMsg(obj) | |
{ | |
return 'There\'s no obvious way to disconnect the cable; it seems | |
to be an integral part of ' + theNameObj + '. '; | |
} | |
/* pulling on the camera is just like pulling on the wire */ | |
dobjFor(Pull) remapTo(Pull, spy9Wire) | |
; | |
++++ Component 'clear tiny (camera) (spy-9) spot/lens/(camera)' 'spy-9 lens' | |
"It's the tiny lens of the camera. " | |
; | |
++++ spy9Wire: PermanentAttachment, Immovable | |
'(spy-9) (camera) blue network cable/wire/insulation' 'network cable' | |
"It looks like a fairly standard blue network cable. It seems to | |
be hard-wired to the <<location.shortName>>, and at the other end | |
feeds into a wall plate. Strangely, it runs directly through the | |
wall plate---there's no plug. This cable probably runs all the way | |
to the wiring closet down in the sub-basement. <<tagNote>> " | |
tagNote() | |
{ | |
/* add a note about the tag if the tag has been exposed */ | |
if (spy9Tag.isIn(self)) | |
"A bright green tag, marked with the digits | |
<<infoKeys.spy9JobNumber>>, is wrapped around the wire | |
near the wall plate."; | |
} | |
dobjFor(Pull) | |
{ | |
verify() { } | |
action() | |
{ | |
if (bwirBlueWire.location == nil) | |
{ | |
"You pull the wire away from the wall plate, not hard | |
enough to break it but enough to take up any slack in | |
the behind-the-wall wiring. You pull it a couple of | |
inches, and a little paper tag wrapped around the | |
wire pops out from behind the wall plate. You get | |
a couple more inches of wire before the runs out of | |
slack. "; | |
/* reveal the tag */ | |
spy9Tag.makePresent(); | |
/* reveal the wire in the sub-basement */ | |
bwirBlueWire.makePresent(); | |
bwirHole.makePresent(); | |
} | |
else | |
"You give it a tug, but there's no more play in the wire. "; | |
} | |
} | |
cannotTakeMsg = 'The cable feeds through the wall plate; there\'s no | |
obvious way to remove it. ' | |
cannotMoveMsg = 'There\'s very little play in the cable. ' | |
cannotPutMsg = (cannotTakeMsg) | |
attachedObjects = [spy9, spy9WallPlate] | |
cannotDetachMsg(obj) | |
{ | |
return (obj == nil | |
? 'The cable seems to be permanently attached. ' | |
: obj.cannotDetachMsg(self)); | |
} | |
dobjFor(Follow) | |
{ | |
verify() { logicalRank(50, 'not followable'); } | |
action() { "You can only follow it as far as the wall plate. "; } | |
} | |
; | |
+++++ spy9Tag: PresentLater, PermanentAttachment, CustomImmovable, Readable | |
'bright green little small paper tag' 'paper tag' | |
"The bright green tag is marked with the digits | |
<<infoKeys.spy9JobNumber>>. You can also make out faint, tiny | |
type on the edge reading <q>NIC.</q> The tag is folded over the | |
wire and glued in place with a strong adhesive. " | |
attachedObjects = [spy9Wire] | |
cannotDetachMsg(obj) { return cannotTakeMsg; } | |
cannotTakeMsg = 'The tag seems to be thoroughly stuck to the wire. ' | |
; | |
++++ spy9WallPlate: PermanentAttachment, Fixture | |
'wall plate' 'wall plate' | |
"The blue cable feeds into the wall plate without any plug or | |
connector. Someone must have set this up as part of the room | |
wiring for some reason---the cable must run all the way down to | |
the wiring closet in the sub-basement. " | |
cannotDetachMsg(obj) { return 'The cable feeds into the wall plate | |
without any obvious plug, so there\'s no way to detach it. '; } | |
dobjFor(LookBehind) { action() { "You can't see behind the plate | |
without removing it, but there's no obvious way to do that. "; } } | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Bridge basement hall - west end | |
*/ | |
bridgeBasementWest: Room 'Basement Hall West' | |
'the west basement hallway' 'hallway' | |
"The low, dimly-lit hallway ends here and continues to the east. | |
On the south side of the hall is a door labeled 023, and on the | |
north side another labeled 024. At the west end of the hall, | |
a very narrow, steep stairway leads down into shadow. " | |
vocabWords = 'basement hall/hallway' | |
east = bridgeBasementEast | |
south = door023 | |
north = door024 | |
west asExit(down) | |
down = bbwStair | |
; | |
+ bbwStair: StairwayDown ->bsubStair | |
'very narrow steep concrete stairs/stairway/shaft' 'stairs' | |
"The concrete stairway is so narrow it almost looks like it's | |
descending straight down a shaft. " | |
isPlural = true | |
; | |
+ SimpleOdor | |
desc = "The air here is damp and musty. " | |
; | |
+ door023: AlwaysLockedDoor 'south s 023 door*doors' 'door 023' | |
"It's labeled with room number 023. " | |
theName = 'door 023' | |
; | |
+ door024: AlwaysLockedDoor 'north n 024 door*doors' 'door 024' | |
"The door is labeled with room number 024. " | |
theName = 'door 024' | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Bridge sub-basement hall | |
*/ | |
bridgeSubHall: Room 'Sub-basement Hall' 'the sub-basement hall' 'hallway' | |
"The ceiling in this short hallway is too low for you to | |
stand up straight, and the walls are just unpainted concrete. | |
It would be too cruel to put anyone's office down here---even | |
grad students---so this sub-basement is used mainly for storage | |
and the building's systems. The hall is piled with junk, | |
evidently overflow from the storage rooms. Faded lettering on | |
the north door reads <q>Electro-Mechanical,</q> and the east | |
and south doors are unlabeled. To the west, a steep stairway | |
leads up. " | |
vocabWords = 'sub-basement subbasement hall/hallway' | |
up = bsubStair | |
west asExit(up) | |
north = bsubNorthDoor | |
east = bsubEastDoor | |
south = bsubSouthDoor | |
roomParts = [defaultFloor] | |
; | |
+ Fixture 'low ceiling' 'ceiling' "The ceiling is so low you can't | |
stand up straight. " | |
; | |
+ Fixture 'north south east west n s e w concrete wall/walls' 'wall' | |
"The walls here are unpainted concrete. " | |
; | |
+ Decoration | |
'leftover construction | |
materials/junk/pile/piles/sheetrock/piece/pieces/wood/pipe/pipes/lengths' | |
'junk' | |
"It looks like leftover construction materials, mostly: pieces of | |
sheetrock and wood, lengths of pipe, that sort of thing. " | |
isMassNoun = true | |
dobjFor(Take) | |
{ | |
verify() { } | |
action() { "You take a quick look through the | |
junk to see if there's anything useful you could borrow, | |
but you find nothing interesting. "; } | |
} | |
dobjFor(Search) asDobjFor(Take) | |
; | |
+ bsubStair: StairwayUp 'very narrow steep uneven concrete stairs/stairway' | |
'stairs' | |
"The uneven concrete stairs lead up. " | |
isPlural = true | |
; | |
+ SimpleOdor | |
desc = "The air here is dry and stale. " | |
; | |
+ bsubNorthDoor: Door ->bwirDoor | |
'north n electro-mechanical door' 'north door' | |
"Faded lettering on the door reads <q>Electro-Mechanical.</q> " | |
; | |
++ Component 'hand-painted faded lettering' 'faded lettering' | |
"<q>Electro-Mechanical.</q> The faded lettering looks | |
like it was hand-painted a long time ago. " | |
; | |
+ bsubEastDoor: Door ->bstorWestDoor 'east e door' 'east door' | |
"The door is unmarked. " | |
; | |
+ bsubSouthDoor: AlwaysLockedDoor 'south s door' 'south door' | |
"The door is unmarked. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Bridge sub-basement wiring closet | |
*/ | |
bridgeWiringCloset: Room 'Wiring Closet' 'the wiring closet' | |
"When you were a student, you helped wire a couple of the Bridge | |
labs for a primitive computer network, which involved pulling a | |
bunch of garden-hose-sized cables through the walls and connecting | |
them to a bank of transponder boxes in this wiring closet. All of | |
that has been long since ripped out and replaced with more modern | |
phone-style wiring. | |
<.p>A door leads south, out to the hall. " | |
vocabWords = 'wiring closet' | |
south = bwirDoor | |
out asExit(south) | |
roomParts = static [defaultFloor] | |
; | |
+ bwirDoor: Door 'south s door' 'door' "The door leads out to the south. " | |
; | |
+ bsubWiring: Immovable, Consultable | |
'blue red white yellow orange green | |
modern phone phone-style network wire/wires/wiring/mass' 'wiring' | |
"There's a huge mass of wires here, mostly along the north wall, | |
coming into the room through small openings in the ceiling and | |
walls. The wires are in numerous colors: blue, red, white, yellow, | |
orange, green. << | |
bwirBlueWire.location != nil | |
? "One blue wire stands out, because it's been pulled tight---you | |
can see it's the one you tugged on upstairs. " : "" >> " | |
isMassNoun = true | |
lookInDesc() { searchResponse(); } | |
searchResponse() | |
{ | |
"There are so many wires, you really don't have any chance | |
of finding a specific one without something that would | |
really set it apart. "; | |
/* if we haven't found the blue wire yet, hint how we could */ | |
if (bwirBlueWire.location == nil) | |
"Back when you were helping with the Bridge wiring project, | |
for example, the easiest way of sorting out what went where | |
was to have someone upstairs give their end of a wire a good | |
tug, and see which one down here moved. "; | |
else | |
"You can pick out the blue wire you pulled on, though, | |
since it's the one that's been pulled tight enough to | |
stand out in front of the others. "; | |
} | |
dobjFor(Follow) asDobjFor(LookIn) | |
dobjFor(Pull) | |
{ | |
verify() { } | |
action() { "Better not; you don't want to run the risk of | |
accidentally disconnecting something. "; } | |
} | |
dobjFor(Move) asDobjFor(Pull) | |
dobjFor(Push) asDobjFor(Pull) | |
isNominallyIn(obj) | |
{ return isIn(obj) || obj == bwirWalls || obj == bwirOpenings; } | |
; | |
++ DefaultConsultTopic | |
topicResponse = (location.searchResponse()) | |
; | |
+ bwirBlueWire: PresentLater, Immovable | |
'blue network pulled tight wire/cable/insulation' | |
'blue cable' | |
"It's a blue network cable, pulled tight so that it stands | |
by itself in front of the mass of wire. | |
Like many of the other wires, it threads in through an opening | |
in the ceiling. You trace the wire through the mass of | |
cabling to see where it's connected, and find that it disappears | |
into a tiny hole drilled in the north wall. | |
<<traceIntoTunnel()>> " | |
/* trace the wire into the steam tunnels */ | |
traceIntoTunnel() | |
{ | |
/* if we haven't already done so, reveal the tunnel-side wiring */ | |
if (!traceDone) | |
{ | |
/* move the steam tunnel wires into position */ | |
PresentLater.makePresentByKey('stWire'); | |
/* no need to do this again */ | |
traceDone = true; | |
} | |
} | |
/* flag: we've traced the wire into the tunnel */ | |
traceDone = nil | |
/* | |
* If we find a resolve list that includes both this wire and the | |
* bunch-of-wires object, throw out the generic object, unless they | |
* don't call us the 'blue' wire. | |
*/ | |
filterResolveList(lst, action, whichObj, np, requiredNum) | |
{ | |
local idx; | |
/* check for the generic wire object */ | |
if ((idx = lst.indexWhich({x: x.obj_ == bsubWiring})) != nil) | |
{ | |
/* | |
* if they didn't say 'blue', drop me; otherwise, drop the | |
* generic wire object | |
*/ | |
if (np.getOrigTokenList() | |
.indexWhich({x: getTokVal(x) == 'blue'}) != nil) | |
{ | |
/* they said 'blue' - throw out the generic wiring */ | |
lst = lst.removeElementAt(idx); | |
} | |
else | |
{ | |
/* no 'blue' - throw me out */ | |
idx = lst.indexWhich({x: x.obj_ == self}); | |
if (idx != nil) | |
lst = lst.removeElementAt(idx); | |
} | |
} | |
/* return the new list */ | |
return lst; | |
} | |
dobjFor(Pull) | |
{ | |
verify() { } | |
action() { "Better not; you might lose it in the crowd if | |
you added the slack back into it again. "; } | |
} | |
isNominallyIn(obj) | |
{ | |
return isIn(obj) || obj == bwirWalls || obj == bwirOpenings | |
|| obj == bwirHole; | |
} | |
; | |
+ bwirWalls: Fixture | |
'north south east west n s e w wall/walls/ceiling' 'ceiling' | |
"A lot of wires are threaded into the room through openings in | |
the walls and ceiling. " | |
; | |
++ bwirOpenings: Fixture 'small openings' 'openings' | |
"The openings lead into conduits running through the walls, | |
providing a wiring path from labs from labs on other floors | |
to this wiring closet. All of the openings are jam-packed with wires. " | |
isPlural = true | |
lookInDesc = "The openings are packed with wires. " | |
; | |
++ bwirHole: PresentLater, Fixture 'small tiny drill-hole/hole' 'drill-hole' | |
"It's a tiny hole drilled in the north wall, seemingly deliberately | |
concealed behind the mass of wiring. A single blue wire is | |
threaded through the hole. " | |
lookInDesc = "A single blue wire is threaded into the hole. " | |
dobjFor(LookThrough) { action() { "The hole is too small to see | |
anything on the other side. "; } } | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Sub-basement storage room | |
*/ | |
bridgeStorage: Room 'Storage Room' 'the storage room' | |
"This is a huge storage area, mostly for old furniture: the | |
room is stacked with desks, file cabinets, bookcases. Cardboard | |
boxes are also piled here and there, and a bunch of scrap | |
wood is leaning against the wall in the northwest corner<< | |
bstorNorthDoor.isIn(self) | |
? bstorWood.pathCleared | |
? ", next to door leading north" | |
: ", in front of a door leading north" | |
: " of the room" | |
>>. A door leads out to the west. " | |
vocabWords = 'storage room' | |
west = bstorWestDoor | |
out asExit(west) | |
; | |
+ bstorWestDoor: Door 'west w door' 'west door' "The door leads west. " | |
; | |
+ bstorNorthDoor: PresentLater, Door | |
'north n door' 'north door' "The door leads north. " | |
/* find the door */ | |
makeFound() | |
{ | |
if (location == nil) | |
{ | |
/* move me into my location */ | |
makePresent(); | |
/* set up a north travel link and a northwest alias */ | |
bridgeStorage.north = self; | |
bridgeStorage.northwest = createUnlistedProxy(); | |
} | |
} | |
dobjFor(Open) | |
{ | |
/* we can't open the door until the path has been cleared */ | |
check() | |
{ | |
if (!bstorWood.pathCleared) | |
{ | |
"You can't get the door open with all the sheets of | |
scrap wood in the way. "; | |
exit; | |
} | |
} | |
} | |
; | |
+ bstorWood: CustomImmovable | |
'large scrap bunch/piece/pieces/plywood/wood/sheet/sheets' 'scrap wood' | |
desc() | |
{ | |
"The wood looks like assorted leftovers from construction | |
projects, mostly sheets of plywood around six feet by eight | |
feet, leaning against the wall. "; | |
if (!pathCleared) | |
{ | |
"On closer inspection, you notice that the wood is stacked | |
in front of a door leading north. "; | |
bstorNorthDoor.makeFound(); | |
} | |
} | |
isMassNoun = true | |
cannotTakeMsg = 'The sheets of wood are too large and heavy to | |
carry; at most you could move them a few feet. ' | |
/* flag: a path has been cleared to the door */ | |
pathCleared = nil | |
dobjFor(LookBehind) | |
{ | |
action() | |
{ | |
if (!pathCleared) | |
{ | |
"You notice that the wood is stacked in front of a door | |
leading north. "; | |
bstorNorthDoor.makeFound(); | |
} | |
else | |
inherited(); | |
} | |
} | |
dobjFor(Move) | |
{ | |
verify() { nonObvious; } | |
action() | |
{ | |
if (!pathCleared) | |
{ | |
"You manage to shove the pile of wood a couple of | |
feet over, << | |
bstorNorthDoor.location != nil | |
? "clearing the way to the door. " | |
: "which reveals a door leading north. | |
You move the wood far enough to clear the way to | |
the door. " | |
>>"; | |
pathCleared = true; | |
bstorNorthDoor.makeFound(); | |
} | |
else | |
"There's no room to move it any further. "; | |
} | |
} | |
dobjFor(Push) asDobjFor(Move) | |
dobjFor(Pull) asDobjFor(Move) | |
dobjFor(PushTravel) asDobjFor(Move) | |
dobjFor(Remove) asDobjFor(Move) | |
; | |
+ Decoration | |
'file furniture/desk/desks/cabinets/bookcases/pile/piles/stack/stacks' | |
'furniture' | |
"The furniture is stacked up two or three high in places and packed | |
together without a lot of leftover space. " | |
isMassNoun = true | |
lookInDesc = "You look look through some of the piles, but | |
it's just old furniture; you find nothing interesting. " | |
dobjFor(Search) asDobjFor(LookIn) | |
notImportantMsg = 'The furniture is all packed in tightly; it | |
would take a huge effort to do anything with it. ' | |
; | |
+ Decoration | |
'cardboard box/boxes' 'cardboard boxes' | |
"They look like they've been here quite a while. They're all | |
sealed up, and there's no indication what's inside. " | |
isPlural = true | |
lookInDesc = "They're all sealed up; you wouldn't want to disturb them. " | |
dobjFor(Open) asDobjFor(LookIn) | |
dobjFor(Search) asDobjFor(LookIn) | |
dobjFor(Read) | |
{ | |
verify() { } | |
action() { "The boxes are unlabeled. "; } | |
} | |
dobjFor(Take) | |
{ | |
verify() { } | |
action() { "There are far too many boxes to carry around. "; } | |
} | |
dobjFor(Move) | |
{ | |
verify() { } | |
action() { "There are so many boxes that all you can do is | |
shift them around a little. "; } | |
} | |
dobjFor(Pull) asDobjFor(Move) | |
dobjFor(Push) asDobjFor(Move) | |
dobjFor(PushTravel) asDobjFor(Move) | |
notImportantMsg = 'You really shouldn\'t disturb the boxes. ' | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* A class for steam tunnel locations | |
*/ | |
class SteamTunnelRoom: Room | |
atmosphereList: ShuffledEventList { [ | |
'A tapping noise echoes down one of the pipes for a few | |
moments, then stops. ', | |
'A deep rumbling reverberates briefly through the tunnel. ', | |
'A wave of hot, dry air blows past. ', | |
'One of the pipes makes a sudden loud hissing noise. ', | |
'A distant rumbling, like the sound of a big truck, | |
comes closer and closer until you\'re sure one of the pipes | |
is about to burst, but it goes right past and fades into | |
the distance. ', | |
'One of the pipes vibrates visibly for a few moments. ', | |
'It seems to be getting a little warmer. ', | |
'A sharp clang sounds right beside you, as though someone | |
smacked one of the pipes with a wrench. ', | |
'Something creaks and groans overhead. '] | |
eventPercent = 80 | |
eventReduceAfter = 5 | |
eventReduceTo = 60 | |
} | |
name = 'steam tunnel' | |
vocabWords = 'steam tunnel' | |
/* what we see when we look behind the steam pipes */ | |
lookBehindPipes() { "You give the pipes a cursory examination, | |
but there are a lot of them packed closely together---it'd | |
be easy to miss something unless you knew exactly where | |
to look. "; } | |
; | |
MultiLoc, SimpleOdor | |
initialLocationClass = SteamTunnelRoom | |
desc = "The air is very warm and dry, and smells of dust and metal. " | |
; | |
MultiInstance | |
initialLocationClass = SteamTunnelRoom | |
instanceObject: Fixture { | |
'steam two-inch big huge pvc pipe/pipes/conduits/plumbing' | |
'steam pipes' | |
"Numerous pipes and conduits run along the walls, ranging | |
from ordinary two-inch PVC plumbing like you'd find in a house, | |
to huge, industrial-strength pipes two feet in diameter. " | |
isPlural = true | |
dobjFor(LookBehind) | |
{ | |
action() { gActor.location.getOutermostRoom().lookBehindPipes(); } | |
} | |
dobjFor(LookUnder) asDobjFor(LookBehind) | |
dobjFor(Search) asDobjFor(LookBehind) | |
} | |
; | |
+ SimpleNoise | |
initialLocationClass = SteamTunnelRoom | |
desc = "Hissing, rumbling, and tapping noises echo intermittently | |
through the pipes. " | |
; | |
MultiInstance | |
initialLocationClass = SteamTunnelRoom | |
instanceObject: Decoration { | |
'bare low-wattage light protective wire | |
lightbulb/bulb/bulbs/cage/cages' | |
'light bulb' | |
"The bulbs emit a feeble yellow light from within their | |
protective wire cages. " | |
notImportantMsg = 'The light bulbs aren\'t important (apart | |
from helping you see where you\'re going, of course). ' | |
dobjFor(Take) | |
{ | |
verify() { } | |
action() { "How many Techers does it take to change a light | |
bulb? Now that you think about it, you've never heard | |
a Caltech light bulb joke, amazingly enough. In any case, | |
these light bulbs aren't important. "; } | |
} | |
dobjFor(Unscrew) asDobjFor(Take) | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Steam tunnel near storage room | |
*/ | |
steamTunnel1: SteamTunnelRoom 'Steam Tunnel at Door' | |
'the steam tunnel near the door' | |
"This is a section of steam tunnel running east and west past | |
a door, which leads out of the tunnel to the south. Pipes and | |
conduits run along the walls, and bare, low-wattage light bulbs | |
are spaced every ten feet or so along the ceiling, providing just | |
enough light to see by. " | |
east = steamTunnel2 | |
west = steamTunnel5 | |
south = st1Door | |
out asExit(south) | |
roomParts = static (inherited - defaultEastWall - defaultWestWall) | |
; | |
+ st1Door: Door ->bstorNorthDoor 'south s door' 'south door' | |
"The door leads out of the tunnel to the south. " | |
; | |
+ st1Wire: PresentLater, Unthing 'blue network cable/wire' 'blue wire' | |
"You don't see the wire here, but if you have your bearings correct, | |
you wouldn't expect to---the wiring closet ought to be just west of | |
here. " | |
plKey = 'stWire' | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* narrow tunnel | |
*/ | |
steamTunnel5: SteamTunnelRoom 'Narrow Tunnel' 'the narrow tunnel' | |
"The steam tunnel is quite narrow here; it widens a bit to the | |
east and west. | |
<<st5Hole.isIn(self) | |
? "<.p>You notice a tiny point of light in the south wall---a | |
small hole, almost hidden behind one of the big steam pipes. | |
You never would have noticed it, except that you have the | |
sense this spot is roughly aligned with the wiring closet. " | |
: "" | |
>> " | |
lookBehindPipes() | |
{ | |
if (st5Hole.isIn(self)) | |
"You find a blue wire fastened along the back of one of the | |
big pipes. "; | |
else | |
inherited(); | |
} | |
east = steamTunnel1 | |
west = steamTunnel6 | |
roomParts = [defaultFloor, defaultCeiling, defaultNorthWall] | |
; | |
+ Fixture 'south s wall*walls' 'south wall' | |
desc() | |
{ | |
if (st5Hole.isIn(self)) | |
"You notice a small hole in the wall, admitting a little | |
pinprick of light from the other side. "; | |
else | |
"You see nothing unusual about it, although you have the | |
sense the wiring closet should be on the other side. "; | |
} | |
; | |
++ st5Hole: PresentLater, Fixture | |
'small tiny hole/pinpoint/(light)' 'small hole' | |
"The hole is almost completely hidden behind one of the big | |
steam pipes. A blue wire comes in through the hole, and then | |
snakes along the back of one of the big pipes, running down | |
the tunnel to the west. " | |
plKey = 'stWire' | |
lookInDesc = "A single blue wire is threaded into the hole. " | |
dobjFor(LookThrough) { action() { "All you can see is a pinpoint | |
of light. "; } } | |
; | |
+ st5Wire: PresentLater, Immovable 'blue network cable/wire' 'blue wire' | |
"The wire comes in through the small hole in the south wall, | |
then snakes along the back of one of the big steam pipes, | |
stretching down the tunnel to the west. " | |
plKey = 'stWire' | |
/* we nominally run through the hole */ | |
isNominallyIn(obj) { return obj == st5Hole || inherited(obj); } | |
dobjFor(Follow) | |
{ | |
verify() { } | |
action() { "The wire continue down the tunnel to the west, | |
and leaves the tunnel through the small hole. "; } | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Intersection | |
*/ | |
steamTunnel6: SteamTunnelRoom 'T-Intersection' 'the T-intersection' | |
"This is a T-intersection, with a north-south tunnel meeting another | |
tunnel continuing east. The pipes are arranged in an elaborate | |
network of crossings, some going straight past the intersection, | |
others turning the corner. | |
<<st6Wire.isIn(self) | |
? "<.p>You notice a blue wire concealed behind one of the | |
large-diameter steam pipes. " | |
: "">> " | |
lookBehindPipes() | |
{ | |
if (st6Wire.isIn(self)) | |
"There's a blue wire concealed behind a large pipe. | |
<<st6Wire.wireDesc>> "; | |
else | |
inherited(); | |
} | |
east = steamTunnel5 | |
south = steamTunnel7 | |
north = steamTunnel13 | |
; | |
+ st6Wire: PresentLater, Immovable 'blue network cable/wire' 'blue wire' | |
"The wire is concealed behind the steam pipes. <<wireDesc>> " | |
wireDesc = "You trace its path and find that it follows a large | |
steam pipe going down the tunnel to the east, runs up another | |
pipe, crosses overhead, runs down a pipe on the west side, and | |
then follows a pipe down the tunnel south. " | |
plKey = 'stWire' | |
dobjFor(Follow) asDobjFor(Examine) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Musty tunnel | |
*/ | |
steamTunnel13: SteamTunnelRoom 'Musty Tunnel' 'the musty tunnel' | |
"This dimly-lit section of tunnel has a slight musty | |
odor. The tunnel runs north and south from here. " | |
south = steamTunnel6 | |
north = steamTunnel14 | |
roomParts = static (inherited - [defaultNorthWall, defaultSouthWall]) | |
; | |
+ SimpleOdor 'musty odor/air' 'musty odor' | |
"A slightly musty odor is in the air. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Corner at stairs | |
*/ | |
steamTunnel14: SteamTunnelRoom | |
'Tunnel at Stairs' 'the tunnel near the stairs' | |
"This is the north end of a north-south section of tunnel. | |
To the east, a steep stairway leads down into a side passage. " | |
south = steamTunnel13 | |
east = st14Stairs | |
down asExit(east) | |
roomParts = static (inherited - defaultSouthWall) | |
; | |
+ EntryPortal ->(location.east) 'east e narrow side passage' 'side passage' | |
"A steep stairway descends into the passage. " | |
; | |
+ st14Stairs: StairwayDown ->st15Stairs | |
'steep stair/stairs/stairway' 'steep stairway' | |
"The stairs lead down into the east passage. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* North-south crawl | |
*/ | |
steamTunnel15: SteamTunnelRoom | |
'Tunnel at Crawl' 'the tunnel at the north-south crawl' | |
"This is an older section of steam tunnel, running east and | |
west. The tunnel ascends a steep stairway to the west. | |
<.p>Cut into the north wall is a low, narrow service tunnel; | |
a pair of giant asbestos-wrapped pipes, each almost a meter | |
in diameter, turn the corner into the service tunnel, leaving | |
just enough space for a person to snake through by lying down | |
flat on top of the steam pipe. This is what you referred to | |
as the north-south crawl in your undergraduate years: the only | |
known connection between the north and south tunnel systems. " | |
west = st15Stairs | |
up asExit(west) | |
east = steamTunnel16 | |
north: NoTravelMessage { "You snaked your way through the crawlway | |
several times in your undergraduate years, and it was a tight | |
squeeze then; you really doubt you'd fit any more. " } | |
roomParts = static (inherited - [defaultEastWall, defaultWestWall, | |
defaultNorthWall]) | |
; | |
+ st15Stairs: StairwayUp 'steep stair/stairs/stairway' 'steep stairway' | |
"The stairs lead up to the west. " | |
; | |
+ Fixture 'north n wall*walls' 'north wall' | |
"A service tunnel, known as the north-south crawl, is cut | |
into the north wall. " | |
; | |
+ EntryPortal ->(location.north) | |
'narrow north-south service crawl/crawlway/tunnel' | |
'north-south crawl' | |
"Two large asbestos-wrapped pipes run through the tunnel, | |
leaving barely enough room for a person to fit through by | |
snaking one's way along the top of the pipes. " | |
; | |
+ Fixture 'giant asbestos-wrapped pipe/pipes' 'asbestos-wrapped pipes' | |
"The pipes are roughly three feet in diameter. They run through | |
the service tunnel branching off to the north. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Older tunnel | |
*/ | |
steamTunnel16: SteamTunnelRoom | |
'Older Tunnel' 'the older tunnel' | |
"This section of tunnel runs east and west. The tunnel looks | |
especially old here. On the south wall, next to what looks like | |
a circuit-breaker box, the stenciled letters <i>DEI</i> are painted. " | |
west = steamTunnel15 | |
east = steamTunnel9 | |
roomParts = static (inherited - [defaultEastWall, defaultWestWall, | |
defaultSouthWall]) | |
; | |
+ Fixture 'south s wall*walls' 'south wall' | |
"The stenciled letters <i>DEI</i> are painted next to an | |
electrical junction box. " | |
/* read wall -> read letters */ | |
dobjFor(Read) remapTo(Read, dei) | |
; | |
++ dei: Fixture 'stenciled painted letters "dei" dei' 'stenciled letters' | |
"<q>DEI</q> is a Techer inside joke that's so inside that no one | |
even knows exactly what it means. It's widely supposed to stand | |
for <q>Dabney eats it,</q> which is widely supposed to refer to | |
some food-service incident from the misty past, but there's no | |
definitive record of exactly when or why or who or what. Despite | |
its mysterious origins, the trigraph has long been used as a | |
sort of secret handshake to covertly identify something as | |
Caltech-related. The steam tunnel DEI's come from decades ago | |
when the TV show <i>Operation: Danger</i> filmed an episode down | |
here, and some students took the opportunity to sneak the insignia | |
into the show. " | |
isPlural = true | |
; | |
+ Fixture 'circuit breaker electrical junction (dei) ("dei") box' | |
'electrical box' | |
"It's an electrical junction box of some kind. " | |
/* read wall -> read letters */ | |
dobjFor(Read) remapTo(Read, dei) | |
dobjFor(Open) | |
{ | |
verify() { } | |
action() { "There's no obvious way to open the box, at least | |
without special tools. "; } | |
} | |
dobjFor(LookIn) | |
{ | |
preCond = [objOpen] | |
verify() { } | |
} | |
isOpen = nil | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Wide tunnel | |
*/ | |
steamTunnel7: SteamTunnelRoom 'Dim Tunnel' 'the dim tunnel' | |
"The lights must be spaced out a little more widely than usual | |
here, because this section of north-south tunnel seems especially | |
dark. | |
<<st7Crawl.isIn(self) | |
? "In the shadows, low on the west wall, an opening is just | |
barely visible. " : "">> | |
<<st7Wire.isIn(self) | |
? "You can make out a blue wire concealed behind one of the | |
large pipes. " : "">> " | |
lookBehindPipes() | |
{ | |
if (st7Wire.isIn(self)) | |
{ | |
"You find a blue wire concealed behind one of the | |
large steam pipes. "; | |
st7Wire.descWire; | |
} | |
else | |
inherited(); | |
} | |
north = steamTunnel6 | |
west = (st7Crawl.isIn(self) ? st7Crawl : inherited) | |
south = steamTunnel12 | |
roomParts = [defaultFloor, defaultCeiling, defaultEastWall] | |
; | |
+ st7Wire: PresentLater, Immovable 'blue network cable/wire' 'blue wire' | |
"The wire is concealed behind one of the large pipes. <<descWire>> " | |
descWire = "It follows the pipe to the north<<revealCrawl>> " | |
revealCrawl() | |
{ | |
if (!st7Crawl.isIn(location)) | |
{ | |
", but it seems to disappear to the south. You trace it | |
carefully, and discover that it goes into an opening low | |
in the west wall. The opening is well concealed in | |
shadows under the pipes; you wouldn't even have seen it | |
if you hadn't been following the wire. "; | |
st7Crawl.makePresent(); | |
} | |
else | |
", and goes into an opening low in the west wall. "; | |
} | |
dobjFor(Follow) asDobjFor(Examine) | |
plKey = 'stWire' | |
; | |
+ Fixture 'west w wall*walls' 'west wall' | |
desc() | |
{ | |
if (st7Crawl.isIn(self)) | |
"An opening is just barely visible low on the wall. "; | |
else | |
"You see nothing unusual about it. "; | |
} | |
; | |
++ st7Crawl: PresentLater, TravelWithMessage, ThroughPassage ->st8Crawl | |
'dark opening/crawlspace' 'opening' | |
"The opening is well concealed in shadows under the steam pipes. | |
It must have been intended to accommodate a couple of the large | |
pipes, but it looks like it's empty right now except for the blue | |
wire, so there might be enough space for you to squeeze through. " | |
travelDesc = "You crouch down on the floor and crawl into the | |
opening head-first. It's an uncomfortably tight squeeze, | |
but you manage to work your way through the eight feet or so | |
of the passage, finally emerging into another tunnel. " | |
/* treat LOOK IN the same as EXAMINE */ | |
dobjFor(LookIn) asDobjFor(Examine) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Tunnel at door | |
*/ | |
steamTunnel12: SteamTunnelRoom 'Tunnel at Door' 'the tunnel near the door' | |
"This section of tunnel ends to the south at a wide metal door | |
marked <q>Steam Plant.</q> The tunnel continues to the north. " | |
north = steamTunnel7 | |
south = st12Door | |
roomParts = static (inherited - defaultNorthWall) | |
; | |
+ st12Door: AlwaysLockedDoor 'wide metal steam plant door' 'metal door' | |
"It's a wide metal door marked <q>Steam Plant.</q> " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Sloping tunnel | |
*/ | |
steamTunnel2: SteamTunnelRoom 'Sloping Tunnel' 'the sloping tunnel' | |
"This section of tunnel runs east and west, sloping sharply down | |
to the east. The steam pipes follow the slope of the tunnel. " | |
west = steamTunnel1 | |
up asExit(west) | |
east = steamTunnel3 | |
down asExit(east) | |
roomParts = static (inherited - defaultEastWall - defaultWestWall) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Tunnel corner | |
*/ | |
steamTunnel3: SteamTunnelRoom 'Bend in Tunnel' | |
'the bend in the tunnel' | |
"The steam tunnel turns a corner here, continuing to the north | |
and to the west. The pipes and conduits running in from each | |
branch of the tunnel intersect, some making the turn and others | |
continuing straight on through the walls. " | |
north = steamTunnel4 | |
west = steamTunnel2 | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Tunnel T | |
*/ | |
steamTunnel4: SteamTunnelRoom 'T-Intersection' 'the T-intersection' | |
"Two steam tunnels meet here, forming a <font face='tads-sans'>T</font>: | |
one tunnel runs north and south, and another tunnel continues off to | |
the east. " | |
north = steamTunnel9 | |
south = steamTunnel3 | |
east = steamTunnel10 | |
roomParts = static (inherited - defaultSouthWall - defaultNorthWall) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Dead end | |
*/ | |
steamTunnel9: SteamTunnelRoom 'Tunnel Corner' 'the corner in the tunnel' | |
"The steam tunnel turns a corner here, one branch running | |
south, the other running west. " | |
south = steamTunnel4 | |
west = steamTunnel16 | |
roomParts = static (inherited - [defaultSouthWall, defaultWestWall]) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Under quad manhole | |
*/ | |
steamTunnel10: SteamTunnelRoom 'Bottom of Shaft' 'the bottom of the shaft' | |
"A column of sunlight spills in from a shaft above, piercing | |
the gloom of the tunnel. A ladder built into the wall ascends | |
into the shaft. The tunnel continues west, and descends a | |
few concrete steps to the east. " | |
west = steamTunnel4 | |
east = st10Stairs | |
down asExit(east) | |
up = st10Ladder | |
roomParts = [defaultFloor, defaultNorthWall, defaultSouthWall] | |
afterTravel(trav, conn) | |
{ | |
/* do the normal work */ | |
inherited(trav, conn); | |
/* | |
* if the PC is coming into the room, and plisnik isn't here, | |
* and we haven't talked to the workers above yet, have them ask | |
* if everything's okay | |
*/ | |
if (trav == me | |
&& !plisnik.isIn(self) | |
&& !gRevealed('plisnik-checked')) | |
{ | |
/* make sure the workers are here, and have them call down */ | |
st10QuadWorkers.makePresent(); | |
st10QuadWorkers.initiateConversation(nil, 'plisnik-check'); | |
} | |
} | |
; | |
+ st10Stairs: StairwayDown ->st11Stairs | |
'short concrete step/steps/stair/stairs/stairway' 'concrete stairs' | |
"The stairway leads a few steps down to the east. " | |
; | |
+ Vaporous 'column/sunlight/light' 'column of sunlight' | |
"The sunlight spills into the tunnel from the opening at | |
the top of the shaft above. " | |
; | |
+ Fixture 'tunnel ceiling' 'ceiling' | |
"A shaft above the tunnel leads up to the surface. " | |
; | |
+ workOrders: Readable, Consultable | |
'work order overstuffed three-ring thick brown binder/form/forms' | |
'three-ring binder' | |
"It's a brown three-ring binder, bulging with papers. " | |
/* | |
* this is the kind of thing that takes some effort to read as | |
* opposed to just looking at it, so we have a separate read desc | |
*/ | |
readDesc = "The binder is stuffed with paper. Flipping through, | |
it seems to be a collection of official Network Installer | |
Company <q>Work Order</q> forms. You sample a few at random, | |
and find they're all pretty much the same: install network | |
port wiring for office X in building Y. These guys must have | |
a contract to rewire the whole campus, from the looks of it. | |
The forms seem to be sorted in order of the Job Number stamped | |
at the top of each form, so it'd be easy to look up a specific | |
one given the number. " | |
topicNotFound() | |
{ | |
local jobnum = gTopic.getTopicText(); | |
if (rexMatch('(job<space>+)?(number<space>+)?[0-9]{4}-[0-9]{4}$', | |
jobnum) != jobnum.length()) | |
{ | |
/* it's not even the right format */ | |
"You don't see any job numbers like that; they all seem | |
to be in the format 1234-5678. "; | |
} | |
else | |
{ | |
/* pull out the job number from the string */ | |
rexMatch('.*([0-9]{4}-[0-9]{4}$', jobnum); | |
jobnum = rexGroup(1)[3]; | |
/* | |
* The format's right: with a 33% chance, if we haven't | |
* filled out the three random responses yet, pick one and | |
* fill it out. The extra random responses don't tell us | |
* anything; they're just there for fun, so that we actually | |
* get non-trivial responses for trying random numbers. | |
* However, if we've previously claimed this number doesn't | |
* exist, then we want to claim this again. | |
*/ | |
if (randomJob3.jobNumber == nil && !jobNotFound[jobnum]) | |
{ | |
/* only do this with a 33% chance */ | |
if (rand(100) < 33) | |
{ | |
local t; | |
/* find the first available random job entry */ | |
t = [randomJob1, randomJob2, randomJob3] | |
.valWhich({x: x.jobNumber == nil}); | |
/* set the topic's job number to what we looked up */ | |
t.setJobNumber(jobnum); | |
/* show the response */ | |
t.handleTopic(gActor, gTopic); | |
/* we've handled it */ | |
return; | |
} | |
/* | |
* we're electing not to find it even though we could, | |
* so for consistency, enter it in our table of jobs we | |
* can never find | |
*/ | |
jobNotFound[jobnum] = true; | |
} | |
/* it's a valid number, but it's not there... */ | |
"There doesn't seem to be a work order form with | |
that number. "; | |
} | |
} | |
/* | |
* a table of job numbers we've claimed do not exist - we keep this | |
* table to ensure that we don't later change our minds and decide | |
* to allow the job number as part of our random job responses | |
*/ | |
jobNotFound = static new LookupTable(16, 16) | |
/* | |
* we're initially described with the spools of wire, so don't list | |
* me separately if we haven't been moved yet | |
*/ | |
isListed = (moved) | |
spoolDesc() | |
{ | |
/* if we haven't been moved yet, add our description to the spools */ | |
if (!moved) | |
"An overstuffed three-ring binder is on the floor next | |
to the spools. "; | |
} | |
/* OPEN and LOOK IN are the same as READ for this */ | |
dobjFor(Open) asDobjFor(Read) | |
dobjFor(LookIn) asDobjFor(Read) | |
dobjFor(Read) | |
{ | |
/* | |
* this is the sort of big, heavy book one has to be holding in | |
* order to read | |
*/ | |
preCond = (inherited() + objHeld) | |
/* the first time we read it, the instruction sheet falls out */ | |
action() | |
{ | |
/* if we haven't found the instruction sheet yet, find it now */ | |
if (!foundSheet) | |
{ | |
local dest, nomDest; | |
/* | |
* get the actor's "drop destination," which is where | |
* things land when the actor drops them right now, and | |
* the "nominal" drop destination, which is where we | |
* *say* things land | |
*/ | |
dest = gActor.getDropDestination(netAnInstructions, nil); | |
nomDest = dest.getNominalDropDestination(); | |
/* mention that the sheet falls out of the binder */ | |
"As you open the binder, a dingy sheet of paper falls | |
out and flutters <<nomDest.putInName()>>.<.p>"; | |
/* move the instruction sheet to the drop destination */ | |
netAnInstructions.moveInto(dest); | |
/* note that we've found it */ | |
foundSheet = true; | |
} | |
/* do the normal work */ | |
inherited(); | |
} | |
} | |
/* as with reading, consulting requires holding the binder */ | |
dobjFor(ConsultAbout) { preCond = (inherited() + objHeld); } | |
/* flag: we've found the instruction sheet */ | |
foundSheet = nil | |
; | |
class JobOrderTopic: ConsultTopic | |
topicResponse = "You scan through the binder and find a page | |
with that job number: | |
<.p><.blockquote><tt><i><b>ATTENTION</b>: All IP address | |
assignments <b>MUST</b> be made through the Campus Network | |
Office (Jorgensen Lab). Report <b>ALL</b> IP changes to the | |
CNO.</i> | |
<.p>Job desc: <<jobDesc>> | |
<br><br>Special notes: <<jobNotes>></tt><./blockquote> | |
<<jobExtra>> " | |
jobNumber = nil | |
/* on setting the job number dynamically, rebuild the match pattern */ | |
setJobNumber(num) | |
{ | |
/* remember the new job number */ | |
jobNumber = num; | |
/* build the new match pattern */ | |
matchPattern = buildMatchPattern(); | |
} | |
/* | |
* build the match pattern as "job number xxx", with "job" and | |
* "number" being optional | |
*/ | |
matchPattern = perInstance(buildMatchPattern()) | |
buildMatchPattern() | |
{ | |
return (jobNumber == nil | |
? nil | |
: new RexPattern( | |
'(job<space>+)?(number<space>+)?' + jobNumber + '$')); | |
} | |
; | |
/* | |
* Set up three topics for random things we look up, to surprise the | |
* user with actual results for looking up random numbers. These don't | |
* tell us anything useful - they're just here for fun, to surprise the | |
* player with non-trivial feedback for trying random numbers. | |
* | |
* These don't have any match pattern initially. When we decide to use | |
* one, we'll assign whatever number the player typed in as the match | |
* pattern, ensuring that they'll get the same response if they ask | |
* about the same number again. | |
*/ | |
++ randomJob1: JobOrderTopic | |
jobDesc = "Install new Gb FO connection in Athenaeum, room 271. | |
Std FO cabling. Std wall plate parts." | |
jobNotes = "Do not drill in N wall w/o checking studs." | |
; | |
++ randomJob2: JobOrderTopic | |
jobDesc = "Repair 100TX wiring, Bridge Lab, room 023." | |
jobNotes = "No voltage. Checked wall wiring 4/17, bad conn | |
prob.\ in s/b wiring closet." | |
; | |
++ randomJob3: JobOrderTopic | |
jobDesc = "Add 2 new ports, Steele 237. Std cat5e cabling. | |
Std wall plate parts." | |
jobNotes = "Need conduit if possible." | |
; | |
++ JobOrderTopic | |
jobNumber = static (infoKeys.spy9JobNumber) | |
jobDesc = "Install SPY-9 in Bridge Lab, room 022. Std cat5 | |
cabling. Std cam wiring profile - pull via s/b wiring | |
closet, tunnel 7g, term at router S-24." | |
jobNotes = "NITE CREW ONLY. Position cam for clear view of | |
main bench area. REF <<infoKeys.spy9IPJobNumber>>." | |
; | |
++ JobOrderTopic | |
jobNumber = static (infoKeys.spy9IPJobNumber) | |
jobDesc = "Need static IP address for SPY-9." | |
jobExtra = "<.p>Below this are big hand-written numbers: | |
<<infoKeys.spy9IPDec>>.<.reveal spy9-ip> " | |
; | |
++ JobOrderTopic | |
jobNumber = static (infoKeys.syncJobNumber) | |
jobDesc = "Install new network port, Sync Lab Office (2nd floor)." | |
jobNotes = "Door combo is <<infoKeys.syncLabCombo>>." | |
topicResponse() | |
{ | |
/* show the description */ | |
inherited(); | |
/* award our points */ | |
scoreMarker.awardPointsOnce(); | |
} | |
scoreMarker: Achievement { +10 "finding out about the Sync Lab Office" } | |
; | |
+ st10Ladder: StairwayUp 'iron ladder/rung/rungs' 'ladder' | |
"The ladder consists of a series of iron rungs embedded in the | |
wall. It goes up the wall into the tunnel. " | |
dobjFor(TravelVia) | |
{ | |
action() | |
{ | |
"You climb the ladder, emerging into the sunlight on | |
the Quad. Just as you get to the top of the ladder, | |
two big guys in bright green jumpsuits grab you by | |
the arms and lift you out of the shaft. | |
<.p><q>What the heck do you think you're doing down | |
there?</q> one of the bearded workers asks. They | |
hustle you past the yellow tape they've set up around | |
the shaft and give you a shove. | |
<.p><q>I'm sick of you people getting in our way all | |
the time,</q> the worker says, jabbing his index finger | |
at you. <q>You're lucky I don't call the cops.</q> He | |
stomps back to the edge of the shaft. "; | |
/* off to the quad */ | |
gActor.travelTo(quad, self, nil); | |
} | |
} | |
; | |
+ st10Spools: CustomImmovable | |
'big phone network wooden spool/spools/wire/(pile)/(piles)' | |
'spools of wire' | |
"They're two-foot-diameter wooden spools wound with wire, | |
which looks like some kind of phone or network wire. " | |
specialDesc = "Several big wooden spools of wire are piled up | |
along the tunnel wall. <<workOrders.spoolDesc>> " | |
isPlural = true | |
cannotTakeMsg = 'The spools of wire are big and heavy, and you\'d | |
rather not waste a lot effort moving them around. ' | |
; | |
+ OutOfReach, Fixture 'shaft/top/opening' 'shaft' | |
"It's a rectangular shaft that rises from the ceiling of | |
the tunnel to the surface above. The shaft is open at | |
the top. A ladder built into the wall ascends into the shaft. " | |
dobjFor(Enter) remapTo(ClimbUp, st10Ladder) | |
dobjFor(Board) remapTo(ClimbUp, st10Ladder) | |
dobjFor(GoThrough) remapTo(ClimbUp, st10Ladder) | |
dobjFor(ClimbUp) remapTo(ClimbUp, st10Ladder) | |
dobjFor(Climb) remapTo(ClimbUp, st10Ladder) | |
dobjFor(LookThrough) { action() { "You can't see anything beyond | |
the shaft except sunlight. "; } } | |
dobjFor(LookIn) asDobjFor(LookThrough) | |
cannotReachFromOutsideMsg(dest) { return 'You can\'t quite reach | |
the shaft from here. '; } | |
; | |
/* | |
* The quad workers above, as seen from the bottom of the shaft. We're | |
* not even here initially - we don't show up until we make some mention | |
* of the workers above. | |
*/ | |
++ st10QuadWorkers: PresentLater, Actor | |
'big heavy unruly workers/man/men/beard/beards' 'workers' | |
"You can't see them from here; the only thing you see through the | |
shaft is sunlight. " | |
isPlural = true | |
/* they're not in sight, so we don't need to mention them */ | |
specialDesc = "" | |
/* as these guys are out of view, downgrade conversational actions */ | |
dobjFor(AskAbout) | |
{ verify() { logicalRank(70, 'out of view'); inherited(); } } | |
dobjFor(TellAbout) | |
{ verify() { logicalRank(70, 'out of view'); inherited(); } } | |
iobjFor(ShowTo) | |
{ verify() { logicalRank(70, 'out of view'); inherited(); } } | |
iobjFor(GiveTo) | |
{ verify() { logicalRank(70, 'out of view'); inherited(); } } | |
; | |
/* we don't want to allow talking to them while Plisnik is here */ | |
+++ TopicGroup | |
isActive = (plisnik.inOrigLocation) | |
; | |
++++ DefaultAnyTopic, ShuffledEventList | |
['<q>Hey, Plisnik,</q> one of the workers above calls down, | |
<q>you got someone down there with you? Better make sure it\'s | |
not a giant talking rat! Har! Har!</q> ', | |
'<q>Shut up, Plisnik,</q> one of the workers calls down, | |
<q>we\'re on break!</q> ', | |
'<q>Who\'s down there with you, Plisnik?</q> one of the | |
workers above calls down. <q>Tell whoever it is to get lost! | |
You got work to do.</q> '] | |
; | |
/* | |
* Have the workers check on the commotion in the tunnels after Plisnik | |
* leaves. The purpose of this brief conversation is to ensure that the | |
* player realizes that it's possible to talk to the workers through the | |
* shaft, since we have to talk to them to solve the network analyzer | |
* puzzle. | |
*/ | |
+++ ConvNode 'plisnik-check' | |
npcGreetingMsg = "<.p>You hear a worker above call down. <q>Hey, | |
Plisnik, what\'s going on down there?</q> His voice becomes | |
taunting. <q>You see a rat or something?</q> He chuckles. " | |
; | |
++++ YesTopic | |
"<q>Yeah, a big, ugly one, but he's gone now,</q> you say, hoping | |
they don't notice it's not Plisnik's voice. You hear the workers | |
above laugh and laugh. | |
<.reveal plisnik-checked> " | |
; | |
++++ NoTopic | |
"<q>No, no problem down here,</q> you say. | |
<.p><q>Hey,</q> one of them calls down, <q>I think I see one | |
now!</q> You can hear the two of them laugh and laugh. | |
<.reveal plisnik-checked> " | |
; | |
/* once plisnik is gone, the conversation changes a bit.. */ | |
+++ TopicGroup | |
isActive = (!plisnik.inOrigLocation) | |
; | |
++++ AskTellAboutForTopic @quadAnalyzer | |
topicResponse() | |
{ | |
"<q>Hey, can you hand down the Netbisco?</q> you call up | |
to the workers above. | |
<.p>One of them reaches down into the shaft, lowering the | |
network box. <q>Watch out, Plisnik,</q> he says. <q>I think | |
I saw a rat inside it earlier. Har! Har!</q> "; | |
/* | |
* Get rid of the fake analyzer on the quad, move the real | |
* analyzer into our location, then take it. Note that we | |
* interrupt the dialog to take the analyzer so that any | |
* implicit action (such as hands-freeing bag-of-holding | |
* insertions) will be described just before our 'take' | |
* description below, rather than after the entire message. | |
*/ | |
quadAnalyzer.moveInto(nil); | |
netAnalyzer.moveInto(steamTunnel10); | |
nestedAction(Take, netAnalyzer); | |
/* and now we can mention that we took it, assuming we did */ | |
if (netAnalyzer.isIn(me)) | |
"You reach up and take it from him, being careful to stay | |
in shadow. "; | |
/* this is worth some points */ | |
scoreMarker.awardPointsOnce(); | |
/* make the new, real analyzer 'it' */ | |
me.setPronounObj(netAnalyzer); | |
} | |
scoreMarker: Achievement { +10 "obtaining the Netbisco 9099"; } | |
; | |
+++++ AltTopic | |
"<q>Hey, guys, how do I work the Netbisco?</q> you say. | |
<.p><q>Working that thing's your job, Plisnik,</q> one of the | |
workers calls down. " | |
/* this topic takes over once we get the analyzer */ | |
isActive = (quadAnalyzer.location == nil) | |
; | |
++++ AskTellTopic @supplyRoomTopic | |
"<q>Hey,</q> you call to the workers above, <q>what's the | |
combo to the supply room?</q> | |
<.p><q>How should I know?</q> one of the workers shouts. | |
<q>You're the one who works down in the tunnels.</q> " | |
; | |
++++ AskForTopic @st10Spools | |
"You call up to the workers above. <q>Could you guys hand down | |
some more wire?</q> | |
<.p><q>What do I look like, a delivery man?</q> shouts one of | |
the workers. <q>You got tons of wire down there already.</q> " | |
; | |
++++ DefaultAnyTopic | |
"<q>You sound funny, Plisnik,</q> one of the workers shouts | |
down the shaft. <q>That rat must have scared you good.</q> " | |
; | |
+ plisnik: IntroPerson | |
'nervous-looking jittery pale gaunt stringy | |
worker/man/beard/plisnik*men' | |
'worker' | |
"He's a pale, gaunt, nervous-looking man with a stringy beard. | |
He's wearing the same sort of green overalls and hardhat as the | |
workers you saw on the Quad. " | |
isHim = true | |
properName = 'Plisnik' | |
specialDescName = (introduced | |
? properName | |
: 'a heavy, bearded man in green overalls') | |
/* am I in my original location? */ | |
inOrigLocation = (isIn(steamTunnel10)) | |
/* | |
* use a global parameter name, so we can use {plisnik} in messages | |
* to refer to our current name, which depends on whether we've been | |
* introduced or not | |
*/ | |
globalParamName = 'plisnik' | |
beforeAction() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* if they're trying to get my binder or spools, don't let them */ | |
if (gActionIs(Take) && gDobj is in(st10Spools, workOrders)) | |
{ | |
"<q>Hey,</q> {the plisnik/he} shouts, standing in your | |
way, <q>that's my stuff!</q> "; | |
exit; | |
} | |
} | |
afterTravel(traveler, connector) | |
{ | |
/* do the normal work */ | |
inherited(traveler, connector); | |
/* if the PC just showed up, kick off our conversation */ | |
if (traveler == me) | |
initiateConversation(plisnikTalking, | |
introduced | |
? 'plisnik-rat' : 'plisnik-intro'); | |
} | |
/* the toy car+rat combo will call this when it comes into our presence */ | |
eekARat() | |
{ | |
"<.p>You hear girlish shrieking from nearby, then {a plisnik/he} | |
comes running down the tunnel in a panic. He practically | |
crashes into you, but stops and grabs you by the arms. | |
<q>Rat!</q> he shouts at you. <q>Huge, ugly, mean! | |
It's not safe down here!</q> He lets you go and continues | |
running, disappearing down the tunnel. "; | |
/* make this the current 'him', since we're mentioned prominently */ | |
me.setPronounObj(self); | |
/* track our departure for "follow", but then disappear */ | |
trackAndDisappear(self, location.south); | |
} | |
/* receive notification that the toy car is leaving under its own power */ | |
ratLeaving() | |
{ | |
"<.p>{The plisnik/he} watches the rat leave. <q>Your pet rat | |
just left!</q> he says, sounding a little frantic. <q>You'd | |
better go catch him before he gets away.</q> "; | |
} | |
/* receive notification that the rat is moving around locally */ | |
ratMoving() { ratMovingScript.doScript(); } | |
ratMovingScript: StopEventList { [ | |
'<.p>{The plisnik/he} shrinks back a little, being careful | |
to keep his distance from the rat. <q>You shouldn\'t let your | |
pet rat wander around down here without a leash on,</q> he | |
says. <q>It\'s a good thing I saw you come in carrying him. | |
I don\'t know what I would have done if that were a <i>wild</i> | |
rat.</q> ', | |
'<.p>{The plisnik/he} watches nervously as the rat toy | |
moves around. <q>I wish you\'d get that thing out of here,</q> | |
he says. <q>Even if he is a pet, I still don\'t trust rats.</q> ', | |
'<.p>{The plisnik/he} watches the rat toy nervously. <q>I wish | |
you\'d get that rat out of here,</q> he says. '] | |
} | |
/* get hit with a thrown object */ | |
throwTargetHitWith(projectile, path) | |
{ | |
if (projectile == ratPuppet) | |
{ | |
/* move the rat to the floor here */ | |
ratPuppet.moveInto(location); | |
/* show our special message */ | |
ratScript.doScript(); | |
} | |
else | |
inherited(projectile, path); | |
} | |
ratScript: StopEventList { [ | |
'The rat puppet hits {the plisnik/him} and falls to the | |
floor. He sees what it is and shrinks back in horror, | |
but only for a moment; his expression turns suspicious, | |
and he goes in for a closer look. <q>That\'s not very | |
nice,</q> he says, <q>throwing a fake rat at someone. Rats | |
are no joking matter.</q> ', | |
'The rat puppet hits {the plisnik/him} and falls to the | |
floor. He\'s startled, but only for a moment. <q>I didn\'t | |
think it was very funny the first time,</q> he says. '] | |
} | |
; | |
++ InitiallyWorn 'bright green hard overalls/hardhat/uniform/hat' | |
'uniform' | |
"His bright green overalls and matching hardhat are marked | |
<q>Network Installer Company</q> in blocky white letters. | |
<.reveal NIC> " | |
isListedInInventory = nil | |
; | |
/* since we mention his hand... */ | |
++ DisambigDeferrer, Decoration | |
'his plisnik\'s man\'s worker\'s hand/hands' 'his hand' | |
"There's nothing remarkable about his hands. " | |
isQualifiedName = true | |
/* never confuse this with the PC's hands */ | |
disambigDeferTo = [myHands] | |
; | |
++ plisnikTalking: InConversationState | |
specialDesc = "\^<<getActor().specialDescName>> is standing | |
here watching you warily. " | |
/* infinite timeout - he never stops talking to us while we're here */ | |
attentionSpan = nil | |
; | |
+++ ConversationReadyState | |
isInitState = true | |
specialDesc = "\^<<getActor().specialDescName>> is doing some kind | |
of work on the wiring in the tunnel. " | |
; | |
++ ConvNode 'plisnik-intro' | |
npcGreetingMsg = "<.p>\^<<getActor().theName>> abruptly drops | |
what he's doing and turns to face you, taking a step back. | |
<q>Who are you?</q> he asks. " | |
; | |
+++ SpecialTopic 'ask who he is' | |
['ask', 'the', 'man', 'him', 'who', 'he', 'is', 'are', 'you'] | |
"<q>Who are you?</q> you ask right back. | |
<.p>He squints and eyes you suspiciously. <q>I'm Plisnik,</q> | |
he says. <q>You haven't seen any rats down here, have you?</q> | |
<<getActor().setIntroduced()>> | |
<.convnode plisnik-rat> " | |
; | |
+++ HelloTopic | |
topicResponse() { replaceAction(TellAbout, plisnik, me); } | |
; | |
+++ SpecialTopic 'introduce yourself' | |
['introduce','myself','me','you','yourself'] | |
topicResponse() { replaceAction(TellAbout, plisnik, me); } | |
; | |
+++ GiveShowTopic [alumniID, driverLicense] | |
topicResponse() { replaceAction(TellAbout, plisnik, me); } | |
; | |
+++ TellTopic @me | |
"<q>I'm Doug,</q> you say. | |
<.p>He eyes you suspiciously. <q>I'm Plisnik,</q> he says | |
at last. <q>You haven't seen any rats down here, have you?</q> | |
<<getActor().setIntroduced()>> | |
<.convnode plisnik-rat> " | |
; | |
+++ DefaultAnyTopic | |
"<q>I want to know who you are first,</q> he says nervously. | |
<.convstay> " | |
/* let the rat puppet through to the enclosing topic database */ | |
excludeMatch = [ratPuppet] | |
; | |
++ ConvNode 'plisnik-rat' | |
npcGreetingMsg = "<.p>Plisnik drops what he's doing and faces you. | |
<q>Oh, you again,</q> he says, but the recognition doesn't | |
seem to make him any less nervous. <q>Hey, you haven't seen | |
any rats down here, have you?</q> " | |
; | |
+++ YesTopic | |
"<q>Yeah, I've seen a few,</q> you say. | |
<.p>He shrinks back and his eyes dart wildly around the tunnel. | |
<q>Well, don't let any come near me!</q> he says. " | |
; | |
+++ NoTopic | |
"<q>I don't think so,</q> you say. | |
<.p><q>Good,</q> he says, looking suspiciously around the | |
tunnel floor. " | |
; | |
+++ DefaultAnyTopic | |
"<q>Hey!</q> he says. <q>I asked if you've seen any rats. | |
Have you?</q><.convstay> " | |
/* let the rat puppet through to the enclosing topic database */ | |
excludeMatch = [ratPuppet] | |
; | |
++ AskTellTopic @quadWorkers | |
"<q>What's with your co-workers outside?</q> | |
<.p><q>They're jerks,</q> he says. <q>Yesterday they caught | |
a rat and dropped it down here. The thing nearly bit me! See?</q> | |
He holds out his hand, presumably to show you the spot where | |
he was almost bitten, but of course an almost-rat-bite doesn't | |
leave a lot of visible evidence. He retracts his hand when he | |
sees your lack of amazement. <q>I hate those guys.</q> " | |
; | |
++ AskTellTopic @plisnik | |
"<q>What are you working on?</q> you ask. | |
<.p><q>I'm just trying to do some network wiring,</q> he says. | |
<q>But the rats are trying to stop me.</q> " | |
; | |
++ AskTellTopic @nicTopic | |
"<q>I've never heard of Network Installer Company before,</q> you say. | |
<.p><q>Look, mister, what you've never heard of could fill a book. | |
You have no idea what's going on in the world today. If you did, | |
you wouldn't be down here where the rats live and breed.</q> He | |
looks around nervously. " | |
; | |
++AskTellTopic @ratTopic | |
"<q>Why are you so worried about rats?</q> | |
<.p>His face turns red and his eyes dart around wildly. <q>I'm not | |
worried,</q> he says. <q>I'm scared out of my mind! And you would | |
be too if you had any idea, any idea, what they were up to.</q> " | |
; | |
++ AskTellShowTopic @workOrders | |
"<q>What's in that binder?</q> you ask. | |
<.p>He shifts a little closer to the binder. <q>None of your | |
business,</q> he says. <q>It's confidential. Proprietary.</q> " | |
; | |
++ AskForTopic @workOrders | |
"<q>Could I see your binder for a moment?</q> you ask. | |
<.p><q>No!</q> he says. <q>It's important confidential | |
information.</q> " | |
; | |
++ AskTellAboutForTopic [netAnalyzer, quadAnalyzer] | |
"<q>Do you have a network analyzer I could borrow?</q> you ask. | |
<.p>His eyes narrow. <q>If I did, I couldn't let you borrow it. | |
That's important business-related equipment.</q> " | |
; | |
++ DefaultAnyTopic, ShuffledEventList | |
['<q>Rats are behind fluoridation of drinking water, you know,</q> | |
he says knowingly. ', | |
'He just looks at you suspiciously. ', | |
'<q>You know what the source of every human disease is?</q> he | |
asks, then answers his own question: <q>Rats,</q> he says, nodding. ', | |
'<q>I bet you didn\'t know that rats are the only mammal whose | |
DNA they\'ve never sequenced,</q> he says. <q>That should tell | |
you something about who\'s in control.</q> ', | |
'He just stares at you. '] | |
; | |
/* | |
* Topics for showing the rat. The first time he sees the rat, we get a | |
* bit of a rise out of him, but once he's seen it he's not startled | |
* again. In addition, differentiate its use when worn and not worn. | |
*/ | |
++ GiveShowTopic @ratPuppet | |
"{The plisnik/he} recoils when he sees the toy rat, but then takes | |
a closer look and relaxes a bit. <q>That's not very nice,</q> | |
he says, <q>trying to scare me with a toy rat. I almost thought | |
it was real until I saw it wasn't moving.</q> | |
<.reveal show-plisnik-rat> | |
<.convstay> " | |
; | |
+++ AltTopic | |
"{The plisnik/he} takes a look at the rat. <q>It's not very nice | |
trying to scare me with that toy rat,</q> he says. <q>It's a good | |
thing it's not moving or I would have thought it was real.</q> | |
<.convstay> " | |
isActive = gRevealed('show-plisnik-rat') | |
; | |
+++ AltTopic | |
"You hold up the rat and wiggle your fingers a little to make | |
its nose twitch like a real rat. {The plisnik/he} cringes and | |
jumps back, but then he takes a closer look and relaxes a bit. | |
<q>That's not very nice,</q> he says. <q>I almost thought | |
that was a real rat, until I saw your hand in it.</q> | |
<.reveal show-plisnik-rat> | |
<.convstay> " | |
isActive = (ratPuppet.isWornBy(me)) | |
; | |
+++ AltTopic | |
"You wiggle your fingers to make the rat's nose twitch. | |
<q>That's not very nice,</q> {the plisnik/he} says. <q>I almost | |
thought it was real until I saw your hand in it.</q> | |
<.convstay> " | |
isActive = (ratPuppet.isWornBy(me) && gRevealed('show-plisnik-rat')) | |
; | |
+ netAnalyzer: Keypad, PresentLater, PlugAttachable, NearbyAttachable, Thing | |
'netbisco 9099 network analyzer' 'network analyzer' | |
"The Netbisco 9099 network analyzer looks a little like the | |
kind of oversized telephone you'd find on a receptionist's | |
desk in a large office. It has a sixteen-digit hexadecimal | |
keypad (with keys numbered 0-F), a small display screen, and | |
a short cord with a special plug on the end. <<screenDesc>> " | |
/* we can only attach to one thing at a time */ | |
dobjFor(AttachTo) { preCond = (inherited() + objNotAttached) } | |
iobjFor(AttachTo) { preCond = (inherited() + objNotAttached) } | |
/* it doesn't need to turn on and off */ | |
cannotTurnOnMsg = 'It doesn\'t seem to have an on/off switch. If you | |
recall correctly, these boxes get powerby connecting to a router. ' | |
cannotTurnOffMsg = (cannotTurnOnMsg) | |
/* entering/typing on me redirects to the keypad */ | |
dobjFor(EnterOn) remapTo(EnterOn, netAnKeypad, IndirectObject) | |
dobjFor(TypeLiteralOn) remapTo(TypeLiteralOn, netAnKeypad, IndirectObject) | |
/* we can plug into the router */ | |
canAttachTo(obj) { return obj == nrRouter; } | |
explainCannotAttachTo(obj) { "The plug only fits a certain kind | |
of multi-prong jack on compatible network routers. "; } | |
/* on attaching to a compatible device, power up */ | |
handleAttach(other) | |
{ | |
"As soon as you plug in the connector, the display screen | |
flashes a series of random gibberish, then displays | |
<q>READY.</q> "; | |
/* reset it */ | |
dispData = 'READY'; | |
sourceIP = nil; | |
} | |
/* we're turned on if anything is attached */ | |
isOn = (attachedObjects.length() != 0) | |
/* add the screen to our description */ | |
screenDesc() | |
{ | |
/* | |
* if we're plugged in, show the screen data; otherwise we have | |
* nothing to add to the main description, since the screen is | |
* blank | |
*/ | |
if (isOn) | |
{ | |
"The screen is currently displaying "; | |
if (sourceIP == nil) | |
"<tt><<dispData>></tt> "; | |
else if (sourceIP == infoKeys.spy9IP) | |
"the number <tt><<infoKeys.spy9DestIP>></tt>, repeated | |
on three lines. You work out the decimal conversion of | |
that address, and come up with <<infoKeys.spy9DestIPDec>>. "; | |
else if (sourceIP.startsWith('C0A8')) | |
"a series of numbers that scroll by as you watch. "; | |
else | |
"<tt>READING...</tt> "; | |
} | |
} | |
/* show the current display */ | |
readDisplay() | |
{ | |
if (!isOn) | |
"It's currently blank. "; | |
else | |
{ | |
"It's a low-resolution dot-matrix LCD screen. "; | |
if (sourceIP == nil) | |
{ | |
/* no IP, so just use the display data */ | |
"It's currently displaying <tt><<dispData>></tt>. "; | |
} | |
else if (sourceIP == infoKeys.spy9IP) | |
{ | |
/* we're showing the camera IP */ | |
"It's currently displaying: | |
\b<tt>\t<<infoKeys.spy9DestIP>> | |
\n\t<<infoKeys.spy9DestIP>> | |
\n\t<<infoKeys.spy9DestIP>></tt> | |
\bYou work out the decimal version of that address, | |
and come up with <<infoKeys.spy9DestIPDec>>. "; | |
} | |
else if (sourceIP.startsWith('C0A8')) | |
{ | |
/* non-camera but valid IP; show random addresses */ | |
"It's currently displaying: | |
\b<tt>\t<<randIP>>\n\t<<randIP>>\n\t<<randIP>></tt> | |
\bAs you watch, the display keeps scrolling, with | |
new numbers appearing at the bottom. "; | |
} | |
else | |
{ | |
/* invalid IP; we just sit here and get nothing */ | |
"It's currently displaying <tt>READING...</tt> "; | |
} | |
} | |
} | |
/* our current display data */ | |
dispData = nil | |
/* IP address we're currently dumping, if any */ | |
sourceIP = nil | |
/* get a random 192.168.x.x IP address */ | |
randIP() | |
{ | |
local res; | |
/* | |
* make up a random four-digit hex number, but one that doesn't | |
* have 0, 1, 254, or 255 in either byte | |
*/ | |
do { | |
res = rand(65536); | |
} while ((res & 0xFF) <= 1 | |
|| (res & 0xFF) >= 254 | |
|| (res & 0xFF00) <= 0x100 | |
|| (res & 0xFF00) >= 0xFE00); | |
/* make it a string */ | |
res = toString(res, 16); | |
/* | |
* extend to four digits (the first digit can't be less than 2, | |
* so at worst we start at three digits) | |
*/ | |
if (res.length() < 4) | |
res = '0' + res; | |
/* add the 192.168 prefix and return the result */ | |
return 'C0A8' + res; | |
} | |
; | |
++ PluggableComponent | |
'(netbisco) (9099) (network) (analyzer) short special cord/plug' | |
'network analyzer plug' | |
"The plug is a special design that fits the diagnostic port on | |
certain kinds of compatible routers. " | |
/* map ATTACH and USE to our parent */ | |
dobjFor(PutIn) remapTo(AttachTo, location, IndirectObject) | |
dobjFor(PutOn) remapTo(AttachTo, location, IndirectObject) | |
dobjFor(AttachTo) remapTo(AttachTo, location, IndirectObject) | |
iobjFor(AttachTo) remapTo(AttachTo, DirectObject, location) | |
dobjFor(UseOn) remapTo(UseOn, location, IndirectObject) | |
iobjFor(UseOn) remapTo(UseOn, DirectObject, location) | |
; | |
++ Component, Readable | |
'(netbisco) (9099) (network) (analyzer) small display | |
dot-matrix lcd screen' | |
'network analyzer display' | |
desc() { location.readDisplay(); } | |
; | |
++ netAnKeypad: Keypad, Component | |
'(netbisco) (9099) (network) (analyzer) sixteen-digit hexadecimal keypad' | |
'network analyzer keypad' | |
"The keypad has digits 0-9 and A-F, for entering hexademical numbers. " | |
dobjFor(TypeLiteralOn) asDobjFor(EnterOn) | |
dobjFor(EnterOn) | |
{ | |
verify() { } | |
action() | |
{ | |
local str; | |
local resp = nil; | |
/* change to upper-case hex */ | |
str = gLiteral.toUpper(); | |
/* make sure it's a hex number */ | |
if (rexMatch('[0-9A-F]+', str) != str.length()) | |
{ | |
reportFailure('(The keypad only accepts hexademical | |
digits, 0 to 9 and A to F.) '); | |
return; | |
} | |
/* whatever happens, we key in the numbers... */ | |
"You key in the sequence. "; | |
/* if we're not plugged in, obviously nothing happens */ | |
if (!location.isOn) | |
{ | |
reportFailure('The display remains blank. '); | |
return; | |
} | |
/* | |
* if we're clearing the screen, and we have an existing IP | |
* address, show the startup for that address again | |
*/ | |
if (str == '55' && location.sourceIP != nil) | |
str = '09' + location.sourceIP; | |
/* check the code */ | |
if (str.startsWith('09') && str.length() == 10) | |
{ | |
local addr; | |
/* get the address from the rest of the string */ | |
location.sourceIP = addr = str.substr(3); | |
/* show the initial display */ | |
if (addr == infoKeys.spy9IP) | |
{ | |
/* it's the camera - show the other side */ | |
"The display clears, then it shows | |
<tt><<infoKeys.spy9DestIP>></tt>. In a few moments, | |
the same number repeats on a new line, then on a | |
third line. You watch for a little bit, but all | |
the packets seem to be going to the same place. | |
<.p>That's good---it means there's only one computer | |
you need to track down. You convert the hex numbers | |
to decimal, and come up with <<infoKeys.spy9DestIPDec>>. | |
Now you just need to find out where the computer | |
with that address is. | |
<.reveal spy9-dest-ip> "; | |
/* this merits some points */ | |
scoreMarker.awardPointsOnce(); | |
} | |
else if (addr.startsWith('C0A8')) | |
{ | |
/* | |
* a valid 192.168 address, but not one we know; | |
* show random results | |
*/ | |
"The display clears, then the display shows | |
<tt>READING...</tt> for a split-second, then | |
the display starts showing numbers that | |
scroll by: | |
\b<tt>\t<<location.randIP>> | |
\n\t<<location.randIP>> | |
\n\t<<location.randIP>></tt> | |
\bNew numbers keep appearing, scrolling the old | |
numbers off the screen. "; | |
} | |
else | |
{ | |
/* not a valid 192.168 address; show no traffic */ | |
"The display clears and shows <tt>READING...</tt> "; | |
} | |
} | |
else if (str == '55') | |
{ | |
/* not sniffing packets - just show READY */ | |
"The display changes to show <tt>READY</tt>. "; | |
resp = 'READY'; | |
} | |
else | |
{ | |
/* use a random error response */ | |
resp = rand(['ERR INV CMDCD', 'ERR UNK CYRRG', | |
'ERR CRW FNZPW', 'ERR CDN CPC C', | |
'ERR STK OVFLW', 'ERR FLTPT ASI', | |
'ERR OUTOFMEMR', 'ERR CLS LDR E', | |
'ERR SIG9 SEGV', 'ERR DLL NTFND']); | |
/* show the error */ | |
"The screen changes to display <tt><<resp>></tt>. "; | |
/* no IP */ | |
location.sourceIP = nil; | |
} | |
/* set the new value */ | |
location.dispData = resp; | |
} | |
} | |
scoreMarker: Achievement { +5 "tracing the camera data packets" } | |
; | |
++ Button, Component | |
'(netbisco) (9099) (network) (analyzer) (keypad) | |
0 1 2 3 4 5 6 7 8 9 "a" "b" "c" "d" "e" "f" button*buttons' | |
'network analyzer keypad button' | |
dobjFor(Push) | |
{ | |
verify() { logicalRank(50, 'decorative buttons'); } | |
action() { "(You don't need to push the buttons | |
individually; just type the digits on the keypad.) "; } | |
} | |
; | |
/* | |
* this falls out of the binder when we open the binder, so it's not | |
* actually anywhere until then | |
*/ | |
netAnInstructions: PresentLater, Readable | |
'dingy ragged nth-generation photocopy/sheet/paper' | |
'dingy sheet of paper' | |
"The sheet of paper is dingy and a bit ragged, like it's | |
been handled a lot. It looks like an nth-generation photocopy. | |
<.p> | |
<.blockquote><tt> | |
<center>-12-</center> | |
<br><br> | |
not for user inserting up. WARMING: ALWAYS NOT TO SETUP IF | |
PLUG POWER OFF!!! But when opened is, keystroke inserted be | |
must. Page 52 instruction continuation referencize to also. | |
<br><br><u>PACKET SMELL</u><br> | |
To monitoring data packet going to and fro. If know nice IP | |
adress to source, then user found IP also destination. MUST | |
TO TYPE IP IN HEXADECIMAL. User to keypadenter FUNCTIONCODE 71 | |
then keypadenter IP source address. ALL TO GETHER!!! E.i., | |
710ABCDEF1 if IP source as 10.188.222.241. Because 0A=10 - | |
BC=188 - DE=222 - F1=241, so IP number dotted notationing to | |
10.188.222.241. BUT MODEL 9099 NOT TO USE FUNCTIONCODE 71!!! | |
Must to referencize page 59 instructioning for MODEL 9099 | |
SPECIALCODES. | |
<br><br>After keypadenteration, IP address to DESTINATION show | |
on displaywindow upon data packet transmittering. IP TO | |
DESTINATION TOO IN HEX!!! Make normal dot notationing with | |
DECIMAL convertate. I.g., upon displaywindow show 0ABCDEF1, | |
convertate for 10.188.222.241. Enjoy so easy! NOTE. If | |
data packets many transmitterate, displaywindow full get will. | |
Displaywindow hesitate, later to scroll. FUNCTIONCODE 72 | |
displaywindow to emptyize. | |
<br><br><u>PUT ADDRES \" I P \" TO PORT</u><br> | |
Nice IP address must assigned be when configurate router. NOT TO | |
REPETITION IP ADDRESS IN TWO PORT PLACES SAMETIME!!! Or bad | |
collisionating happen. Referencize to page 40 for collisionation | |
resolviating if suspicioned to happen, | |
</tt><./blockquote> | |
<.p>Handwritten at the bottom of the page are a few numbers: | |
<font face='tads-sans'>9099: 70=07, 71=09, 72=55, 57=18</font> " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Tunnel end | |
*/ | |
steamTunnel11: SteamTunnelRoom 'Ath Tunnel' 'the tunnel near the Ath' | |
"The steam pipes thin out a bit here, and the tunnel ends to | |
the east at a door that leads into the Ath, if you recall correctly. | |
The tunnel continues west, up a short concrete stairway. " | |
west = st11Stairs | |
up asExit(west) | |
east = st11Door | |
; | |
+ st11Stairs: StairwayUp | |
'short concrete step/steps/stair/stairs/stairway' 'short stairway' | |
"The stairway leads up a few steps to the west. " | |
; | |
+ st11Door: AlwaysLockedDoor 'ath athenaeum east e door' 'Ath door' | |
"The door is unmarked, but your recollection is that it leads | |
into the Ath basement. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Steam tunnel at dead end | |
*/ | |
steamTunnel8: SteamTunnelRoom 'Dead End' 'the dead end' | |
"<<seen ? "" : | |
"You spent a lot of time exploring the steam tunnel system | |
during your years here as a student, but you don't think you've | |
encountered this section before.<.p>">> | |
The steam tunnel from the south reaches a dead end here; it | |
widens slightly at the northeast corner, creating a shadowy nook a | |
couple of feet deep. On the west side of the tunnel is a door | |
labeled <q>Supplies.</q> A small opening low on the east wall leads | |
into a crawlspace. | |
<.p>You notice a blue wire partially concealed behind the steam | |
pipes. " | |
lookBehindPipes() | |
{ | |
"There's a blue network wire partially concealed behind | |
one of the large steam pipes. <<st8Wire.wireDesc>> "; | |
} | |
east = st8Crawl | |
west = st8Door | |
south: TravelConnector | |
{ | |
connectorStagingLocation = lexicalParent | |
dobjFor(TravelVia) | |
{ | |
action() | |
{ | |
if (lexicalParent.workersPresent) | |
{ | |
/* the workers are in the room, so get them out here */ | |
"You start down the tunnel, but the door opens before | |
you make it past, and two workers in bright green | |
overalls step out. "; | |
} | |
else | |
{ | |
/* | |
* the workers aren't in the room, so they come in | |
* from the tunnel | |
*/ | |
"Before you get very far, a couple of workers in | |
bright green overalls come walking up the tunnel | |
from the south. They abruptly stop talking when | |
they see you. "; | |
} | |
/* in either case, cue the workers, and we're done */ | |
st8Workers.workerEntry(nil); | |
} | |
} | |
} | |
roomParts = [defaultFloor, defaultCeiling, defaultNorthWall] | |
/* flag: the workers are present in the network room */ | |
workersPresent = true | |
/* flag: the workers need to come back later */ | |
workersReturning = nil | |
/* our score award for making it here */ | |
scoreMarker: Achievement { +10 "tracing the camera wire" } | |
/* number of consecutive turns we've been here */ | |
hereCount = 0 | |
/* do some special work when a traveler arrives */ | |
afterTravel(traveler, conn) | |
{ | |
/* | |
* award points for tracing the wire all the way here on our | |
* first arrival | |
*/ | |
scoreMarker.awardPointsOnce(); | |
/* reset the count of consecutive turns we've been here */ | |
hereCount = 0; | |
/* do the normal work */ | |
inherited(traveler, conn); | |
} | |
/* reset the turns-here counter on nested room travel as well */ | |
actorTravelingWithin(origin, dest) | |
{ | |
if (dest == self) | |
hereCount = 0; | |
} | |
/* whenever we're in the room, check for worker arrivals/departures */ | |
roomDaemon() | |
{ | |
/* do the normal work first */ | |
inherited(); | |
/* | |
* Count this as a turn we're in the room. Since the room daemon | |
* only fires when the PC is in the room, we know they're here if | |
* we're running at all. | |
*/ | |
++hereCount; | |
/* | |
* If the workers are in the control room, they might want to | |
* leave; if they're not, they might want to arrive. If they're | |
* already in the hallway with us, we obviously don't need to do | |
* either of these. | |
*/ | |
if (!st8Workers.isIn(steamTunnel8)) | |
{ | |
if (workersPresent) | |
{ | |
/* | |
* the workers are here; if the PC has been here a few | |
* turns, it's time for the workers to leave | |
*/ | |
if (hereCount == 3) | |
{ | |
/* warn that they're about to leave */ | |
"<.p>Someone on the other side of the door opens it | |
slightly. You hear a male voice say, <q>Come on, | |
let's go.</q> "; | |
/* mark the door as unlocked and ajar */ | |
st8Door.makeLocked(nil); | |
st8Door.isAjar = true; | |
/* start the one-turn fuse to get them going */ | |
st8Workers.setEntryFuse(); | |
} | |
} | |
else | |
{ | |
/* | |
* the workers are gone; if we left without seeing them | |
* leave before, bring them back after a few turns | |
*/ | |
if (workersReturning && hereCount == 2) | |
{ | |
/* warn that the workers are coming back */ | |
"<.p>You hear someone approaching from the tunnel | |
to the south. "; | |
/* start the fuse to bring them back */ | |
st8Workers.setEntryFuse(); | |
} | |
} | |
} | |
} | |
; | |
+ st8Workers: PresentLater, Person 'larger smaller workers*men' 'workers' | |
"The two men are wearing bright green overalls marked | |
<q>Network Installer Company</q> in white letters. The larger | |
of the two is built like a football player. They're standing | |
uncomfortably close to you, their arms crossed, suspicion on | |
their faces.<.reveal NIC> " | |
actorHereDesc = "The two workers are standing menacingly close to you, | |
their arms crossed. " | |
isPlural = true | |
isHim = true | |
/* schedule the entry fuse */ | |
setEntryFuse() | |
{ | |
/* note that we're about to arrive */ | |
aboutToArrive = true; | |
/* set the fuse for the next turn */ | |
new Fuse(self, &entryFuse, 1); | |
} | |
/* fuse that fires when it's time for the workers to enter */ | |
entryFuse() | |
{ | |
/* | |
* have the workers enter, showing an appropriate arrival | |
* announcement | |
*/ | |
workerEntry(true); | |
} | |
/* flag: we're about to arrive */ | |
aboutToArrive = nil | |
/* | |
* handle worker entry - this is called by our fuse that controls | |
* the workers' movements, but can also be called directly to | |
* trigger their appearance on specific events | |
*/ | |
workerEntry(announce) | |
{ | |
local fromDoor = (steamTunnel8.workersPresent); | |
/* we're no longer about to arrive */ | |
aboutToArrive = nil; | |
/* | |
* we always go through the door (either in or out), so reset | |
* the combination lock, since it resets each time the door is | |
* opened | |
*/ | |
st8DoorLock.resetCombo(); | |
/* | |
* if we're already in steamTunnel8, we must have been | |
* expedited; ignore it if so | |
*/ | |
if (isIn(steamTunnel8)) | |
return; | |
/* if the player character is present, mention what's going on */ | |
if (me.isIn(steamTunnel8)) | |
{ | |
/* announce the entry if desired */ | |
if (announce) | |
{ | |
/* our arrival mode depends on where we're coming from */ | |
if (fromDoor) | |
"<.p>The door opens fully and two workers in bright | |
green overalls step out, closing the door behind them. "; | |
else | |
"<.p>Two workers in bright green overalls walk in from | |
the south tunnel. "; | |
} | |
/* are we hiding? */ | |
if (me.isIn(st8Nook) || me.isIn(st8CrawlOpening)) | |
{ | |
if (me.isIn(st8Nook)) | |
"You pretend you're in a spy film and press yourself | |
against the wall, hiding behind some steam pipes | |
near the corner. Which seems to work---the two don't | |
seem to notice you. "; | |
else | |
"They don't seem to notice you hiding in the crawlway. "; | |
if (fromDoor) | |
{ | |
"They shut the door and head south down the tunnel. | |
After a few steps, you hear one of them say <q>Hang | |
on, I forgot something.</q> "; | |
if (me.isIn(st8Nook)) | |
"You watch from behind the cover of the pipes | |
as the smaller guy returns to the door and | |
enters a combination, which you manage to | |
make out: <<st8Door.showCombo>>. "; | |
else | |
"You watch as the smaller guy returns to the door | |
and enters a combination, but you can't quite make | |
out the digits from such a low vantage point. "; | |
"He slips into the room for a moment, then he's | |
back, closing the door behind him. The two head | |
south; you hear their steps recede down the tunnel. "; | |
} | |
else | |
{ | |
if (me.isIn(st8Nook)) | |
"You watch from behind the cover of the pipes as | |
the smaller guy goes to the door and keys in a | |
combination, which you just manage to make out: | |
<<st8Door.showCombo>>. "; | |
else | |
"You watch as the smaller guy goes to the door | |
and keys in a combination, but you can't quite | |
make out the digits from such a low vantage point. "; | |
"He goes through the door; the bigger guy stays | |
outside. After a few moments, the smaller worker | |
comes out the door. <q>Got it,</q> he says, closing | |
the door. The two walk off down the tunnel. "; | |
} | |
if (me.isIn(st8Nook)) | |
{ | |
/* we now know the combination */ | |
gReveal('supplies-combo'); | |
/* the workers are now gone for good */ | |
steamTunnel8.workersPresent = nil; | |
steamTunnel8.workersReturning = nil; | |
} | |
else | |
{ | |
/* | |
* We weren't caught, but we didn't get the combo; | |
* we'll have to bring the workers back again. Since | |
* we didn't go anywhere, the workers have to come | |
* from down the tunnel next time - they can't come | |
* from the door, as we won't have seen them go into | |
* the room if we don't leave. | |
*/ | |
steamTunnel8.workersPresent = nil; | |
steamTunnel8.workersReturning = true; | |
} | |
} | |
else | |
{ | |
/* not hiding - move the workers here */ | |
makePresent(); | |
/* start the ejection conversation */ | |
initiateConversation(nil, 'intruder-alert'); | |
} | |
} | |
else | |
{ | |
/* | |
* We snuck off at the first sign of danger. That'll save us | |
* from being caught, but it doesn't let us see the | |
* combination to the door. So, to give us a chance to get | |
* the combo again: swap the workers 'present' and | |
* 'returning' states, so that they'll come back if they just | |
* left, and they'll leave if they just came back. | |
*/ | |
steamTunnel8.workersPresent = !fromDoor; | |
steamTunnel8.workersReturning = fromDoor; | |
} | |
/* whatever happened, the door is no longer ajar */ | |
st8Door.isAjar = nil; | |
} | |
/* intercept actions when we're present */ | |
beforeAction() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* | |
* if the action is travel, or involves the nook, the door, or | |
* the crawlspace, take over and kick them out of the tunnel | |
*/ | |
if (gActionIs(TravelVia) | |
|| gDobj == st8Nook | |
|| gDobj == st8Door | |
|| (gDobj != nil && gDobj.isIn(st8Door)) | |
|| gDobj == st8Crawl) | |
{ | |
/* kick them out */ | |
"The bigger worker moves even closer, blocking you. | |
<q>Oh, no you don't,</q> he says. "; | |
escortOut(); | |
/* don't continue with the original command */ | |
gAction.cancelIteration(); | |
exit; | |
} | |
/* special handling for "yell" */ | |
if (gActionIs(Yell)) | |
{ | |
"On second thought, it might not be that great an idea to | |
attract even more attention. "; | |
exit; | |
} | |
} | |
/* if we've scheduled an escort, do so now */ | |
afterAction() | |
{ | |
/* escort out if we asked for it */ | |
if (escortPending) | |
{ | |
/* do the escort */ | |
escortOut(); | |
/* clear the pending flag */ | |
escortPending = nil; | |
} | |
} | |
/* | |
* Schedule an escortOut for the end of this turn. This is a | |
* convenience for topic responses; we don't want to put the actual | |
* escort action in a topic response, since it can confuse the | |
* response processor by making it think the whole new room | |
* description is part of the response text. | |
*/ | |
escortLater() { escortPending = true; } | |
escortPending = nil | |
/* kick the player out of the steam tunnel */ | |
escortOut() | |
{ | |
"<.p>He roughly grabs you by the arm, and starts steering | |
you down the tunnel. There's not much you can do to resist. | |
He marches you through a series of turns, up a stairway, and | |
out a door into the sunlight. | |
<.p><q>You're lucky I don't call Security,</q> he says. | |
<q>Don't let me catch you trespassing again!</q> He lets | |
you go, goes back through the door, and slams it shut. "; | |
/* move me out */ | |
me.moveIntoForTravel(ldCourtyard); | |
/* | |
* Reset the workers. Simply leave us in the control room; it'll | |
* take the PC a while to get back there, so it'll be plausible | |
* that we've returned by that time. When the PC returns, we'll | |
* repeat the whole cycle. If we've already learned the | |
* combination, then we did the escort only to keep the PC from | |
* going south, hence we don't need to repeat the cycle. | |
*/ | |
moveInto(nil); | |
steamTunnel8.workersPresent = !gRevealed('supplies-combo'); | |
steamTunnel8.workersReturning = nil; | |
/* show our new location as though we just traveled normally */ | |
"<.p>"; | |
me.lookAround(gameMain.verboseMode.isOn); | |
/* note that we've been escorted out at least once */ | |
gReveal('escorted-out-of-tunnels'); | |
/* we're not in conversation any more; reset our ConvNode */ | |
setConvNode(nil); | |
} | |
; | |
++ InitiallyWorn 'bright green hard overalls/uniforms' 'overalls' | |
"The workers are wearing bright green overalls and matching hardhats, | |
marked <q>Network Installer Company</q> in blocky white letters. | |
<.reveal NIC> " | |
isPlural = true | |
isListedInInventory = nil | |
; | |
++ ConvNode 'intruder-alert' | |
npcGreetingMsg = "They look at you suspiciously. | |
<.p><q>Hey! Who are you?</q> asks the larger of the two, | |
the one built like a football player. He moves menacingly | |
close to you, crossing his arms, frowning." | |
; | |
+++ SpecialTopic 'say that you\'re lost' | |
['say','that','i\'m','i','am','you','are','you\'re','lost'] | |
"<q>I somehow got lost,</q> you say. | |
<.p>The bigger guy squints at you. <q>Lost?</q> he says. <q>Well, | |
let me help you find your way.</q><<st8Workers.escortLater>> " | |
; | |
+++ SpecialTopic 'explain about the camera' | |
['explain','about','the','camera'] | |
"<q>So, I found this, um...</q> It suddenly occurs to you that | |
it might not be such a great idea to tell just anyone about the | |
camera, at least until you have a better idea about what's going | |
on. And these guys don't even look like they're Caltech staff, | |
after all. <q>...um, overheating steam pipe...</q> | |
<.p><q>Well, maybe you should call B-and-G and report it, then. | |
Here, let me help you find a phone.</q> | |
<<st8Workers.escortLater>> " | |
; | |
+++ SpecialTopic 'claim to be a supervisor' | |
['claim','to','be','a','supervisor'] | |
"<q>The main office sent me down to, uh, check your productivity. | |
I want to see your time cards right now, please.</q> | |
<.p>The two look at each other. <q>I don't see your uniform,</q> | |
the bigger guy says. <q>I don't see your badge, either. I suppose | |
you left them in the <q>main office.</q> Here, let me show you the | |
way there.</q><<st8Workers.escortLater>> " | |
; | |
+++ DefaultAnyTopic | |
"The bigger guy grunts. <q>Whoever you are, you're not allowed | |
down here,</q> he says. <q>You're coming with me.</q> | |
<<st8Workers.escortLater>> " | |
; | |
+ st8Wire: Immovable 'blue network cable/wire' 'blue wire' | |
"The wire is partially concealed behind a large steam pipe. <<wireDesc>> " | |
wireDesc = "The wire comes in from the crawlspace, then follows one | |
of the large pipes north along the east wall, turns to follow | |
another pipe across the north wall, and turns again along | |
the west wall. Just a couple of inches from the door, the | |
wire disappears into a tiny hole drilled in the wall. " | |
dobjFor(Follow) asDobjFor(Examine) | |
isNominallyIn(obj) { return obj == st8Hole || inherited(obj); } | |
; | |
+ Fixture 'west w wall*walls' 'west wall' | |
"The blue wire is threaded into a tiny hole in the wall, a few | |
inches from the door. " | |
; | |
++ st8Hole: Fixture 'tiny hole' 'tiny hole' | |
"The hole is a couple of inches away from the door. The blue | |
wire is threaded into the hole. " | |
lookInDesc = "The blue wire is threaded into the hole. " | |
; | |
+ Fixture 'east e wall*walls' 'east wall' | |
"Low on the wall is an opening to a tight crawlspace. " | |
; | |
++ st8Crawl: TravelWithMessage, ThroughPassage | |
'tight low opening/crawlspace/crawlway/tunnel' 'crawlspace' | |
"The opening looks just large enough to crawl into. " | |
travelDesc = "You crawl head-first into the passage, and drag | |
yourself along for eight feet or so before emerging into | |
another steam tunnel. " | |
dobjFor(HideIn) remapTo(LieOn, st8CrawlOpening) | |
; | |
/* secret nested room for when we're hiding in the tunnel */ | |
+ st8CrawlOpening: OutOfReach, Fixture, BasicChair | |
name = 'crawlspace' | |
objInPrep = 'in' | |
actorInPrep = 'in' | |
actorOutOfPrep = 'out of' | |
obviousPostures = [lying] | |
allowedPostures = [lying] | |
tryRemovingFromNested() { return tryImplicitAction(GetOutOf, self); } | |
/* as with the nook, we can reach inside from outside, not vice versa */ | |
canObjReachContents(obj) { return true; } | |
canReachSelfFromInside(obj) { return true; } | |
/* we can reach our surrogate crawlway opening, of course */ | |
canReachFromInside(obj, dest) { return dest == st8Crawl; } | |
/* we don't show up for 'all', but allow in defaults */ | |
hideFromAll(action) { return true; } | |
hideFromDefault(action) { return inherited(action); } | |
dobjFor(LieOn) | |
{ | |
action() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* mention that we're hiding in the opening */ | |
"You crawl backwards into the opening, far enough that | |
you're out of view. "; | |
} | |
} | |
makeStandingUp() | |
{ | |
/* stand us up in the main tunnel */ | |
gActor.makePosture(location.defaultPosture); | |
gActor.travelWithin(location); | |
"You crawl out of the tunnel and stand up. "; | |
} | |
/* we can't go east from here */ | |
east: NoTravelMessage { "It's too hard to crawl backwards through | |
such a tight space; you'll have to get out first so you can | |
turn around. "; } | |
/* likewise for ENTER CRAWLWAY */ | |
tryMakingTravelReady(conn) | |
{ | |
if (conn == st8Crawl) | |
return replaceAction(East); | |
else | |
return inherited(conn); | |
} | |
; | |
+ st8Nook: OutOfReach, Fixture, Platform 'shadowy dark nook/shadows' 'nook' | |
"The nook is a couple of feet deep---enough to stand in, but it | |
doesn't go anywhere. It's almost all in shadow. " | |
objInPrep = 'in' | |
actorInPrep = 'in' | |
actorOutOfPrep = 'out of' | |
dobjFor(Enter) asDobjFor(StandOn) | |
tryRemovingFromNested() { return tryImplicitAction(GetOutOf, self); } | |
dobjFor(HideIn) remapTo(StandOn, self) | |
dobjFor(StandOn) | |
{ | |
action() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* add an extra message if we succeeded */ | |
if (gActor.isIn(self)) | |
"You step into the nook's shadows. "; | |
} | |
} | |
/* | |
* We can't reach things outside the nook from inside the nook, but | |
* we can reach inside the nook from outside. The asymmetry comes | |
* from the idea that the nook is a small part of the room, so we | |
* have to move out of the nook to reach anything else in the room. | |
*/ | |
canObjReachContents(obj) { return true; } | |
canReachSelfFromInside(obj) { return true; } | |
/* | |
* to remove the out-of-reach obstruction, get out of the nook if | |
* we're inside | |
*/ | |
tryImplicitRemoveObstructor(sense, obj) | |
{ | |
if (sense == touch && gActor.isIn(self)) | |
return tryRemovingFromNested(); | |
else | |
return inherited(sense, obj); | |
} | |
; | |
+ st8Door: Keypad, Lockable, Door '"supplies" supply room west w door' 'door' | |
"The door is labeled <q>Supplies,</q> and it has one of those | |
mechanical-keypad locks. " | |
/* | |
* we're locked if we're closed and the right combo isn't on the | |
* keypad accumulator | |
*/ | |
isLocked() { return !isOpen && st8DoorLock.comboAcc != internCombo; } | |
/* a special state for "slightly ajar" */ | |
isAjar = nil | |
/* supplement the open status message for the "ajar" state */ | |
openDesc = (isAjar ? 'slightly ajar' : inherited) | |
/* the internal combo - this is just the string of combo digits */ | |
internCombo = static (infoKeys.st8DoorCombo) | |
/* | |
* the display version of the combo - to generate the display | |
* version, put a hyphen between each digit, which we can do by | |
* replacing each digit that's followed by another digit with itself | |
* plus a hyphen | |
*/ | |
showCombo = static (rexReplace('(<digit>)(?=<digit>)', internCombo, | |
'%1-', ReplaceAll, 1)) | |
/* | |
* once we've successfully used the combination, allow automatic | |
* unlocking on future attempts to open the door | |
*/ | |
autoUnlockOnOpen = (usedCombo) | |
/* the lock status is never visually apparent */ | |
lockStatusObvious = nil | |
/* | |
* Have we successfully used the combination before? We track this | |
* so that we can grant that the PC knows the combo after they've | |
* used it once manually, in which case we don't need to make them | |
* repeat the manual key entry on future visits; we'll just enter the | |
* combo automatically for them. | |
*/ | |
usedCombo = nil | |
dobjFor(Open) | |
{ | |
check() | |
{ | |
if (isAjar && steamTunnel8.workersPresent) | |
{ | |
/* the workers are inside */ | |
"You reach to open the door, but someone opens it from | |
the other side first. Two workers in bright green | |
overalls step out into the tunnel and close the | |
door behind them. "; | |
/* trigger the workers */ | |
st8Workers.workerEntry(nil); | |
/* forget the 'open' action */ | |
exit; | |
} | |
else if (isLocked()) | |
{ | |
/* reset the combination on attempting to turn the handle */ | |
st8DoorLock.resetCombo(); | |
/* mention what happens */ | |
"You try the door, but the only effect is some clicking | |
from the lock mechanism. "; | |
/* terminate the action */ | |
exit; | |
} | |
else if (steamTunnel8.workersPresent) | |
{ | |
/* the workers are inside */ | |
"You open the door, revealing a pair of workers in | |
bright green overalls. They look at you with a start, | |
then step out into the hallway, closing the door behind | |
them. "; | |
/* cue the workers */ | |
st8Workers.workerEntry(nil); | |
/* forget the rest of the 'open' action */ | |
exit; | |
} | |
else if (steamTunnel8.workersReturning) | |
{ | |
/* | |
* they're not inside, but they're coming back; expedite | |
* their return to happen right now | |
*/ | |
st8Workers.workerEntry(true); | |
/* forget the 'open' action */ | |
exit; | |
} | |
} | |
action() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* if we're now open, reset the lock */ | |
if (isOpen) | |
{ | |
/* mention it */ | |
"The lock mechanism clicks and whirs, and the door swings | |
open. "; | |
/* reset it */ | |
st8DoorLock.resetCombo(); | |
/* note that we've successfully used the combo */ | |
usedCombo = true; | |
} | |
} | |
} | |
dobjFor(Lock) | |
{ | |
verify() { logicalRank(50, 'self-locking door'); } | |
check() | |
{ | |
"There's no obvious way to lock the door, but your past | |
experience is that the door will re-lock automatically | |
each time it's opened and then closed again. "; | |
exit; | |
} | |
} | |
dobjFor(Unlock) | |
{ | |
verify() { logicalRank(50, 'self-locking door'); } | |
check() | |
{ | |
/* | |
* if we've used the combo before, allow it; if not, we need | |
* to do this manually with keypad entry, so say so | |
*/ | |
if (!usedCombo) | |
{ | |
"It looks like you'll have to enter a combination on | |
the keypad to unlock the door. "; | |
exit; | |
} | |
} | |
action() | |
{ | |
/* | |
* if we made it this far, we know the combo, so just say | |
* we're using it | |
*/ | |
"You key in the combination on the keypad. "; | |
st8DoorLock.resetCombo(); | |
st8DoorLock.addToCombo(internCombo); | |
} | |
} | |
dobjFor(EnterOn) remapTo(EnterOn, st8DoorLock, IndirectObject) | |
dobjFor(TypeLiteralOn) remapTo(TypeLiteralOn, st8DoorLock, IndirectObject) | |
dobjFor(Knock) | |
{ | |
action() | |
{ | |
"The sound of your knocking echoes down the tunnel. "; | |
if (steamTunnel8.workersPresent) | |
{ | |
"The door opens, revealing a large man in bright green | |
overalls. He steps out, along with another, shorter | |
man dressed the same way, and they close the door. "; | |
st8Workers.workerEntry(nil); | |
} | |
else | |
"There doesn't seem to be any response. "; | |
} | |
} | |
; | |
++ st8DoorLock: Keypad, Fixture | |
'mechanical-keypad keypad (door) lock locking (key) | |
key-pad/pad/lock/mechanism/keypad' | |
'keypad lock' | |
"It's a kind of lock you've seen before in offices and other | |
public buildings. The keypad has push-buttons labeled 0 to 9, | |
arranged in two columns, plus a larger <q>Reset</q> button at | |
the bottom. " | |
/* reset the combination */ | |
resetCombo() { comboAcc = ''; } | |
/* add a string of digits to the combination */ | |
addToCombo(digits) { comboAcc += digits; } | |
/* | |
* the combination accumulator - as buttons are pressed, we keep | |
* track of the combination so far | |
*/ | |
comboAcc = '' | |
dobjFor(EnterOn) | |
{ | |
verify() { } | |
action() | |
{ | |
local combo; | |
/* get the literal string we're entering */ | |
combo = gLiteral; | |
/* remove spaces and dashes */ | |
combo = rexReplace('<space|->', combo, '', ReplaceAll, 1); | |
/* make sure it looks valid */ | |
if (rexMatch('[0-9]+', combo, 1) != combo.length()) | |
{ | |
"You can only enter digits on the keypad. "; | |
return; | |
} | |
/* add the digits */ | |
addToCombo(combo); | |
/* let them know it worked */ | |
"You push the buttons in sequence: "; | |
for (local i = 1, local len = combo.length() ; i <= len ; ++i) | |
{ | |
say(combo.substr(i, 1)); | |
if (i != len) | |
"-"; | |
} | |
". The mechanism in the keypad clicks with each button push. "; | |
} | |
} | |
dobjFor(TypeLiteralOn) asDobjFor(EnterOn) | |
; | |
+++ Button, Component | |
'(lock) (keypad) numbered metal 0 1 2 3 4 5 6 7 8 9 "reset" | |
push-button/button*buttons*push-buttons' | |
'lock keypad button' | |
"Numbered buttons labeled 0 to 9 are arranged in two columns, and | |
at the bottom is a larger button labeled <q>Reset.</q> " | |
dobjFor(Push) { action() { "(You don't need to push the buttons | |
individually; just enter the combination on the keypad.) "; } } | |
; | |
+++ Button, Component | |
'"reset" metal push-button/button*buttons*push-buttons' | |
'<q>Reset</q> button' | |
"It's a metal push-button labeled <q>Reset.</q> " | |
dobjFor(Push) | |
{ | |
action() | |
{ | |
"The mechanism inside the lock clicks several times as | |
you push in the button. "; | |
location.resetCombo(); | |
} | |
} | |
; | |
++ SimpleNoise | |
desc = "You think you hear voices on the other side of the door. " | |
soundPresence = (steamTunnel8.workersPresent) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The network room in the tunnels | |
*/ | |
networkRoom: Room 'Network Room' 'the network room' | |
"The door outside is labeled <q>Supplies,</q> but that's apparently | |
out of date; it looks like this is actually being used as a network | |
control room. The room is dominated by a floor-standing network | |
router box, connected to which are hundreds of orange, white, | |
and yellow network cables. The cables are gathered into giant | |
bundles the size of fire hoses, which feed into conduits leading | |
out through the walls. | |
<.p>A lone blue network cable comes in through a small hole in | |
the wall next to the door, which leads out of the room to the east. " | |
vocabWords = 'network room' | |
east = nrDoor | |
out asExit(east) | |
/* award points on first arrival */ | |
afterTravel(traveler, conn) | |
{ | |
scoreMarker.awardPointsOnce(); | |
inherited(traveler, conn); | |
} | |
scoreMarker: Achievement { +5 "getting into the network room" } | |
roomParts = static (inherited - defaultEastWall) | |
; | |
+ nrDoor: Door ->st8Door 'east e door' 'door' | |
"The door leads out of the room to the east. " | |
dobjFor(Lock) | |
{ | |
verify() { logicalRank(50, 'auto-locking'); } | |
action() { "There's no obvious way to lock the door; it probably | |
locks automatically when closed. "; } | |
} | |
dobjFor(Unlock) | |
{ | |
verify() { logicalRank(50, 'auto-locking'); } | |
action() { "There's no obvious way to unlock it, but the door | |
can be opened from this side without unlocking it. "; } | |
} | |
; | |
+ Fixture 'east e wall*walls' 'east wall' | |
"A blue wire is threaded through a small hole in the wall next | |
to the door. " | |
; | |
++ nrHole: Fixture 'small tiny hole' 'small hole' | |
"The hole is right next to the door. A blue network cable comes | |
in through the hole. " | |
lookInDesc = "A blue network wire is threaded through the hole. " | |
; | |
+ PlugAttachable, PermanentAttachment, Immovable | |
'lone blue network wire/cable' 'blue network cable' | |
"The blue wire is threaded through a small hole in the wall, | |
next to the door. It joins into one of the big bundles of | |
cables---but it's the only blue wire, so it's easy to follow it to | |
the router box, where it terminates in one of the router sockets. | |
<.p>There's obviously no way to trace the blue wire's physical | |
connection any further; the router must connect to the main | |
campus network, so the camera is effectively connected to every | |
computer on campus. The only way to trace it from here would be | |
to hook up a network analyzer and see where the data packets are | |
going. | |
<.reveal need-net-analyzer> " | |
attachedObjects = [nrRouter, nrRouterSockets] | |
cannotDetachMsg(obj) { return 'You\'re probably better leaving it as | |
it is, so no one gets suspicious that you\'re snooping around. '; } | |
isNominallyIn(obj) { return obj == nrHole || inherited(obj); } | |
; | |
+ nrConduits: Fixture 'conduits' 'conduits' | |
"Big bundles of network cable feed through the conduits, which | |
presumably lead out to nearby buildings. " | |
isPlural = true | |
dobjFor(LookIn) asDobjFor(Examine) | |
; | |
+ PlugAttachable, PermanentAttachment, Immovable | |
'big giant white yellow orange network wire/wires/cable/cables/bundles' | |
'bundles of cable' | |
"Hundreds of white, yellow, and orange cables are connected to the | |
router. The cables are gathered into giant bundles, which feed into | |
conduits. " | |
isPlural = true | |
isNominallyIn(obj) | |
{ | |
return obj == nrConduits || obj == nrRouterSockets || inherited(obj); | |
} | |
attachedObjects = [nrRouter, nrRouterSockets] | |
cannotDetachMsg(obj) { return 'You shouldn\'t do that; you don\'t want | |
to interrupt anyone\'s network service. '; } | |
; | |
+ nrRouter: PlugAttachable, NearbyAttachable, Heavy | |
'floor-standing network router box' 'network router' | |
"It's a big piece of equipment, the size of a tall bookcase. | |
Hundreds of network wires are connected to sockets arrayed across | |
the front of the box. One one side is a multi-prong diagnostic jack. " | |
cannotDetachMsg(obj) { return 'You shouldn\'t do that; you don\'t | |
want to interrupt anyone\'s network service. '; } | |
iobjFor(PutIn) remapTo(PlugInto, DirectObject, self) | |
; | |
/* | |
* An object representing all of the sockets, and any individual socket | |
* that we don't otherwise enumerate with its own object. | |
*/ | |
++ nrRouterSockets: PermanentAttachment, Component | |
'network socket/sockets' 'network sockets' | |
"The router has hundreds of sockets; wires are connected to most | |
of them. " | |
isPlural = true | |
dobjFor(LookIn) asDobjFor(Examine) | |
iobjFor(PutIn) { verify() { illogical('{That dobj/he} doesn\'t | |
look like it will fit any of the network sockets. '); } } | |
iobjFor(PlugInto) asIobjFor(PutIn) | |
iobjFor(AttachTo) asIobjFor(PutIn) | |
; | |
++ PluggableComponent | |
'diagnostic multi-prong jack/port/socket' 'diagnostic jack' | |
"It's a jack for a special kind of connector, probably for | |
a network analyzer.<.reveal need-net-analyzer> " | |
iobjFor(PutIn) remapTo(AttachTo, DirectObject, self) | |
; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - campus section. This contains most of the | |
* campus outdoor locations, and the smaller campus building interiors. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
#include <bignum.h> | |
#include "ditch3.h" | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* A plot event describing the start of the on-campus section of the | |
* game. This is ostensibly three weeks after the introductory section - | |
* but we don't have a calendar in the game, so just make it day 2, and | |
* no one will ever know. | |
*/ | |
onCampusPlotEvent: ClockEvent eventTime = [2, 8, 30]; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Set up for the campus section. Show the transition passage, move the | |
* player character to the starting campus location, and adjust the | |
* inventory appropriately. | |
*/ | |
campusInit() | |
{ | |
/* pause, then clear the screen */ | |
"\b"; | |
inputManager.pauseForMore(nil); | |
initScreen(true); | |
/* notify the clock manager that we've reached a plot point */ | |
onCampusPlotEvent.eventReached(); | |
/* show the transitional text */ | |
"\b\b\b\b<i>Three weeks later...</i> | |
\b\b | |
\bThe airport shuttle drops you off at the end of San Pasqual | |
St., at the east edge of the Caltech campus, and drives off. | |
<.p>You'd forgotten how incredibly heinous LA traffic can be. | |
The 105 was solid tail-lights all the way from LAX to downtown, | |
and the 110 was a parking lot most of the way up to Pas. You had | |
thought you might poke around campus a little before your | |
appointment, but that's out of the question now; better get | |
straight to the Career Center Office. Speaking of which, it | |
seems like they move the Career Center to a new building every | |
time you're down here; this time it's in the Student Services | |
building, a short distance up Holliston.<.p> "; | |
/* get rid of all of the power-plant-related possessions */ | |
foreach (local cur in me.contents) | |
{ | |
/* | |
* if it's not clothing or a non-portable, or it's marked as | |
* 'introOnly', get rid of it | |
*/ | |
if (cur.introOnly | |
|| (!cur.ofKind(Wearable) && !cur.ofKind(NonPortable))) | |
cur.moveInto(nil); | |
} | |
/* restore the normal shoes */ | |
myShoes.moveInto(me); | |
myLeftShoe.moveInto(nil); | |
/* un-rip the pants and brush off the accumulated dust */ | |
khakis.makeTorn(nil); | |
myDust.moveInto(nil); | |
/* move us to San Pasqual and have a look around */ | |
me.moveIntoForTravel(sanPasqual); | |
me.lookAround(true); | |
/* move our briefcase (and its contents) into the player's inventory */ | |
toteBag.moveInto(me); | |
/* | |
* Now that we have the tote bag, set a reasonable carrying limit, so | |
* that our hands don't get ridiculously full. The tote bag is a | |
* "bag of holding," so it'll manage our inventory for us | |
* automatically as our hands get overfull, which means that there's | |
* no inconvenience for the player in setting a holding limit. | |
*/ | |
me.bulkCapacity = 6; | |
/* also pick up our keyring and wallet */ | |
myKeyring.moveInto(myPocket); | |
myWallet.moveInto(myPocket); | |
/* | |
* Since this represents an abrupt transition in the game, and since | |
* we're triggered by a simple EXAMINE command, cancel anything | |
* remaining on the command line. Anything remaining on the command | |
* line will probably be suitable only in the old context, and won't | |
* make any sense now that we've jumped to a new chapter. | |
*/ | |
throw new TerminateCommandException(); | |
} | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* A few topics for questions in this part of the game | |
*/ | |
ddTopic: Topic 'ditch day'; | |
nicTopic: Topic 'network installer company/nic/n.i.c.'; | |
stackTopic: Topic 'ditch day stack/stacks'; | |
stamerStackTopic: Topic '(brian) (brian\'s) (stamer\'s) ditch day stack'; | |
paulStackTopic: Topic '(paul) (paul\'s) ditch day stack/password'; | |
caltechTopic: Topic '(cal) caltech/tech'; | |
stamerLabTopic: Topic | |
'(brian) (brian\'s) (stamer\'s) 022 lab/laboratory/bridge'; | |
stamerTopic: Topic 'brian stamer'; | |
ratTopic: Topic 'rat/rats'; | |
plisnikTopic: Topic 'plisnik'; | |
scottTopic: Topic 'scott'; | |
jayTopic: Topic 'jay santoshnimoorthy'; | |
turboTopic: Topic 'turbo power animals'; | |
windTunnelTopic: Topic 'guggenheim wind hypervelocity shock tunnel'; | |
guggenheimTopic: Topic 'guggenheim'; | |
galvaniTopic: Topic 'project 2 galvani/galvani-2'; | |
explosionTopic: Topic 'explosion'; | |
jobNumberTopic: Topic 'job number'; | |
ipAddressesTopic: Topic 'ip address/addresses'; | |
jobTopic: Topic 'my your job'; | |
bossTopic: Topic 'my your vice boss/vp/president/v.p.'; | |
productsTopic: Topic | |
'omegatron omegatron\'s company\'s my your product/products'; | |
bureaucracyTopic: Topic | |
'omegatron omegatron\'s company\'s bureaucracy'; | |
otherJobOffersTopic: Topic 'other job offer/offers'; | |
startupsTopic: Topic 'start-up startup company/companies/start-ups/startups'; | |
supplyRoomTopic: Topic 'supply supplies room keypad room/door/lock'; | |
eeTextbookRecTopic: Topic | |
'(ee) (electrical) (engineering) book books textbooks (recommendations)'; | |
eeLabRecTopic: Topic | |
'(ee) (electrical) (engineering) lab laboratory manual (recommendations)'; | |
physicsTextTopic: Topic | |
'quantum physics text book/books/text/texts/textbook/textbooks'; | |
eeTextTopic: Topic | |
'ee electrical engineering text book/books/text/texts/textbook/textbooks'; | |
labManualTopic: Topic 'ee electrical engineering lab manual'; | |
drdTopic: Topic | |
'drd math mathematics mathematical functions/table/tables/handbook/book' | |
; | |
quantumComputingTopic: Topic 'quantum computing/computer/computers'; | |
qubitsTopic: Topic 'qubits programming language/qubit/qubits'; | |
videoAmpTopic: Topic 'video amp/amps/amplifiers'; | |
waveformTopic: Topic 'waveforms'; | |
the1a80Topic: Topic '1a80 cpu'; | |
hovarthTopic: Topic 'hovarth number/numbers/function/functions'; | |
programmingHovarthTopic: Topic | |
'programming (hovarth) (numbers)/(number)/(function)/(functions)'; | |
lostQuarterTopic: Topic 'lost quarter'; | |
lunchTopic: Topic 'lunch'; | |
bloemnerTopic: Topic | |
'by blomner bloemner bl\u00F6mner blomner\'s bloemner\'s bl\u00F6mner\'s | |
introductory quantum physics | |
text book/books/text/texts/textbook/textbooks'; | |
sAndP3Topic: Topic | |
'science&progress s&p science & progress magazine | |
issue number xlvi-3'; | |
sAndPTopic: Topic | |
'science&progress s&p science & progress magazine issue number -'; | |
qrlTopic: Topic | |
'quantum review letters qrl number issue volume journal/letters/qrl'; | |
qrlVolumeTopic: Topic | |
'quantum review letters qrl number issue 70 73 volume | |
journal/letters/qrl'; | |
qrl7011cTopic: Topic 'quantum review letters qrl number issue 70:11c'; | |
qrl739aTopic: Topic 'quantum review letters qrl number issue 73:9a'; | |
morgenTopic: Topic | |
'yves morgen electronics lectures text | |
book/books/text/texts/textbook/textbooks'; | |
townsendTopic: Topic 'e.j. townsend lab laboratory manual'; | |
efficiencyStudy37Topic: Topic '37 efficiency study'; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* A person who starts off unknown to the PC but can be introduced. Once | |
* introduced, we refer to the person by proper name; before that, we can | |
* refer to them by description only, since we don't know their name yet. | |
*/ | |
class IntroPerson: Person | |
/* my proper name - this will replace our name when we're introduced */ | |
properName = nil | |
/* have we been introduced yet? */ | |
introduced = nil | |
/* mark the person as introduced */ | |
setIntroduced() | |
{ | |
/* | |
* once we're introduced, we refer to the person using the proper | |
* name in messages | |
*/ | |
isProperName = true; | |
name = properName; | |
disambigName = properName; | |
/* note that we're now introduced */ | |
introduced = true; | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Some inventory items we gain when we start the campus section. | |
*/ | |
myKeyring: Keyring 'key keyring/ring' 'keyring' | |
"It's just a simple metal ring you use to hold your keys. " | |
/* this item can go in my pocket, and that's where it naturally goes */ | |
okayForPocket = true | |
bestForPocket = true | |
/* | |
* use an elevated affinity for my keys, so we prefer to put them on | |
* the keyring to putting them in a pocket | |
*/ | |
affinityFor(obj) { return isMyKey(obj) ? 300 : 0; } | |
/* | |
* on taking the keyring, attach any keys in my pocket or in the tote | |
* bag, as long as the tote bag is open and in my possession | |
*/ | |
getLooseKeys(actor) | |
{ | |
local lst; | |
/* inherit the default handling first to get my directly held keys */ | |
lst = inherited(actor); | |
/* if it's 'me', add in what's in my pocket and tote bag */ | |
if (actor == me) | |
{ | |
/* add what's in my pocket */ | |
lst += myPocket.contents; | |
/* if we have the tote bag, and it's open, add its contents */ | |
if (toteBag.isIn(actor) && toteBag.isOpen) | |
lst += toteBag.subContainer.contents; | |
} | |
/* return what we found */ | |
return lst; | |
} | |
; | |
/* all of the keys in the game are suitable for pocketing */ | |
class DitchKey: Key | |
okayForPocket = true | |
bestForPocket = true | |
; | |
+ DitchKey 'house key/(house)*keys' 'house key' | |
"It's the key to your house. " | |
; | |
/* special precondition for closing the wallet if we're pocketing it */ | |
walletClosedInPocket: objClosed | |
checkPreCondition(obj, allowImplicit) | |
{ | |
/* | |
* if we're pocketing the wallet, inherit the objClosed handling; | |
* if it's going anywhere else, we have nothing to enforce | |
*/ | |
if (gIobj == myPocket) | |
return inherited(obj, allowImplicit); | |
else | |
return nil; | |
} | |
verifyPreCondition(obj) | |
{ | |
/* if we're pocketing the wallet, apply the default handling */ | |
if (gIobj == myPocket) | |
inherited(obj); | |
} | |
; | |
myWallet: BagOfHolding, Openable, RestrictedContainer | |
'brown leather wallet' 'wallet' | |
"It's a simple brown leather wallet. " | |
owner = me | |
/* putting the wallet in my pocket requires the wallet to be closed */ | |
dobjFor(PutIn) { preCond = ([walletClosedInPocket] + inherited) } | |
/* opening the wallet requires holding it */ | |
dobjFor(Open) { preCond = (inherited + objHeld) } | |
/* start closed */ | |
initiallyOpen = nil | |
/* restrict our contents to walletable items */ | |
canPutIn(obj) { return obj.okayForWallet; } | |
cannotPutInMsg(obj) { return 'You don\'t like to stuff your wallet | |
with miscellaneous junk. '; } | |
/* | |
* We have a high affinity for walletable items, but only if we're | |
* already open. If we're closed, don't bother trying to accept | |
* items, since we might have to take it out of the pocket to open | |
* it, which could cause a cascade of other hands-freeing operations. | |
* Better to avoid these and just let the walletable item go directly | |
* in the pocket, since we're allowed to pocket walletable items | |
* directly. | |
* | |
* Use an especially high affinity when we're open and the object is | |
* walletable. This ensures that we'll prefer to put walletable | |
* items in the wallet rather than directly in the pocket when | |
* possible, even though walletable items are also pocketable. | |
*/ | |
affinityFor(obj) { return isOpen && obj.okayForWallet ? 300 : 0; } | |
/* this item naturally belongs in our pocket */ | |
okayForPocket = true | |
bestForPocket = true | |
; | |
class WalletItem: Thing | |
/* these are all okay for the wallet */ | |
okayForWallet = true | |
/* these are also directly pocketable */ | |
okayForPocket = true | |
/* | |
* Because this kind of thing is small, we need to be holding it to | |
* show it to someone, and it has to be visible. Don't actually | |
* require holding it, though, since this would needlessly take it | |
* out of the wallet; instead, just require that we can touch it, so | |
* that we at least know we could in theory take it out and put it | |
* away again. | |
*/ | |
dobjFor(ShowTo) { preCond = [objVisible, touchObj]; } | |
; | |
+ alumniID: WalletItem 'alumni id identification card' 'alumni ID card' | |
"This card identifies you as a Caltech alum. " | |
owner = me | |
; | |
+ cxCard: WalletItem 'corporate consumer express credit cumex card' | |
'Consumer Express card' | |
"Omegatron chose Consumer Express as the corporate credit card | |
because it was the cheapest, no doubt, ignoring the inconvenient | |
practical matter that hardly anyone accepts it. " | |
owner = me | |
; | |
+ driverLicense: WalletItem | |
'california driver\'s driving license/licence/photo/photograph' | |
'driver\'s license' | |
"This is your California driver's license, complete with eyes-closed | |
photo. " | |
owner = me | |
; | |
/* | |
* Our tote bag, which we carry around everywhere on campus. This is a | |
* "bag of holding," which means that we can stuff practically anything | |
* into it, and that we'll automatically put items into the bag whenever | |
* our hands get full (provided we're still carrying the bag). | |
* | |
* To allow hands-free use of the bag, we'll make the bag wearable; | |
* we'll give the bag a shoulder strap so that this makes sense. | |
* Because we need the strap to be a component attached to the outside | |
* of the bag, rather than inside the bag, we need to make the bag a | |
* "complex container." This means that the bag has a secret inner | |
* container, called its "subContainer," that actually holds the bag's | |
* contents. | |
*/ | |
toteBag: BagOfHolding, Wearable, ComplexContainer 'tote bag' 'tote bag' | |
"You always bring this tote bag when traveling. It's made of a | |
tough, pliable synthetic, and it expands to accommodate almost | |
anything. << | |
isWornBy(me) | |
? "The bag is currently slung over your shoulder by its strap. " | |
: "A strap lets you wear it over your shoulder. ">> " | |
subContainer: ComplexComponent, OpenableContainer { | |
/* | |
* Since the tote bag will tend to become rather full, list its | |
* contents separately rather than in-line; the in-line form of | |
* the list becomes pretty unwieldy when we contain a lot of | |
* items. | |
*/ | |
contentsListedSeparately = true | |
} | |
dobjFor(Wear) | |
{ | |
action() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* if this isn't an implied action, add an explanation */ | |
if (!gAction.isImplicit) | |
"You sling the bag over your shoulder. "; | |
} | |
} | |
; | |
+ Component '(tote) (bag) padded shoulder strap' 'shoulder strap' | |
"It's a padded strap that lets you carry the bag over one | |
shoulder. " | |
; | |
+ Readable 'quick reference career center office information sheet' | |
'information sheet' | |
"<i>Caltech Career Center Office</i> | |
<.p>Thanks for your interest in on-campus recruiting at Caltech. | |
This is a quick reference to help ensure your visit is smooth and | |
productive. | |
<.p>When you arrive on campus, come by the Career Center Office. | |
We're in the Student Services building on S.\ Holliston Ave., on | |
the first floor—please refer to the enclosed map of the campus | |
if you have any trouble finding us. When you get here, check in | |
with our staff and we'll direct you to your interview location. | |
<.p>We're here to ensure that your visit is productive for both | |
your company and our students. If there's anything we can do to | |
make your visit more successful, let us know. | |
<.p>Sincerely, | |
\n<i>the Caltech Career Center Office staff</i> " | |
/* this is inside the bag's subContainer */ | |
subLocation = &subContainer | |
; | |
/* | |
* A map of the campus, showing where each building is. The player can | |
* use the map to get directions to a building by typing FIND <building> | |
* ON MAP. | |
* | |
* We include a map to address the potential gap between the player | |
* character's knowledge and the player's. The PC is a Caltech alum, so | |
* he knows the campus well enough that he wouldn't need this map. We | |
* can't assume the same thing about the player, though - the player | |
* might never even have heard of Caltech. (And even if the player were | |
* a Caltech alum, they still might want a map, because the campus | |
* depicted in the game isn't entirely realistic,) So, we somehow need to | |
* give the player access to the knowledge of the (fake) campus that the | |
* PC has. | |
* | |
* There are several ways we could have solved this problem: | |
* | |
* - Show a JPEG graphical map of the campus on-screen during the game. | |
* There are a couple of huge drawbacks with this approach, though. | |
* First, it would only work on the full HTML interpreters, so we'd have | |
* to provide an alternative for players using text-only terps. Second, | |
* I personally dislike the subjective experience of playing a game | |
* that's mostly text with occasional graphics. A game that mixes | |
* graphics and text destroys my ability to become immersed in a verbal | |
* frame of mind by constantly drawing my attention to my physical | |
* senses, which in turn reminds me that I'm reading text. And once I | |
* start looking at graphics, I want to see graphics of everything; it | |
* starts to feel like the game is hiding the graphics from me when it | |
* selectively shows pictures for some things but not for others. | |
* | |
* - Provide an ASCII-graphics map. This would address the text-only | |
* interpreter problem above, but it has exactly the same problems with | |
* immersion, because it forces the player to shift mental gears just | |
* like a real picture does. Plus, ASCII graphics tend to look pretty | |
* cheesy. | |
* | |
* - Provide a graphical map of the campus as part of the game's | |
* "feelies" package (as PDF, say). This solves the problem above with | |
* text-only interpreters, but it has all of the same problems with | |
* mixing graphical and verbal "mental modes." I also find it quite | |
* jarring when a game employs "extrasomatic" props; when a game tells | |
* you something like "please refer to the campus map enclosed with your | |
* game package," it destroys my sense of immersion by calling explicit | |
* attention to the fact that I'm playing a game. The more | |
* self-contained the game world is, the more immersive I find it. | |
* | |
* - Let the player find their way around by exploration. This would | |
* have been marginally acceptable, since the campus setting isn't that | |
* complex, but I think it would have robbed the game of a certain | |
* momentum early on. In the early parts of the game, the player | |
* character has specific goals that require going to specific places | |
* that the PC is depicted as being very familiar with. If we were to | |
* force the player to wander around to find these things the PC is | |
* supposedly already familiar with, it would create a weird disconnect | |
* between the story and the action. Now, the player could choose to | |
* wander around and explore anyway, but that would be their choice, so | |
* there'd be no reason for them to feel any incongruity; but we don't | |
* want to force a player who just wants to get on with the story to | |
* stumble around making a map. | |
* | |
* - Find an excuse to give directions within the game. I actually | |
* experimented with that at first, by having Ms. Dinsdale give Frosst a | |
* list of turns to make to get to Dabney; the stack instructions could | |
* likewise have given directions to Bridge. But this felt a bit forced; | |
* in real life, Ms. Dinsdale would have just handed Frosst a map and | |
* said "go here," pointing to #58. (This is actually what motivated me | |
* to put the map in the game: I did the "what would really happen?" | |
* thought experiment, and this is clearly what would happen in real | |
* life.) | |
* | |
* - Provide a GO TO command: GO TO DABNEY HOUSE, GO TO BRIDGE LAB. This | |
* would essentially give the player a way to tap into the PC's knowledge | |
* automatically. This would have made sense from a story perspective, | |
* and overall it's really not a bad approach, but I ultimately decided | |
* against it for a couple of reasons. First, it's not a conventional IF | |
* command. That's not a showstopper, certainly, but it's a point | |
* against this approach. Second, and more importantly, it creates an | |
* odd user interface in which a single command performs multiple travel | |
* steps. This is the part of the UI that I really don't like. If we do | |
* a LOOK at each stop along the way, as we would for a single travel | |
* step, we could have pages and pages of output; if we didn't do a LOOK | |
* at each step, we could omit some crucial detail. In either case, the | |
* player might miss something because we showed too much or too little | |
* output. I think it's important in text IF design to keep the level of | |
* detail of interaction consistent. A GO TO command would obviously | |
* create a level of interaction detail substantially different from | |
* ordinary travel commands. Third, my personal experience playing games | |
* with things similar to GO TO commands is that this approach actually | |
* makes it very difficult to learn the layout of the game world. The | |
* (usually quick) learning process of traversing the directions manually | |
* just doesn't happen when the game is doing the work for you, and as a | |
* result, your mind's eye doesn't seem to be able to put together a | |
* clear picture of the game world. So I'd prefer to give the player the | |
* tools to find their way around easily, but still let the player carry | |
* out the individual steps of the travel manually. For authors who do | |
* like the idea of a GO TO command, note that the implementation here | |
* could be adapted pretty easily for use in a GO TO command, since this | |
* code has to do all the work of finding the path between two points; | |
* the only thing you'd have to do is execute the series of nested | |
* commands necessary to carry out the travel. | |
* | |
* The solution that seems best to me is to include this *narrated* map. | |
* The map fits into the narration style of the rest of the game - its | |
* appearance is described textually, not included as a graphic. There's | |
* a perfectly good reason in the context of the story for the map to be | |
* in the PC's inventory: the Career Center Office would certainly send a | |
* campus map to a recruiter coming to campus. Interacting with the map | |
* is achieved with a convention well established in text IF, | |
* specifically "consultation." So the map fits well into the game in | |
* terms of both story and user interface, and solves the problem we set | |
* out to solve: it gives the player the same effective familiarity with | |
* the campus that the PC has. | |
*/ | |
+ campusMap: Consultable '(caltech) (cal) (tech) campus map' 'campus map' | |
"It's a schematic map showing the locations of the campus's main | |
buildings. You know your way around campus from your days here | |
as a student, of course, but it's still worth having a | |
map in case anything's changed lately. If you need to | |
go somewhere unfamiliar, you can just look it up on the map. " | |
/* this is inside the bag's subContainer */ | |
subLocation = &subContainer | |
/* | |
* Note that we implement a custom ConsultAbout handler, rather than | |
* using the standard topic database mechanism. There's a special | |
* kind of topic we recognize, so rather than making a database of | |
* match association objects, we just look for our topics directly. | |
* We could have used the topic database scheme, but this would | |
* require us to create a separate ConsultTopic object for each | |
* CampusMapEntry object, which in this case is unnecessary | |
* duplication. | |
*/ | |
dobjFor(ConsultAbout) | |
{ | |
/* we have to be holding the map to find something on it */ | |
preCond = [objHeld] | |
action() | |
{ | |
local lst; | |
local dst; | |
local loc; | |
local path; | |
/* note the consultation */ | |
gActor.noteConsultation(self); | |
/* get the topic matches */ | |
lst = gTopic.inScopeList; | |
/* make sure we have at least one match to the topic phrase */ | |
if (lst.length() == 0) | |
{ | |
"You can't find anything on the map by that name. "; | |
return; | |
} | |
/* pick the entry with the highest match strength */ | |
dst = lst[1]; | |
foreach (local cur in lst) | |
{ | |
/* if this one has a higher match strength, use it */ | |
if (cur.mapMatchStrength > dst.mapMatchStrength) | |
dst = cur; | |
} | |
/* if we have more than one match, note which one we picked */ | |
if (lst.length() > 1) | |
"<.assume><<dst.name>><./assume>\n"; | |
/* mention where it is */ | |
dst.desc; | |
/* if it's not in the game, say that it's not important */ | |
if (!dst.isInGame) | |
"You really have no reason to go there right now, though, | |
and much as you'd like to explore, you remind yourself to | |
stay focused on the task at hand. "; | |
/* | |
* in any case, if there's no location, we can't give | |
* directions, so we're done | |
*/ | |
if (dst.location == nil) | |
return; | |
/* find the nearest enclosing campus outdoor location */ | |
for (loc = gActor.location ; loc != nil ; loc = loc.location) | |
{ | |
/* if this is an outdoors location on campus, we're set */ | |
if (loc.ofKind(CampusOutdoorRoom)) | |
break; | |
} | |
/* if we're not outdoors on campus, we can't navigate from here */ | |
if (loc == nil) | |
{ | |
"(The map only shows the locations of buildings, not | |
interior floor plans, so you'll have to go outside and | |
get your bearings if you want to figure the way there.) "; | |
return; | |
} | |
/* if we're already there, just say so */ | |
if (dst.location == loc || dst.altLocations.indexOf(loc) != nil) | |
{ | |
"It looks from the map like it should be right here. "; | |
return; | |
} | |
/* compute the path */ | |
path = campusMapPathFinder.findPath(gActor, loc, dst.location); | |
/* if we found the path, show it */ | |
if (loc.propDefined(&cannotUseMapHere)) | |
{ | |
/* | |
* this location has a special reason that the map can't | |
* be used here - show it | |
*/ | |
loc.cannotUseMapHere; | |
} | |
else if (path != nil) | |
{ | |
local idx; | |
/* | |
* Each map entry can represent more than one game | |
* location. For example, the Olive Walk spans several | |
* rooms, and any of them should be considered the olive | |
* walk for path-finding purposes. Find the first entry | |
* in the path list that appears as an alternative | |
* location for the destination, and cut off the list at | |
* that point. | |
*/ | |
if ((idx = path.indexWhich( | |
{p: dst.altLocations.indexOf(p) != nil})) != nil | |
&& idx < path.length()) | |
{ | |
/* | |
* 'idx' is the first entry that can be considered | |
* our destination, so truncate the list there | |
*/ | |
path.removeRange(idx + 1, path.length()); | |
} | |
/* show the path */ | |
"To get there from here, you'd go "; | |
for (local i = 2, local len = path.length() ; i <= len ; ++i) | |
{ | |
local prv = path[i-1]; | |
local cur = path[i]; | |
/* find the direction to get from 'prv' to 'cur' */ | |
local dir = directionFromTo(prv, cur); | |
/* show the direction if we found one */ | |
if (dir != nil) | |
"<<dir.name>> "; | |
else | |
"then "; | |
/* add the destination name */ | |
"to <<cur.getDestName(gActor, prv)>>"; | |
/* add appropriate list separation */ | |
if (i == len - 1) | |
", and "; | |
else if (i < len) | |
", "; | |
} | |
". "; | |
/* remember the current destination and the path there */ | |
lastDest = dst; | |
lastPath = path; | |
lastPathIdx = 1; | |
} | |
else | |
{ | |
/* | |
* this shouldn't ever happen, since every outdoor | |
* location on our rendition of the campus is connected | |
* to every other outdoor location, but just in case... | |
*/ | |
"You can't tell from the map how to get there from here. "; | |
} | |
} | |
} | |
/* find the direction of the connector from 'a' to 'b' */ | |
directionFromTo(a, b) | |
{ | |
/* scan each possible direction for a matching connector */ | |
return Direction.allDirections.valWhich(new function(d) | |
{ | |
local conn = a.getTravelConnector(d, gActor); | |
return (conn != nil | |
&& conn.isConnectorListed | |
&& conn.getDestination(a, gActor) == b); | |
}); | |
} | |
/* | |
* The last destination we looked up, the path there, and the index | |
* of our current position in the path. As we travel from here, | |
* we'll follow along; if they stay on the path, we'll provide | |
* updates on the next direction to go at each point. | |
*/ | |
lastDest = nil | |
lastPath = nil | |
lastPathIdx = nil | |
/* | |
* Check to see if we can continue from our current location to the | |
* last destination we looked up. If we're still on the path, we'll | |
* show how to continue; otherwise, we'll assume they've wandered | |
* off, so we'll forget about trying to provide directions. | |
*/ | |
continueToDestination() | |
{ | |
/* if it's an NPC traveling, it's obviously not relevant */ | |
if (!gActor.isPlayerChar) | |
return; | |
/* | |
* if the PC is traveling, and we have a working destination, | |
* and we're in the next location on the path we calculated, | |
* show how to get to the next location from here | |
*/ | |
if (lastDest != nil | |
&& lastPathIdx + 1 <= lastPath.length() | |
&& me.isIn(lastPath[lastPathIdx + 1])) | |
{ | |
/* note that we're in the next location now */ | |
++lastPathIdx; | |
/* | |
* if we're at the end of the path, say so; otherwise, show | |
* how to continue | |
*/ | |
if (lastPathIdx == lastPath.length()) | |
{ | |
"<.p>It looks like you've arrived at <<lastDest.name>>. "; | |
} | |
else | |
{ | |
/* get the direction name */ | |
local dir = directionFromTo(lastPath[lastPathIdx], | |
lastPath[lastPathIdx + 1]); | |
/* show how to continue */ | |
if (dir != nil) | |
"<.p>To continue to <<lastDest.name>>, it looks like | |
you'd go <<dir.name>> from here. "; | |
} | |
} | |
else | |
{ | |
/* | |
* we must have gone off the path, or reached the | |
* destination; we're no longer on a path, so clear out the | |
* navigation memory | |
*/ | |
lastDest = nil; | |
} | |
} | |
/* | |
* Resolve the topic. Only CampusMapEntry objects can be found on | |
* the map, so limit the resolved topic to these. | |
*/ | |
resolveConsultTopic(lst, np, resolver) | |
{ | |
/* keep only CampusMapEntry objects in the resolved topic list */ | |
lst = lst.subset({x: x.obj_.ofKind(CampusMapEntry)}); | |
/* return a ResolvedTopic with the map entries */ | |
return new ResolvedTopic(lst, [], [], np); | |
} | |
; | |
/* | |
* A path finder for the campus map. We only include outdoor campus | |
* rooms in the map, since the map doesn't cover the insides of buildings | |
* or non-campus locations. | |
*/ | |
campusMapPathFinder: roomPathFinder | |
/* include only campus outdoor rooms in the map */ | |
includeRoom(loc) { return loc.ofKind(CampusOutdoorRoom); } | |
; | |
/* | |
* A class for campus map building entries. This is simply a Topic for a | |
* named building on the campus map. For convenience in defining the | |
* map, we can associate each of these with a CampusOutdoorRoom on the | |
* game map, using the 'location' property of the map entry to put the | |
* map entry inside its associated campus outdoor room. If an entry has | |
* no associated room, then it's there for completeness only and can't be | |
* used as a navigation destination. | |
*/ | |
class CampusMapEntry: Topic | |
/* the associated CampusOutdoorRoom, if any */ | |
location = nil | |
/* are we in the game? we are if we have a location */ | |
isInGame = (location != nil) | |
/* alternative set of locations that are also considered to be here */ | |
altLocations = [] | |
/* the full name */ | |
name = nil | |
/* | |
* The description of the general location of the building on | |
* campus. We'll report this when the player looks up the building | |
* on the map. By default, we'll report that it's in the quadrant | |
* defined for the building. | |
*/ | |
desc = "You scan the map and find <<name>> in the <<quadrant>> part | |
of the campus. " | |
/* | |
* The "quadrant" of the campus containing the building. This is | |
* used by the default 'desc' to describe roughly where the building | |
* is. If 'desc' is customized so that it doesn't refer to the | |
* quadrant, this won't be needed. | |
*/ | |
quadrant = 'central' | |
/* all of these are known by virtue of appearing on the map */ | |
isKnown = true | |
/* | |
* Match strength of this map entry. In cases of ambiguous matches, | |
* we'll take the one with the highest match strength. | |
*/ | |
mapMatchStrength = 100 | |
; | |
CampusMapEntry template 'vocabWords' 'name' 'quadrant' | "desc"; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Map entries for various Dabney locations. We don't need a map for | |
* these, but the player might not know where things are, so this is a | |
* fairly natural way for us to reveal PC knowledge to the player. | |
*/ | |
class DabneyMapEntry: CampusMapEntry | |
vocabWords = 'dabney house hovse -' | |
desc = "The campus map isn't detailed enough to show the interior | |
layout of Dabney, but after four years of living there, you | |
know where <<name>> is: <<subdesc>>. " | |
/* | |
* all of these are in the game map, even though we don't give | |
* specific directions to them | |
*/ | |
isInGame = true | |
; | |
DabneyMapEntry template 'vocabWords' 'name' "subdesc"; | |
DabneyMapEntry '1 alley one' 'Alley One' | |
"north of the breezeway, which is just west of the courtyard" | |
; | |
DabneyMapEntry '2 alley two' 'Alley Two' | |
"upstairs from Alley One" | |
; | |
DabneyMapEntry '3 alley three' 'Alley Three' | |
"just off the courtyard, to the southwest" | |
; | |
DabneyMapEntry '4 alley four' 'Alley Four' | |
"upstairs from Alley Three" | |
; | |
DabneyMapEntry '5 alley five' 'Alley Five' | |
"just off the courtyard, to the southeast" | |
; | |
DabneyMapEntry '6 alley six' 'Alley Six' | |
"upstairs from Alley Five" | |
; | |
DabneyMapEntry '7 upper lower alley seven' 'Alley Seven' | |
"upstairs from the courtyard" | |
; | |
DabneyMapEntry 'courtyard' 'the courtyard' | |
"in the middle of the house, right inside from the Orange Walk" | |
; | |
DabneyMapEntry 'lounge' 'the lounge' | |
"just off the courtyard, to the east" | |
; | |
DabneyMapEntry 'dining room' 'the dining room' | |
"north of the lounge" | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Define a bunch of map points that aren't part of the game world. | |
* These will show up on the campus map for completeness, but they're | |
* not associated with anything in the game map. We can look these up, | |
* but the mapper won't be able to generate routes to these for obvious | |
* reasons; we'll simply say that we're not interested in going there if | |
* asked for a route. | |
*/ | |
CampusMapEntry 'alles lab/laboratory' 'Alles Laboratory' 'southwest'; | |
CampusMapEntry 'alumni house' 'the Alumni House' 'northeast'; | |
CampusMapEntry 'arms lab/laboratory' 'Arms Laboratory' 'southwest'; | |
CampusMapEntry 'avery center' 'Avery Center' 'north'; | |
CampusMapEntry 'baxter hall' 'Baxter Hall' 'north'; | |
CampusMapEntry 'beckman institute' 'the Beckman Institute' 'northwest'; | |
CampusMapEntry 'beckman behavioral lab/labs/laboratory/laboratories/science' | |
'Beckman Laboratories of Behavioral Science' 'north'; | |
CampusMapEntry 'beckman chemical lab/laboratory/synthesis' | |
'Beckman Laboratory of Chemical Synthesis' 'southwest'; | |
CampusMapEntry 'blacker house/hovse' 'Blacker House' 'southeast'; | |
CampusMapEntry 'braun athletic center/gym' 'Braun Athletic Center' | |
"It's on the south side of California Blvd. "; | |
CampusMapEntry 'braun house' 'Braun House' 'east'; | |
CampusMapEntry 'braun lab/labs/laboratory/laboratories' | |
'Braun Laboratories' 'west'; | |
CampusMapEntry 'catalina graduate housing' | |
'the Catalina Graduate Housing' 'northwest'; | |
CampusMapEntry 'central engineering services' | |
'Central Engineering Services' 'northeast'; | |
CampusMapEntry 'central plant' 'Central Plant' | |
"It's on the south side of California Blvd. "; | |
CampusMapEntry 'chandler dining hall' 'Chandler Dining Hall' 'east'; | |
CampusMapEntry 'church lab/laboratory' 'Church Laboratory' 'southwest'; | |
CampusMapEntry 'crellin lab/laboratory' 'Crellin Laboratory' 'southwest'; | |
CampusMapEntry '(dabney) hall/humanities' 'Dabney Hall' 'southwest' | |
desc = "Confusingly, the campus has two buildings named | |
Dabney: Dabney Hall of the Humanities, which is an academic | |
building full of classrooms and faculty offices, and Dabney | |
House, the student house. | |
<.p><<inherited>>" | |
; | |
CampusMapEntry '(dabney) gardens' 'Dabney Gardens' 'southwest'; | |
CampusMapEntry '(holliston) parking structure' | |
'the Holliston parking structure' 'northeast'; | |
CampusMapEntry 'industrial relations center' | |
'the Industrial Relations Center' 'northeast'; | |
CampusMapEntry 'isotope handling lab/laboratory' | |
'the Isotope Handling Laboratory' 'southwest'; | |
CampusMapEntry 'karman lab/laboratory' 'Karman Laboratory' 'south'; | |
CampusMapEntry 'keck lab/laboratories' 'Keck Laboratories' 'central'; | |
CampusMapEntry 'keith spalding building' | |
'the Keith Spalding Building' 'southeast'; | |
CampusMapEntry 'kellogg radiation lab/laboratory' | |
'the Kellogg Radiation Laboratory' 'southwest'; | |
CampusMapEntry 'kerckhoff lab/labs/laboratory/laboratories' | |
'Kerckhoff Laboratories' 'southwest'; | |
CampusMapEntry 'marks house' 'Marks House' 'east'; | |
CampusMapEntry 'mead lab/laboratory' 'Mead Laboratory' 'northwest'; | |
CampusMapEntry 'mechanical universe' 'the Mechanical Universe' 'northeast'; | |
CampusMapEntry 'project mathematics' 'Project MATHEMATICS!' 'northeast'; | |
CampusMapEntry 'moore lab/laboratory' 'Moore Laboratory' 'north'; | |
CampusMapEntry 'morrisroe astroscience lab/laboratory' | |
'Morrisroe Astroscience Laboratory' | |
"It's on the south side of California Blvd., out by the track. "; | |
CampusMapEntry 'north mudd lab/laboratory' | |
'North Mudd Laboratory' 'southwest'; | |
CampusMapEntry 'noyes lab/laboratory' 'Noyes Laboratory' 'northwest'; | |
CampusMapEntry 'parsons-gates hall/administration' | |
'the Parsons-Gates Hall of Administration' 'southwest'; | |
CampusMapEntry 'powell-booth lab/laboratory' | |
'Powell-Booth Laboratory' 'central'; | |
CampusMapEntry 'public events ticket office/offices' | |
'the Public Events ticket offices' 'northwest'; | |
CampusMapEntry 'public relations' 'Public Relations' 'northeast'; | |
CampusMapEntry 'media relations' 'Media Relations' 'northeast'; | |
CampusMapEntry 'visitors center' 'the Visitors Center' 'northeast'; | |
CampusMapEntry 'robinson lab/laboratory' 'Robinson Laboratory' 'southwest'; | |
CampusMapEntry 'sherman fairchild (library)' | |
'the Sherman Fairchild Library' 'south'; | |
CampusMapEntry 'sloan lab/laboratory' 'Sloan Laboratory' 'southwest'; | |
CampusMapEntry 'south mudd lab/laboratory' | |
'South Mudd Laboratory' 'southwest'; | |
CampusMapEntry 'spalding lab/laboratory' 'Spalding Laboratory' 'south'; | |
CampusMapEntry 'steele house' 'Steele House' 'northeast'; | |
CampusMapEntry 'steele lab/laboratory' 'Steele Laboratory' 'north'; | |
CampusMapEntry 'theater arts/tacit' 'Theater Arts' 'northeast'; | |
CampusMapEntry 'watson lab/labs/laboratory/laboratories' | |
'Watson Laboratories' 'north'; | |
CampusMapEntry 'wilson parking structure' | |
'the Wilson parking structure' 'west'; | |
CampusMapEntry 'winnett center' 'Winnett Center' 'south'; | |
CampusMapEntry 'red door cafe' 'the Red Door Cafe' 'south'; | |
CampusMapEntry 'young health center' 'the Young Health Center' | |
"It's on the south side of California Blvd. "; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Outdoor Room on the campus | |
*/ | |
class CampusOutdoorRoom: OutdoorRoom | |
/* we have the normal ground, and the special pasadena sky */ | |
roomParts = [defaultGround, pasSky] | |
/* | |
* The cherry picker can travel to some outdoor rooms, so we need to | |
* describe the arrival when the PC drives it here. Some rooms | |
* override this, but provide a default message for those that | |
* don't. | |
*/ | |
descCherryPickerArrival = "You find an out-of-the-way spot, and | |
park the cherry picker. " | |
/* | |
* provide the cherry picker's specialDesc when the PC is in the | |
* cherry picker, and the cherry picker is in this location | |
*/ | |
inCherryPickerSpecialDesc = "You're standing in the basket of | |
a cherry picker. " | |
/* provide the specialDesc for the cherry picker when it's here */ | |
cherryPickerSpecialDesc = "A cherry picker is parked here. " | |
/* note that the cherry picker basket has just been raised here */ | |
noteCherryPickerRaised = "" | |
/* | |
* On arriving, check to see if we are continuing our journey to a | |
* location we previously looked up on the map, and provide advice | |
* on how to proceed if so. | |
*/ | |
travelerArriving(traveler, origin, connector, backConnector) | |
{ | |
/* do the normal work, which will display the room description */ | |
inherited(traveler, origin, connector, backConnector); | |
/* check to see if we should offer continuing navigation advice */ | |
campusMap.continueToDestination(); | |
} | |
; | |
/* | |
* A class for our various doors for which we will never find a key. | |
* These are just limits of the map, but we want them to look like | |
* ordinary doors. These are simply keyed lockables with no keys; the | |
* main reason for the extra class is to make it clear in the code which | |
* doors cannot ever be opened. | |
*/ | |
class AlwaysLockedDoor: LockableWithKey, Door | |
; | |
pasSky: defaultSky | |
vocabWords = '(clear) (blue) orange-brown hazy smog/haze' | |
desc() | |
{ | |
/* | |
* the smog starts around noon, so if we haven't reached an | |
* event that's noon or after yet, it's not yet smoggy | |
*/ | |
if (clockManager.curTime[2] < 12) | |
"The sky is relatively blue and clear. It's a warm, still | |
day, though, so it'll probably get smoggy in the afternoon. "; | |
else | |
"The smog isn't terrible today, but the sky has taken on | |
that distinctive orange-brown color and is looking a bit | |
hazy. "; | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* We have a few location that are or contain streets; crossing one of | |
* these has a standard response indicating that we just need to say | |
* which way to go. | |
*/ | |
class Street: object | |
dobjFor(Cross) | |
{ | |
verify() { } | |
action() { "(If you want to go somewhere, just say which | |
direction to go.) "; } | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* San Pasqual Street | |
*/ | |
sanPasqual: Street, CampusOutdoorRoom 'San Pasqual St.' 'San Pasqual' | |
'San Pasqual street' | |
"This is the end of a wide residential street. Parked cars line | |
both sides of the street, which continues to the east, out into | |
Pasadena. The street ends here and becomes a walkway to | |
the west, leading deeper into the campus. Tall bushes along the | |
south side of the street partially conceal a wall, which encloses | |
the rear of the North Houses complex. Holliston Ave., which | |
ends here in a T-intersection, continues north. " | |
vocabWords = 'san pasqual st./street' | |
isProperName = true | |
north = spHolliston | |
west = spWalkway | |
east: FakeConnector { "That way lies Pasadena. Nice enough | |
town, but you should attend to your business here first. " } | |
atmosphereList: ShuffledEventList { | |
[ | |
'', | |
'A beat-up old car drives slowly by, obviously looking | |
for parking. The driver looks your way, undoubtedly hopeful | |
that you\'re about to get in a car and leave. She creeps | |
along the curb for a bit, but after a bit she abruptly | |
drives off. ', | |
'A police siren races by on one of the nearby streets. ', | |
'An older man dressed in a garishly colorful | |
medieval court jester outfit walks past. ' | |
] | |
[ | |
'Two maintenance guys drive by, at about two miles per | |
hour, in one of those B&G electric golf carts. ', | |
'A car drives by looking for parking, but finds none | |
and leaves. ', | |
'A small group of students walks past and heads up | |
Holliston. ', | |
'A couple walking their dog strolls past, heading | |
west into the campus. ', | |
'A B&G golf cart drives by carrying a couple of | |
big metal trash cans. ' | |
] | |
eventPercent = 66 | |
} | |
; | |
+ CampusMapEntry 'san pasqual st./street' 'San Pasqual St.' 'northeast'; | |
+ Decoration 'parked car/cars' 'parked cars' | |
"San Pasqual is one of the closest places to campus where it's | |
possible to park a car, but the street is too short to accommodate | |
more than a couple dozen cars. The cars probably mostly belong to | |
students. " | |
isPlural = true | |
; | |
+ spHolliston: Street, PathPassage | |
's. s south holliston ave./avenue' 'Holliston' | |
"Holliston is another residential-style street. It ends here | |
in a T-intersection and continues north. " | |
isProperName = true | |
/* | |
* we mention in the room description that the Career Center Office | |
* is 'up' holliston... | |
*/ | |
dobjFor(ClimbUp) asDobjFor(GoThrough) | |
; | |
+ spWalkway: PathPassage 'campus/walkway/path' 'walkway' | |
"The paved walkway leads deeper into the campus to the west. " | |
/* we can't go this way until we've finished at the career center */ | |
canTravelerPass(trav) { return ccOffice.doneWithAppointment; } | |
explainTravelBarrier(trav) | |
{ | |
reportFailure('Much as you\'d like to spend some time looking | |
around the campus, you really need to get to the Career | |
Center if you\'re going to make your appointment. '); | |
} | |
; | |
+ Decoration 'eight-foot concrete tall north house houses | |
bush/bushes/wall/complex' 'wall' | |
"The North Houses are on the other side of the eight-foot | |
concrete wall, but all you can see from here is the wall. " | |
dobjFor(Climb) | |
{ | |
verify() { } | |
action() { "That really wouldn't save much time versus just | |
walking around to the Olive Walk, where you can go in | |
the front door. "; } | |
} | |
dobjFor(ClimbUp) asDobjFor(Climb) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Holliston Ave. | |
*/ | |
holliston: Street, CampusOutdoorRoom | |
'Holliston Ave.' 'Holliston' 'Holliston Avenue' | |
"This is a typically wide Pasadena residential street, running | |
north and south. The back side of the Physical Plant machine shop is | |
to the west, and the Student Services building is on the east side | |
of the street. " | |
isProperName = true | |
vocabWords = 'holliston ave./avenue' | |
south = holSanPasqual | |
east = cssLobby | |
north: FakeConnector { "There's not much more of the campus further | |
up Holliston---there are the two new undergraduate houses | |
they built a few years ago, but you should probably save the | |
sightseeing for after you're done with more urgent matters. " } | |
; | |
+ CampusMapEntry '(physical) (plant) machine shop' | |
'the physical plant machine shop' 'northeast'; | |
+ CampusMapEntry 'student services career center office building/(office)' | |
'Student Services' 'northeast'; | |
+ CampusMapEntry 's. s south holliston ave./avenue' | |
'Holliston Ave.' 'northeast'; | |
+ holSanPasqual: Street, PathPassage ->spHolliston | |
'san pasqual st./street' 'San Pasqual Street' | |
"Holliston ends in a T-intersection with San Pasqual St.\ just | |
to the south of here. " | |
isProperName = true | |
; | |
+ Fixture 'back physical plant machine (loading) shop/(dock)/(docks)/side' | |
'machine shop' | |
"The only thing to see on this side of the shop is the | |
loading docks. No trucks are loading right now, so all of | |
the roll-down doors are closed. " | |
; | |
++ Decoration 'loading dock docks (physical) (plant) (machine) (shop) | |
roll-down roll down door/doors' 'loading dock doors' | |
"The loading dock doors are all closed. " | |
isPlural = true | |
; | |
+ EntryPortal ->cssLobby 'student services building/entrance/center' | |
'Student Services building' | |
"This building was a graduate student house when you were a | |
student, and it still looks a lot like a dorm: it's a squat, | |
three-story, rectangular concrete box with little windows spaced | |
at monotonously regular intervals along the wall. A sign by | |
the entrance reads <q>Student Services Center.</q> You can enter | |
to the east. " | |
; | |
++ Readable, Decoration '(student) (services) (center) (building) sign' | |
'sign' | |
"It reads simply: <q>Student Services Center.</q> " | |
; | |
++ Decoration '(student) (services) (building) little small casement windows' | |
'windows' | |
"They're small casement windows, spaced at regular intervals. " | |
isPlural = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Center for Student Services lobby | |
*/ | |
cssLobby: Room 'Student Services Lobby' 'the Student Services lobby' 'lobby' | |
"A brightly-colored sofa and a couple of big potted plants | |
lend some cheer to this small lobby. Several doors | |
lead to adjoining rooms: to the north is a door labeled | |
<q>Office of Graduate Studies</q>; to the east, a door marked | |
<q>Student Affairs</q>; and to the south, <q>Career Center | |
Office.</q> A doorway to the west leads out to the street. " | |
vocabWords = 'student services lobby' | |
north = cssDoorN | |
south = cssDoorS | |
east = cssDoorE | |
west = holliston | |
out asExit(west) | |
; | |
+ EntryPortal ->holliston 'doorway' 'doorway' | |
"The doorway leads out to the street. " | |
; | |
+ cssDoorN: AlwaysLockedDoor | |
'"office of graduate studies" office graduate studies north n door*doors' | |
'north door' | |
"It's marked <q>Office of Graduate Studies.</q> " | |
; | |
+ cssDoorE: AlwaysLockedDoor | |
'"student affairs" student affairs east e door*doors' 'east door' | |
"It's marked <q>Student Affairs.</q> " | |
; | |
+ cssDoorS: Door | |
'"career center office" career center office south s door*doors' | |
'south door' | |
"It's marked <q>Career Center Office.</q> " | |
initiallyOpen = true | |
; | |
+ Fixture, Chair | |
'colorful brightly-colored colored sofa/couch*furniture' | |
'sofa' | |
"It's a full-sized sofa upholstered in cloth with random | |
splotches of bright colors. " | |
/* allow four people at once on the sofa (a person's bulk is 10) */ | |
bulkCapacity = 40 | |
; | |
+ Decoration 'big green leafy potted plant/plants/tree/trees' 'potted plants' | |
"They're big, green, leafy trees in clay pots. " | |
isPlural = true | |
; | |
+ Decoration 'clay pot/pots' 'clay pots' | |
"There's nothing remarkable about them; they're just there | |
to hold the trees. " | |
isPlural = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* San Pasqual walkway | |
*/ | |
sanPasqualWalkway: CampusOutdoorRoom 'San Pasqual Walkway' | |
'the San Pasqual walkway' 'walkway' | |
"This broad paved walkway curves through a well-maintained lawn | |
surrounded by campus buildings. The entrance to Jorgensen lab is | |
to the north, the Physical Plant offices are to the northeast, | |
and the back of Page House is to the southeast. | |
<.p>The walkway continues west, and abuts the end of San | |
Pasqual Street to the east. A narrower intersecting walkway leads | |
south. " | |
isProperName = true | |
east = spwStreet | |
west = spwWestWalk | |
south = spwSouthWalk | |
north = jorgensen | |
northeast = ppOffice | |
southeast: NoTravelMessage { "There's no entrance to the | |
building on this side. " } | |
; | |
+ CampusMapEntry 'jorgensen lab/laboratory' | |
'Jorgensen Laboratory' 'northeast' | |
; | |
+ CampusMapEntry 'b&g physical plant office/offices' | |
'the Physical Plant offices' 'northeast' | |
; | |
+ EntryPortal ->(location.northwest) | |
'jorgensen windowless concrete exterior/lab/laboratory/entrance' | |
'Jorgensen Lab' | |
"Jorgensen is the main computer science building. It's always | |
looked vaguely like a bunker to you, probably because of its almost | |
windowless concrete exterior. The entrance is to the north. " | |
isProperName = true | |
; | |
+ EntryPortal ->(location.north) | |
'b&g physical plant offices/building/door/doorway' | |
'Physical Plant offices' | |
"Physical Plant, a/k/a Buildings and Grounds, a/k/a B&G, is | |
the department that maintains the campus's infrastructure. This | |
modest single-story structure is where the B&G managers | |
have their offices. A doorway leads inside. " | |
isPlural = true | |
; | |
+ Immovable 'back page house/corner' 'Page House' | |
"Page is one of the student houses. Only the back corner is | |
visible here; there's no entrance on this side. " | |
isProperName = true | |
dobjFor(Enter) { verify() { illogical('There\'s no entrance to | |
the building on this side. '); } } | |
; | |
+ spwStreet: Street, PathPassage ->spWalkway | |
'san pasqual st./street' 'San Pasqual Street' | |
"San Pasqual is just to the east. " | |
isProperName = true | |
canTravelerPass(trav) { return trav != cherryPicker; } | |
explainTravelBarrier(trav) | |
{ | |
"Better not drive the cherry picker off campus; it | |
certainly can't be street-legal. "; | |
} | |
; | |
+ spwSouthWalk: PathPassage ->quadNorthWalk | |
'narrower intersecting south s walkway/path' 'intersecting walkway' | |
"The intersecting walkway leads south. " | |
; | |
+ spwWestWalk: PathPassage ->blEastWalk | |
'west w san pasqual walkway' 'west walkway' | |
"The walkway extends west, and opens into San Pasqual Street to the | |
east. " | |
; | |
+ Decoration 'well maintained grassy large lawn/grass' 'lawns' | |
"The lawns are covered with healthy green grass. " | |
isPlural = true | |
; | |
/* | |
* Create special noun phrase syntax for "Gunther the gardener" - | |
* normally, articles aren't allowed in the middle of a noun phrase like | |
* that. | |
*/ | |
grammar qualifiedSingularNounPhrase(gunther): 'gunther' 'the' 'gardener' | |
: SpecialNounPhraseProd | |
/* we match only gunther */ | |
getMatchList = [gunther] | |
/* get our "adjusted" tokens; make 'the' a miscellaneous word */ | |
getAdjustedTokens() | |
{ return ['gunther', &noun, 'the', &miscWord, 'gardener', &noun]; } | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The Physical Plant office | |
*/ | |
ppOffice: Room 'Physical Plant Office' 'the Physical Plant Office' | |
"A service counter divides this small room roughly in half, | |
the south side being for customers and the north side for | |
employees. A doorway to the southwest leads outside; on the | |
other side of the counter, a narrow hallway leads north. " | |
vocabWords = 'physical plant office' | |
southwest = sanPasqualWalkway | |
south asExit(southwest) | |
out asExit(south) | |
north = ppoHall | |
; | |
+ EntryPortal ->(location.out) 'southwest sw door/doorway' 'doorway' | |
"The doorway leads outside to the southwest. " | |
; | |
+ Surface, Fixture 'service counter' 'service counter' | |
"The counter runs across the middle of the room, dividing it | |
roughly into two halves: the south side for customers, the | |
north side for employees. " | |
dobjFor(LookBehind) { action() { "You lean over the counter a little | |
to get a look behind it, but you see nothing interesting. "; } } | |
dobjFor(StandOn) | |
{ | |
verify() { } | |
action() { "Better not; it would look weird if someone | |
walked in. "; } | |
} | |
dobjFor(SitOn) asDobjFor(StandOn) | |
dobjFor(LieOn) asDobjFor(StandOn) | |
dobjFor(Board) asDobjFor(StandOn) | |
; | |
++ oakJobCard: JobCard 'green -' 'green index card' | |
cardColor = 'green' | |
cardDept = 'GARDENING' | |
cardJob = 'TRIM OAK' | |
cardLoc = 'BECKMAN LAWN' | |
cardDesc = 'TRIM/SHAPE OAK BRANCHES. 1-MAN PERSONAL LIFTER AUTH\'D. ' | |
; | |
++ oliveJobCard: JobCard 'yellow - ' 'yellow index card' | |
cardColor = 'yellow' | |
cardDept = 'GARDENING' | |
cardJob = 'TRIM TREES' | |
cardLoc = 'OLIVE WK @ FLEMING' | |
cardDesc = 'TRIM OLIVE TREE BRANCHES & CLEAR DEBRIS. USE | |
PERSONAL LIFTER (SINGLE) IF NEEDED. ' | |
; | |
++ orwalkJobCard: JobCard 'blue -' 'blue index card' | |
cardColor = 'blue' | |
cardDept = 'ELECTRICAL' | |
cardJob = 'REPAIR LIGHTING' | |
cardLoc = 'ORANGE WK @ DABNEY HOUSE' | |
cardDesc = 'PED.\ LIGHT #S30 OUT.\ BULB OK, CHECK WIRING.' | |
; | |
++ lauritsenJobCard: JobCard 'pink -' 'pink index card' | |
cardColor = 'pink' | |
cardDept = 'ELECTRICAL' | |
cardJob = 'LIGHTING MAINT.' | |
cardLoc = 'LAURITSEN' | |
cardDesc = 'CHECK EXT.\ LIGHTING, REPAIR AS NEEDED.' | |
; | |
++ Readable '(physical) (plant) memo' 'memo' | |
"<tt>To: Shift Supervisors | |
\nFrom: Management | |
\nRe: Staff disagreements | |
\bIt has come to our attention that certain staff members have | |
not been getting along with each other. We do not want to name | |
any names, but we ask supervisors to make their daily schedules | |
to keep ERNST and GUNTHER apart. I.e., DO NOT schedule tree | |
trimming and electrical maintenance at the same place and time. | |
If possible do not even let these two cross paths during the day. | |
We do not want another repeat of the incident of last week, when | |
someone we shall not name left his service vehicle unattended | |
while he chased the other unnamed party all around campus for | |
two hours. Leaving service vehicles unattended makes them | |
vulnerable to all sorts of student and faculty hijinks, as we | |
saw once again in last week's incident when said service vehicle | |
was found on the roof of our offices the next morning.</tt> " | |
disambigName = 'physical plant memo' | |
; | |
+ ppoHall: OutOfReach, ThroughPassage 'hall/hallway' 'hallway' | |
"The hallway leads north, further into the building. " | |
cannotReachFromOutsideMsg(dest) { return 'You can\'t reach the | |
hallway from this side of the counter. '; } | |
dobjFor(TravelVia) | |
{ | |
preCond = [] | |
verify() { } | |
check() | |
{ | |
"You have to assume that the area behind the counter is | |
for authorized personnel only. "; | |
exit; | |
} | |
} | |
; | |
/* | |
* A class for the job cards on the physical plant counter, which all | |
* list together. | |
*/ | |
class JobCard: Readable 'colored index job assignment card*cards' 'index card' | |
"It's a <<cardColor>> five-by-seven index card, pre-printed | |
with a form that's been filled out with a typewriter: | |
<font face='tads-sans'> | |
\bCALTECH PHYSICAL PLANT | |
\nJOB ASSIGNMENT CARD | |
\bDept.: <font face='tads-typewriter'><u> <<cardDept | |
>> </u></font> | |
\nJob: <font face='tads-typewriter'><u> <<cardJob | |
>> </u></font> | |
\nLocation: <font face='tads-typewriter'><u> <<cardLoc | |
>> </u></font> | |
\nDesc.: <font face='tads-typewriter'><u> <<cardDesc | |
>> </u></font> | |
</font> " | |
/* the color of the card */ | |
cardColor = '' | |
/* the various job assignment data */ | |
cardDept = '' | |
cardJob = '' | |
cardLoc = '' | |
cardDesc = '' | |
/* use our special listing group */ | |
listWith = [jobCardGroup] | |
; | |
jobCardGroup: ListGroupParen | |
/* the group name, as a number of cards */ | |
showGroupCountName(lst) | |
{ | |
if (lst.length() == 2) | |
"a couple of colored index cards"; | |
else | |
"several colored index cards"; | |
} | |
/* the name of an individual item in the parenthesized sublist */ | |
showGroupItem(lister, obj, options, pov, info) | |
{ "one <<obj.cardColor>>"; } | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Jorgensen - main lobby | |
*/ | |
jorgensen: Room 'Jorgensen Lobby' 'the lobby of Jorgensen' 'lobby' | |
"This is a large, open area, decorated by framed photos | |
along the walls. Classrooms lie to the east and west, | |
and a corridor leads north. The building's exit is to | |
the south. " | |
vocabWords = 'jorgensen lab/laboratory/lobby' | |
north = jorgensenHall | |
south = sanPasqualWalkway | |
out asExit(south) | |
west: FakeConnector { "You peek into the classroom, but you | |
don't see anything interesting. "; } | |
east: FakeConnector { "You have a look in the classroom, but | |
you see nothing of interest. "; } | |
; | |
+ ExitPortal ->(location.south) 'building building\'s exit' 'exit' | |
"The exit is to the south. " | |
; | |
+ Enterable ->(location.north) 'corridor/hall/hallway' 'corridor' | |
"The corridor leads north. " | |
; | |
+ Decoration 'framed black-and-white photo/photos/picture/pictures' | |
'framed photos' | |
"The photos are mostly black-and-white, showing historical | |
computer equipment. The oldest ones are analog computers; | |
operators peer into hooded displays to read oscilloscope | |
traces. Later pictures show what look like giant | |
switchboards adorned with masses of tangled cables: a rather | |
literal kind of <q>spaghetti code.</q> After that are the early | |
mainframes, huge boxes attended by white-coated technicians. " | |
isNominallyIn(obj) { return inherited(obj) || obj.ofKind(DefaultWall); } | |
isPlural = true | |
; | |
+ Decoration 'east west e w classroom/classrooms' 'classroom' | |
"Classrooms lie to the east and west. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Jorgensen - hallway | |
*/ | |
jorgensenHall: Room 'Corridor' 'the corridor' | |
"This is a hallway running north and south. Doors line the | |
hall on both sides; apart from the door to the west, which is | |
labeled <q>Campus Network Office,</q> the doors appear to be | |
to private offices. The hallway ends a short way to the north, | |
and opens into a lobby to the south. " | |
vocabWords = 'jorgensen hall/hallway/corridor/lab/laboratory' | |
south = jorgensen | |
out asExit(south) | |
east = jhPrivateDoor | |
northeast = jhPrivateDoor | |
southeast = jhPrivateDoor | |
northwest = jhPrivateDoor | |
southwest = jhPrivateDoor | |
west = jhNetworkOfficeDoor | |
north: NoTravelMessage { "The hallway ends a short distance | |
to the north. "; } | |
; | |
+ Enterable ->(location.south) 'lobby' 'lobby' | |
"The lobby lies to the south. " | |
; | |
+ jhPrivateDoor: ForbiddenDoor | |
'private office door*doors' 'private office doors' | |
"Most of the doors along the hall appear to be to private offices. " | |
isPlural = true | |
cannotEnter = "That appears to be a private office. It would be | |
rude to just walk in. " | |
; | |
+ jhNetworkOfficeDoor: LockableWithKey, Door | |
'west w campus network office door*doors' 'Campus Network Office door' | |
"The door is labeled <q>Campus Network Office.</q> " | |
; | |
++ jhSign: CustomImmovable, Readable 'handwritten sign' 'handwritten sign' | |
"The sign reads <q>Closed - Back at 1 PM.</q> " | |
/* | |
* Show a special description. Note that we use separate special | |
* descriptions for room descriptions and contents descriptions | |
* because of the surplus of doors. In the room description, we need | |
* to say which door the sign is on; in the contents description, | |
* that would be redundant, since we're just examining the door. | |
*/ | |
specialDesc = "A hand-written sign on the Network Office door | |
reads <q>Closed - Back at 1 PM.</q> " | |
showSpecialDescInContents(actor, cont) { "A hand-written sign | |
on the door reads <q>Closed - Back at 1 PM.</q> "; } | |
cannotTakeMsg = 'You have no reason to remove the sign. ' | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The Campus Network Office | |
*/ | |
networkOffice: Room 'Network Office' 'the Network Office' 'office' | |
"This room is set up roughly like a private office. A desk | |
is positioned across the center of the room, facing the door, | |
and behind the desk are floor-to-ceiling bookshelves, crammed | |
with print-outs, binders, folders, and loose papers. Most | |
of the space on the desk is occupied by several terminals and | |
workstations. The door leads out to the east. " | |
vocabWords = 'campus network office' | |
east = netOfcDoor | |
out asExit(east) | |
west: NoTravelMessage { "You'd best stay on this side of the desk. "; } | |
/* on entry, have Dave greet us */ | |
enteringRoom(traveler) | |
{ | |
dave.addToAgenda(daveGreet); | |
} | |
/* open the office for our return from lunch */ | |
endLunch() | |
{ | |
/* open the door */ | |
jhNetworkOfficeDoor.makeLocked(nil); | |
jhNetworkOfficeDoor.makeOpen(true); | |
/* remove the "back at 1pm" sign */ | |
jhSign.moveInto(nil); | |
} | |
; | |
+ netOfcDoor: Door ->jhNetworkOfficeDoor 'door' 'door' | |
"The door leads out to the east. " | |
; | |
+ Heavy, Surface 'desk' 'desk' | |
"The desk faces the door, informally dividing the room. Several | |
terminals and workstations vie for space on the surface. " | |
iobjFor(PutOn) | |
{ | |
check() | |
{ | |
"There's very little free space on the desk where you | |
could put anything new. "; | |
exit; | |
} | |
} | |
dobjFor(LookBehind) { action() { "The main thing you see behind | |
the desk is the bookshelves. "; } } | |
; | |
++ Heavy 'terminal/terminals/workstation/workstations' 'workstations' | |
"They're facing the other way, so you can't see exactly what | |
kind they are from here. " | |
isPlural = true | |
dobjFor(Use) asDobjFor(TypeOn) | |
dobjFor(TypeLiteralOn) asDobjFor(TypeOn) | |
dobjFor(TypeOn) | |
{ | |
verify() { } | |
action() { "The terminals are facing the other way; you can't | |
easily type on them from here. "; } | |
} | |
; | |
+ Heavy 'rolling office chair' 'office chair' | |
"It's a simple rolling office chair. " | |
; | |
++ dave: IntroPerson 'heavy-set middle-aged dave/david/man*men' 'man' | |
"He's a heavy-set, middle-aged man, wearing a slightly rumpled | |
white dress shirt without a tie. " | |
specialDesc = "{A/he dave} is sitting behind the desk. " | |
isHim = true | |
globalParamName = 'dave' | |
properName = 'Dave' | |
posture = sitting | |
postureDesc = "He's sitting behind the desk. " | |
; | |
+++ daveGreet: AgendaItem | |
invokeItem() | |
{ | |
"{The/he dave} looks up as you enter. <q>Hi,</q> he says. "; | |
me.noteConversation(dave); | |
isDone = true; | |
} | |
; | |
+++ InitiallyWorn 'rumpled wrinkled white dress shirt' 'white shirt' | |
"It's a little wrinkled, but otherwise presentable. " | |
isListedInInventory = nil | |
; | |
+++ HelloTopic | |
topicResponse() | |
{ | |
"<q>Hi,</q> you say, <q>I'm Doug.</q> | |
<.p>He gives you a little nod. <q>I'm Dave,</q> he says. | |
<q>What can I do you for?</q> "; | |
/* note that he's introduced now */ | |
dave.setIntroduced(); | |
} | |
; | |
++++ AltTopic | |
"<q>Hi again,</q> you say. Dave gives you a little nod. " | |
isActive = (dave.introduced) | |
; | |
+++ AskTopic @dave | |
topicResponse() | |
{ | |
"<q>Do you work here?</q> you ask. | |
<.p><q>Yep,</q> he says. <q>I'm Dave. What can I do you for?</q> "; | |
dave.setIntroduced(); | |
} | |
; | |
++++ AltTopic | |
"<q>What do you do here?</q> you ask. | |
<.p>He shrugs. <q>Network operations, officially. Mostly means I | |
run around yelling at people for using unassigned IP addresses, | |
downloading too many MP3's, that sort of thing.</q> " | |
isActive = (dave.introduced) | |
; | |
+++ AskTopic @networkOffice | |
"<q>What does the Network Office do?</q> you ask. | |
<.p><q>All sorts of things,</q> he says. <q>We run the physical | |
network, manage IP assignments, configure the routers, you name it.</q> " | |
; | |
+++ AskTellTopic, StopEventList [quadWorkers, nicTopic] | |
['<q>What are the guys out on the quad doing?</q> you ask. | |
<.p><q>You mean the NIC guys?</q> he asks. <q>They\'re doing some | |
contracting for us. Network wiring upgrades.</q> He looks around | |
you to see if anyone\'s coming, and lowers his voice. <q>Just between | |
you and me, I think they\'re goldbricking us something awful, | |
frankly.</q><.reveal dave-suspects-nic> ', | |
'<q>The NIC people...</q> you start. <q>What did you mean about | |
them goldbricking?</q> | |
<.p><q>Well,</q> he says, <q>half the time I find them down in the | |
tunnels, doing jobs that, near as I can tell, they just made up.</q> ', | |
'<q>So you\'re suspicious of the NIC contractors?</q> you ask. | |
<.p><q>Yeah, you could say that,</q> he says. <q>I\'m going to | |
have to do some digging at some point.</q> '] | |
; | |
+++ AskTellTopic, SuggestedAskTopic | |
topicResponse = "<q>Could you look up an IP address for me?</q> | |
you ask. | |
<.p>He frowns and scrutinizes your face for a few moments. | |
<q>Why would you want me to do that?</q> he asks suspiciously. | |
<.convnode dave-why-ip><.topics> " | |
/* match the decimal IP address (but convert the dots to regex format) */ | |
matchPattern = static (new RexPattern( | |
infoKeys.spy9DestIPDec.findReplace('.', '<dot>', ReplaceAll, 1) | |
+ '$')) | |
/* the name to offer is the decimal IP format */ | |
name = static (infoKeys.spy9DestIPDec) | |
/* we're active only if the number has been revealed */ | |
isActive = gRevealed('spy9-dest-ip') | |
; | |
++++ AltTopic | |
"<q>What was that you found out about that IP address again? | |
It was <<infoKeys.spy9DestIPDec>>.</q> | |
you ask. | |
<.p>He checks the binder again. <q>Let's see. Here it is. | |
It's a job number with our network contractor: | |
<<infoKeys.syncJobNumber>>.</q> " | |
isActive = gRevealed('sync-job-number') | |
; | |
+++ AskTellTopic @ipAddressesTopic | |
"<q>You handle IP assignments on campus, right?</q> you ask. | |
<.p><q>Yep,</q> he says. <q>That's us.</q> " | |
; | |
+++ AskTellTopic @nrRouter | |
"<q>You manage the campus routers, right?</q> you ask. | |
<.p><q>Right,</q> he says, <q>among other things.</q> " | |
; | |
+++ AskTellGiveShowTopic @netAnalyzer | |
"<q>Do you know how to work Netbisco 9099's?</q> you ask. | |
<.p><q>That's a net analyzer, right?</q> He stares off into | |
the distance for a while. <q>Yeah, I've used them, but I'm | |
sorry to say I can't remember the first thing about them. | |
They have this crappy hex function-code interface.</q> | |
He mimes punching keys in the air with his index finger. | |
<q>Never could figure out those darn codes.</q> " | |
; | |
/* | |
* answer requests for random IP addresses; rank this below distinguished | |
* IP address queries | |
*/ | |
+++ AskTellTopic | |
matchScore = 70 | |
matchPattern = static new RexPattern( | |
'(<digit>{1,3}<dot>){3}<digit>{1,3}$') | |
topicResponse() | |
{ | |
"You're not sure how cooperative he'll be about looking up IP | |
addresses for you, so you don't want to press your luck by asking | |
about random addresses. Better to limit yourself to asking about | |
ones you really need to track down. "; | |
} | |
isConversational = nil | |
; | |
++++ AltTopic | |
"You don't want to pester him with requests to look up random IP | |
addresses. " | |
isConversational = nil | |
isActive = gRevealed('sync-job-number') | |
; | |
/* we might ask about the camera IP number as well */ | |
+++ AskTellTopic | |
matchPattern = static (new RexPattern( | |
infoKeys.spy9IPDec.findReplace('.', '<dot>', ReplaceAll, 1) + '$')) | |
topicResponse = "You don't want to wear out your welcome with | |
unnecessary requests. You already know everything you need | |
to know about that IP address---it's the one the SPY-9 camera | |
is using. " | |
isConversational = nil | |
isActive = gRevealed('spy9-ip') | |
; | |
/* | |
* If we give him the IP in hex, make a joke of it. To be sure we don't | |
* accidentally match any better topics that are words that happen to | |
* contain only the letters A through F, use a low ranking. Also, only | |
* accept 2 to 8 hex digits. | |
*/ | |
+++ AskTellTopic, StopEventList | |
eventList = [&firstResponse, &nthResponse] | |
matchPattern = static new RexPattern('<nocase>[0-9a-f]{2,8}$') | |
firstResponse() | |
{ | |
"<q>What can you tell me about <q><<gTopic.getTopicText() | |
>></q>?</q>you ask. | |
<.p>He laughs. <q>So, you've heard I'm the human hex-to-decimal | |
converter.</q> He closes his eyes for a few moments. | |
<q>\^<<convertLiteral>>!</q> | |
he says, looking pleased with himself. "; | |
} | |
nthResponse() | |
{ | |
"<q>How about <q><<gTopic.getTopicText()>></q>?</q> you ask. | |
<.p>He squints and thinks, then announces, | |
<q>\^<<convertLiteral>>!</q> "; | |
} | |
/* return current topic text as a spelled-out decimal number */ | |
convertLiteral() | |
{ | |
local txt; | |
local num; | |
local str = ''; | |
/* | |
* Convert the value to a BigNumber. Since the value could be | |
* too big to fit in a 32-bit integer, convert it as a BigNumber. | |
* We only allow hex numbers up to 8 digits, so we only need | |
* enough precision for billions. | |
*/ | |
txt = gTopic.getTopicText(); | |
num = new BigNumber( | |
toInteger(txt.substr(1, txt.length() - 1), 16), 10); | |
num *= 16; | |
num += toInteger(txt.substr(txt.length()), 16); | |
/* | |
* if it's over two billion, handle the billions part ourselves, | |
* so that we don't overflow a 32-bit signed integer | |
*/ | |
if (num > 2000000000) | |
{ | |
/* spell out the billions part */ | |
str = spellInt(toInteger(num / 1000000000)) + ' billion, '; | |
/* take the billions out of the value */ | |
while (num > 1000000000) | |
num -= 1000000000; | |
} | |
/* convert the number to an integer for spellIntExt's use */ | |
num = toInteger(num); | |
/* spell the number */ | |
return str + spellIntExt(num, | |
SpellIntCommas | |
| SpellIntTeenHundreds | |
| SpellIntAndTens); | |
} | |
; | |
+++ AskTellTopic | |
matchScore = 70 | |
matchPattern = static new RexPattern('<nocase>[0-9a-f]{9,}$') | |
topicResponse = "<q>What can you tell me about | |
<q><<gTopic.getTopicText()>></q>?</q>you ask. | |
<.p><q>That's an awfully big hex number,</q> he says. " | |
; | |
+++ AskAboutForTopic, SuggestedAskTopic @jobNumberTopic | |
"<q>Just out of curiosity, what was that job number?</q> | |
<.p>He shrugs and pulls out the binder again, and finds the | |
right page. <q>Here it is. <<infoKeys.syncJobNumber>>. Won't | |
do you much good, though---they have their own private system | |
for those.</q><.reveal sync-job-number> " | |
name = 'the job number' | |
isActive = gRevealed('sync-job-number-available') | |
; | |
++++ AltTopic | |
"<q>What was that job number you gave me again?</q> | |
<.p>He sighs and pulls out the binder. <q>It's | |
<<infoKeys.syncJobNumber>>. I really don't know anything more | |
about it, though. Sorry.</q> " | |
isActive = gRevealed('sync-job-number') | |
; | |
+++ DefaultAnyTopic, ShuffledEventList | |
['He types something on one of the workstations. <q>Just a sec,</q> | |
he says, <q>I have to answer this chat.</q> ', | |
'Something on one of the terminals distracts him as you\'re talking. ', | |
'<q>Sorry,</q> he says, <q>not my department.</q> '] | |
; | |
+++ ConvNode 'dave-why-ip' | |
genericSearch = "He types on his terminal, looks at the screen, | |
types some more. <q>Hmm.</q> He rolls his chair over to the | |
wall and scans his fingers over some binders, then pulls one | |
out and flips through it. He puts it back and returns to the | |
terminal. <q>That's in a block we gave to NIC,</q> he says. | |
<q>The contractors we have doing some new wiring.</q> He taps | |
away at the terminal. <q>They're supposed to tell us how they're | |
using these, but they're not always all that prompt.</q> He | |
goes back to the shelf and pulls out another binder, and scans | |
through it. <q>Here we are. Sorry, bad news. All they gave us | |
is a job number, which won't do either of us a fat lot of good.</q> | |
He puts the binder back.<.reveal sync-job-number-available>" | |
; | |
++++ TellTopic, SuggestedTopicTree, SuggestedTellTopic @spy9 | |
"You hesitate to say too much, because you can't be sure who | |
was involved with planting the camera. <q>I'm trying to track | |
down some monitoring equipment,</q> you say. <q>We're not sure | |
exactly where it\'s plugged in.</q> | |
<.p>He seems to accept that. <q>Okay, let me see what I can | |
find.</q> <<location.genericSearch>> " | |
name = 'the SPY-9 camera' | |
; | |
+++++ AltTopic | |
"You hesitate to say too much, but given his suspicions about | |
the NIC guys, you figure he might be willing to help. <q>I'm | |
trying to track down some unauthorized equipment that I think | |
the NIC people installed,</q> you say. | |
<.p>He raises his eyebrows. <q>Really,</q> he says. <q>Well, | |
let me see what I can find.</q> He types on his terminal, | |
looks at the screen, types some more. <q>Hmm.</q> He rolls his | |
chair over to the wall and scans his fingers over some binders, | |
then pulls one out and flips through it. He puts it back and | |
returns to the terminal. <q>Sure enough, that's in a block we | |
gave to NIC,</q> he says. He taps away at the terminal. | |
<q>They're <i>supposed</i> to tell us how they're using these, | |
but they take their sweet time.</q> He goes back to the shelf | |
and pulls out another binder, and scans through it. <q>Here we | |
are. Well, what a surprise. All they gave us is a job number, | |
which won't do either of us a fat lot of good.</q> He puts | |
the binder back.<.reveal sync-job-number-available> " | |
isActive = gRevealed('dave-suspects-nic') | |
; | |
++++ TellTopic [ddTopic, stamerStackTopic, stackTopic, stamerTopic, | |
stamerLabTopic] | |
"<q>It's for a Ditch Day stack,</q> you say. | |
<.p>He smiles. <q>Ah, gotcha,</q> he says. <q>Happy to help.</q> | |
<<location.genericSearch>> " | |
; | |
++++ SpecialTopic 'say you\'re a private detective' | |
['say','you\'re','i\'m','i','am','you','are','a','private','detective'] | |
"<q>I'm doing an informal investigation,</q> you say, trying to | |
sound conspiratorial. | |
<.p>He regards you suspiciously. <q>What kind of investigation?</q> | |
he asks.<.convstay> " | |
; | |
/* a secret out-of-reach object representing the back half of the room */ | |
+ OutOfReach, SecretFixture | |
cannotReachFromOutsideMsg(dest) { return 'You can\'t reach that | |
from this side of the room. '; } | |
; | |
++ Fixture | |
'floor-to-ceiling bookshelf/bookshelves/shelf/shelves' 'bookshelves' | |
"The bookshelves cover the walls behind the desk. They're | |
stuffed with print-outs, binders, folders, and loose papers. " | |
isPlural = true | |
; | |
+++ Thing | |
'loose print-out/print-outs/binder/binders/folder/folders/paper/papers' | |
'papers' | |
"You can't see much from here apart from the labels on a few | |
of the binders: <q>1996 Wiring Plan,</q> <q>2000-1 Budget,</q> | |
<q>South Campus Router Configuration.</q> " | |
isPlural = true | |
; | |
++++ Component 'binder label/labels' 'binder labels' | |
desc = (location.desc) | |
isPlural = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* we need some rather similar 'tree' objects in a few places | |
*/ | |
class TreeDecoration: Fixture | |
dobjFor(Climb) | |
{ | |
verify() { } | |
action() { "You have no reason to climb <<isPlural ? | |
'any of these trees' : 'the tree'>>. "; } | |
} | |
; | |
/* an orange - fruit for the orange trees */ | |
class OrangeDecoration: CustomImmovable, Food | |
'orange/oranges/fruit' 'oranges' | |
"These trees yield a fairly small type of orange. " | |
isPlural = true | |
cannotTakeMsg = 'You tried an orange from one of these trees years | |
ago, when you were a student, and you have no interest in repeating | |
the sour experience. ' | |
dobjFor(Eat) | |
{ | |
preCond = [] | |
action() { "You know from experience that these oranges are | |
too acidic to eat. "; } | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Lawn outside Beckman Auditorium | |
*/ | |
beckmanLawn: CampusOutdoorRoom 'Lawn near Beckman' | |
'the lawn near Beckman' 'lawn' | |
"The circular Beckman Auditorium is just north of this expansive | |
lawn area; a towering double door leads inside. A leafy oak tree | |
leans over the grass, providing some shade. A walkway leads east. " | |
north = blDoors | |
east = blEastWalk | |
; | |
+ CampusMapEntry 'beckman auditorium' 'Beckman Auditorium' 'north'; | |
+ Decoration 'expansive lawn/grass' 'lawn' | |
"The grass is well tended. " | |
; | |
+ blDoors: AlwaysLockedDoor | |
'(beckman) (auditorium) towering double door/entrance' | |
'entrance to the auditorium' | |
"The imposing set of doors are at least twenty feet high. " | |
cannotOpenLockedMsg = 'The doors seem to be locked---which isn\'t | |
surprising, as there\'s not usually anything scheduled here | |
during the day. ' | |
; | |
+ Enterable ->(location.north) | |
'circular beckman auditorium' 'Beckman Auditorium' | |
"Beckman is known as the <q>wedding cake</q> for its visual | |
resemblance to same: it's round, tall, and white, with a | |
gently sloping cone for its roof. " | |
isProperName = true | |
; | |
++ Distant, Component | |
'(beckman) (auditorium) gently sloping conical roof/cone' | |
'roof of the auditorium' | |
"The roof is a gently sloping cone. " | |
; | |
+ blEastWalk: PathPassage 'east e walkway' 'east walkway' | |
"The walkway leads east past the oak. " | |
; | |
+ blOak: TreeDecoration | |
'tall full leafy oak tree/shade/branch/branches' 'oak tree' | |
"The oak is tall and full. <<gunther.isIn(location) | |
? "A gardener is standing in a cherry picker parked under | |
the tree, trimming branches. " : "">> " | |
/* | |
* this is obviously reachable from the cherry picker, whether raised | |
* or lowered | |
*/ | |
isCherryPickerReachable = true | |
; | |
+ cherryPicker: Heavy, Vehicle | |
'(personal) small electric cherry picker/cart/lifter' | |
'cherry picker' | |
"It's a small electric cart with a passenger basket at the | |
end of a boom, which <<cherryPickerBasket.isRaised | |
? "is currently raised up to its full height of about ten feet. " | |
: "looks like it can raise the basket to a height of about ten | |
feet, but is currently retracted. " | |
>> The operating controls appear to be located in the basket. " | |
specialDesc() | |
{ | |
if (gActionIs(TravelVia) && me.isIn(self)) | |
{ | |
/* | |
* we're showing the description as part of our arrival when | |
* the PC is driving the cherry picker somewhere - say | |
* nothing here, as we'll add a mention in our afterTravel() | |
* of where we end up parking the cherry picker | |
*/ | |
} | |
else if (gunther.isIn(self)) | |
{ | |
/* | |
* Gunther will mention us as part of his own specialDesc as | |
* long as he's in the cherry picker, so we don't need or | |
* want a separate description of the cherry picker itself | |
* in this case | |
*/ | |
} | |
else if (me.isIn(self)) | |
{ | |
/* | |
* we're riding the cherry picker, so describe it as such, | |
* using a location-specific description | |
*/ | |
location.inCherryPickerSpecialDesc; | |
} | |
else | |
{ | |
/* we're just parked - show a location-specific description */ | |
location.cherryPickerSpecialDesc; | |
} | |
} | |
/* when we board/unboard, simply get in/out of the basket */ | |
dobjFor(Board) remapTo(Board, cherryPickerBasket) | |
dobjFor(Enter) remapTo(Board, cherryPickerBasket) | |
dobjFor(GetOutOf) remapTo(GetOutOf, cherryPickerBasket) | |
/* the basket must be lowered before travel can commence */ | |
travelerPreCond(conn) { return [cherryPickerLowered]; } | |
/* | |
* Before allowing travel to proceed, make sure we're going to a | |
* compatible location. We can only go to campus outdoor locations. | |
* | |
* Note that we could have set up travel barriers everywhere needed | |
* to keep the cherry picker within an allowed region, but that | |
* would have been a pain to maintain as new rooms were added. It's | |
* simpler and more reliable to take advantage of the fact that the | |
* cherry picker has to be outdoors across the board. | |
*/ | |
travelerTravelTo(dest, connector, backConnector) | |
{ | |
/* make sure the location is compatible */ | |
if (!dest.ofKind(CampusOutdoorRoom)) | |
{ | |
/* explain the problem */ | |
"You'd better keep the cherry picker outdoors. "; | |
/* terminate the command */ | |
exit; | |
} | |
/* inherit the standard behavior to carry out the travel */ | |
inherited(dest, connector, backConnector); | |
} | |
/* add a message when we travel */ | |
moveIntoForTravel(dest) | |
{ | |
/* if the PC is driving, mention what's going on */ | |
if (me.isIn(self)) | |
"You step on the pedal, and the cart accelerates to a full | |
two miles per hour. You steer the cart along the path. "; | |
/* do the normal work */ | |
inherited(dest); | |
} | |
/* after driving here, mention where we park */ | |
afterTravel(traveler, connector) | |
{ | |
/* if the PC is driving, mention where we park */ | |
if (traveler == self && traveler.isActorTraveling(me)) | |
location.descCherryPickerArrival(); | |
/* do any inherited work */ | |
inherited(traveler, connector); | |
} | |
/* use special arrival/departure messages when Gunther is driving */ | |
sayArrivingViaPath(conn) | |
{ | |
if (gunther.isIn(self)) | |
"\^<<gunther.aName>> drives a cherry picker in | |
from <<conn.theName>>. "; | |
else | |
inherited(conn); | |
} | |
sayDepartingViaPath(conn) | |
{ | |
if (gunther.isIn(self)) | |
"\^<<gunther.theName>> steers the cherry picker away | |
down <<conn.theName>>. "; | |
else | |
inherited(conn); | |
} | |
/* to get out, just EXIT */ | |
tryRemovingFromNested() { return tryImplicitAction(GetOutOf, self); } | |
/* attempting to move the cherry picker explains it's a vehicle */ | |
dobjFor(Move) asDobjFor(PushTravel) | |
dobjFor(Push) asDobjFor(PushTravel) | |
dobjFor(Pull) asDobjFor(PushTravel) | |
dobjFor(Turn) asDobjFor(PushTravel) | |
dobjFor(PushTravel) | |
{ | |
verify() { logicalRank(50, 'too heavy'); } | |
check() | |
{ | |
"The cherry picker is much too heavy to push around | |
by hand; you'd have to get in and drive it to move | |
it anywhere. "; | |
exit; | |
} | |
} | |
; | |
++ cherryPickerBoom: Component '(cherry) (picker) (cart) boom' 'boom' | |
"The boom raises and lowers the passenger basket. " | |
disambigName = 'cherry picker boom' | |
dobjFor(ClimbUp) asDobjFor(Climb) | |
dobjFor(Climb) | |
{ | |
verify() | |
{ | |
if (!cherryPickerBasket.isRaised) | |
illogicalNow('There\'s not much point in that when the | |
boom is in the retracted position. '); | |
} | |
check() | |
{ | |
"You give it a try, but you quickly find that the boom | |
doesn't have any good handholds. "; | |
exit; | |
} | |
} | |
dobjFor(Lower) remapTo(Pull, cherryPickerLever) | |
dobjFor(Raise) remapTo(Push, cherryPickerLever) | |
/* the boom can be reached from the basket in all positions */ | |
isCherryPickerReachable = true | |
; | |
++ cherryPickerBasket: OutOfReach, Component, Booth | |
'(cherry) (picker) (cart) metal passenger basket/enclosure' | |
'basket of the cherry picker' | |
"It's a small metal enclosure, about waist-high, just large enough | |
for one person to stand in. <<isRaised | |
? "It's currently raised to its full height of about | |
ten feet above the ground." | |
: "It's currently in its fully-lowered position." | |
>> The basket contains the operating controls for the cart. " | |
/* when we exit, we move to the cart's location */ | |
exitDestination = (location.location) | |
/* we can't get out when the cart is raised */ | |
dobjFor(GetOutOf) { preCond = (inherited() + cherryPickerLowered) } | |
/* lowering and raising the basket is accomplished with the lever */ | |
dobjFor(Lower) remapTo(Pull, cherryPickerLever) | |
dobjFor(Raise) remapTo(Push, cherryPickerLever) | |
/* | |
* We can reach in when we're lowered. We can't reach out at all, | |
* except that we can reach certain specially marked objects. | |
*/ | |
canObjReachContents(obj) { return !isRaised; } | |
canReachFromInside(obj, dest) | |
{ | |
local loc; | |
/* | |
* the destination is reachable if (1) it's marked as always | |
* reachable from here, or (2) it's marked as reachable from the | |
* raised basket and we're currently raised | |
*/ | |
if (dest.isCherryPickerReachable | |
|| (isRaised && dest.isRaisedCherryPickerReachable)) | |
return true; | |
/* | |
* if the destination is inside something that's reachable, it's | |
* reachable | |
*/ | |
if ((loc = dest.location) != nil | |
&& canReachFromInside(obj, loc)) | |
return true; | |
/* the destination isn't reachable */ | |
return nil; | |
} | |
/* obviously, the basket is reachable from the basket */ | |
isCherryPickerReachable = true | |
/* is the basket currently raised? */ | |
isRaised = true | |
cannotReachFromOutsideMsg(dest) { return 'The basket is raised | |
too high for you to reach. '; } | |
cannotReachFromInsideMsg(dest) { return 'You can\'t reach that | |
from the basket. '; } | |
/* remove an out-of-reach obstruction by getting out of the basket */ | |
tryImplicitRemoveObstructor(sense, obj) | |
{ | |
if (sense == touch && gActor.isIn(self)) | |
return tryRemovingFromNested(); | |
else | |
return inherited(sense, obj); | |
} | |
/* | |
* When we're raised, looking into the basket from outside counts as | |
* 'distant'. Allow looking out to work normally, though, since | |
* we're really not that far from anything. | |
*/ | |
transSensingIn(sense) | |
{ return sense == sight && isRaised ? distant : inherited(sense); } | |
transSensingOut(sense) { return transparent; } | |
/* we can be boarded directly from the cherry picker's location */ | |
stagingLocations = [location.location] | |
/* show a note on how to operate the vehicle */ | |
howToDrive = "(If you want to drive the cart | |
somewhere, just <<gActor.isIn(cherryPickerBasket) ? '' | |
: "get in and" >> say which direction you want to go.) "; | |
; | |
+++ gunther: IntroPerson | |
'wild white fuzzy gunther der | |
gardener/hair/mustache/gunther/gartner/g\u00e4rtner/gaertner/man*men' | |
'gardener' | |
"The gardener is a short man with wild white hair and a | |
fuzzy white mustache. " | |
properName = 'Gunther the gardener' | |
/* presume we're known, since we're mentioned in a memo */ | |
isKnown = true | |
/* | |
* don't show our posture separately when we're in the basket (which | |
* is the only place we ever appear in the game), since that's | |
* included in the base description | |
*/ | |
postureDesc() { } | |
/* | |
* use a special state-dependent rendering to describe us when we | |
* appear in our location's contents listing | |
*/ | |
showSpecialDescInContents(actor, cont) { curState.specialDescInContents; } | |
/* | |
* allow receiving things via GIVE TO without being within reach; | |
* this allows handing us things while we're in the raised basket, | |
* which is where we spend most of our time | |
*/ | |
iobjFor(GiveTo) { preCond = static (inherited - [touchObj]) } | |
/* Gunther is a "he" */ | |
isHim = true | |
/* | |
* allow him to be seen in full detail from a distance (the only | |
* time this will come up is when we're in the cherry picker, which | |
* isn't all that far from the ground) | |
*/ | |
sightSize = large | |
/* if ernst arrives while we're here, start the fight */ | |
afterTravel(traveler, connector) | |
{ | |
inherited(traveler, connector); | |
if (traveler.isActorTraveling(ernst)) | |
ernst.startFight(); | |
} | |
; | |
++++ Thing '(tree) clippers/trimmers/blade/blades/pair' 'tree clippers' | |
"They're like very large scissors, with two-foot blades. " | |
isPlural = true | |
isListedInInventory = nil | |
; | |
/* base state for trimming tress */ | |
class GuntherTrimmingState: ActorState | |
stateDesc = "He's standing in the basket of the cherry picker, | |
trimming branches from <<treeName>>. " | |
specialDesc = "\^<<location.aName>> is standing in the raised basket | |
of a cherry picker, trimming branches from <<treeName>>. " | |
specialDescInContents = "\^<<location.aName>> is standing in the | |
basket, trimming <<treeName>>. " | |
/* | |
* we're distant when we're in the raised cherry picker, but don't | |
* let that change our description in this state | |
*/ | |
distantSpecialDesc = (specialDesc) | |
/* the tree we're trimming */ | |
// treeName = '' | |
; | |
/* our initial state, trimming the oak tree */ | |
++++ guntherTrimmingOak: GuntherTrimmingState | |
isInitState = true | |
treeName = 'the oak' | |
; | |
/* our state when trimming olive trees on the olive walk */ | |
++++ guntherTrimmingOlive: GuntherTrimmingState | |
treeName = 'one of the olive trees' | |
; | |
++++ DefaultCommandTopic | |
"<q>Can't you see I'm busy here?</q> he asks with annoyance. " | |
; | |
++++ HelloTopic | |
"<q>Hi,</q> you say. | |
<.p>He glances down but keeps trimming. <q><i>Ja</i>, hi,</q> | |
he says. " | |
; | |
++++ AskTellTopic, StopEventList @ernst | |
['<q>Do you know any electricians around here?</q> you ask. | |
<.p><q>Electricians?</q> His eyes narrow. <q>Do you speak to | |
me of that horrible Ernst?</q> He starts speaking rapid | |
German, which you don\'t know enough to follow what he\'s | |
saying, but you can tell he\'s angry about something. He | |
finally stops and goes back to his trimming, still muttering. ', | |
'You start to bring up the subject of electricians again, | |
but he just starts saying <q><i>Nein</i></q> and ignoring | |
you. '] | |
; | |
++++ AskTellShowTopic [blOak, owTrees] | |
"<q>How\'s the tree coming?</q> you ask. | |
<.p><q>Coming,</q> he says. <q>Dis tree needs a big lot | |
of trimming.</q> " | |
; | |
++++ AskTellTopic, StopEventList @gunther | |
[&doIntro, | |
'<q>How\'s it going?</q> you ask. | |
<.p>He glances down. <q>Not so bad,</q> he says. '] | |
doIntro() | |
{ | |
"<q>What\'s your name?</q> you ask. | |
<.p>He keeps trimming while he talks. <q>Gunther,</q> he says. | |
<q><i>Ja</i>, I know, Gunther der Gärtner, very funny, | |
I know, I know.</q> "; | |
gunther.setIntroduced(); | |
} | |
; | |
++++ AskTellShowTopic @cherryPicker | |
"<q>What kind of cherry picker is that?</q> you ask. | |
<.p>He looks down at you, still trimming. <q>Das ist no concern | |
of yours,</q> he says dismissively. " | |
; | |
++++ AskForTopic @cherryPicker | |
"<q>Um, do you think I could borrow that cherry picker for | |
a while?</q> You smile plaintively. | |
<.p>The gardener stops his trimming for a moment and stares | |
at you. <q><i>Nein!</i></q> he says, shaking his head emphatically. | |
<q><i>Nein, nein, nein!</i></q> He returns to clipping with | |
renewed vigor. " | |
; | |
class GuntherJobCardTopic: GiveShowTopic | |
topicResponse() | |
{ | |
"<q>Here,</q> you say, holding the card up for the gardener. | |
<.p>He lowers the basket enough to reach the card, and looks | |
it over. "; | |
/* check to see if we're already there */ | |
if (gunther.isIn(destLoc)) | |
{ | |
"<q>What looks like here already is going on that I work | |
on, <i>ja</i>?</q> he says, waving the card around. | |
He testily hands the card back to you, raises the basket | |
back up, and goes back to trimming the tree. "; | |
return; | |
} | |
/* get going */ | |
"<q>They said trim <i>this</i> tree!</q> he says. He reads | |
the card again, then looks resigned. <q><i>Ja wohl,</i></q> he | |
says, <q>now I go trim this other tree.</q> He lowers the | |
basket, muttering in German. "; | |
/* lower the basket and go into transit state */ | |
cherryPickerBasket.isRaised = nil; | |
gunther.setCurState(guntherInTransit); | |
/* remember our destination */ | |
guntherInTransit.destPath = destPath; | |
guntherInTransit.destState = destState; | |
} | |
/* the path to the destination */ | |
destPath = [] | |
/* the destination is the last path element */ | |
destLoc = (destPath[destPath.length()]) | |
/* the state to go into when we reach the destination */ | |
destState = nil | |
; | |
++++ GuntherJobCardTopic @oakJobCard | |
destPath = [quad, sanPasqualWalkway, beckmanLawn] | |
destState = guntherTrimmingOak | |
; | |
++++ GuntherJobCardTopic @oliveJobCard | |
destPath = [sanPasqualWalkway, quad, oliveWalk] | |
destState = guntherTrimmingOlive | |
; | |
++++ GiveShowTopic [orwalkJobCard, lauritsenJobCard] | |
"<q>Here,</q> you say, holding the card up for the gardener. | |
<.p>He lowers the basket enough to reach the card, and looks | |
it over. <q>Das ist not for <i>Gärtner</i>!</q> he says, | |
handing the card back. <q>You want that job to do, go find | |
<i>einen Elektromechaniker</i>!</q> " | |
; | |
++++ DefaultAnyTopic | |
"He just ignores you and keeps trimming the tree. " | |
; | |
/* our state while we're traveling to our next location */ | |
++++ guntherInTransit: ActorState | |
stateDesc = "He's driving through in the cherry picker. " | |
specialDesc = "\^<<location.theName>> is driving through in a | |
cherry picker. " | |
specialDescInContents = "\^<<location.theName>> is driving the | |
cherry picker somewhere. " | |
takeTurn() | |
{ | |
/* find our next location from our current location on the path */ | |
local idx = destPath.indexOf(gunther.location.getOutermostRoom()); | |
/* | |
* if we haven't even started yet, move to the first location on | |
* the path; otherwise, move to the next one | |
*/ | |
idx = (idx == nil ? 0 : idx) + 1; | |
/* travel to the next stop on the path */ | |
gunther.scriptedTravelTo(destPath[idx]); | |
/* if that's the final destination, get to work */ | |
if (idx == destPath.length()) | |
{ | |
/* raise the basket */ | |
cherryPickerBasket.isRaised = true; | |
"<.p>The gardener parks the cherry picker under | |
<<destState.treeName>>, raises the basket, and | |
starts trimming. "; | |
/* switch to the new state */ | |
gunther.setCurState(destState); | |
} | |
} | |
/* the path to our destination */ | |
destPath = [] | |
/* the state to go into when reaching our destination */ | |
destState = nil | |
beforeAction() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* don't let anyone into the basket while we're traveling */ | |
if ((gActionIs(Board) || gActionIs(Enter) || gActionIs(StandOn)) | |
&& gDobj == cherryPickerBasket) | |
{ | |
"<q><i>Nein!</i></q> the gardener shouts, blocking | |
the way. <q>One occupant only this device can handle!</q> "; | |
exit; | |
} | |
} | |
; | |
/* while we're in this state, we ignore conversations entirely */ | |
+++++ DefaultAnyTopic | |
"<q>Not now!</q> the gardener says without slowing down. <q>I must | |
be going to the new job now!</q> " | |
/* use this one while we're in transit */ | |
isActive = (gunther.curState == guntherInTransit) | |
; | |
/* our state while fighting */ | |
++++ guntherFightingState: ActorState | |
stateDesc = "He's chasing the electrician with a pair of tree clippers. " | |
specialDesc = "\^<<ernst.aName>> runs toward you, a panicked look | |
on his face, nearly crashing into you before swerving sharply | |
to one side. \^<<gunther.aName>> is right behind him, chasing | |
him with a pair of tree clippers. " | |
; | |
+++ Component '(cherry) (picker) operating iconic | |
silhouette/controls/icon/icons/arrow/arrows' 'controls' | |
"The controls look simple. A lever is labeled with an | |
iconic silhouette of the basket on the boom, with arrows pointing | |
up and down. Next to the lever is a steering wheel, and below | |
the wheel on the floor of the basket is a pedal, presumably the | |
accelerator. An icon above the steering wheel shows a silhouette | |
of the cart with its basket raised, and an arrow pointing forward, | |
and a big red X through the whole thing. " | |
distantDesc = "The basket is too high up; you can't see the controls | |
clearly from here. " | |
isPlural = true | |
disambigName = 'cherry picker controls' | |
; | |
+++ NestedRoomFloor 'floor/(basket)' 'floor of the basket' | |
"A pedal is on the floor. " | |
; | |
++++ Component 'accelerator pedal' 'pedal' | |
"It looks like an accelerator pedal. " | |
dobjFor(Push) | |
{ | |
verify() | |
{ | |
if (!gActor.isIn(cherryPickerBasket)) | |
illogicalNow('You need to be in the basket to operate | |
the controls. '); | |
} | |
action() | |
{ | |
"You give the pedal a little push<< | |
cherryPickerBasket.isRaised | |
? ". Nothing seems to happen." | |
: ", and the cart jerks forward a little bit. You | |
release the pedal and the cart stops." | |
>> <<location.location.howToDrive>> "; | |
} | |
} | |
; | |
+++ Component 'miniature steering wheel' 'steering wheel' | |
"It's like the steering wheel of a car, but in miniature. " | |
dobjFor(Turn) | |
{ | |
verify() | |
{ | |
if (!gActor.isIn(location)) | |
illogicalNow('You need to be in the basket to operate | |
the controls. '); | |
} | |
action() | |
{ | |
"You turn the wheel a little, but it seems hard to turn | |
while the cart is stopped. <<location.howToDrive>> "; | |
} | |
} | |
; | |
+++ cherryPickerLever: Component '(cherry) (picker) lever' 'lever' | |
"The lever is labeled with an icon showing the basket moving | |
up and down. " | |
disambigName = 'cherry picker lever' | |
cannotMoveMsg = 'The lever doesn\'t move that way; you can only | |
push and pull it. ' | |
dobjFor(Move) | |
{ | |
/* it's logical to move the lever, but more specificity is required */ | |
verify() { logicalRank(50, 'move lever'); } | |
action() { "(You'll have to be more specific: you can either push | |
it or pull it.) "; } | |
} | |
/* translate MOVE UP into PUSH, and MOVE DOWN into PULL */ | |
dobjFor(PushTravel) | |
{ | |
verify() | |
{ | |
/* allow UP, FOREWARD, DOWN, and BACK only */ | |
if (gAction.getDirection() not in | |
(upDirection, downDirection, foreDirection, backDirection)) | |
inherited(); | |
} | |
action() | |
{ | |
if (gAction.getDirection() is in (upDirection, foreDirection)) | |
replaceAction(Push, self); | |
else | |
replaceAction(Pull, self); | |
} | |
} | |
dobjFor(Pull) | |
{ | |
verify() | |
{ | |
/* we need to be in the basket to operate the lever */ | |
if (!gActor.isIn(location)) | |
illogicalNow('You need to be in the basket to operate | |
the controls. '); | |
} | |
action() | |
{ | |
if (cherryPickerBasket.isRaised) | |
{ | |
"You pull the lever all the way back, and the basket | |
slowly descends. Once the boom is fully retracted, | |
the descent stops, and you let the lever spring back | |
to the center position. "; | |
/* note that we're no longer raised */ | |
cherryPickerBasket.isRaised = nil; | |
} | |
else | |
"A motor whines a little, but nothing else happens, | |
so you release the lever and let it spring back to | |
its center position. "; | |
} | |
} | |
dobjFor(Push) | |
{ | |
verify() | |
{ | |
/* we need to be in the basket to operate the lever */ | |
if (!gActor.isIn(location)) | |
illogicalNow('You need to be in the basket to operate | |
the controls. '); | |
} | |
action() | |
{ | |
if (cherryPickerBasket.isRaised) | |
"A motor whines a little, but nothing else happens. | |
You release the lever and let it spring back to its | |
center position. "; | |
else | |
{ | |
"You push the lever all the way forward, and a whining | |
electric motor slowly raises the basket until the boom | |
is fully extended. You release the lever, and it | |
springs back to the center position. "; | |
/* note that we're now raised */ | |
cherryPickerBasket.isRaised = true; | |
/* let the location add any comment it wants to */ | |
cherryPicker.location.noteCherryPickerRaised; | |
} | |
} | |
} | |
; | |
/* | |
* A precondition for lowering the cherry picker basket. We use this as | |
* a convenience for the player if they try to disembark the cherry | |
* picker or drive it around while the basket is raised. We *could* | |
* have just aborted those actions with an error, but since there's no | |
* mystery to operating the basket, there's no reason not to perform | |
* these actions automatically when needed. | |
*/ | |
+ cherryPickerLowered: PreCondition | |
checkPreCondition(obj, allowImplicit) | |
{ | |
/* if the basket is already lowered, we're done */ | |
if (!cherryPickerBasket.isRaised) | |
return nil; | |
/* try lowering the basket implicitly */ | |
if (allowImplicit && tryImplicitAction(Lower, cherryPickerBasket)) | |
{ | |
/* make sure the basket was lowered */ | |
if (cherryPickerBasket.isRaised) | |
exit; | |
/* tell the caller we executed an implied command */ | |
return true; | |
} | |
/* we can't do it implicitly - report the failure */ | |
reportFailure('You\'ll have to lower the basket first. '); | |
exit; | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Quad | |
*/ | |
quad: CampusOutdoorRoom | |
'Quad' 'the Quad' 'Quad' | |
"This open lawn area is known as the Quad, for its rectangular | |
shape. Several walkways meet here: the Olive Walk leads east and | |
west, the Orange Walk leads down a few steps to the south, and | |
another walkway leads north. To the northwest is the campus | |
bookstore. The two main student house complexes lie a short distance | |
to the east: the North Houses on the north side of the Olive Walk, | |
and the older South Houses on the south side. " | |
vocabWords = 'quad' | |
north = quadNorthWalk | |
east = quadEastWalk | |
west = quadWestWalk | |
south = quadSouthWalk | |
down = quadManhole | |
northwest = bookstore | |
in asExit(northwest) | |
; | |
+ CampusMapEntry '(campus) (caltech) bookstore' 'the bookstore' 'southeast'; | |
+ CampusMapEntry 'winnett center' 'Winnett Center' 'southeast'; | |
+ CampusMapEntry 'quad' 'the Quad' 'southeast'; | |
+ CampusMapEntry 'olive walk' 'the Olive Walk' 'southeast' | |
altLocations = [westOliveWalk, oliveWalk] | |
; | |
+ Enterable ->(location.northwest) | |
'(campus) (caltech) (book) bookstore/store' 'bookstore' | |
"The bookstore has been given a new facade since you were here | |
last, in the same Mediterranean style as many of the other | |
nearby buildings. The entrance is to the northwest. " | |
; | |
+ Decoration 'lawn/grass' 'grass' | |
"It's fairly ordinary grass. " | |
; | |
+ quadNorthWalk: PathPassage 'paved north n walkway/path' 'north walkway' | |
"It's a paved walkway leading north. " | |
; | |
+ quadSouthWalk: PathPassage -> orwNorthWalk | |
'(brick) orange south s stair/stairs/step/steps/walk/walkway/path' | |
'Orange Walk' | |
"The Orange Walk lies down a few steps to the south. " | |
dobjFor(ClimbDown) asDobjFor(Enter) | |
dobjFor(Climb) asDobjFor(Enter) | |
canTravelerPass(trav) { return trav != cherryPicker; } | |
explainTravelBarrier(trav) | |
{ "There's no way to get the cherry picker down the stairs. "; } | |
; | |
+ quadEastWalk: PathPassage ->olwWestWalk | |
'(brick) east e olive walk/walkway/path' 'east walkway' | |
"The brick walkway leads east. " | |
; | |
+ quadWestWalk: PathPassage | |
'(brick) west w olive walk/walkway/path' 'west walkway' | |
"The brick walkway leads west. " | |
; | |
+ Decoration 'red brick/bricks' 'bricks' | |
"The path is paved with fairly ordinary red bricks. " | |
isPlural = true | |
; | |
+ Distant 'old older new newer south north undergraduate student | |
house/houses/complex' 'student houses' | |
"The student houses lie a short distance to the east, down the | |
Olive Walk. The newer houses are on the north side, and the older | |
houses on the south side. " | |
isPlural = true | |
; | |
+ CustomImmovable | |
'yellow "caution" plastic stake/stakes/tape' '<q>Caution</q> tape' | |
"Plastic yellow tape, with the word <q>Caution</q> written every | |
couple of feet, is strung around some stakes surrounding the | |
manhole. " | |
cannotTakeMsg = 'The workers would probably be cross with you | |
if you did that. ' | |
dobjFor(Enter) | |
{ | |
verify() { } | |
action() { "At a guess, you'd say the workers taped off the area | |
to keep people like you out. "; } | |
} | |
; | |
+ OutOfReach, SecretFixture | |
cannotReachFromOutsideMsg(dest) | |
{ | |
gMessageParams(dest); | |
return '{The dest/he} {is} on the other side of the yellow tape, | |
which the workers undoubtedly put there to prevent just this | |
sort of interference from passersby. '; | |
} | |
; | |
++ quadAnalyzer: Thing | |
'electronic netbisco 9099 network analyzer/piece/gear' | |
name = (described ? 'network analyzer' : 'electronic gear') | |
aName = (described ? 'a network analyzer' : 'a piece of electronic gear') | |
desc = "It looks a little like the kind of oversized telephone | |
you'd find on a receptionist's desk in a large office. You | |
can make out a name printed on top: it's a Netbisco 9099, | |
which is a network analyzer, if you recall correctly. You've | |
used something like it before when setting up a network; it's | |
useful for tasks like configuring routers. " | |
/* we list these in the worker specialDesc, so don't list separately */ | |
isListed = nil | |
dobjFor(Take) | |
{ | |
preCond = [] | |
check() | |
{ | |
"For one thing, it's behind the yellow tape, which is | |
plainly meant to keep people like you out. For another, | |
the workers would undoubtedly stop you if you tried to | |
take it. "; | |
exit; | |
} | |
} | |
; | |
++ Thing 'big phone network wooden spool/spools/wire' 'spools of wire' | |
"They're two-foot-diameter wooden spools wound with wire, | |
which looks like some kind of phone or network wire. " | |
isPlural = true | |
/* we list these in the worker specialDesc, so don't list separately */ | |
isListed = nil | |
; | |
++ quadManhole: ThroughPassage | |
'large rectangular manhole manhole/hole/shaft/opening' 'manhole' | |
"It's a rectangular opening in the ground, big enough to accomodate | |
a couple of people at once, with a pair of metal doors. A shaft | |
descends from the opening, and you're pretty sure it leads down to | |
the steam tunnel that runs roughly under the Olive Walk. " | |
specialDesc() | |
{ | |
"Alongside the Olive Walk, a couple of workers are | |
standing by an open rectangular manhole, which they've | |
cordoned off with yellow <q>Caution</q> tape strung around | |
some stakes in the ground. Several big spools of wire are | |
piled up next to the manhole"; | |
if (quadAnalyzer.isIn(location)) | |
", along with <<quadAnalyzer.aName>>"; | |
". "; | |
} | |
dobjFor(Board) asDobjFor(Enter) | |
dobjFor(Climb) asDobjFor(Enter) | |
dobjFor(ClimbDown) asDobjFor(Enter) | |
lookInDesc = "You can't see anything from here, but you're pretty | |
sure the shaft leads down to a steam tunnel. " | |
/* | |
* since the manhole is at a slight distance, we have to throw | |
* things into the manhole | |
*/ | |
iobjFor(PutIn) remapTo(ThrowAt, DirectObject, self) | |
/* throwing something at/into the manhole */ | |
throwTargetHitWith(projectile, path) | |
{ | |
throwScript.doScript(projectile); | |
} | |
throwScript: StopEventList { [ &firstTime, &secondTime ] | |
doScript(projectile) | |
{ | |
/* set up the projectile as a message parameter */ | |
gMessageParams(projectile); | |
/* remember it internally for our use in the event handler */ | |
projectile_ = projectile; | |
/* do the normal work */ | |
inherited(); | |
} | |
/* the projectile of this call */ | |
projectile_ = nil | |
firstTime() | |
{ | |
"<q>Hey!</q> one of the workers shouts, catching | |
{the projectile/him} just before {it/he} fall{s} into | |
the manhole. <q>There's a guy down there working!</q> "; | |
if (projectile_ == ratPuppet) | |
"He looks at the toy rat and laughs, then waves | |
it around the manhole opening and shouts into the | |
shaft. <q>Hey, Plisnik! Look what I got here!</q> | |
There's some angry shouting from the shaft.<.p>"; | |
"The worker tosses {the projectile/him} back to you. | |
<q>I'm gonna pretend that was a accident,</q> he says | |
to you menacingly. "; | |
} | |
secondTime() | |
{ | |
"One of the workers grabs {the projectile/him} out of | |
the air before {it/he} fall{s} into the shaft. "; | |
if (projectile_ == ratPuppet) | |
"<q>Hey, Plisnik!</q> he shouts into the tunnel. | |
<q>This jerk up here thinks you're gonna be scared | |
by rats falling from the sky! Very scary---flying | |
rats! Oooh!</q> He tosses the rat back to you. | |
<q>Look, would you quit it with the throwing junk | |
at us? It's dangerous!</q> "; | |
else | |
"<q>Hey!</q> he yells, tossing {it/him} back to you. | |
<q>Knock it off with the throwing junk at us! It's | |
dangerous!</q> "; | |
} | |
} | |
; | |
+++ Door '(manhole) metal door/doors' | |
'manhole doors' | |
"The doors are sheets of metal, hinged at the edges to open | |
like a barn door. " | |
isPlural = true | |
initiallyOpen = true | |
dobjFor(Board) asDobjFor(Enter) | |
; | |
++ quadWorkers: Person | |
'big heavy unruly workers/man/beard/beards*men' 'workers' | |
"The two workers are both big, heavy men with unruly beards. | |
They're wearing bright green overalls and matching hardhats. " | |
isHim = true | |
isPlural = true | |
/* | |
* we don't need to be mentioned in the room description, since the | |
* manhole includes us in its description | |
*/ | |
specialDesc = "" | |
/* | |
* don't require touching for GIVE TO - all of our GIVE TO topics | |
* just amount to SHOW TO anyway, so there's no need to touch | |
*/ | |
iobjFor(GiveTo) { preCond = (inherited - touchObj); } | |
; | |
+++ InitiallyWorn 'bright green hard overalls/hardhats/uniforms/hat/hats' | |
'uniforms' | |
"The workers are wearing bright green overalls and matching hardhats, | |
marked <q>Network Installer Company</q> in blocky white letters. | |
<.reveal NIC> " | |
isPlural = true | |
isListedInInventory = nil | |
; | |
+++ Unthing 'plisnik' 'plisnik' | |
notHereMsg = 'Plisnin\'s down the shaft, you take it---not here. ' | |
; | |
+++ InConversationState, StopEventList | |
[ | |
'<.p>One worker pokes the other playfully with his elbow. <q>Hey, | |
you know what\'s a laugh? Plisnik\'s scared of rats. Watch | |
this.</q> He cups his hands and yells into the shaft. <q>Hey, | |
Plisnik! Is that a rat?</q> There\'s some commotion from down | |
in the shaft, then a lot of cursing. The two workers chuckle and | |
do a high-five.<.reveal plisnik-n-rats> ', | |
'<.p>You can hear someone calling from the shaft, but you can\'t | |
make out what he\'s saying. One of the workers responds by | |
handing down one of the spools of wire. ', | |
'<.p><q>Hey,</q> one of the workers says to the other, <q>how | |
sweet would it be to have a rat right now?</q> He chuckles. | |
<.p><q>Yeah,</q> the other says, chuckling, <q>we could drop | |
it down there and watch Plisnik freak out.</q> ', | |
'<.p>Someone in the shaft says something you can\'t quite | |
make out. One of the workers grumbles and grabs a spool of | |
wire. <q>What am I, a delivery service?</q> he complains, | |
handing the spool down into the shaft. ', | |
'<.p>One of the workers says to the other, <q>You seen them new | |
self-crimping connectors? Pretty sweet, huh?</q> The other | |
nods and grunts acknowledgment. ', | |
/* | |
* use a nil last element, so that we do nothing once we run out | |
* of items to show | |
*/ | |
nil | |
] | |
stateDesc = "Both are looking at you---not exactly expectantly; more | |
like you just did something idiotic and they're wondering what | |
you're going to do next. " | |
/* | |
* use a relatively long attention span, so we get to see a few of | |
* the background messages if we're idle | |
*/ | |
attentionSpan = 5 | |
/* when we're in conversation, generate some comments while idle */ | |
doScript() | |
{ | |
/* | |
* Only make a comment if we didn't converse on this turn. | |
* Also, we mention Plisnik a lot, so don't do anything unless | |
* Plisnik is still in the tunnel below. | |
*/ | |
if (!getActor().conversedThisTurn() && plisnik.inOrigLocation) | |
inherited(); | |
} | |
; | |
++++ AskTellTopic [plisnikTopic, ratTopic] | |
"<q>Why's Plisnik so afraid of rats?</q> you ask. | |
<.p>The workers chuckle. One of them yells down into the | |
shaft, <q>Hey, Plisnik! I think I see another rat!</q> | |
You hear some yelling coming from the shaft but you can't | |
tell what's being said. " | |
isActive = (gRevealed('plisnik-n-rats')) | |
; | |
+++++ AltTopic | |
"<q>Why's Plisnik so afraid of rats?</q> you ask. | |
<.p>The workers just look at each other and chuckle. " | |
isActive = (!plisnik.inOrigLocation) | |
; | |
/* | |
* if we TELL ABOUT PLISNIK, mention that he ran off, if he did - use an | |
* elevated score for this so we override the generic plisnik/rat topic | |
*/ | |
++++ TellTopic +110 @plisnikTopic | |
"<q>Did you guys know Plisnik saw a rat and ran off?</q> you ask. | |
<.p>The two roll their eyes. <q>Yep, he does that every day,</q> one | |
of them says. " | |
isActive = (!plisnik.inOrigLocation) | |
; | |
++++ GiveShowTopic @ratPuppet | |
"You show the workers the rat. <q>Cute,</q> one of them says. | |
He elbows the other guy. <q>Wouldn't it be a hoot if we had | |
a <i>real</i> rat?</q> " | |
; | |
++++ AskTellShowTopic, SuggestedAskTopic @quadWorkers | |
"<q>What are you fellas workin' on?</q> you ask, affecting your | |
idea of a working-class diction for some reason beyond your control. | |
<.p>One of the two just rolls his eyes a bit, and the other one | |
slowly, elaborately looks down at his uniform and points to the | |
blocky letters. <q>Network,</q> he reads, moving his finger over | |
the words, <q>Installer... Company.</q> He looks up at you. <q>We're | |
installing a network.</q><.convnode what-network><.reveal NIC> " | |
name = 'themselves' | |
; | |
++++ AskTellTopic, SuggestedAskTopic @ddTopic | |
"<q>Are you doing anything as part of Ditch Day?</q> you ask. | |
<.p>They both look a little angry. <q>Do we look like ditch diggers | |
to you?</q> one says. <q>We're in high tech.</q> " | |
name = 'Ditch Day' | |
; | |
++++ AskTellTopic, SuggestedAskTopic @nicTopic | |
"<q>I've never heard of <q>Network Installer Company</q> before,</q> | |
you say. <q>What do they do?</q> | |
<.p>The two give you blank stares for a long few moments, then one | |
says, <q>Uh, we install networks.</q> | |
<.convnode what-network>" | |
name = 'Network Installer Company' | |
isActive = (gRevealed('NIC')) | |
; | |
/* a couple of topics pertain to the network analyzer */ | |
++++ TopicGroup | |
isActive = (quadAnalyzer.isIn(quad) && quadAnalyzer.described) | |
; | |
+++++ AskTellShowTopic @quadAnalyzer | |
"<q>Is that a Netbisco 9099?</q> you ask. | |
<.p>The two glance at the network analyzer. <q>Yep,</q> one says. " | |
; | |
+++++ AskForTopic @quadAnalyzer | |
"<q>Could I borrow your Netbisco 9099?</q> you ask. | |
<.p><q>Uh, no,</q> one of the workers says, sounding annoyed. | |
<q>Our guy down in the tunnel might need it.</q> " | |
; | |
/* a topic about the netbisco *before* we know what it is */ | |
++++ AskTellAboutForTopic @quadAnalyzer | |
"<q>What kind of equipment is that?</q> you ask, pointing to it. | |
<.p>One of the workers looks at it. <q>I dunno,</q> he says. | |
<q>Something we use for network installing, I guess.</q> " | |
isActive = (quadAnalyzer.isIn(quad) && !quadAnalyzer.described) | |
; | |
/* trying to return the analyzer after we have it */ | |
++++ GiveShowTopic @netAnalyzer | |
"You think it might be a bad idea to call too much attention to the | |
fact you borrowed their analyzer. It would probably better to just | |
leave it somewhere nearby and let them find it on their own. " | |
isConversational = nil | |
; | |
++++ DefaultAnyTopic, ShuffledEventList | |
['The two just stare at you, chewing whatever it is they\'re chewing. ', | |
'The two workers look at each other, then back at you, but neither | |
one says anything. ', | |
'The workers roll their eyes and say nothing. ', | |
'One of the workers laughs a bit, and the other one looks at | |
him, but neither one says anything. '] | |
; | |
++++ ConversationReadyState | |
isInitState = true | |
stateDesc = "Both workers are standing by the manhole, looking down | |
into it with bored expressions, lazily chewing something. " | |
; | |
+++++ HelloTopic, StopEventList | |
['You stand close to the yellow tape and try to get the workers\' | |
attention. <q>Excuse me...</q> you call to them. | |
<.p>The two slowly look up from the hole, and look over at you | |
as though you interrupted an important conversation. <q>Yeah?</q> | |
one asks. ', | |
'<q>Excuse me,</q> you say to the workers. | |
<.p>The two look up from the manhole. <q>You again,</q> one | |
says. '] | |
; | |
+++++ ByeTopic "The two workers go back to staring into the hole. " | |
; | |
+++ ConvNode 'what-network'; | |
++++ SpecialTopic 'ask what kind of network' | |
['ask','what','kind','of','network'] | |
"You don't want to seem thin-skinned, so you force a laugh. | |
<q>Yeah, but what kind of network are you working on?</q> | |
<.p><q>Cables, wires, little boxes with blinking lights,</q> he | |
says, miming flashing lights by flapping his fingers. | |
<q>See, they just give us these work orders, and we do the | |
work.</q> He takes a piece of paper out of his pocket and | |
makes a half-hearted effort to hold it out for you to see. | |
You read enough to get the idea they're doing some upgrades | |
for the campus network, but he puts it away before you can | |
read the whole thing. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Olive Walk | |
*/ | |
oliveWalk: CampusOutdoorRoom | |
'East Olive Walk' 'the east Olive Walk' 'Olive Walk' | |
"The Olive Walk is named for the olive trees lining both sides | |
of the brick-paved walkway and arching overhead. The walkway runs | |
east and west between the two main undergraduate house complexes: | |
the newer North Houses on the north side, and the older South Houses | |
on the south side. The front entrances to the houses face the walkway. | |
An antique cannon sits in front of Fleming to the south. " | |
vocabWords = 'east e olive walk' | |
west = olwWestWalk | |
east = olwEastWalk | |
north: NoTravelMessage { "It would certainly be entertaining to | |
go through all the houses and see the different stacks this | |
year, but you should probably try to stay focused for now. " } | |
south: NoTravelMessage { "Much as you'd like to survey all of the | |
stacks in the other houses, you should probably try to stay | |
focused on Stamer's stack. " } | |
/* provide some special descriptions for the cherry picker when here */ | |
descCherryPickerArrival = "You steer the cherry picker to a spot | |
alongside the cannon, off the main path, and park. " | |
inCherryPickerSpecialDesc = "You're standing in the basket of a | |
cherry picker, which is parked alongside the cannon. " | |
cherryPickerSpecialDesc = "A cherry picker is parked alongside the | |
cannon. " | |
; | |
+ CampusMapEntry 'south s student houses' 'the South Houses' 'southeast'; | |
+ CampusMapEntry 'north n student houses' 'the North Houses' 'southeast'; | |
+ CampusMapEntry 'fleming house/hovse' 'Fleming House' 'southeast'; | |
+ CampusMapEntry 'page house' 'Page House' 'southeast'; | |
+ CampusMapEntry 'lloyd house' 'Lloyd House' 'southeast'; | |
+ CampusMapEntry 'ruddock house' 'Ruddock House' 'southeast'; | |
+ CampusMapEntry 'ricketts house/hovse' 'Ricketts House' 'southeast'; | |
+ owTrees: TreeDecoration | |
'(long) olive row/rows/tree/trees/branches' 'olive trees' | |
"The trees are planted in neat rows, one on each side of the walkway, | |
their long, thin branches arching over the path. " | |
isPlural = true | |
; | |
+ Decoration 'red path/brick/bricks' 'bricks' | |
"The path is paved with fairly ordinary red bricks. It continues | |
east and west. " | |
isPlural = true | |
; | |
+ olwWestWalk: PathPassage '(brick) west w walkway/path' 'west walkway' | |
"The walkway continues to the west. " | |
; | |
+ olwEastWalk: PathPassage ->alWestWalk | |
'(brick) east e walkway/path' 'east walkway' | |
"The walkway continues to the east. " | |
; | |
+ Fixture, Chair, ComplexContainer | |
'antique (fleming) (house) cannon' 'cannon' | |
"It's the Fleming House cannon, an actual piece of nineteenth-century | |
artillery. The barrel is at least twelve feet long, and it rides on | |
a pair of big wagon wheels; the carriage is painted the official | |
Fleming House bright red. It's been a fixture here since | |
before your time, and you don't remember the whole story behind it; | |
distant ancestors of modern Flems absconded with it as part of a | |
rivalry with another local college, or something like that. " | |
cannotMoveMsg = 'The cannon is much too heavy to move on your own. ' | |
/* if they just say 'down', translate to 'get off' what we're on */ | |
down = noTravelDown | |
/* the barrel is our container */ | |
subContainer: Occluder, Component, RestrictedContainer | |
{ | |
'(cannon) barrel/end' 'barrel of the cannon' | |
desc() | |
{ | |
"It's twelve feet long or so. The barrel tilts up; "; | |
if (isBarrelAccessible(getPOV())) | |
"the end is easily reachable from here. "; | |
else if (getPOV().isIn(location)) | |
"even sitting on the cannon, it's still a long | |
way to the end of the barrel. "; | |
else | |
"the end is well overhead. "; | |
} | |
/* we're a component of the cannon */ | |
location = lexicalParent | |
/* map operations involving sitting on the barrel to the cannon */ | |
dobjFor(Board) remapTo(Board, location) | |
dobjFor(Climb) remapTo(Climb, location) | |
dobjFor(ClimbUp) remapTo(ClimbUp, location) | |
dobjFor(SitOn) remapTo(SitOn, location) | |
/* this object is reachable from the cherry picker */ | |
isCherryPickerReachable = true | |
/* check for barrel access, showing the message and exiting if not */ | |
checkBarrelAccess(msg) | |
{ | |
if (!isBarrelAccessible(gActor)) | |
{ | |
/* show the failure message */ | |
reportFailure(msg); | |
/* abort */ | |
exit; | |
} | |
} | |
/* explain why we can't look in from the ground */ | |
dobjFor(LookIn) | |
{ | |
check() | |
{ | |
checkBarrelAccess( | |
gActor.isIn(location) | |
? 'From your perch on the cannon, the barrel extends | |
another six or eight feet; there\'s no way to | |
see into it from here. ' | |
: 'The barrel tilts up, and its open end is well | |
overhead, so there\'s no way to see inside it | |
from here. '); | |
} | |
} | |
iobjFor(PutIn) | |
{ | |
check() | |
{ | |
/* first, check that the barrel is accessible at all */ | |
checkBarrelAccess( | |
gActor.isIn(location) | |
? 'From your perch on the cannon, the barrel extends | |
another six or either feet; there\'s no way to | |
reach into the end from here. ' | |
: 'The end of the barrel is too high overhead to | |
reach from here. '); | |
/* do the normal checks */ | |
inherited(); | |
} | |
} | |
/* why can't we put an object inside? */ | |
validContents = [] | |
cannotPutInMsg(obj) { return 'You probably shouldn\'t; it could | |
create a safety hazard the next time the cannon is fired. '; } | |
/* | |
* Is the inside of the barrel accessible from the given point of | |
* view? It is only if the point of view is in the raised cherry | |
* picker basket. | |
*/ | |
isBarrelAccessible(pov) | |
{ | |
return (pov.isIn(cherryPickerBasket) | |
&& cherryPickerBasket.isRaised); | |
} | |
/* we can't even see inside unless we're on high */ | |
occludeObj(obj, sense, pov) | |
{ | |
/* | |
* if the object is inside me, we can't see it (or anything | |
* else) unless the inside of the barrel is accessible | |
*/ | |
return (obj.isIn(self) && !isBarrelAccessible(pov)); | |
} | |
} | |
dobjFor(SitOn) | |
{ | |
action() | |
{ | |
"It takes a little figuring out, but you manage to climb | |
up onto the carriage and sit down astride the barrel. "; | |
inherited(); | |
} | |
} | |
dobjFor(Climb) asDobjFor(SitOn) | |
dobjFor(ClimbUp) asDobjFor(SitOn) | |
/* | |
* we're not an ordinary chair: to extricate ourselves, we want to | |
* put it in terms of getting off the cannon rather than merely | |
* standing up, as is the default for chairs | |
*/ | |
tryMakingTravelReady(conn) { return tryImplicitAction(GetOffOf, self); } | |
notTravelReadyMsg = 'You\'ll have to get off the cannon first. ' | |
; | |
++ Component '(cannon) big red wagon wheel/wheels' | |
'wheels of the cannon' | |
"They're about five feet in diameter, and they look just like | |
wagon wheels. " | |
isPlural = true | |
; | |
++ Component '(cannon) counterweight/carriage' 'carriage' | |
"It's mostly just a big counterweight for the barrel. | |
It's painted the standard Fleming bright red. " | |
; | |
/* | |
* this is just a joke reference to in Ditch Day Drifter - Mr. Happy | |
* Gear was one of the random scavenger-hunt items you had to obtain in | |
* that game's Ditch Day stack | |
*/ | |
++ mrHappyGear: Hidden | |
'mr. happy happy-face metal machine gear/face/ | |
cut-out/cut-outs/pattern' | |
'machine gear' | |
"It's a metal gear about an inch and a half in diameter, with a | |
pattern of cut-outs that makes it look a lot like a happy-face icon. | |
For some reason, the name <q>Mr.\ Happy Gear</q> comes to mind. " | |
subLocation = &subContainer | |
; | |
+ EntryPortal ->(location.north) | |
'new newer north n undergraduate student front house page lloyd ruddock | |
house/houses/complex/dorm/dorms/entrances' | |
'North Houses' | |
"The North Houses complex was built in the 1960s; it's a group of | |
three dorms—Page, Lloyd, and Ruddock—more or less | |
under one roof. The houses are built in a 1960s-modern style, | |
with simple, straight lines. " | |
isPlural = true | |
; | |
+ EntryPortal ->(location.south) | |
'old older south s undergraduate student house clay tile | |
stucco front fleming ricketts arch/arches/roof/roofs/ | |
house/houses/hovses/complex/dorm/dorms/wall/walls/entrances' | |
'South Houses' | |
"The South Houses were built in the 1930s, and were designed in | |
a Mediterranean style: arches, clay tile roofs, stucco walls, | |
irregular lines, a somewhat rambling layout. Fleming and Ricketts | |
are on this side of the complex, Dabney and Blacker on the south side. " | |
isPlural = true | |
; | |
+ Distant 'blacker dabney house/houses/hovse/hovses' 'Blacker and Dabney' | |
"Blacker and Dabney aren't visible from here; they're on the south side | |
of the group of buildings. " | |
isPlural = true | |
isProperName = true | |
tooDistantMsg = 'Blacker and Dabney aren\'t visible from here. ' | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Ath lawn | |
*/ | |
athLawn: CampusOutdoorRoom 'Athenaeum Lawn' 'the Ath lawn' 'lawn' | |
"This is a wide lawn outside the campus's faculty club, known | |
as the Athenaeum: a stately, Mediterranean-style structure of | |
white stucco, columns, and red roof tiles, looming to the east. | |
The Ath's open-air dining room faces the lawn. The olive walk | |
ends here, and continues into the campus to the west. " | |
east = athDiningRoom | |
in asExit(east) | |
west = alWestWalk | |
; | |
+ CampusMapEntry 'athenaeum/ath' 'the Athenaeum' 'southeast'; | |
+ Distant, Enterable -> (location.east) | |
'stately mediterranean-style dining faculty | |
ath/athenaeum/structure/building/room/club' | |
'Athenaeum' | |
"The Ath is a sprawling stucco-and-red-tile-roof structure, which | |
makes it look like a lot of SoCal movie-star mansions. The | |
open-air dining room looks out onto the lawn. " | |
; | |
++ Decoration 'white red roof tile stucco/column/columns/tile/tiles/roof' | |
'architectural details' | |
"The Ath has that Mediterranean style that's ubiquitous here | |
on the campus. " | |
isPlural = true | |
; | |
+ alWestWalk: PathPassage | |
'(brick) west w olive walk/walkway/path' 'west walkway' | |
"The walkway continues to the west. " | |
; | |
+ Decoration 'grass/lawn' 'lawn' | |
"The lawn looks well tended. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Ath dining room | |
*/ | |
athDiningRoom: Room 'Athenaeum Dining Room' 'the Ath dining room' | |
'dining room' | |
"Dining tables are set up around this spacious room, which is | |
open to the lawn to the west. Huge round columns support the | |
high ceiling. A wide double door leads east. " | |
vocabWords = 'spacious ath athenaeum dining room' | |
west = athLawn | |
east = adrDoor | |
out asExit(west) | |
; | |
+ Distant, Enterable -> (location.west) 'lawn/grass' 'lawn' | |
"The lawn is outside, to the west. " | |
; | |
+ adrDoor: AlwaysLockedDoor | |
'wide tall wood double door/doors' 'double doors' | |
"The doors are wide and tall. They lead into the structure | |
to the east. " | |
isPlural = true | |
; | |
+ Decoration 'dining tables/chairs/table/chair' 'tables' | |
"The tables are currently empty, since the dining room isn't | |
serving a meal at the moment. " | |
isPlural = true | |
; | |
+ Decoration 'huge round column/columns' 'columns' | |
"The columns are holding up the roof, and lend the room a | |
formal air. " | |
isPlural = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* California Blvd is visible from several locations, so make it a | |
* class. We'd normally use MultiFaceted for this kind of situation, | |
* but in this case it's easier to make it a class, because we want to | |
* point room directional connectors to it. | |
*/ | |
class CalBlvd: PathPassage | |
'california boulevard/blvd/blvd./street/road' | |
'California Boulevard' | |
"California Boulevard is a busy four-lane street that forms | |
the south border of the campus. " | |
isProperName = true | |
dobjFor(TravelVia) | |
{ | |
action() { reportFailure('You shouldn\'t go wandering off | |
campus until you\'ve finished your business here. '); } | |
} | |
dobjFor(Cross) remapTo(TravelVia, self) | |
; | |
class CalBlvdTraffic: Distant | |
'traffic/car/cars/auto/automobile' 'traffic' | |
"The traffic is fairly heavy, but moving steadily. " | |
; | |
class CalBlvdNoise: SimpleNoise | |
desc = "Constant traffic zips by on California Boulevard. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Orange walk | |
*/ | |
orangeWalk: CampusOutdoorRoom 'Orange Walk' 'the Orange Walk' 'Orange Walk' | |
"This north-south walkway is named for the orange trees planted | |
alongside. A few steps ascend to the north. To the south, the | |
walkway ends at California Boulevard, which forms the southern | |
limit of the campus. An unpaved path through the orange trees | |
leads west. To the east, a passage leads into Dabney Hovse. " | |
vocabWords = 'orange walk/walkway' | |
north = orwNorthWalk | |
up asExit(north) | |
south = orwCalBlvd | |
east = dabneyBreezeway | |
in asExit(east) | |
west = orwWestPath | |
atmosphereList = (owMovers.isIn(self) ? moversAtmosphereList : nil) | |
; | |
+ CampusMapEntry 'orange walk' 'the Orange Walk' 'southeast'; | |
+ CampusMapEntry 'dabney house/hovse' 'Dabney House' 'southeast' | |
altLocations = [dabneyCourtyard] | |
/* this is the most important "dabney" and "house" we might seek */ | |
mapMatchStrength = 200 | |
; | |
+ Decoration | |
'metal (path) lighting (light) clear | |
fixture/fixtures/light/lights/post/posts/enclosures' | |
'path lights' | |
"The path lights are simple metal posts with light bulbs | |
inside clear enclosures near the top. They're arranged every | |
few feet alongside the path. " | |
isPlural = true | |
; | |
++ Decoration '(path) light bulb/bulbs' 'path light bulbs' | |
"You are too much a child of your era to find light bulbs | |
especially noteworthy. While it is no doubt a scientific | |
and technological marvel to coax photons from excited | |
electrons returning to their ground states in a low-pressure | |
capsule of sodium vapor, you are all too quick to conclude | |
that these look like ordinary light bulbs to you. " | |
; | |
+ orwNorthWalk: PathPassage | |
'north n paved path/step/steps/stair/stairs' 'steps' | |
"The walkway ascends a few steps to the north. " | |
isPlural = true | |
dobjFor(ClimbUp) asDobjFor(Enter) | |
dobjFor(Climb) asDobjFor(Enter) | |
; | |
+ TreeDecoration 'large dense orange tree/trees/foliage' 'orange trees' | |
"They're large trees with dense foliage and lots of oranges. " | |
isPlural = true | |
lookInDesc = "Apart from the oranges, you see nothing in the trees. " | |
; | |
++ OrangeDecoration; | |
+ EntryPortal ->(location.east) | |
'faux decorative letters/passage/stonework' 'passage' | |
"It's a generously proportioned passage leading east. The passage | |
is framed in decorative faux stonework, and the words <q>DABNEY HOVSE</q> | |
are inscribed above it. (The letters are styled to look like | |
they're chiseled in stone, but they're actually just stamped in | |
the stucco.) " | |
; | |
+ Enterable ->(location.east) | |
'big stucco dabney building/house/hovse/word/words' 'Dabney House' | |
"It's a big stucco building designed to resemble classic | |
Mediterranean architecture. The words <q>DABNEY HOVSE</q> are | |
inscribed above the passage to the east. " | |
isProperName = true | |
; | |
+ orwWestPath: PathPassage ->syncEastPath | |
'west w unpaved dirt path' 'unpaved path' | |
"The dirt path leads west through the orange trees. " | |
; | |
+ owMovers: MitaMovers | |
"A seemingly endless stream of movers is coming up from California | |
Boulevard and heading into Dabney, carrying boxes and crates. Others | |
are returning from Dabney empty-handed, probably on their way to pick | |
up the next load. " | |
"Mitachron movers are working their way up from California Boulevard | |
and into Dabney, carrying loads of boxes and crates. " | |
; | |
/* add our California Blvd instance, and its constituent parts */ | |
+ orwCalBlvd: CalBlvd; | |
++ CalBlvdTraffic; | |
+++ CalBlvdNoise; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Sync lot | |
*/ | |
syncLot: CampusOutdoorRoom 'Sync Lot' 'the Sync lot' 'parking lot' | |
"This narrow parking lot has room for only four or five cars; | |
the ones parked here today look like they haven't been moved | |
in a while. The gray bulk of the Synchrotron Lab looms to the | |
west, featureless but for a dull metal door, and the back side | |
of Firestone rises to the north. A row of orange trees borders | |
the parking lot to the east; a dirt path leads through the | |
trees. To the south is California Boulevard. " | |
vocabWords = '(sync) (synchrotron) parking lot' | |
west = syncDoor | |
east = syncEastPath | |
south = syncCalBlvd | |
; | |
+ CampusMapEntry 'synchrotron sync synch lab/laboratory' | |
'the Synchrotron Laboratory' 'southeast'; | |
+ syncEastPath: PathPassage 'east e unpaved dirt path' 'dirt path' | |
"The path leads east through a row of orange trees. " | |
; | |
+ TreeDecoration 'large dense orange tree/trees/foliage' 'orange trees' | |
"They're large trees with dense foliage and lots of oranges. " | |
isPlural = true | |
lookInDesc = "Apart from the oranges, you see nothing in the trees. " | |
; | |
++ OrangeDecoration; | |
+ Decoration 'older parked car/cars/auto/autos/automobiles' 'cars' | |
"Five cars are squeezed into the lot today. They're mostly older | |
models, and they look like they haven't been moved in a while. " | |
isPlural = true | |
notImportantMsg = 'You shouldn\'t mess with other people\'s cars. ' | |
; | |
+ syncDoor: LockableWithKey, Door | |
'dull metal (sync) (synchrotron) (lab) (laboratory) door' | |
'dull metal door' | |
"The door is made of a dull metal. " | |
; | |
+ Immovable | |
'two-story gray sync synchrotron lab laboratory building/bulk/wall' | |
'Sync Lab' | |
"The only feature of the two-story gray building is a dull | |
metal door. The Sync Lab is so named because it housed a | |
synchrotron particle accelerator back in the 1960s, but that | |
has long since been dismantled. " | |
dobjFor(Enter) remapTo(Enter, syncDoor) | |
; | |
+ Immovable | |
'three-story firestone lab laboratory back building/wall/side/backside' | |
'back side of Firestone' | |
"This side of Firestone is just just a featureless three-story wall. " | |
dobjFor(Enter) { verify() { illogical('There is no entrance to | |
Firestone here. '); } } | |
; | |
/* add our California Blvd instance, and its constituent parts */ | |
+ syncCalBlvd: CalBlvd; | |
++ CalBlvdTraffic | |
/* this is even less likely than the parked cars decoration for EXAMINE */ | |
dobjFor(Examine) { verify() { logicalRank(50, 'x decoration'); } } | |
; | |
+++ CalBlvdNoise; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* West olive walk | |
*/ | |
westOliveWalk: CampusOutdoorRoom 'West Olive Walk' 'the west Olive Walk' | |
'Olive Walk' | |
"The olive walk runs east and west here through a broad lawn | |
flanked by academic buildings. On the north side is the beige, | |
rectilinear expanse of Thomas. Firestone, to the southeast, | |
and Guggenheim, to the southwest, are joined together by a | |
second-story bridge over the wide gap between them; a paved | |
walkway leads south under the bridge. " | |
vocabWords = 'west w olive walk' | |
east = wowEastWalk | |
west = wowWestWalk | |
south = wowSouthWalk | |
north = wowThomasDoor | |
southeast = firestoneDoor | |
southwest = guggenheimDoor | |
/* | |
* After we've climbed firestone once, allow climbing again by | |
* simply saying 'up'. If we haven't climbed firestone at least | |
* once already, or we're not positioned to do so currently, use the | |
* inherited 'up' instead. | |
*/ | |
up = (gActor.isIn(cherryPickerBasket) | |
&& cherryPickerBasket.isRaised | |
&& gActor.hasSeen(climbingFirestone) | |
? wowFirestoneLattice : inherited) | |
/* provide special descriptions for the cherry picker when it's here */ | |
descCherryPickerArrival = "You steer the cherry picker to a spot | |
off the path, right next to the wall of Firestone, and park. " | |
inCherryPickerSpecialDesc = "You're standing in the basket of a | |
cherry picker, which is parked next to Firestone. | |
<<cherryPickerBasket.isRaised | |
? "The bottom of the latticework grill is within arm's | |
reach of the basket." : "">> " | |
cherryPickerSpecialDesc = "A cherry picker is parked next to | |
Firestone. " | |
noteCherryPickerRaised = "<.p>The latticework grill facing Firestone | |
is now within arm's reach. " | |
; | |
+ CampusMapEntry 'firestone lab/laboratory' 'Firestone Laboratory' 'south'; | |
+ CampusMapEntry 'guggenheim lab/laboratory' 'Guggenheim Laboratory' 'south'; | |
+ CampusMapEntry 'thomas lab/laboratory' 'Thomas Laboratory' 'south'; | |
+ Decoration 'red path/brick/bricks' 'bricks' | |
"The path is paved with fairly ordinary red bricks. It continues | |
east and west. " | |
isPlural = true | |
; | |
+ firestoneDoor: AlwaysLockedDoor | |
'front white metal (firestone) (firestone)/door*doors' | |
'door of Firestone' | |
"The front door of Firestone is just a simple metal door painted white. " | |
cannotOpenLockedMsg = 'The door seems to be locked. Security must | |
not have bothered to unlock the building today, knowing that | |
most classes would be canceled for Ditch Day. ' | |
; | |
+ guggenheimDoor: AlwaysLockedDoor | |
'front wood (guggenheim) (guggenheim)/door*doors' 'door of Guggenheim' | |
"It's a simple wood door. " | |
cannotOpenLockedMsg = 'The door seems to be locked. It\'s strange | |
for campus buildings to be locked during the day, but the usual | |
routines don\'t always apply on Ditch Day. ' | |
; | |
+ wowWestWalk: PathPassage '(brick) west w path/walkway' 'west walkway' | |
"The walkway leads west. " | |
; | |
+ wowEastWalk: PathPassage ->quadWestWalk | |
'(brick) east e path/walkway' 'east walkway' | |
"The walkway leads east. " | |
; | |
+ wowSouthWalk: PathPassage ->ldcNorthWalk | |
'south s walk/walkway' 'south walkway' | |
"The walkway leads south, under the second-story bridge. " | |
; | |
+ TreeDecoration 'olive tree/trees/branches' 'olive trees' | |
"The olive trees here are planted randomly around the lawn. " | |
isPlural = true | |
; | |
+ Decoration 'wide grass/lawn' 'lawn' | |
"The wide lawn is punctuated here and there by olive trees. " | |
; | |
+ Enterable ->(location.southeast) | |
'firestone flight sciences lab/laboratory/wall/building*buildings*walls' | |
'Firestone Lab' | |
"Firestone is the flight sciences lab. The building is | |
three stories tall, and faced with a series of lattice-work | |
grills. The door is the southeast. " | |
isProperName = true | |
dobjFor(Climb) remapTo(Climb, wowFirestoneLattice) | |
dobjFor(ClimbUp) remapTo(Climb, wowFirestoneLattice) | |
; | |
++ wowFirestoneLattice: OutOfReach, TravelWithMessage, StairwayUp ->cfDown | |
'(firestone) asterisk lattice-work | |
x\'s/plus-signs/shapes/grill/grills/series/lattice/lattices/columns' | |
'lattice' | |
"The grills don't cover the entire face of the building but | |
are spaced out in columns, each one starting at the second floor and | |
continuing up to the roofline. Each lattice column is a grid of little | |
asterisk shapes, X's superimposed on plus-signs—sized | |
perfectly as hand-holds for climbing. Security frowns vigorously | |
upon anyone climbing the building, but it's too tempting a target, | |
and it seems like every year someone takes up the challenge. | |
Of course, this isn't trivial, as the bottom of each lattice is | |
well overhead. " | |
/* we can reach this only from the raised cherry picker basket */ | |
canObjReachContents(obj) | |
{ | |
/* | |
* 'obj' must be in the cherry picker, and the basket must be | |
* raised; otherwise, we can't reach the building | |
*/ | |
return obj.isIn(cherryPickerBasket) && cherryPickerBasket.isRaised; | |
} | |
/* explain that it's out of reach - from the ground, at least */ | |
cannotReachFromOutsideMsg(dest) { return 'The lattice starts at the | |
second floor. You can\'t reach it from the ground. '; } | |
/* | |
* this object is reachable from the raised cherry picker, and ONLY | |
* from the raised cherry picker | |
*/ | |
isRaisedCherryPickerReachable = true | |
/* to climb this way, we have to start in the cherry picker basket */ | |
connectorStagingLocation = cherryPickerBasket | |
/* apply some special preconditions */ | |
connectorTravelPreCond() | |
{ | |
local lst; | |
/* get the inherited list */ | |
lst = inherited(); | |
/* in addition, our hands must be empty */ | |
lst += handsEmpty; | |
/* | |
* Remove the staging location precondition. Our staging | |
* location is important, but we don't need or want a | |
* precondition for it. We don't need the precondition because | |
* the fact that the lattice is out of reach is enough to prevent | |
* climbing the lattice until we're in the raised cherry picker. | |
* We don't want the precondition because we want to leave it to | |
* the player to figure out what to do - we don't want to make | |
* this automatic. | |
*/ | |
lst = lst.subset({x: !x.ofKind(TravelerDirectlyInRoom)}); | |
/* return the result */ | |
return lst; | |
} | |
/* add a message on traversal */ | |
travelDesc { travelDescScript.doScript(); } | |
travelDescScript: StopEventList { [ | |
'You carefully climb up onto the edge of the basket | |
and reach out for the Firestone grill. The pattern of | |
the latticework offers surprisingly easy handholds. | |
You feel you have a secure grip on the wall, so you | |
take one foot off the basket and search for a toehold. | |
No problem so far. You take a deep breath and commit | |
yourself, taking your other foot off the basket, shifting | |
your full weight to the wall. | |
<.p>You don\'t immediately fall, so you try moving | |
up the wall a few inches. It\'s actually pretty easy; | |
almost like climbing a ladder. You move up a few feet, | |
getting the hang of it. ', | |
'You reach out for the wall and find a handhold, then | |
shift your feet over. You climb up the wall a few feet. '] } | |
/* "get on grill" is the same as climbing it */ | |
dobjFor(Board) remapTo(Climb, self) | |
; | |
+ Enterable ->(location.southwest) | |
'guggenheim aeronautics lab/laboratory/wall/building*buildings*walls' | |
'Guggenheim Lab' | |
"Guggenheim is an aeronautics lab. It's a three-story | |
building with large windows framed in wrought iron. From here, | |
you can just get a glimpse of the wind tunnel structure on the | |
roof of the building. The entrance is to the southwest. " | |
isProperName = true | |
; | |
++ wowWindTunnel: Distant 'wind tunnel structure' 'wind tunnel' | |
"You can just barely see the long, low structure on the roof | |
of Guggenheim. You can't make out any detail from here. " | |
; | |
++ Distant 'wrought iron frame/frames/window/windows/(guggenheim)' | |
'windows of Guggenheim' | |
"The windows are large, many-paned windows in wrought iron frames. " | |
owner = (location) | |
; | |
+ Enterable ->(location.north) | |
'beige rectilinear thomas civil mechanical engineering | |
lab/laboratory/expanse/wall/building*buildings*labs' | |
'Thomas Lab' | |
"Thomas is the civil and mechanical engineering building. It's | |
a large building with what you've always thought is a rather | |
spartan appearance: it's essentially a big rectangular box, | |
three stories high, with a regular grid of small windows trimmed | |
in rectangular grids of wrought iron. A door in the center of | |
the structure leads inside. " | |
isProperName = true | |
; | |
++ wowThomasDoor: Door | |
'wide double (thomas) door/(thomas)/(lab)/(laboratory)*doors' | |
'door to Thomas' | |
"It's a wide double door, but the bulk of Thomas makes it look | |
miniature. " | |
; | |
++ Distant | |
'wrought iron window/windows/grid/pane/panes/frame/frames/(thomas)' | |
'windows of Thomas' | |
"The windows are arranged in a regular grid, and each window | |
is itself a grid of panes in a wrought iron frame. " | |
isPlural = true | |
owner = (location) | |
; | |
+ Distant 'second-story story bridge' 'second-story bridge' | |
"The bridge is decorated with the same lattice-work facing | |
as Firestone. It connects Firestone and Guggenheim on their | |
second floors. A walkway leads south under the bridge. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Climbing on the Firestone grill | |
*/ | |
climbingFirestone: Floorless, CampusOutdoorRoom | |
'Climbing Firestone' 'halfway up the side of Firstone' | |
"This point is about halfway up the side of Firestone. The | |
latticework grill facing the building is relatively easy to | |
climb; it extends above to the roof, and below to about one | |
story above ground level. " | |
up = cfUp | |
down = cfDown | |
roomBeforeAction() | |
{ | |
/* don't allow taking or dropping anything here */ | |
if (gActionIs(Take) || gActionIs(TakeFrom) | |
|| gActionIs(Doff) || gActionIs(Drop)) | |
{ | |
"Your hands are already fully occupied holding onto | |
the side of the building. "; | |
exit; | |
} | |
/* if we try to jump, it's the same as 'down' */ | |
if (gActionIn(Jump, JumpOffI)) | |
replaceAction(Down); | |
} | |
/* on entry, set up the proxy version of the cherry picker */ | |
enteringRoom(traveler) | |
{ | |
/* set up the local proxy version of the cherry picker */ | |
cfCherryPicker.makePresentIf(cherryPicker.isIn(westOliveWalk)); | |
} | |
; | |
+ Fixture | |
'front north latticework firestone | |
grill/building/lab/laboratory/wall/firestone' | |
'wall of Firestone' | |
"The asterisk-shaped pattern of the grill is relatively easy | |
to hang onto. The latticework continues up to the roof, but | |
ends below at about one story above ground level. " | |
dobjFor(JumpOff) | |
{ | |
verify() { } | |
action() { replaceAction(Down); } | |
} | |
/* climbing up/down the wall maps to climbing the internal connectors */ | |
dobjFor(ClimbUp) remapTo(TravelVia, cfUp) | |
dobjFor(Climb) remapTo(TravelVia, cfUp) | |
dobjFor(ClimbDown) remapTo(TravelVia, cfDown) | |
; | |
/* an internal connector leading up to the roof */ | |
+ cfUp: TravelWithMessage, StairwayUp ->frGrill | |
travelDesc = "You work your way up to the top of the wall. Once | |
you're at the top, you roll yourself over the edge and onto | |
the roof. " | |
/* we're for internal use only, so hide from 'all' */ | |
hideFromAll(action) { return true; } | |
; | |
/* an internal connector leading down to ground level */ | |
+ cfDown: TravelWithMessage, StairwayDown | |
travelDesc = "You carefully climb down the grill until you're level | |
with the basket, then ease yourself into the basket. " | |
/* we're for internal use only, so hide from 'all' */ | |
hideFromAll(action) { return true; } | |
/* our destination is the cherry picker basket */ | |
destination = cherryPickerBasket | |
/* | |
* we can only go down if the cherry picker is below us and the | |
* basket is raised | |
*/ | |
canTravelerPass(trav) | |
{ | |
return cfCherryPicker.isIn(location) && cherryPickerBasket.isRaised; | |
} | |
explainTravelBarrier(trav) { "The grill ends about one story up, | |
so you can\'t climb down far enough to safely jump to the | |
ground. "; } | |
; | |
+ Distant 'olive ground/walk/walkway/lawn' 'ground' | |
"The ground is about two stories below. " | |
; | |
+ Distant 'thomas lab/building' 'Thomas Lab' | |
"Thomas is across the lawn, a distance to the north. " | |
isProperName = true | |
; | |
/* a local proxy for the cherry picker */ | |
+ cfCherryPicker: PresentLater, Distant | |
'cherry picker' 'cherry picker' | |
desc() | |
{ | |
"The cherry picker is directly below. "; | |
if (cherryPickerBasket.isRaised) | |
"Its basket is raised to full height, | |
so it shouldn't be too hard to climb into it from here. "; | |
else | |
"The basket is lowered, though, so there's no way you | |
could climb into it from here. "; | |
} | |
specialDesc = "The cherry picker is parked next to the building, | |
almost directly below. " | |
/* we can enter it without touching it */ | |
dobjFor(Enter) remapTo(TravelVia, cfDown) | |
dobjFor(Board) asDobjFor(Enter) | |
dobjFor(StandOn) asDobjFor(Enter) | |
; | |
++ Distant | |
'(cherry) (picker) basket' 'basket of the cherry picker' | |
desc() | |
{ | |
if (cherryPickerBasket.isRaised) | |
"The basket is raised up to full height, putting it at | |
about second-floor level. It shouldn't be too hard to | |
climb into it from here. "; | |
else | |
"The basket is lowered, so there's no way you could climb | |
into it from here. "; | |
} | |
/* we can enter it without touching it */ | |
dobjFor(Enter) remapTo(TravelVia, cfDown) | |
dobjFor(Board) asDobjFor(Enter) | |
dobjFor(StandOn) asDobjFor(Enter) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* A class for our complex of roofs around Firestone | |
*/ | |
class RoofRoom: CampusOutdoorRoom | |
/* | |
* we can't use the map to navigate from here, as we're not | |
* trivially connected to the ground map | |
*/ | |
cannotUseMapHere = "You'll have to get down to the ground before | |
you can go there, of course. " | |
/* don't use the standard ground; we'll use a special 'roof' instead */ | |
roomParts = static (inherited - defaultGround) | |
roomBeforeAction() | |
{ | |
if (gActionIn(Jump, JumpOffI)) | |
{ | |
"It's too far to the ground to jump. "; | |
exit; | |
} | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Roof of Firestone | |
*/ | |
firestoneRoof: RoofRoom 'Roof of Firestone' 'the roof of Firestone' 'roof' | |
"A knee-level lip runs around the perimeter of this flat, | |
tar-coated roof, providing a modicum of protection against | |
walking off the edge. Numerous vents, pipes, and other fixtures | |
protrude at random spots. | |
<.p>The roof narrows and continues west, over the bridge | |
connecting Firestone and Guggenheim. The latticework grill | |
facing the north side of the building comes up to the roofline, | |
so it should be possible to climb down that way. At the southeast | |
corner, a ladder leads down the south wall. " | |
vocabWords = 'firestone lab/laboratory/building/(roof)' | |
north = frGrill | |
west = guggenheimRoof | |
southeast = frLadder | |
/* 'down' is ambiguous, so ask what they intend to climb down */ | |
down: AskConnector { | |
travelAction = ClimbDownAction | |
askMissingObject(actor, action, which) | |
{ | |
"Do you want to climb down the ladder, or the front wall | |
of Firestone? "; | |
} | |
} | |
/* 'climb down firestone' == 'climb down grill' */ | |
dobjFor(Climb) remapTo(TravelVia, frGrill) | |
dobjFor(ClimbDown) remapTo(TravelVia, frGrill) | |
; | |
+ Floor 'flat tar tar-coated roof/floor' 'roof' | |
"It's a flat, tar-coated roof. " | |
dobjFor(JumpOff) remapTo(Jump) | |
; | |
+ Decoration 'knee-level lip/(roof)' 'lip of roof' | |
"The lip is about two feet high, and runs around the perimeter | |
of the roof. It's probably meant more to block water run-off | |
than to protect climbers. " | |
dobjFor(JumpOff) remapTo(Jump) | |
; | |
+ Decoration 'vent/vents/pipe/pipes/fixtures' 'fixtures' | |
"The protrusions would make it dangerous to walk around up here | |
at night, but they're easy enough to avoid in the daylight. " | |
isPlural = true | |
; | |
+ Distant, Decoration | |
'guggenheim (roof)/(lab)/(laboratory)/(building)' 'Guggenheim' | |
"Guggenheim is the next building west, but the roof is connected | |
via the bridge. " | |
; | |
+ Enterable ->(location.west) '(roof) bridge' 'bridge' | |
"From here, the bridge is simply a slightly narrower section of | |
roof leading west. " | |
; | |
+ Distant, Decoration 'ground' 'ground/walkway' | |
"The ground is three stories below. " | |
; | |
+ Distant, Decoration 'cherry picker basket' 'cherry picker' | |
"The cherry picker is too close to the wall and too far | |
below to get a good look from here. " | |
; | |
+ frGrill: TravelWithMessage, StairwayDown | |
'north n front latticework grill/wall/side/(building)/(firestone)' | |
'latticework grill' | |
"The building is faced with a latticework grill, whose | |
asterisk-shaped openings make nice handholds for climbing. " | |
travelDesc = "You back up to the north edge of the roof and | |
lie down on the lip, then lower your legs over the edge. | |
You find a toehold and start working your way down the wall. " | |
/* hands must be empty to climb down the building */ | |
connectorTravelPreCond = (inherited() + handsEmpty) | |
; | |
+ frLadder: StairwayDown 'metal ladder' 'ladder' | |
"The metal ladder is permanently attached at the southwest | |
corner of the roof. It leads over the edge of the roof, down | |
the south side of the building. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Guggenheim roof | |
*/ | |
guggenheimRoof: RoofRoom | |
'Roof of Guggenheim' 'the roof of Guggenheim' 'roof' | |
"The whole south side of the roof is dominated by the Guggenheim | |
wind tunnel: a long, low steel structure, resembling an | |
extremely elongated storage shed. <<grPanel.roomDesc>> The | |
roof continues east, narrowing over the bridge connecting | |
Guggenheim to Firestone. " | |
vocabWords = 'guggenheim lab/laboratory/building/(roof)' | |
east = firestoneRoof | |
south: NoTravelMessage { "There's no obvious entrance to the | |
structure here. Which isn't surprising: you'd expect the | |
wind tunnel scientists to enter from inside Guggenheim, not | |
by scaling the exterior walls. " } | |
down: NoTravelMessage { "The face of Guggenheim is not as | |
climber-friendly as Firestone, so there's no obvious | |
way down from here. " } | |
; | |
+ Floor 'flat tar tar-coated roof/floor' 'roof' | |
"It's a flat, tar-coated roof. " | |
dobjFor(JumpOff) remapTo(Jump) | |
; | |
+ Distant, Decoration 'ground' 'ground/walkway' | |
"The ground is three stories below. " | |
; | |
+ Distant, Decoration | |
'firestone (roof)/(lab)/(laboratory)/(building)' 'Firestone' | |
"Firestone is the next building east, but the roof connects | |
via the bridge. " | |
; | |
+ Enterable ->(location.east) '(roof) bridge' 'bridge' | |
"From here, the bridge is simply a slightly narrower section of | |
roof leading east. " | |
; | |
+ Enterable ->(location.south) | |
'long low steel (guggenheim) wind structure/tunnel/wall' | |
'wind tunnel' | |
"The structure runs the full east-west extent of Guggenheim, but | |
it's low and narrow. A complex network of pipes and conduits | |
runs along the walls of the structure, turning and twisting | |
around one another. A group of pipes makes right-angle turns | |
around <<grPanel.aName>> low on the wall. " | |
; | |
++ Fixture 'complex metal electrical pipe/pipes/conduits/network' 'pipes' | |
"Metal pipes are arrayed along the wall, mostly running | |
horizontally but frequently turning to make room for | |
other pipes, or to enter the structure, or to plunge down | |
through the roof. Electrical conduits are mixed in with | |
the pipes. " | |
isPlural = true | |
; | |
++ grPanel: TravelWithMessage, Door ->wtPassage | |
'access small low panel/door/passage' 'small panel' | |
"The panel looks to be a small door, about a meter square, | |
near the bottom of the wall. It's probably an access door | |
for mechanical work on the tunnel apparatus. " | |
makeOpen(stat) | |
{ | |
/* do the normal work */ | |
inherited(stat); | |
/* if this is the first time being opened, do some extra work */ | |
if (stat && !openedBefore) | |
{ | |
/* make me the way south when I'm first opened */ | |
guggenheimRoof.south = self; | |
/* we've been opened now */ | |
openedBefore = true; | |
/* mention what we see */ | |
"Opening the panel reveals a passage that seems to | |
lead inside the structure. "; | |
} | |
/* change the name to passage or panel, according to our status */ | |
name = (stat ? 'access door' : 'small panel'); | |
} | |
travelDesc = "You get down on all fours and crawl through the | |
passage. " | |
roomDesc() | |
{ | |
if (openedBefore) | |
"A small, low door leads into the structure. "; | |
} | |
/* have we been opened before? */ | |
openedBefore = nil | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Wind tunnel interior | |
*/ | |
windTunnel: Room 'Wind Tunnel Lab' 'the wind tunnel lab' | |
"This is a long, low, narrow space, crammed with equipment. It | |
doesn't seem to be the sort of wind tunnel with a gigantic fan | |
at one end of a big, empty room. Instead, the <q>tunnel</q> | |
looks more like a cement mixer: a bright blue metal cylinder | |
about ten feet long and six feet in diameter, lying on its side. | |
The cylinder is tapered to a smaller diameter at each end, where | |
it's connected to stacks of heavy industrial machinery that would | |
look at home in an auto plant. | |
<.p>A wrought-iron spiral stairway leads down a narrow shaft. | |
A low door to the north, almost hidden under pieces of equipment, | |
leads outside. " | |
north = wtPassage | |
out asExit(north) | |
down = wtStairs | |
; | |
+ wtPassage: TravelWithMessage, Door | |
'small low passage/door' 'low door' | |
"It's a small door, only about three feet high, leading | |
out of the structure to the north. " | |
travelDesc = "You get on your hands and knees and crawl out | |
through the passage. " | |
; | |
+ wtStairs: StairwayDown ->wtsStairs | |
'black wrought-iron spiral narrow stairs/staircase/stairway/shaft' | |
'spiral stairs' | |
"The narrow spiral stairs lead down a shaft. " | |
isPlural = true | |
; | |
+ Decoration | |
'heavy industrial measuring imaging | |
piece/pieces/equipment/machinery/plumbing/compressors/pumps/apparatus' | |
'equipment' | |
"A lot of the equipment, especially the industrial-looking | |
machinery, seems to be plumbing for the wind tunnel, probably | |
compressors and pumps and the like. Other equipment is clearly | |
imaging and measuring apparatus. " | |
isMassNoun = true | |
notImportantMsg = 'The wind tunnel is undoubtedly both delicate | |
and powerful, so you could probably do a lot of damage if you | |
started fooling around with it. ' | |
dobjFor(LookUnder) { action() { "A low door leading north is | |
partially hidden under some of the equipment stacked | |
along the wall. Other than that, you see nothing | |
apart from more machinery. "; } } | |
; | |
+ ComplexContainer, Fixture | |
'bright blue metal wind cement tunnel/cylinder/mixer' | |
'metal cylinder' | |
"The cylinder appears to be the main part of the wind tunnel. | |
It's lying on its side; it's about ten feet long and six feet | |
in diameter, so it almost fills the short floor-to-ceiling | |
distance of the room. The cylinder tapers at each end to | |
a smaller diameter. Many pieces of equipment attach to | |
the cylinder at each end. A round, steel hatch, about two | |
feet in diameter, provides access to the interior; it's | |
currently <<subContainer.openDesc>>. " | |
subContainer: ComplexComponent, OpenableContainer { | |
/* | |
* use the normal container contents lister, as we describe the | |
* open/closed status explicitly in the long description (the | |
* openable contents lister would add its own open/closed status | |
* description, which we don't want in this case) | |
*/ | |
descContentsLister = thingDescContentsLister | |
} | |
; | |
++ ContainerDoor 'round steel metal hatch' 'hatch' | |
"It's a round metal hatch, about two feet in diameter. " | |
; | |
++ squirrel: TurboPowerAnimal | |
'turbo power sqirrel action figure/squirrel' 'squirrel action figure' | |
"It's half robot, half plush animal toy, vaguely resembling a | |
squirrel. <q>Turbo Power Squirrel!</q> is emblazoned on the | |
front. " | |
isIt = true | |
isHim = true | |
subLocation = &subContainer | |
/* make us known from the start, as we're mentioned in a stack */ | |
isKnown = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The little room at the bottom of the wind tunnel stairs. This room is | |
* only here for "realism" - there obviously has to be a way into the | |
* wind tunnel from within Guggenheim, since the scientists working in | |
* the wind tunnel wouldn't get there by climbing the face of Firestone, | |
* crossing over the roof, and crawling in the access door. We don't | |
* want the player to be able to enter Guggenheim at all, though, so this | |
* is just a dead-end on the game map: it's meant to suggest a connection | |
* to the interior of Guggenheim without actually providing one. | |
*/ | |
windTunnelShaft: Room 'Bottom of Stairs' 'the bottom of the stairs' | |
'narrow shaft' | |
"This is the bottom of a narrow shaft housing a wrought-iron | |
spiral stairway. An old wooden door leads south. " | |
vocabWords = 'narrow shaft/bottom/(stairs)/(stairway)/(staircase)' | |
up = wtsStairs | |
south = wtsDoor | |
/* we have no ceiling, since we're the bottom of a shaft */ | |
roomParts = static (inherited - defaultCeiling) | |
; | |
+ wtsStairs: StairwayUp | |
'black wrought-iron spiral narrow stairs/staircase/stairway' | |
'spiral stairway' | |
"The narrow black stairway ascends the shaft. " | |
; | |
+ wtsDoor: AlwaysLockedDoor 'old dark wood wooden door' 'wooden door' | |
"The door is made of a dark wood, and looks old and worn. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Thomas Lobby | |
*/ | |
thomasLobby: Room 'Thomas Lobby' 'the Thomas lobby' 'lobby' | |
"This wide, high-ceilinged entryway is paneled in dark wood, | |
making it feel a little gloomy. Doors lead off the lobby | |
to the east and west, and a wide double door leads outside | |
to the south. | |
<.p>A hand-written sign taped to the east doorway reads | |
<q>Robotics Competition,</q> with an arrow pointing east. " | |
vocabWords = 'thomas lobby/thomas' | |
south = tlSouthDoor | |
out asExit(south) | |
east = tlEastDoor | |
west = tlWestDoor | |
; | |
+ Decoration | |
'shiny dark wood varnished | |
varnish/finish/wood/panel/panels/paneling/panelling' | |
'dark wood' | |
"Despite the shiny varnished finish, the wood is so dark, and | |
there's so much of it, that it gives the room a gloomy look. " | |
; | |
+ tlSouthDoor: Door ->wowThomasDoor 'wide double door*doors' 'double door' | |
"The wide double door leads south, outside. " | |
; | |
+ Fixture, Readable 'hand-written sign' 'sign' | |
"The sign reads <q>Robotics Competition,</q> written above | |
an arrow pointing into the room to the east. " | |
cannotTakeMsg = 'You have no reason to take the sign down. ' | |
; | |
+ tlEastDoor: Door -> teWestDoor 'east e door/doorway*doors' 'east door' | |
"The door leads into a room to the east. A sign taped to the | |
door reads <q>Robotics Competition,</q> with an arrow pointing | |
into the room. " | |
; | |
+ tlWestDoor: Door -> twEastDoor 'west w door/doorway*doors' 'west door' | |
"The door leads west. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Thomas west classroom | |
*/ | |
thomasWest: Room 'Classroom' 'the classroom' | |
"This is a good-sized classroom, with about a dozen rows of | |
built-in wooden chairs facing a wide blackboard. A door | |
leads out to the east. " | |
vocabWords = 'classroom/room' | |
east = twEastDoor | |
out asExit(east) | |
; | |
+ twEastDoor: Door 'east e door/doorway' 'east door' | |
"The door leads out to the east. " | |
; | |
+ Fixture, Chair 'wooden row/rows/chair/chairs/seat/seats' 'wooden chairs' | |
"The chairs are all fixed in place, arranged into about | |
a dozen rows. " | |
isPlural = true | |
dobjFor(SitOn) { action() { "There's not much point when | |
there's no lecture going on. "; } } | |
; | |
+ Fixture, Readable 'wide blackboard' 'blackboard' | |
"The blackboard is the width of the room. It has a few | |
unintelligible scribbles, partially erased, from a recent | |
lecture. " | |
; | |
++ Fixture, Readable 'unintelligible scribbles/writing' | |
'scribbles on blackboard' | |
"There's nothing you can make out; most of the writing has | |
already been erased. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Thomas east - robotics competition room | |
*/ | |
thomasEast: Room 'Robotics Lab' 'the robotics lab' | |
"This is a large, open room, with a door leading out to the west. | |
A miniature obstacle course fills the floor: little orange traffic | |
cones, sheet metal half-cylinders serving as tunnels, wooden ramps. | |
Everything is about the right scale for a Chihauhau, although given | |
the sign outside, it must be for pint-sized robots. " | |
vocabWords = 'robotics lab/laboratory' | |
west = teWestDoor | |
out asExit(west) | |
; | |
+ teWestDoor: Door 'west w door/doorway' 'west door' | |
"The door leads out to the west. " | |
; | |
class ObstacleItem: CustomImmovable | |
cannotTakeMsg = 'The course looks carefully laid out; you shouldn\'t | |
mess it up. ' | |
iobjFor(PutIn) | |
{ | |
verify() { } | |
check() | |
{ | |
if (gDobj == toyCar) | |
"It would be fun to drive the car around the course, | |
but you probably shouldn't; it'd be too easy to knock | |
parts out of place. "; | |
else | |
"<<cannotTakeMsg>>"; | |
exit; | |
} | |
} | |
iobjFor(PutOn) asIobjFor(PutIn) | |
; | |
+ ObstacleItem 'obstacle course' 'obstacle course' | |
"The obstacle course fills most of the room. Everything's | |
in miniature, about the right scale for Chihuahuas. " | |
; | |
+ ObstacleItem 'little orange traffic cones' 'traffic cones' | |
"The cones are miniature versions of what you'd see on the highway, | |
about a foot high. " | |
isPlural = true | |
; | |
+ ObstacleItem 'wooden ramp/ramps' 'wooden ramps' | |
"There are a number of ramps, some that just turn back down at | |
their apex, some with brief plateaus, others that turn a corner | |
at the top. " | |
isPlural = true | |
; | |
+ ObstacleItem | |
'sheet metal half half-cylinder/half-cylinders/cylinder/cylinders/tunnels' | |
'tunnels' | |
"There are a couple of tunnels made of pieces of sheet metal | |
curved into half-cylinders. " | |
isPlural = true | |
; | |
/* a property for Rooms indicating that the car can't travel here */ | |
property blockToyCar; | |
+ toyCar: Thing, Traveler | |
'radio-controlled rc miniature scale-model toy car/toy*toys' 'toy car' | |
desc() | |
{ | |
if (ratPuppet.isIn(self)) | |
"The toy car is currently disguised as a rat---a plush rat | |
puppet has been put over the car, leaving only the wheels | |
sticking out from the bottom. "; | |
else | |
"It's a scale-model car about six inches long. It's | |
radio-controlled: a long wire antenna sticks up from the | |
back. "; | |
} | |
/* when we appear in contents lists, mention when we're in disguise */ | |
listName = (ratPuppet.isIn(self) | |
? 'a toy car (disguised as a rat)' : 'a toy car') | |
/* | |
* Do not list my contents in listings involving me, even for direct | |
* examination. The only thing I can contain is the plush rat, and | |
* we don't contain that in the usual way - it's more like wearing | |
* the puppet. | |
*/ | |
contentsListedInExamine = nil | |
useInitSpecialDesc = (inherited && !ratPuppet.isIn(self)) | |
initSpecialDesc = "A small toy car is lying on the floor at one corner | |
of the obstacle course; someone must have been driving it | |
around the course for fun. | |
<<toyCarControl.location == location | |
? 'Next to it is a little box that must be its remote control. ' | |
: ''>> " | |
/* on the first move, add an extra ethics message */ | |
moveInto(obj) | |
{ | |
/* if this is the first purloining, rationalize it */ | |
if (!moved) | |
extraReport('It looks like the car has just been sitting here | |
for a while, so you figure it\'s probably okay to borrow | |
it for the day.<.p>'); | |
/* do the normal work */ | |
inherited(obj); | |
} | |
iobjFor(PutOn) | |
{ | |
verify() { logicalRank(50, 'toy car surface'); } | |
check() | |
{ | |
if (gDobj != ratPuppet) | |
{ | |
"There's no way to balance {the dobj/him} on the toy car. "; | |
exit; | |
} | |
} | |
action() | |
{ | |
"The plush rat's hand opening fits nicely over the | |
toy car, leaving the wheels sticking out just enough | |
to let it drive around. "; | |
gDobj.moveInto(self); | |
} | |
} | |
dobjFor(ThrowAt) | |
{ | |
check() | |
{ | |
"That would probably break the toy car. "; | |
exit; | |
} | |
} | |
/* determine if the car is capable of moving while in a given room */ | |
canTravelIn(room) | |
{ | |
/* | |
* we can travel in any indoor room except those that | |
* specifically don't allow it | |
*/ | |
return (!room.ofKind(OutdoorRoom) && !room.blockToyCar); | |
} | |
/* I can be followed */ | |
verifyFollowable() { return true; } | |
/* carry out a joystick control input */ | |
joystickCommand(actor, action) | |
{ | |
local dir; | |
local room; | |
local vis, aud; | |
local visMsg, audMsg, extraMsg; | |
local didMove = nil; | |
local didLocalMove = nil; | |
local plisnikSaw = plisnik.canSee(self); | |
/* | |
* only allow directional PushTravel's, and only in the compass | |
* directions | |
*/ | |
if (action.baseActionClass != PushTravelDirAction | |
|| !(dir = action.getDirection()).ofKind(CompassDirection)) | |
{ | |
"(The joystick can be moved in any of the compass | |
directions: MOVE JOYSTICK NORTH, for example.) "; | |
return; | |
} | |
/* get the actor's outermost room */ | |
room = actor.location.getOutermostRoom(); | |
/* note whether or not the car is visible/audible */ | |
vis = actor.canSee(self); | |
aud = actor.canHear(self); | |
/* by default, we notice nothing going on with the car */ | |
visMsg = ''; | |
audMsg = ''; | |
/* check to see where the car is */ | |
if (isDirectlyIn(room)) | |
{ | |
/* if we can't travel while in this room, just sit and spin */ | |
if (!canTravelIn(room)) | |
{ | |
visMsg = '. The toy car revs its engine but | |
can\'t seem to move on this surface'; | |
audMsg = '. You hear the toy car\'s engine rev'; | |
} | |
else | |
{ | |
local conn; | |
local dest; | |
/* | |
* most of the audible-only messages are the same, so | |
* set a default | |
*/ | |
audMsg = '. You hear the car moving around nearby'; | |
/* | |
* The car is directly in the same room as the actor, and | |
* travel is allowed here. Find the exit in the | |
* direction we're going, and check for compatibility. | |
*/ | |
conn = room.getTravelConnector(dir, nil); | |
if (conn == nil) | |
{ | |
/* no connector; we just zip around the floor */ | |
visMsg = ', and the toy car zips a short | |
distance ' + dir.name; | |
/* we moved around within the room */ | |
didLocalMove = true; | |
} | |
else | |
{ | |
/* get the destination */ | |
dest = conn.getDestination(location, self); | |
/* | |
* Check to see if we can travel. If the connector | |
* isn't passable, we can't. If the connector is a | |
* stairway up, we can't. Otherwise, allow it. | |
*/ | |
if (!conn.isConnectorPassable(location, self) | |
|| conn.ofKind(StairwayUp)) | |
{ | |
/* the connector blocks us */ | |
if (conn.name not in (nil, '')) | |
{ | |
visMsg = '. The toy car zips over to ' | |
+ conn.theName + ' and stops'; | |
} | |
else | |
{ | |
visMsg = '. The toy car zips a ways ' | |
+ dir.name + ' and stops'; | |
} | |
/* we moved around within the room */ | |
didLocalMove = true; | |
} | |
else | |
{ | |
/* the toy car departs via the connector */ | |
visMsg = '. The toy car zips out of the room | |
to the ' + dir.name; | |
audMsg = '. You hear the car moving away'; | |
/* if it's a stairway down, mention that */ | |
if (conn.ofKind(StairwayDown) | |
&& conn.name != nil) | |
{ | |
visMsg += ' and goes tumbling down ' | |
+ conn.theName; | |
} | |
/* track the follow */ | |
me.trackFollowInfo(self, conn, room); | |
/* | |
* Move the car to its new location. Note that | |
* we don't have to worry about it entering the | |
* player character's location, since it can't | |
* operate unless it started in the PC's location | |
* and thus can only be leaving. | |
*/ | |
moveIntoForTravel(dest); | |
/* note that we did move the car */ | |
didMove = true; | |
} | |
} | |
} | |
} | |
else if (isIn(room)) | |
{ | |
/* | |
* We're in the same room, but only indirectly. If we're on | |
* a surface, fall off. If we're in a container, just move | |
* aimlessly. If we're in an actor, rev the wheels. | |
*/ | |
if (location.ofKind(Surface)) | |
{ | |
/* it falls off */ | |
visMsg = '. The toy car zips ' + dir.name + ' and | |
falls off ' + location.theName; | |
audMsg = '. You hear the toy car moving around, | |
then you hear something falling'; | |
/* move it to the surface's container's drop destination */ | |
moveInto(location.getDropDestination(self, nil)); | |
/* we moved around within the room */ | |
didLocalMove = true; | |
} | |
else if (location.ofKind(Actor)) | |
{ | |
/* just spin our wheels */ | |
visMsg = ', and the toy car\'s wheels spin a bit'; | |
audMsg = '. You hear the toy car\'s engine rev'; | |
} | |
else | |
{ | |
/* just move aimlessly */ | |
visMsg = '. The toy car moves around a bit'; | |
/* we moved around within the room */ | |
didLocalMove = true; | |
} | |
} | |
else | |
{ | |
/* | |
* The car isn't in the same room as the actor, so it's out | |
* of range of the remote control. Just use the default | |
* empty messages, since there's nothing to report with the | |
* car. The first time this happens, though, if we've seen | |
* the car move previously, add a mention that it seems to | |
* be out of range. | |
*/ | |
if (seenMove && ++outOfRangeNote == 1) | |
extraMsg = '. Nothing seems to happen; the toy car | |
must be out of range of the remote'; | |
} | |
/* | |
* Acknowlege that we moved the joystick, and show the result. | |
* If we can see the car, show the visual message; if we can't | |
* see it but can hear it, show the aural message; otherwise | |
* don't even mention the car. | |
*/ | |
"You nudge the joystick <<dir.name>><< | |
vis ? visMsg : aud ? audMsg : ''>><<extraMsg>>. "; | |
/* | |
* if we just saw any kind of reaction, note that we know that | |
* the remote controls the car now | |
*/ | |
if ((vis && visMsg != '') || (aud && audMsg != '')) | |
seenMove = true; | |
/* if we're dressed up as a rat, check to see if plisnik notices */ | |
if (ratPuppet.isIn(self)) | |
{ | |
local plisnikSees = plisnik.canSee(self); | |
/* if we moved it away from plisnik, notify him */ | |
if (didMove && plisnikSaw) | |
plisnik.ratLeaving(); | |
/* if we just did a local move, and plisnik saw, notify him */ | |
if (didLocalMove && plisnikSees) | |
plisnik.ratMoving(); | |
/* if we're arriving at plisnik's location, he freaks out */ | |
if (didMove && plisnikSees) | |
plisnik.eekARat(); | |
} | |
} | |
/* flag: we've seen the car move around under joystick control */ | |
seenMove = nil | |
/* number of times we've noted that the car might be out of range */ | |
outOfRangeNote = 0 | |
; | |
++ Component 'fine stiff long looped wire (toy) (car) car\'s antenna/loop' | |
'antenna' | |
"It's a piece of fine, stiff wire about four inches long, curved | |
into a little loop at the top. " | |
disambigName = 'toy car\'s antenna' | |
; | |
+ toyCarControl: Thing 'little rf remote control/box' 'remote control' | |
"It's a little box with an antenna and a joystick. " | |
isListed = (toyCar.isListed || location != toyCar.location) | |
filterResolveList(lst, action, whichObj, np, requiredNum) | |
{ | |
/* | |
* if the phrase was just "box", and there's anything else that | |
* can match, take the other thing | |
*/ | |
if (np.getOrigText() == 'box' && lst.length() > 1) | |
lst = lst.subset({x: x.obj_ != self}); | |
/* return the result */ | |
return lst; | |
} | |
; | |
++ Component 'wire (remote) (control) antenna' 'antenna' | |
"It's a piece of wire sticking out from the remote control box. " | |
disambigName = 'remote control antenna' | |
; | |
++ Component '(remote) (control) joystick' 'remote control joystick' | |
"It's a stubby control stick that you can move in any of the | |
compass directions. " | |
dobjFor(Move) | |
{ | |
verify() { } | |
action() { "(The joystick can be moved in any of the compass | |
directions: MOVE JOYSTICK NORTH, for example.) "; } | |
} | |
dobjFor(Push) asDobjFor(Move) | |
dobjFor(Pull) asDobjFor(Move) | |
dobjFor(PushTravel) | |
{ | |
preCond = [mainObjHeld] | |
verify() { } | |
action() { toyCar.joystickCommand(gActor, gAction); } | |
} | |
; | |
/* | |
* ------------------------------------------------------------------------ | |
*/ | |
/* | |
* Lauritsen-Downs Courtyard | |
*/ | |
ldCourtyard: CampusOutdoorRoom 'Lauritsen-Downs Courtyard' | |
'Lauritsen-Downs courtyard' 'courtyard' | |
"This is a small courtyard bounded by lab buildings: | |
Guggenheim to the north, Firestone to the east, and | |
Lauritsen-Downs to the south and west. A walkway leads north | |
under a second-story bridge connecting Firestone and Guggenheim. | |
The entrance to Lauritsen-Downs is to the south; the | |
other buildings are just blank walls here, except for a door | |
marked <q>Emergency Exit</q> in the northwest corner of | |
Guggenheim. " | |
vocabWords = '(lauritsen-downs) courtyard' | |
north = ldcNorthWalk | |
northwest = ldEmergencyDoor | |
south: FakeConnector { "Lauritsen-Downs is mostly offices and | |
classrooms; given that it's Ditch Day, there's probably not | |
much going on here today. "; } | |
/* we have some walls despite being an outdoor location */ | |
roomParts = static (inherited + [defaultNorthWall, defaultSouthWall, | |
defaultEastWall, defaultWestWall]) | |
; | |
+ CampusMapEntry 'downs lab/laboratory' 'Downs Laboratory' 'southwest'; | |
+ CampusMapEntry 'lauritsen lab/laboratory' 'Lauritsen Laboratory' 'south'; | |
+ CampusMapEntry 'lauritsen-downs lab/laboratory/courtyard' | |
'Lauritsen-Downs courtyard' 'south'; | |
+ ldcNorthWalk: PathPassage 'north n walkway' 'north walkway' | |
"The walkway leads north under the second-story bridge. " | |
; | |
+ ldEmergencyDoor: Door | |
'"emergency exit" emergency exit door' 'emergency exit door' | |
"The door is marked <q>Emergency Exit.</q> It looks like a one-way | |
door leading out; there's no obvious way to open it from this side. " | |
initiallyOpen = nil | |
dobjFor(Open) | |
{ | |
check() | |
{ | |
"The door has no knob or handle on this side; it looks like | |
it's only possible to open it from the other side. "; | |
exit; | |
} | |
} | |
; | |
+ Enterable ->(location.south) | |
'beige large lauritsen downs lauritsen-downs | |
glass/windows/stucco/lab/laboratory/building' | |
'Lauritsen-Downs' | |
"Lauritsen and Downs are nominally separate buildings, but | |
they're adjoined into a single L-shaped structure. The combined | |
building is modern-looking: beige stucco, right angles, large | |
rectangular windows. Most of the Institute's high-energy | |
physicists have their offices in these buildings. An entrance | |
is to the south. " | |
isProperName = true | |
; | |
+ Fixture 'blank back guggenheim lab/laboratory/wall/side' 'Guggenheim' | |
"The back side of Guggenheim faces the courtyard. Apart from the | |
emergency exit door, it's just a blank wall. " | |
isProperName = true | |
; | |
+ Fixture 'blank back firestone lab/laboratory/wall/side' 'Firestone' | |
"The back side of Firestone faces the courtyard; it's just a blank | |
wall on this side. " | |
isProperName = true | |
; | |
+ Distant 'second-story story bridge' 'second-story bridge' | |
"The bridge connects Firestone and Guggenheim on the second story. | |
A walkway leads north under the bridge. " | |
; | |
/* | |
* Ernst, the electrician. | |
*/ | |
+ ernst: IntroPerson | |
'tall rotund ruddy curly hair/electrician/ernst/man*men' | |
'tall, rotund man' | |
"He's a tall, rotund, ruddy man with curly hair, wearing denim | |
overalls with an embroidered nametag reading <q>Ernst.</q> You | |
surmise that he's an electrician, from the tools hanging off his | |
belt---voltmeters, crimpers, wire cutters. | |
<<setIntroduced()>> " | |
properName = 'Ernst' | |
isHim = true | |
/* presume we're known, since we're mentioned in a memo */ | |
isKnown = true | |
/* if gunther arrives while we're here, start the fight */ | |
afterTravel(traveler, connector) | |
{ | |
inherited(traveler, connector); | |
if (traveler.isActorTraveling(gunther)) | |
startFight(); | |
} | |
/* start the fight */ | |
startFight() | |
{ | |
/* get gunther out of the cherry picker */ | |
gunther.moveIntoForTravel(cherryPicker.location); | |
/* | |
* the only way we can meet is when the PC just sent Gunther to | |
* a new job, so the PC is always one room away; note the start | |
* of the fight | |
*/ | |
callWithSenseContext(nil, nil, new function() { | |
"<.p>You hear a commotion from somewhere nearby. "; | |
}); | |
/* activate the nearby fight sounds */ | |
guntherErnstNearbyFightNoise.makePresent(); | |
/* set them both in the fighting states */ | |
setCurState(ernstFightingState); | |
gunther.setCurState(guntherFightingState); | |
} | |
; | |
++ InitiallyWorn 'denim overalls' 'denim overalls' | |
"An embroidered nametag on the overalls reads <q>Ernst.</q> " | |
isPlural = true | |
isListedInInventory = nil | |
; | |
+++ Component, Readable 'embroidered nametag' 'nametag' | |
"It reads <q>Ernst</q> in embroidered cursive letters. " | |
; | |
++ Thing | |
'tool (electrician\'s) (wire) tool | |
voltmeters/crimpers/cutters/belt/toolbelt/tools' | |
'electrician\'s tools' | |
"The tools look like the sorts of things an electrician would carry: | |
voltmeters, crimpers, wire cutters, that sort of thing. " | |
isPlural = true | |
isListedInInventory = nil | |
; | |
++ DefaultAnyTopic, ShuffledEventList | |
['<q><i>Guten Tag,</i></q> he says. Unfortunately, you don\'t | |
speak much German. He goes back to his work. ', | |
'<q><i>Ich verstehe die Sprache nicht,</i></q> he says. You | |
recognize it as German, but you don\'t know enough German to | |
carry on a conversation with him. He goes back to his work. ', | |
'<q><i>Bitte?</i></q> he says. He looks at you quizzically | |
for a few moments, then shrugs and goes back to work. ', | |
'He just smiles and nods. <q><i>Guten Tag,</i></q> he finally | |
says, then goes back to his work. '] | |
; | |
++ AskTellTopic, StopEventList @gunther | |
['He interrupts you before you finish. <q>Gunther?</q> he says, | |
looking all around, then looks back at you. He says something | |
lengthy in rapid German, jabbing his finger in the air for | |
emphasis every few words, then stops and stands there looking | |
indignant. He grunts and goes back to what he was doing, still | |
looking a bit agitated. ', | |
'He spins to face you, says something angry in German, and | |
then goes back to his work. '] | |
; | |
class ErnstJobCardTopic: GiveShowTopic | |
topicResponse() | |
{ | |
"<q>Excuse me,</q> you say, holding out the card. | |
<.p>He reads it over and says something in German, then hands | |
the card back to you. "; | |
/* check to see if we're already there */ | |
if (ernst.isIn(destLoc)) | |
{ | |
"He goes back to what he was doing. "; | |
return; | |
} | |
"He stops what he was doing and starts putting things away | |
and rearranging things on his toolbelt. "; | |
/* switch to the preparing-to-go state for a brief delay */ | |
ernst.setCurState(ernstPreparingState); | |
ernstPreparingState.delayCount = 2; | |
/* remember our destination */ | |
ernstInTransitState.destPath = destPath; | |
ernstInTransitState.destState = destState; | |
} | |
/* the path to our destination */ | |
destPath = [] | |
/* the destination is the last path element */ | |
destLoc = (destPath[destPath.length()]) | |
/* the state to go into when we reach the destination */ | |
destState = nil | |
; | |
++ ErnstJobCardTopic @lauritsenJobCard | |
destPath = [orangeWalk, quad, westOliveWalk, ldCourtyard] | |
destState = ernstLauritsenState | |
; | |
++ ErnstJobCardTopic @orwalkJobCard | |
destPath = [ldCourtyard, westOliveWalk, quad, orangeWalk] | |
destState = ernstOrangeState | |
; | |
++ ErnstJobCardTopic [oakJobCard, oliveJobCard] | |
"<q>Excuse me,</q> you say, holding out the card. | |
<.p>He reads it over and says something in German, then hands | |
the card back to you, and goes back to his work. " | |
; | |
/* our initial state: checking around Thomas */ | |
++ ernstLauritsenState: ActorState | |
isInitState = true | |
stateDesc = "He's walking slowly near Lauritsen, looking closely at | |
the building. " | |
specialDesc = "\^<<location.aName>> is walking slowly along the | |
outside of Lauritsen, looking closely at the building. " | |
/* show a message when we arrive and start doing this state */ | |
introDesc = "\^<<location.theName>> starts walking slowly along | |
the outside of Lauritsen, inspecting the building. " | |
; | |
/* our state on the orange walk: fixing a light fixture */ | |
++ ernstOrangeState: ActorState | |
stateDesc = "He's working on a lighting fixture alongside the | |
walkway. " | |
specialDesc = "\^<<location.aName>> is standing alongside the | |
walkway, working on a lighting fixture. " | |
/* show a message when we arrive and start doing this state */ | |
introDesc = "\^<<location.theName>> walks over to a lighting | |
fixture alongside the walkway, and starts taking it apart. " | |
; | |
/* preparing to leave on our way to a new job */ | |
++ ernstPreparingState: ActorState | |
stateDesc = "He's rearranging things on his toolbelt. " | |
specialDesc = "\^<<location.aName>> is standing here rearranging | |
things on his toolbelt. " | |
takeTurn() | |
{ | |
/* if we're done fussing, commence travel to our new job */ | |
if (delayCount-- == 0) | |
{ | |
/* switch to our travel state */ | |
ernst.setCurState(ernstInTransitState); | |
/* move to the first point on the path */ | |
ernst.scriptedTravelTo(ernstInTransitState.destPath[1]); | |
} | |
} | |
/* the number of turns remaining before we start traveling */ | |
delayCount = 2 | |
; | |
+++ DefaultAnyTopic | |
"He seems too busy with his tools to notice that you're trying | |
to talk to him. " | |
; | |
/* our state while in transit through the quad */ | |
++ ernstInTransitState: ActorState | |
stateDesc = "He's walking past, on his way somewhere. " | |
specialDesc = "\^<<location.aName>> is walking through, on his | |
way somewhere. " | |
takeTurn() | |
{ | |
/* get our current position on the path, and figure out next */ | |
local idx = destPath.indexOf(ernst.location.getOutermostRoom()) + 1; | |
/* move to our next location */ | |
ernst.scriptedTravelTo(destPath[idx]); | |
/* if we're in our destination, go to work */ | |
if (idx == destPath.length()) | |
{ | |
/* switch to our new state */ | |
ernst.setCurState(destState); | |
/* give our new state's intro message */ | |
"<.p>"; | |
destState.introDesc; | |
} | |
} | |
/* our destination path and state */ | |
destPath = nil | |
destState = nil | |
; | |
+++ DefaultAnyTopic | |
"He just smiles and keeps walking. " | |
; | |
/* our state while fighting */ | |
++ ernstFightingState: ActorState | |
stateDesc = "He's running from the gardener, who's chasing him | |
with tree clippers. " | |
/* gunther provides the special description for the both of us */ | |
specialDesc = "" | |
/* when the PC arrives, take our fight off-stage */ | |
afterTravel(traveler, connector) | |
{ | |
if (traveler.isActorTraveling(me)) | |
{ | |
"<.p>\^<<ernst.theName>> abruptly stops running from | |
<<gunther.theName>> and turns around, holding out a pair | |
of wire cutters. He yells something in German, and starts | |
chasing the gardener. The two run off to the north. "; | |
trackAndDisappear(ernst, ernst.location.north); | |
trackAndDisappear(gunther, gunther.location.north); | |
/* this is worth some points */ | |
scoreMarker.awardPointsOnce(); | |
/* get rid of the commotion noises */ | |
guntherErnstNearbyFightNoise.moveInto(nil); | |
} | |
} | |
scoreMarker: Achievement { +5 "commandeering the cherry picker" } | |
; | |
+++ DefaultAnyTopic | |
"He seems to be preoccupied at the moment. " | |
; | |
/* | |
* A multi-instance noise object that shows up in each location adjacent | |
* to the fight. | |
*/ | |
guntherErnstNearbyFightNoise: PresentLater, MultiInstance | |
locationList = [sanPasqualWalkway, orangeWalk, westOliveWalk, oliveWalk] | |
instanceObject: SimpleNoise { | |
desc = "You hear a commotion from somewhere nearby. It sounds | |
like someone's shouting in German. " | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* A class for our various water features. | |
*/ | |
class Pond: object | |
dobjFor(Drink) | |
{ | |
preCond = [] | |
verify() { } | |
action() { "It's probably not a good idea to drink water | |
from a pond. "; } | |
} | |
dobjFor(Eat) asDobjFor(Drink) | |
dobjFor(Enter) | |
{ | |
verify() { logicalRank(50, 'enter pond'); } | |
action() { "You have no interest in getting all wet right now. "; } | |
} | |
dobjFor(Board) asDobjFor(Enter) | |
dobjFor(StandOn) asDobjFor(Enter) | |
dobjFor(SitOn) asDobjFor(Enter) | |
dobjFor(LieOn) asDobjFor(Enter) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Pond/rock garden | |
*/ | |
rockGarden: CampusOutdoorRoom 'Rock Garden' 'the rock garden' | |
"The walkway winds its way through a rock garden set within a | |
terraced series of ponds. The path ascends the hillside to the | |
west, and leads to the Olive Walk to the east. A distance off | |
to the west, you can see the Millikan library towering above. " | |
east = rgEastWalk | |
down asExit(east) | |
west = rgWestWalk | |
up asExit(west) | |
; | |
+ Decoration 'walkway/path' 'walkway' | |
"The walkway leads uphill to the west, and to the Olive Walk | |
to the east. " | |
; | |
+ rgEastWalk: PathPassage ->wowWestWalk 'olive walk' 'Olive Walk' | |
"The Olive Walk lies to the east. " | |
; | |
+ rgWestWalk: PathPassage ->mpEastWalk 'path/walkway' 'path' | |
"The path winds up the hillside to the west. " | |
canTravelerPass(trav) { return trav != cherryPicker; } | |
explainTravelBarrier(trav) { "The cherry picker won't fit on | |
the narrow path. "; } | |
; | |
+ Decoration 'hill/hillside' 'hillside' | |
"The path winds its way up the ten feet or so of the hillside | |
to the west. " | |
; | |
+ Decoration 'rock garden/rock/rocks/stone/stones/boulder/boulders' 'rocks' | |
"The rocks range from baseball-sized to boulders. They're | |
arranged in no particular pattern among a meandering series | |
of pools and lush vegetation. " | |
isPlural = true | |
owner = (self) | |
; | |
+ Pond, Decoration | |
'small miniature terraced water/series/pool/pond/ponds/pools/waterfalls' | |
'ponds' | |
"Small pools are terraced into the hillside, the higher ones | |
gently overflowing in miniature waterfalls into the ones below. | |
Water lilies float on the surface, and dense foliage surrounds | |
each pool. The rock garden's stones and boulders are arranged | |
throughout. " | |
isPlural = true | |
dobjFor(Enter) { action() { "That would only disturb the careful | |
arrangement of the rocks, and besides, you have no desire | |
to get all wet. "; } } | |
lookInDesc = "You see no lost treasures in the water; just | |
rocks and water lilies. " | |
iobjFor(PutIn) | |
{ | |
verify() { } | |
action() { "Better not; you'd get {it dobj/him} all wet. "; } | |
} | |
; | |
++ Decoration 'water lily/lilies' 'water lilies' | |
"The lilies bob on the rippling surface of the water. " | |
isPlural = true | |
; | |
+ Decoration 'lush dense vegetation/plant/plants/foliage' 'vegetation' | |
"Plants are arranged around the ponds and rocks, forming a | |
green backdrop to the rock garden. " | |
isMassNoun = true | |
; | |
+ Distant 'black nine-story millikan library/monolith' 'Millikan Library' | |
"From here, you can see the top half or so of the nine-story | |
black monolith. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Millikan pond | |
*/ | |
millikanPond: CampusOutdoorRoom 'Millikan Pond' 'Millikan Pond' | |
'pond walkway' | |
"The nine-story obsidian tower of the Millikan Library is to | |
the west, rising above the wide rectangular pond that fills | |
the large open area north of the walkway. A combination abstract | |
sculpture and fountain in the center of the pond shoots several | |
streams of water into the air. | |
<.p>Bridge Lab is a short distance to the south, and the entrance | |
to the library is to the west. A path leads east, down a hill. " | |
vocabWords = 'pond walkway' | |
west = millikanLobby | |
east = rockGarden | |
down asExit(east) | |
south = bridgeEntry | |
; | |
+ CampusMapEntry 'east e bridge lab/laboratory/bridge' | |
'Bridge Laboratory' 'southwest' | |
/* | |
* since we mention this specifically as a destination the player | |
* should seek, elevate its match strength in case of ambiguity | |
* (which can happen if we look up simply "lab") | |
*/ | |
mapMatchStrength = 200 | |
; | |
+ CampusMapEntry 'millikan library' 'Millikan Library' 'southwest' | |
/* elevate the match strength in case we look up just "library" */ | |
mapMatchStrength = 200 | |
; | |
+ mpEastWalk: PathPassage 'path/hill/hillside' 'path' | |
"The path leads down a hill to the east. " | |
; | |
+ Enterable ->(location.west) | |
'millikan nine-story obsidian black library/tower/monolith' | |
'Millikan Library' | |
"The black, windowless face of the library rises up from the | |
west end of the pool to a height of nine stories, towering | |
over everything else nearby. The entrance is to the west. " | |
; | |
+ EntryPortal ->(location.south) | |
'norman bridge lab/laboratory/arcade/entrance' 'Bridge Lab' | |
"The Norman Bridge Laboratory of Physics is where they teach | |
phys 1, which virtually every frosh has to take. The entrance | |
is to the south, through an arched walkway. " | |
isProperName = true | |
; | |
+ EntryPortal ->(location.south) | |
'arched (bridge) walkway/arcade' 'arched walkway' | |
"The arcade runs the length of this side of Bridge. " | |
; | |
+ Pond, Fixture '(millikan) wide rectangular shallow pond/pool/water' 'pond' | |
"The pond is quite shallow, a couple of feet deep at most. | |
A fountain in the center is shooting water into the air. " | |
lookInDesc = "You see nothing interesting submerged in the pond. " | |
iobjFor(PutIn) | |
{ | |
verify() { } | |
action() { "Better not; you'd get {it dobj/him} all wet. "; } | |
} | |
; | |
+ Distant | |
'twisting curving abstract (water) | |
sculpture/fountain/shape/shapes/stream/streams/(water)' | |
'fountain' | |
"The fountain is a sculpture of twisting, curving shapes. Several | |
streams of water shoot up out of the fountain and arc away in | |
different directions. " | |
; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - career center office. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
#include "ditch3.h" | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The Career Center Office | |
*/ | |
ccOffice: Room 'Career Center Office' 'the Career Center office' 'office' | |
"This room seems to be loosely divided into an office area and a | |
public area. Several desks are grouped on one side of the room, | |
making up the office area. On the other side, a couple of | |
couches (one large and one small) are arranged in an <q>L</q> | |
around a square glass coffee table. A literature rack stands | |
near the couches. | |
<.p>A door to the north leads out to the lobby. " | |
vocabWords = 'career center office' | |
north = ccDoorN | |
out asExit(north) | |
/* flag: we've finished our appointment here */ | |
doneWithAppointment = nil | |
/* on the first arrival, award points for finding our way here */ | |
afterTravel(traveler, conn) | |
{ | |
scoreMarker.awardPointsOnce(); | |
inherited(traveler, conn); | |
} | |
scoreMarker: Achievement { +1 "finding the Career Center office" } | |
roomDaemon() | |
{ | |
switch(turnsInRoom++) | |
{ | |
case 3: | |
"<.p>The woman covers the phone with her hand and says | |
to you loudly, <q>Hold your horses---I'll be with you | |
in a minute.</q> She goes back to her phone call. "; | |
break; | |
case 5: | |
/* the return of Frosst Belker */ | |
"<.p>You hear someone coming through the door, and look over | |
to see a slender man in a white double-breasted jacket and | |
white slacks sauntering into the room. You almost groan | |
out loud when you realize that it's none other than Frosst | |
Belker. You just hope he's not here to interview the same | |
student you are---as tough as it is to beat them at selling, | |
it's even harder to beat them at hiring. | |
<.p>Belker surveys the room. He looks at the woman on | |
the phone, then at you, then smiles---not warmly, not like | |
he's glad to see you, more like he's amused at a private | |
joke. <q>Mr.\ Mittling,</q> he says with his slight accent, | |
<q>what a pleasant surprise. Our respective employers | |
evidently have similar tastes in prospective employees as | |
well as in clients. Let us hope that on this occasion, | |
your reach is more consonant with your grasp, as it were, | |
than it has so often proved in our past encounters.</q> "; | |
/* move frosst here */ | |
frosst.moveIntoForTravel(self); | |
/* make frosst 'him', since he's been mentioned prominently */ | |
me.setHim(frosst); | |
break; | |
case 6: | |
/* give Ms. Dinsdale her folder */ | |
ccFolder.makePresent(); | |
/* set up everyone in conversation */ | |
ccDinsdale.initiateConversation(dinsdaleTalking, 'cc-welcome'); | |
frosst.setCurState(frosstTalking); | |
/* this introduces her */ | |
ccDinsdale.setIntroduced(); | |
break; | |
} | |
} | |
turnsInRoom = 0 | |
; | |
+ ccDoorN: Door -> cssDoorS 'north n door*doors' 'north door' | |
"It leads north, back out to the lobby. " | |
/* once we're here, don't leave until we finish our appointment */ | |
canTravelerPass(trav) { return location.doneWithAppointment; } | |
explainTravelBarrier(trav) | |
{ | |
reportFailure('You really shouldn\'t go anywhere until you\'ve | |
finished with your appointment here. '); | |
} | |
; | |
+ Fixture, Chair | |
'large larger couch/sofa*couches*sofas*furniture' 'large couch' | |
"It could easily seat three or four people. " | |
disambigName = 'larger couch' | |
; | |
+ Fixture, Chair | |
'small smaller couch/sofa*couches*sofas*furniture' 'small couch' | |
"It's big enough for two or three people. " | |
disambigName = 'smaller couch' | |
dobjFor(SitOn) | |
{ | |
/* | |
* to avoid pointless disambiguation queries, arbitrarily pick | |
* the larger couch for 'sit on' | |
*/ | |
verify() | |
{ | |
inherited(); | |
logicalRank(90, 'pick arbitrary couch'); | |
} | |
} | |
; | |
+ Fixture, Surface 'square glass coffee table/top*furniture' 'coffee table' | |
"The coffee table has a glass top about four feet on a side. " | |
; | |
+ Fixture, RestrictedContainer 'chromed metal literature rack' | |
'literature rack' | |
"It's a chromed metal rack designed to display magazines or similar | |
items, currently being used for a collection of glossy brochures | |
from companies recruiting on campus. " | |
canPutIn(obj) { return obj.ofKind(RecruitingBrochure); } | |
cannotPutInMsg(obj) { return 'The rack is just for recruiting | |
brochures; you wouldn\'t want to mess it up by putting | |
anything else in it. '; } | |
/* | |
* don't list my contents in the room description (but note that our | |
* contents will still be listed when we're examined directly) | |
*/ | |
contentsListed = nil | |
/* customize our Examine contents listing slightly */ | |
descContentsLister: thingDescContentsLister { | |
getFilteredList(lst, infoTab) | |
{ | |
/* make sure our 'various brochures' item goes last */ | |
if (lst.indexOf(variousBrochures) != lst.length()) | |
lst = (lst - variousBrochures) + variousBrochures; | |
/* otherwise use the standard handling */ | |
return inherited(lst, infoTab); | |
} | |
showListPrefixWide(itemCount, pov, parent) | |
{ "The rack is displaying "; } | |
} | |
; | |
class RecruitingBrochure: Readable 'glossy recruiting brochure*brochures'; | |
++ RecruitingBrochure 'mortera -' 'Mortera brochure' | |
"The brochure features photos of the surprisingly many consumer products | |
Mortera makes: <i>Manly</i> Cigarettes, the <i>Mrs.\ Pillskruft's | |
Olde-Fashioned</i> line of processed meat and cheese food products, | |
<i>Elephant</i> Cigarettes, the popular <i>Ellie Phant</i> | |
character line of plush toys for toddlers and logo merchandise | |
for teens, <i>HomeAir</i> medical oxygen products for home nursing | |
care, <i>MassiveMeal</i> brand frozen entrees, <i>Muffin Farm</i> | |
preserved bakery goods, <i>I'm Starving!</i>\ brand fried salty | |
snacks, <i>Fitswell-Plus</i> super-sized pants and | |
<i>Underswell-Plus</i> super-sized undergarments, and many more. | |
You had no idea how many consumer brands this company owns; now | |
you appreciate the reason for the fairly recent name change from | |
J.R.R.\ Tobacco. " | |
; | |
++ RecruitingBrochure 'toxicola -' 'ToxiCola brochure' | |
"It's about what you'd expect from the beverage giant. They seem | |
mostly interested in chemistry PhD's. " | |
; | |
++ RecruitingBrochure 'locktheon -' 'Locktheon brochure' | |
"This brochure is heavily adorned with exciting, action-packed | |
military photos: a fighter jet being readied for take-off from the | |
deck of an aircraft carrier, a tank rolling past a burning building, | |
a chemical warfare suit-clad soldier handing a smiling ethnic | |
child a little American flag, a missile being launched from the | |
deck of a battleship, a submarine cresting the surface of the | |
sea. | |
<.p><blockquote><font face='tads-sans'> | |
<b><i>Locktheon: Dangerous Weapons for a Dangerous World</i></b>. | |
Here at <b><i>Locktheon</i></b>, a lot of our work is <q>top secret,</q> | |
but there's one thing that's not <q>top secret</q> these days: the | |
world is more dangerous than ever. War, terrorism, and | |
international pacifism threaten America and all Americans like | |
never before. Loose arms control protocols allow---even | |
encourage---companies like <b><i>Locktheon</i></b> to sell some of | |
the most advanced weapons systems ever built to just about any | |
tin-pot dictator. Here at <b><i>Locktheon</i></b>, we know that | |
the advanced defense systems that keep America safe today are the | |
same devastating weapons that will threaten America tomorrow. That's | |
why we at <b><i>Locktheon</i></b> can never stop innovating. | |
<.p><b><i>Locktheon: Exporting Products, Not Jobs</i></b>. While | |
the rest of corporate America sends jobs overseas to cheaper labor | |
markets, <b><i>Locktheon</i></b> is creating jobs while helping to | |
contain America's alarming trade deficit. <b><i>Locktheon</i></b> | |
defense systems are among America's leading exports. And thanks to | |
<b><i>Locktheon</i></b>'s industry-leading lobbying efforts, | |
America's defense industry is breaking down the trade barriers | |
that once held back American competitiveness by limiting what | |
America's enterprises could sell to tin-pot dictators. | |
<.p><b><i>Locktheon: Keeping America Working</i></b>. America | |
has the world's strongest, most vibrant free-market economy, an | |
economy built on uniquely American virtues: rugged individualism, | |
risk-taking, and constant innovation. But all of those virtues | |
would mean nothing without the greatest American virtue of all: | |
government spending. Study after classified study has proven | |
that America's free-market economy would collapse instantly | |
without billions and billions of dollars of federal largesse. | |
And that vital federal spending wouldn't be possible without | |
companies like <b><i>Locktheon</i></b>. Think about it: | |
in a world without expensive weapons systems, all of those untold | |
federal billions would have to be spent on something else; cheese, | |
for example. If the government were to spend the same amount of | |
money on cheese that it spends on just one <b><i>Locktheon Liberation | |
Star T-702GKV Global Kill Vehicle</i></b>, it would receive enough | |
cheese to stretch to the moon and back---<i>sixty-seven times</i>. | |
The logistical problems of storage alone would cripple America's | |
economy. But thanks to the industry-leading dollar cost density of | |
<b><i>Locktheon</i></b> advanced defense systems, storage space is | |
never a problem. | |
<.p><b><i>Locktheon: We Build the Weapons that Protect America | |
from the Weapons that We Build.</i></b> | |
</font></blockquote> | |
<.p>It goes on and on like that. " | |
; | |
++ variousBrochures: Decoration | |
'various other another glossy recruiting others/brochure*brochures' | |
'other brochures' | |
"They all have that slick, glossy look of an annual report. | |
None of them look especially interesting; just a bunch of | |
defense contractors, chemical companies, chip manufacturers, | |
and software companies. " | |
/* | |
* if we're alone on the rack, we're just 'various brochures'; | |
* otherwise we're 'various other brochures' | |
*/ | |
aName = (location.contents.length() == 1 | |
? 'various brochures' : 'various other brochures') | |
theName = (aName) | |
disambigName = 'another brochure' | |
isPlural = true | |
notImportantMsg = 'There are too many of them; it would take all | |
day to look through them all, and none of them | |
look all that interesting. ' | |
/* | |
* list me among the rack's contents when the rack is examined (but | |
* only when the rack itself is examined: don't list in the main | |
* room description, as it's too much detail for that level) | |
*/ | |
isListedInContents = true | |
; | |
+ Decoration | |
'white plastic wood veneer desk/desks/top/tops*furniture' 'desks' | |
"The desks are fairly ordinary office furniture, wood veneer | |
with white plastic tops. They're grouped together on one side | |
of the room. Each is paired with a chair. " | |
isPlural = true | |
; | |
+ Decoration 'desk chair/chairs*furniture' 'desk chairs' | |
"They're just ordinary office desk chairs. " | |
isPlural = true | |
; | |
+ Decoration 'phone/phones' 'phones' | |
"Each desk is equipped with a phone. " | |
isPlural = true | |
; | |
+ ccDinsdale: IntroPerson 'ma\'am ms. dinsdale/woman*women' | |
name = 'woman' | |
properName = 'Ms.\ Dinsdale' | |
npcDesc = "She's a short, stout woman in her late forties, | |
built like a PE teacher. She's dressed in a white | |
polo shirt and dark slacks, and her dark but graying | |
hair is arranged in a tight bun. " | |
isHer = true | |
; | |
++ Component 'dark graying tight hair/bun' 'dark hair' | |
"It's arranged in a tight bun. " | |
; | |
++ InitiallyWorn 'white polo shirt' 'polo shirt' | |
"It's a plain white polo shirt. " | |
isListedInInventory = nil | |
; | |
++ InitiallyWorn 'dark black navy slacks/pants' 'dark slacks' | |
"The pants one of those dark colors that's hard to tell apart | |
from one another---navy, black, something like that. " | |
isListedInInventory = nil | |
; | |
++ ccFolder: PresentLater, Thing 'manila file folder' 'manila folder' | |
"It's a thin manila file folder. " | |
; | |
++ HermitActorState | |
isInitState = true | |
noResponse = "You don't want to interrupt her while she's on the phone. " | |
stateDesc = "She's sitting at a desk talking on the phone. " | |
specialDesc() | |
{ | |
"A woman is sitting at one of the desks, talking | |
on the phone. "; | |
/* if we've never seen the PC before, note the arrival */ | |
if (!sawPC) | |
{ | |
"She sees you come in, gives you a little wave, and | |
holds up her index finger in the standard <q>just a | |
moment</q> gesture. "; | |
/* note that we've seen the PC come in now */ | |
sawPC = true; | |
/* set 'her,' since we've been prominently mentioned */ | |
me.setHer(ccDinsdale); | |
} | |
} | |
sawPC = nil | |
; | |
++ dinsdaleTalking: ActorState | |
stateDesc = "She's standing here talking with you. " | |
specialDesc = "Ms.\ Dinsdale is standing here talking with you. " | |
; | |
+++ HelloTopic "You already have her attention. "; | |
++ workingAtDesk: ActorState | |
stateDesc = "She's sitting at one of the desks, working. " | |
specialDesc = "Ms.\ Dinsdale is sitting at one of the desks, working. " | |
; | |
+++ DefaultAnyTopic | |
"She seems kind of busy, so you'd rather not bother her. " | |
; | |
++ ConvNode 'cc-welcome' | |
npcGreetingMsg = | |
"<.p>The woman hangs up the phone and walks over to you and | |
Belker, carrying a manila folder. She looks the two of you | |
over. <q>Would you two be...</q> She searches her folder | |
for a moment, the continues, <q>Belker and Mittling?</q> | |
<.p><q>Yes, I'm Frosst Belker,</q> Belker says, offering his | |
hand, which she shakes briskly. | |
<.p><q>I'm Ms.\ Dinsdale,</q> she says, <q>but you can call | |
me <q>ma'am.</q></q> She suddenly bursts into laughter, | |
literally slapping her knee and throwing her head back, and | |
then, just as suddenly, she's all business again. <q>No, seriously, | |
<q>Ms.\ Dinsdale</q> will be fine.</q> She turns to you. | |
<q>And you would be Mittling, from Omegatron?</q> " | |
commonFollowup = "<.p>She looks again at her folder, | |
speaking while flipping through its contents. <q>And you both | |
came to see Mr.\ Stamer, I see.</q> She looks up and shakes | |
her head. <q>Well,</q> she says in a peevish tone, <q>don't | |
shoot the messenger, but I'm afraid Stamer won't be seeing you | |
today. It seems he had something more <q>important</q> to | |
do, namely Ditch Day. I don't suppose either of you have | |
ever heard of that before?</q><.convnode cc-ditch> " | |
; | |
+++ YesTopic | |
"<q>That's right,</q> you say, shaking her hand. | |
<<location.commonFollowup>> " | |
; | |
+++ NoTopic | |
"<q>You can call me <b>Mr.</b>\ Mittling,</q> you say with a smile, | |
but she doesn't seem to think it's very funny. She just glares at | |
you for several seconds. | |
<<location.commonFollowup>> " | |
; | |
+++ DefaultAnyTopic | |
"<q>Whoa, there, fella,</q> Ms.\ Dinsdale says. <q>Let's make | |
sure we have our P's and Q's all crossed and dotted here. | |
You're Mittling, right? Yes or no?</q><.convstay> " | |
; | |
++ ConvNode 'cc-ditch' | |
commonFollowup() | |
{ | |
"This <q>Ditch Day</q> would be the tradition | |
wherein the seniors abandon the campus for the day, yes? | |
Leaving behind all manner of creative puzzles and challenges | |
which are known, I believe, as <q>stacks,</q> hmm? Ah, and the | |
underclassmen all strive to overcome these challenges to gain | |
access to the rooms of the seniors. Am I informed properly?</q> | |
<.p><q>Yep,</q> Ms.\ Dinsdale says, <q>that's basically it.</q> | |
<.p><q>So,</q> Belker says, <q>I take it we are to arrange new | |
appointments with Mr.\ Stamer?</q> | |
<.p>Ms.\ Dinsdale draws closer, speaks almost conspiratorially. | |
<q>Let me tell you, if I were you, and he pulled something like | |
this with me, I'd flush the little punk right down the john. | |
Here, look at this note he left for you and tell me this isn't | |
the most arrogant thing you've never seen.</q> She hands a note | |
to Belker; he reads it quickly and hands it to you. | |
<.reveal ditch-day-explained> | |
<.convnode accept-stack-pre> "; | |
stackNote.moveInto(me); | |
me.setPronounObj(stackNote); | |
} | |
; | |
+++ YesTopic | |
"<q>I'm actually an alum,</q> you say. | |
<.p><q>Good,</q> Ms.\ Dinsdale replies. <q>I hate explaining things. | |
Belker, what about you?</q> | |
<.p><q><<location.commonFollowup>> " | |
; | |
+++ NoTopic | |
"<q>I'm actually a Caltech alum,</q> you say, <q>but I think | |
you might have to explain it for Mr.\ Belker here.</q> | |
<.p>Belker smiles faintly. <q>Mr.\ Mittling makes a habit of | |
underestimating me, it seems. <<location.commonFollowup>> " | |
; | |
+++ DefaultAnyTopic | |
"You think she's waiting for a yes-or-no answer. You'd rather | |
not change the subject right now, since you need to find out what's | |
going on.<.convstay> " | |
; | |
+++ stackNote: Readable | |
'note from (mr.) (brian) (stamer)/note' | |
'note from Brian Stamer' | |
"<font face='tads-sans'>Dear Ms.\ Dinsdale, | |
<.p>Sorry to spring this on you with no notice, but I have an unusual | |
request for the Mitachron and Omegatron reps. I'd like to ask them | |
to take a stab at solving my stack. If they do, I'll take a job | |
at the winner's company, assuming they want to make an offer. Sorry | |
if it seems arrogant of me to want to interview the interviewers like | |
this, but most of the recruiters I've met so far weren't technical | |
enough to change a lightbulb. As you know, I want to work someplace | |
where I'll be around smart people, and I thought this would be a | |
good filter. | |
<br><br>---Brian (Dabney, room 4) | |
</font> " | |
mainExamine() | |
{ | |
/* | |
* we want to initiate the conversation only if this is the first | |
* time we've been described - note the status before we show the | |
* desription | |
*/ | |
local conv = (!described); | |
/* do the normal work */ | |
inherited(); | |
/* initiate the conversation if this is the first time through */ | |
if (conv) | |
ccDinsdale.initiateConversation(dinsdaleTalking, 'accept-stack'); | |
} | |
/* it's not actually here yet */ | |
location = nil | |
; | |
++ ConvNode 'accept-stack-pre'; | |
+++ AskTopic @stackNote | |
"<q>What's this note?</q> you ask. | |
<.p><q>Have you tried just reading it?</q> she asks. <q>Oops, | |
I guess not.</q> " | |
; | |
+++ DefaultAnyTopic | |
"Ms.\ Dinsdale shakes her head. <q>Just read the note,</q> she | |
says impatiently.<.convstay> " | |
; | |
class AcceptStackYes: SpecialTopic | |
'accept the challenge' ['yes','i','accept','the','challenge','it'] | |
topicResponse() | |
{ | |
"You hardly have a choice, given Rudy's personal interest in | |
hiring Stamer; even so, this could be kind of fun. | |
<q>Count me in,</q> you tell Ms.\ Dinsdale. "; | |
if (!gRevealed('belker-accepted-stack')) | |
"<.p>Belker chuckles. <q>I also accept, of course.</q> | |
<.reveal belker-accepted-stack> "; | |
"<.p>She gives you both a shocked look. <q>Really? Well, | |
I think you're both crazy, but I guess it's your cheesecake. | |
Stamer lives in Dabney House, room 4. I hope you kept the | |
campus maps I sent you because you're not getting new ones.</q> | |
She slaps her knee again and guffaws. <q>No, seriously, we're | |
out of maps. Anyway. I think Ditch Day goes until five PM, so | |
you've got a whole day to blow.</q> She shakes her head, rolls | |
her eyes, gives you and Belker a little mock salute, and returns | |
to her desk. | |
<.p>Belker watches her, looking amused. He finally turns | |
to you. <q>It seems we find ourselves once again in a friendly | |
rivalry.</q> He pulls a cell phone out of his pocket and | |
absently keys in a number. <q>May the better man prevail, as | |
always.</q> With a little nasal chuckle, he turns and heads | |
out the door, talking into his cell phone. | |
<.reveal stack-accepted> "; | |
/* our appointment is now concluded */ | |
ccOffice.doneWithAppointment = true; | |
/* set belker heading to the stack */ | |
frosst.setCurState(goingToStack); | |
frosst.knownFollowDest = alley1N; | |
/* send Ms. Dinsdale back to her desk, and discard her folder */ | |
ccDinsdale.setCurState(workingAtDesk); | |
ccFolder.moveInto(nil); | |
/* this counts toward our score */ | |
scoreMarker.awardPointsOnce(); | |
} | |
scoreMarker: Achievement { +1 "accepting the Ditch Day challenge" } | |
; | |
class AcceptStackNo: SpecialTopic, StopEventList | |
'decline the challenge' | |
['no','i','decline','reject','the','challenge','it'] | |
eventList = [&firstResponse, &secondResponse, &thirdResponse] | |
firstResponse() | |
{ | |
"You really want to decline and come back later for | |
a normal interview, but you hesitate. If Belker decides to | |
stay here today, you might not get another chance for an | |
interview. And Rudy did seem awfully intent on hiring | |
Stamer.<.convstay> "; | |
} | |
secondResponse() | |
{ | |
"You hesitate again; you feel like you're in a lose-lose | |
situation here. Solving Stamer's Ditch Day stack could be | |
fun, but it hardly seems like the right way to conduct a | |
job interview. Even so, it's hard to just leave, knowing | |
how much Rudy wants to hire Stamer.<.convstay> "; | |
} | |
thirdResponse() | |
{ | |
"<q>I think I'll come back when I can do a normal | |
interview,</q> you say. | |
<.p><q>You actually want to come back and talk to this | |
loser?</q> Ms.\ Dinsdale asks. <q>All right, if that's | |
what you want. I'll talk to Stamer to set up a new time, | |
and I'll let you know.</q> | |
<.p>You still have a few hours before your flight back, so | |
you say your goodbyes and spend some time touring the campus. | |
There are several changes since you went to school here, | |
especially with all the new construction in the north part of | |
the campus. It's fun seeing the place again, and seeing how | |
it's grown. | |
<.p>A couple of days go by, then someone from the Career | |
Center office calls you at your office to let you know that | |
Brian Stamer took a job with Mitachron. | |
<<laterMsg>> | |
Needless to say, Carl and RudyB are very disappointed. | |
It's not like they're going to fire you, but it's unarguably | |
another dismal failure, and this time you can't avoid | |
the feeling that there was more you could have done. "; | |
/* offer finishing options */ | |
finishGameMsg('YOU HAVE GIVEN UP', | |
[finishOptionUndo, finishOptionFullScore, | |
finishOptionCredits, finishOptionCopyright]); | |
} | |
/* add-on message for what happens later */ | |
laterMsg = "" | |
; | |
++ ConvNode 'accept-stack' | |
npcContinueMsg = "Belker chuckles. <q>I accept the challenge,</q> | |
he says. | |
<.p><q>You're serious?</q> Ms.\ Dinsdale asks. <q>I think | |
you're crazy, but how do I know? What about you, Mittling?</q> | |
<.reveal belker-accepted-stack> | |
<.convnode accept-stack2> " | |
; | |
+++ AcceptStackYes; | |
+++ AcceptStackNo | |
laterMsg = "It turns out that Belker accepted Stamer's Ditch Day | |
challenge after you took off. " | |
; | |
+++ DefaultAnyTopic | |
"You really should tell her whether or not you're going to accept | |
Stamer's challenge.<.convstay> " | |
; | |
++ ConvNode 'accept-stack2' | |
npcContinueList: StopEventList { [ | |
'<q>Hey, Mittling!</q> Ms.\ Dinsdale says. <q>How about you?</q> ', | |
'<q>Hello-ooo!</q> Ms.\ Dinsdale says, waving her hand in front | |
of your eyes like you\'re asleep. ', | |
'<q>Well, Mittling, what\'s it going to be?</q> Ms.\ Dinsdale | |
says. ' | |
] } | |
; | |
+++ AcceptStackYes; | |
+++ AcceptStackNo | |
laterMsg = "Belker must have been able to solve the stack, or maybe | |
he just made Stamer a great offer. " | |
; | |
+++ DefaultAnyTopic | |
"You really should answer her question first.<.convstay> " | |
; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - Dabney Hovse. Dabney is implemented in | |
* considerable detail, so it rates its own module. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
#include "ditch3.h" | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* A class for the outdoor rooms within Dabney. | |
*/ | |
class DabneyOutdoorRoom: CampusOutdoorRoom | |
/* | |
* Dabney outdoor rooms are technically campus outdoor rooms, but | |
* they don't connect directly to the main outdoor campus location | |
* graph (as you have to go through Dabney interior rooms to reach | |
* the main campus outdoor graph). So, we can't use the map in | |
* these locations. Explain why. | |
*/ | |
cannotUseMapHere = "(The map only shows the locations of buildings, | |
so you'll have to go outside Dabney if you want to figure the | |
way there.) " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* breezeway | |
*/ | |
dabneyBreezeway: Room 'Breezeway' 'the breezeway' | |
"This wide passageway opens onto a courtyard to the east, and | |
to the west leads out of the house to the Orange Walk. A dimly-lit | |
hallway leads into the house to the north; painted on the wall | |
alongside the hallway is the legend <q>1-9</q>. " | |
vocabWords = 'breezeway' | |
west = orangeWalk | |
out asExit(west) | |
north = alley1S | |
in asExit(north) | |
east = dabneyCourtyard | |
roomParts = static (inherited - [defaultEastWall, defaultWestWall]) | |
atmosphereList = (dbwMovers.isIn(self) ? moversAtmosphereList : nil) | |
; | |
+ bwSmoke: PresentLater, Vaporous 'thick black smoke/cloud/clouds' 'smoke' | |
"The smoke billows out of the hallway in thick black clouds. " | |
specialDesc = "Thick black smoke billows out of the hallway. " | |
isMassNoun = true | |
lookInDesc = "The smoke is almost opaque; you can't see into the | |
alley at all. " | |
beforeTravel(traveler, connector) | |
{ | |
/* | |
* if Erin and Aaron are still on their way, run into them on | |
* our way out | |
*/ | |
erin.interruptLunchFuse(); | |
} | |
; | |
++ SimpleOdor | |
desc = "The smoke has the acrid odor of burning electronics. " | |
; | |
+ Decoration 'legend/numbers' 'legend' | |
"The legend <q>1-9</q> is painted on the wall alongside the hallway, | |
indicating that rooms 1 through 9 lie this way. " | |
; | |
+ Enterable ->(location.east) 'courtyard' 'courtyard' | |
"The courtyard lies to the east. " | |
; | |
+ EntryPortal ->(location.north) | |
'dimly-lit dim north n 1 alley one/hall/hallway' 'hallway' | |
"The hallway leads north. " | |
; | |
+ Enterable ->(location.west) 'orange walk/walkway' 'Orange Walk' | |
"The Orange Walk is outside to the west. " | |
; | |
+ Enterable ->(location.east) 'courtyard' 'courtyard' | |
"The courtyard lies to the east. " | |
; | |
+ dbwMovers: MitaMovers | |
"Movers just keep coming in from the Orange Walk, carrying their | |
boxes into the hallway. Others emerge from the alley empty-handed | |
and head outside. " | |
"Mitachron movers are steadily coming in from the Orange Walk and | |
carrying their loads into the hallway. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Stuff inside the movers. We create an intermediate MultiInstance | |
* dummy object that we put inside all of the different mover objects, | |
* and then put the mover components inside the dummy object. This will | |
* automatically set up the individual copies of mover components for | |
* each mover, while still letting us use separate mover objects - which | |
* we want to do because we want to describe each one a little | |
* differently. | |
*/ | |
MultiInstance | |
initialLocationClass = MitaMovers | |
instanceObject: Decoration { | |
name = 'movers' | |
isPlural = true | |
} | |
; | |
+ Immovable 'white uniforms/jumpsuits' 'uniforms' | |
"The movers are wearing white jumpsuits with big Mitachron logos | |
on the back. " | |
isPlural = true | |
; | |
++ Component 'mitachron logo/logos' 'Mitachron logo' | |
"It's the standard Mitachron yellow <q>M</q> against an outline | |
of a globe. " | |
; | |
+ Immovable 'cardboard wood wooden box/boxes/crate/crates' 'crates' | |
"The movers are carrying cardboard boxes and wooden crates | |
of varying sizes, some large enough or evidently heavy enough | |
to require two or three people to carry. " | |
isPlural = true | |
lookInDesc = "There's no way you can look through the boxes | |
while the Mitachron people are busy with them. " | |
; | |
/* | |
* A class for the movers. The movers all show up later in the game, so | |
* make the PresentLater objects. | |
*/ | |
class MitaMovers: PresentLater, Person | |
'mitachron mover/movers/man/woman*men women' 'movers' | |
isPlural = true | |
isHim = true | |
isHer = true | |
/* | |
* use the class as the PresentLater key - this lets us move all of | |
* the movers into the game at once simply by referring to this key | |
* in PresentLater.makePresentByKey() | |
*/ | |
plKey = MitaMovers | |
; | |
/* atmosphere messages for the movers */ | |
moversAtmosphereList: ShuffledEventList | |
['One of the movers bumps into you with a crate. You start to | |
apologize, but he\'s already pushed past. ', | |
'Two movers carrying an especially large crate work their way | |
past you. ', | |
'Several empty-handed movers stream past. ', | |
'Four movers carrying identical boxes maneuver past you. ', | |
'A pair of movers carrying an eight-foot cardboard tube go past. ', | |
'Four movers work their way past carrying a heavy-looking | |
crate suspended from a pair of poles. ', | |
'A traffic jam briefly forms as a half-dozen movers try to | |
come through at the same time, but the knot soon clears. '] | |
eventPercent = 66 | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Doors to rooms that we're not welcome to enter. We'll simply say so | |
* if we try to enter, or even open the door. | |
*/ | |
class ForbiddenDoor: Door | |
dobjFor(Open) | |
{ | |
check() | |
{ | |
cannotEnter; | |
exit; | |
} | |
} | |
dobjFor(TravelVia) | |
{ | |
verify() | |
{ | |
/* | |
* slightly downgrade this, as wanting to enter a private | |
* room is unlikely if there's a better enterable nearby | |
*/ | |
logicalRank(50, 'private room'); | |
} | |
check() | |
{ | |
cannotEnter; | |
exit; | |
} | |
} | |
/* explain why we can't enter */ | |
cannotEnter = "It would be rude to enter without permission. " | |
/* | |
* since we don't want to open this door, don't bother making | |
* opening it a precondition to travel | |
*/ | |
getDoorOpenPreCond() { return nil; } | |
; | |
/* | |
* A default door to a student room in an alley. In the usual case, | |
* these are just forbidden doors, since they're private rooms we don't | |
* want to enter uninvited. | |
*/ | |
class AlleyDoor: ForbiddenDoor 'wood wooden room room/door*doors' | |
cannotEnter = "That's someone's private room---you wouldn't want to | |
enter uninvited. " | |
dobjFor(Knock) { action() { "You knock, but there's no reply. | |
The person who lives here is probably off doing something | |
Ditch Day-related. "; } } | |
; | |
/* | |
* A door for a room with a stack | |
*/ | |
class StackDoor: AlleyDoor | |
cannotEnter() | |
{ | |
if (isSolved) | |
"Even though you did manage to solve the stack, you wouldn't | |
feel right about spoiling it for the undergrads. Besides, | |
the <q>bribes</q> most seniors leave as rewards for breaking | |
their stacks are just a bunch of junk food. "; | |
else | |
"You can't enter until the stack is solved. "; | |
} | |
dobjFor(Knock) { action() { "You knock, but there's no reply. | |
The owner is presumably away for Ditch Day. "; } } | |
/* flag: we've solved the stack */ | |
isSolved = nil | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley walls. Define a mix-in class that describes the alley walls; | |
* we'll mix this with the default directional walls to create the | |
* corresponding directional alley walls. | |
*/ | |
class AlleyWall: object | |
desc = "The walls are decorated with graffiti. " | |
; | |
/* define the individual walls in the different directions */ | |
alleyNorthWall: AlleyWall, defaultNorthWall; | |
alleySouthWall: AlleyWall, defaultSouthWall; | |
alleyEastWall: AlleyWall, defaultEastWall; | |
alleyWestWall: AlleyWall, defaultWestWall; | |
/* | |
* Base alley room subclass. These rooms have the generic floor and | |
* ceiling, but need one or more alley walls to be added to their room | |
* parts. | |
*/ | |
class AlleyRoom: Room | |
roomParts = [defaultFloor, defaultCeiling] | |
vocabWords = 'alley/alleyway/hall/hallway' | |
name = 'hallway' | |
; | |
/* base class for graffiti on the alley walls */ | |
class Graffiti: Decoration 'graffiti/graffito' 'graffiti' | |
dobjFor(Read) asDobjFor(Examine) | |
; | |
Graffiti template "desc"; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* alley one south | |
*/ | |
alley1S: AlleyRoom 'Alley One South' 'south end of Alley One' | |
"Some of the other houses give fanciful names to their alleys, but | |
for some reason the Darbs never bothered; they just refer to their | |
alleys by number, this one being Alley One. Room 1 is to the east and | |
room 2 to the west; the hallway continues north, and a doorway leads | |
south. | |
<.p>The walls are heavily adorned with graffiti, as they were when | |
you were a student here. " | |
vocabWords = '1 alley one' | |
south = dabneyBreezeway | |
out asExit(south) | |
north = alley1N | |
east = room1Door | |
west = room2Door | |
roomParts = static (inherited + [alleyWestWall, alleyEastWall]) | |
atmosphereList = (a1sMovers.isIn(self) ? moversAtmosphereList : nil) | |
/* can't enter alley when the smoke is present */ | |
canTravelerPass(traveler) { return bwSmoke.location == nil; } | |
explainTravelBarrier(traveler) | |
{ | |
"You try to make your way into the alley, but the smoke is | |
too thick. "; | |
} | |
; | |
+ room1Door: AlleyDoor '1 -' 'door to room 1' | |
"It's a worn wooden door labeled <q>1.</q> " | |
; | |
+ room2Door: AlleyDoor '2 -' 'door to room 2' | |
"It's a wooden door labeled <q>2.</q> " | |
; | |
+ EntryPortal ->(location.south) 'south s doorway' 'doorway' | |
"The doorway leads out of the hallway to the south. " | |
; | |
++ Decoration 'main left right photocopy/rat/pictures/photos/caption' | |
'photocopy' | |
"It's a photocopy of a page from a textbook. It has two pictures | |
of rats, side by side. In the left picture, captioned <q>Rat out | |
of control,</q> the rat looks terrible: it's emaciated, and | |
big clumps of fur are missing. In the right picture, labeled | |
<q>Rat in control,</q> the rat looks happy and healthy. The | |
main caption reads: | |
<.p><b>Stress and control.</b> In the photo on the left, | |
the rat is given small, harmless electrical shocks at random. The | |
rat has no way of controlling the shocks, and in only a few days | |
becomes overwhelmed with stress. In the photo on the right, the | |
rat is given the same electrical shocks, but can stop each shock | |
by pressing on a paddle in its cage. Even though both rats are | |
subjected to similar types and amounts of stress, the rat with | |
control over the source of the stress remains healthy. " | |
specialDesc = "A photocopy is attached to the door. " | |
useSpecialDescInRoom(room) { return nil; } | |
; | |
+ Graffiti | |
'new newer old older abstract fantasy | |
art/artwork/part/parts/psychedelica/mural/murals/scribbling/scrawling' | |
'graffiti' | |
"When the Institute renovated the South Houses a few | |
years ago, the students persuaded the housing office to preserve | |
some of the better bits of Dabney graffiti, so what's on the walls | |
now is a mix of old and new. The artwork here is mostly | |
abstract psychedelica and fantasy murals, some pretty good; | |
there's also a lot of less-than-artistic scribbling. " | |
; | |
+ a1sMovers: MitaMovers | |
"Movers keep streaming in from the south, carrying their | |
loads to the north end of the alley, others returning empty-handed | |
and heading back outside. " | |
"A steady stream of Mitachron movers are pushing their way past | |
you with bulky loads of boxes and crates, heading to the north | |
end of the alley. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 2 - north end | |
*/ | |
alley2N: AlleyRoom 'Alley Two' 'Alley Two' | |
"Alley Two is just a short run of hall at the top of a broad | |
stairwell. The door to room 6 is to the east. The stairs to | |
the north lead down, and the hall continues south. " | |
vocabWords = '2 alley two' | |
roomParts = static (inherited + [alleyWestWall, alleyEastWall]) | |
down = alley2Stairs | |
north asExit(down) | |
east = room6door | |
south = alley2S | |
; | |
+ alley2Stairs: StairwayDown ->alley1Stairs | |
'broad stair/stairway/stairs/stairwell' 'stairway' | |
"The stairs lead down. " | |
; | |
+ room6door: AlleyDoor '6 -' 'door to room 6' | |
"It's a wooden door labeled <q>6.</q> " | |
; | |
+ Graffiti | |
'large fantasy landscape | |
mural/tower/towers/spire/spires/mountain/mountains' | |
'graffiti' | |
"A large fantasy landscape mural covers one wall: towers, spires, | |
mountains in the distance. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 2 - south end | |
*/ | |
alley2S: AlleyRoom 'Alley Two South' 'the south end of Alley Two' | |
"Alley Two ends here in two more room doors, room 7 to the west | |
and room 9 to the east. The alley continues north. " | |
vocabWords = '2 alley two' | |
north = alley2N | |
west = room7door | |
east = room9door | |
roomParts = static (inherited | |
+ [alleyWestWall, alleyEastWall, alleySouthWall]) | |
; | |
+ room7door: AlleyDoor '7 -' 'door to room 7' | |
"It's a wooden door labeled <q>7.</q> " | |
; | |
+ room9door: AlleyDoor '9 -' 'door to room 9' | |
"It's a wooden door labeled <q>9.</q> " | |
; | |
+ Graffiti 'expansive intertwined vine/vines/pattern' 'graffiti' | |
"This end of the hall is decorated with an expansive pattern of | |
intertwined vines. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Dabney courtyard | |
*/ | |
dabneyCourtyard: DabneyOutdoorRoom 'Courtyard' 'the courtyard' | |
"This large interior courtyard is dominated by what looks to be | |
a papier-mâché scale-model mountain: a two-story | |
edifice of cliffs and boulders, rising in the center of the | |
courtyard to a peak higher than the surrounding courtyard walls. | |
Piled randomly around the base are metal canisters marked with | |
radiation warnings, many split open and leaking green goo. | |
<<initialExplanation>> | |
<.p> | |
The courtyard itself follows the Mediterranean style of the | |
south houses: red brick floor, stucco walls, casement windows, | |
columns supporting an arcaded overhang to the east, terra cotta | |
tiles on the gently-sloped roofs. Under the overhang, doors | |
lead east into the lounge. The alley 3 entrance is to the | |
southwest, and alley 5 is to the southeast. A small alcove | |
lies to the south. To the west is a breezeway leading outside, | |
and a long concrete stairway goes up. " | |
/* provide a background explanation on the first viewing */ | |
initialExplanation() | |
{ | |
if (!seen) | |
"<.p>This must all be a decoration for a recent | |
multi-house party. It's pretty remarkable; back in your | |
day, the Darbs couldn't usually be bothered to pick a theme | |
for those parties, much less implement one, much less so | |
elaborately. "; | |
} | |
vocabWords = '(dabney) courtyard' | |
up = dcStairsUp | |
west = dabneyBreezeway | |
east = dcDoors | |
southwest = alley3main | |
southeast = alley5main | |
south = dabneyCourtyardAlcove | |
roomParts = static (inherited - defaultGround) | |
; | |
+ EntryPortal ->(location.west) 'breezeway' 'breezeway' | |
"The breezeway leads west. " | |
; | |
+ dcDoors: Door ->dlDoors | |
'lounge door/doors/lounge' 'doors to the lounge' | |
"Doors lead into the lounge to the east. " | |
isPlural = true | |
initiallyOpen = true | |
; | |
+ EntryPortal ->(location.southwest) | |
'southwest sw alley 3 entrance/doorway/legend' | |
'alley 3 entrance' | |
"The entrance to alley 3 is to the southwest. The doorway is | |
marked with the legend <q>10-16, 18-22.</q> " | |
; | |
+ EntryPortal ->(location.southeast) | |
'southeast se alley 5 entrance/doorway/legend' | |
'alley 5 entrance' | |
"The entrance to alley 5 is to the southeast. The doorway is | |
marked with the legend <q>23-31, 33-37.</q> " | |
; | |
+ Enterable ->(location.south) 'small alcove' 'alcove' | |
"A small alcove is to the south. " | |
; | |
+ dcStairsUp: StairwayUp 'long concrete stairway stairs up' 'stairway up' | |
"The stairway leads up. It looks like two normal floor-heights | |
worth of stairs. " | |
; | |
+ Floor '(courtyard) dark red brick floor/bricks' 'red brick floor' | |
"The courtyard floor is paved in dark red bricks. " | |
; | |
+ Fixture '(courtyard) east west north south e w n s stucco wall/walls' | |
'courtyard walls' | |
"The walls are lined with windows looking out into the courtyard | |
from student rooms. " | |
isPlural = true | |
; | |
++ Distant 'casement window/windows' 'windows' | |
"The walls are lined with windows facing the courtyard. " | |
isPlural = true | |
tooDistantMsg = 'The windows are all up too high to reach. ' | |
; | |
+ Fixture 'covered arcaded column/columns/overhang/arch/arches/area' 'arcade' | |
"The overhang provides a covered area outside the lounge. " | |
dobjFor(StandOn) | |
{ | |
verify() { } | |
action() { "If you want to enter the lounge, just go east. "; } | |
} | |
dobjFor(LookUnder) { action() { "Doors lead into the lounge to | |
the east. "; } } | |
; | |
+ Distant 'rounded gently-sloping terra cotta tile/tiles/roof/roofs' 'roof' | |
"The roof is covered with rounded terra cotta tiles, which look | |
rather like flower pots that were cut in half down their height. " | |
; | |
+ Fixture 'papier-mache paper-mache papier-m\u00E2ch\u00E9 scale-model model | |
mountain/boulders/cliff/cliffs/edifice/peak' 'mountain' | |
"It's an effective model: the cliffs near the bottom rise almost | |
vertically, increasing the sense that the peak looms overhead | |
at a great height, even though it only rises about the height | |
of the surrounding building. Boulders are strewn about the | |
base, mixed with big metal canisters marked with radiation warnings. " | |
dobjFor(Climb) | |
{ | |
verify() { } | |
action() { "It's too steep, and even if it weren't, it probably | |
wouldn't support your weight. "; } | |
} | |
dobjFor(StandOn) asDobjFor(Climb) | |
dobjFor(ClimbUp) asDobjFor(Climb) | |
dobjFor(Board) asDobjFor(Climb) | |
lookInDesc = "You can't see inside the mountain, and you find | |
nothing hidden among the boulders. " | |
; | |
+ CustomImmovable 'big large metal radiation warning | |
(symbols)/warnings/canisters/drum/drums/cylinders' | |
'canisters' | |
"Rusty metal cylinders, three feet long, are randomly placed among | |
the boulders around the base of the mountain. They're marked with | |
the three-pronged radition warning symbol. Many are split open and | |
leaking a viscous green goo. " | |
isPlural = true | |
lookInDesc = "You see nothing apart from the oozing green goo. " | |
cannotTakeMsg = 'You don\'t want to mess up the careful design. ' | |
; | |
+ CustomImmovable 'viscous oozing gelatinous bright green goo' 'green goo' | |
"It's some kind of gelatinous goo, bright green in color. " | |
cannotTakeMsg = 'There\'s no good way to move it, and besides, | |
you wouldn\'t want to mess up the careful design. ' | |
dobjFor(Eat) | |
{ | |
preCond = [] | |
verify() { } | |
action() { "On second thought, you probably shouldn't. | |
Anywhere else, you could assume that it's nice, safe | |
lime jello, but around here it's hard to be sure that | |
it's not authentic radioactive waste. "; } | |
} | |
dobjFor(Taste) asDobjFor(Eat) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Dabney lounge | |
*/ | |
dabneyLounge: Room 'Lounge' 'the lounge' | |
"This large common room is essentially the Hovse's living room. | |
It must have been necessary to clear the courtyard to make room | |
for the mountain, because the piles of junk normally out there seem | |
to be in here. Several couches are shoved together near the fireplace | |
to make room for the heaps of stuff filling the south side of the | |
room. The courtyard is out the doors to the west, and a wide | |
passage leads north into the dining room. " | |
vocabWords = '(dabney) (house) (hovse) lounge' | |
west = dlDoors | |
north = dlPassage | |
out asExit(west) | |
; | |
+ dlDoors: Door ->dcDoors 'courtyard door/doors/courtyard' | |
'doors to the courtyard' | |
"The doors lead out to the courtyard to the west. " | |
; | |
+ dlPassage: ThroughPassage ->ddPassage | |
'wide arched dining room north n passage' 'wide passage' | |
"It's an arched passage leading north into the dining room. " | |
; | |
+ Chair, Heavy 'big sturdy leather couch/couches/sofa/sofas' 'couch' | |
"The couches don't match one another, but they're all big, sturdy, | |
upholstered in well-worn leather. " | |
; | |
+ Fixture, Booth 'oversized stone fire/fireplace' 'fireplace' | |
"The oversized stone fireplace is on the east side of the room. | |
It was converted long ago to burn gas rather than wood, so it | |
contains only simulated logs. It's currently turned off. " | |
up: NoTravelMessage { "The chimney isn't big enough to climb into. "; } | |
dobjFor(TurnOn) | |
{ | |
verify() { } | |
action() { "It's a warm enough day; no need to make it an oven | |
in here. "; } | |
} | |
; | |
++ EntryPortal ->(location.up) 'chimney/soot' 'chimney' | |
"It's just a dark shaft a foot in diameter. " | |
dobjFor(Climb) asDobjFor(Enter) | |
dobjFor(ClimbUp) asDobjFor(Enter) | |
dobjFor(Board) asDobjFor(Enter) | |
lookInDesc = "There's nothing but soot up there. " | |
; | |
++ Heavy 'black simulated concrete logs' 'simulated logs' | |
"The logs are made out of concrete or something like it, molded | |
to resemble the real thing. They're black from long exposure to | |
a gas flame. " | |
isPlural = true | |
; | |
+ CustomImmovable | |
'scrap old bed car auto | |
junk/pile/piles/stuff/blankets/frame/frames/part/parts/wood' | |
'junk' | |
"Old blankets, bed frames, car parts, scrap wood; most of the stuff | |
isn't even immediately identifiable. " | |
isMassNoun = true | |
cannotTakeMsg = 'There\'s too much junk to move around. ' | |
lookInDesc = "You poke around, looking for a hidden gem, but you | |
find nothing interesting. You'd imagine it's already been | |
given a good going-over by people with a more generous | |
trash/treasure threshold than your own. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Dining room | |
*/ | |
dabneyDining: Room 'Dining Room' 'the dining room' | |
"This is the Hovse's dining room. Several long wooden tables are | |
arranged into a couple of rows, and chairs are lined up on either | |
side of each table. A wide passage leads south, to the lounge, | |
and a pair of swinging doors to the east leads to the kitchen. " | |
vocabWords = '(dabney) (hovse) (house) dining room' | |
south = ddPassage | |
out asExit(south) | |
east = ddKitchenDoors | |
/* begin lunch */ | |
startLunch() | |
{ | |
/* activate the students, food, etc */ | |
PresentLater.makePresentByKey('lunch'); | |
/* open the doors */ | |
ddKitchenDoors.makeOpen(true); | |
/* note that it's lunchtime */ | |
isLunchtime = true; | |
/* set a daemon to nag about going to lunch */ | |
new Daemon(self, &nagAboutLunch, 1); | |
} | |
/* end lunch */ | |
endLunch() | |
{ | |
/* deactivate the students, food, etc */ | |
PresentLater.makePresentByKeyIf('lunch', nil); | |
myLunch.moveInto(nil); | |
/* close the doors */ | |
ddKitchenDoors.makeOpen(nil); | |
/* it's lunchtime no more */ | |
isLunchtime = nil; | |
/* make sure the lunch-winding-down daemon is removed */ | |
eventManager.removeMatchingEvents(self, &lunchMonitor); | |
} | |
/* flag: it's lunchtime */ | |
isLunchtime = nil | |
/* daemon to nag about going to lunch */ | |
nagAboutLunch() | |
{ | |
/* nag with 20% probability */ | |
if (rand(100) < 20) | |
nagMessages.doScript(); | |
} | |
/* lunch nag messages */ | |
nagMessages: ShuffledEventList { [ | |
'You\'re getting pretty hungry. You really should go have | |
some lunch. ', | |
'It\'s not as though you\'re going to drop dead from lack | |
of nutrition or anything, but you are feeling a bit hungry. ', | |
'You should go get some lunch while the dining room is | |
still open. ', | |
'You\'re feeling hungry. You should take up Erin on her | |
offer to join Aaron and her for lunch. ' ] } | |
/* | |
* Lunch monitor daemon. This watches what's going on at lunch, to | |
* evaluate whether we should start nudging the player to finish up | |
* and get back to the stack already. The main point here is to let | |
* the player know that there's not much more they're going to find | |
* out by talking more to Aaron and Erin during lunch, so they don't | |
* think they have to just sit around all day waiting for something | |
* to happen here. | |
*/ | |
lunchMonitor() | |
{ | |
/* check to see if they've seen enough of the lunch topics */ | |
if (gRevealed('lunch-satisfied-1')) | |
{ | |
/* if we haven't already, fire the end-of-lunch plot event */ | |
if (endOfLunchNoted++ == 0) | |
lunchWindingPlotEvent.eventReached(); | |
/* mention that lunch is done with 33% probability */ | |
if (rand(100) < 33) | |
lunchDoneMessages.doScript(); | |
} | |
} | |
/* count of times we've noted the end-of-lunch status */ | |
endOfLunchNoted = 0 | |
/* messages to hint to player that they should finish lunch */ | |
lunchDoneMessages: ShuffledEventList { [ | |
'The lunch crowd seems to be thinning out a bit. People | |
are probably eager to get back to their stacks. ', | |
'Lunch seems to be winding down. ', | |
'People seem to be finishing up with lunch. '] } | |
/* if we arrive during lunch, do some extra work */ | |
travelerArriving(traveler, origin, connector, backConnector) | |
{ | |
/* do the normal work */ | |
inherited(traveler, origin, connector, backConnector); | |
/* if it's lunchtime, begin lunch */ | |
if (isLunchtime) | |
{ | |
/* remove the nag-about-lunch daemon */ | |
eventManager.removeMatchingEvents(self, &nagAboutLunch); | |
/* start the lunch monitor daemon */ | |
new Daemon(self, &lunchMonitor, 1); | |
/* move aaron and erin here */ | |
aaron.moveIntoForTravel(self); | |
erin.moveIntoForTravel(self); | |
/* set them to their lunch states */ | |
aaron.setCurState(aaronLunch); | |
erin.setCurState(erinLunch); | |
/* describe the start of lunch */ | |
"<.p>You spot Aaron and Erin at one of the tables. Erin | |
takes you into the kitchen and shows you where to buy a | |
meal pass, and gives you a quick tour of the lunch options. | |
The set-up hasn't changed much since you were a student, | |
nor, apparently, has the quality of the food. You load | |
up a tray, go back to the dining room, and sit down with | |
Aaron and Erin. "; | |
/* sit down */ | |
me.moveInto(ddChair); | |
me.posture = sitting; | |
/* fetch the PC's lunch */ | |
myLunch.makePresent(); | |
/* note the turn number when we arrived at lunch */ | |
lunchStartTime = Schedulable.gameClockTime; | |
} | |
} | |
/* the turn number when we arrived at lunch */ | |
lunchStartTime = nil | |
/* if we depart during lunch, finish up lunch */ | |
leavingRoom(traveler) | |
{ | |
/* if it's lunchtime, end lunch */ | |
if (isLunchtime) | |
{ | |
/* | |
* Describe the end of lunch. If we've only been here a | |
* couple of turns, mention that a lot of time has passed. | |
*/ | |
if (Schedulable.gameClockTime - lunchStartTime < 7) | |
"You spend a leisurely hour or so eating lunch | |
and chatting with Aaron and Erin. Eventually, the | |
dining room starts to clear out, and Aaron mentions | |
that he and Erin need to get back to their stack. | |
You finish up the edible parts of your lunch and | |
drop off your tray. "; | |
else | |
"Lunch has been winding down for a while now, and the | |
dining room has started clearing out. Aaron mentions | |
that he and Erin need to get back to their stack, so | |
you finish up the edible parts of your lunch and drop | |
off your tray. "; | |
/* mention that lunch is over now, not just for us */ | |
"Erin and Aaron head toward Alley Seven; you wish them luck | |
on their way out. "; | |
/* put out the fire */ | |
alley1N.endBlowout(); | |
/* send aaron and erin back to alley 7 */ | |
aaron.moveIntoForTravel(upper7N); | |
erin.moveIntoForTravel(upper7N); | |
aaron.setCurState(aaronUpper7); | |
erin.setCurState(erinUpper7); | |
/* we mentioned where they were going */ | |
aaron.knownFollowDest = upper7N; | |
erin.knownFollowDest = upper7N; | |
/* fire the end-of-lunch plot clock event */ | |
lunchDonePlotEvent.eventReached(); | |
/* bus the tables (etc) */ | |
endLunch(); | |
/* the Campus Network Office is now open */ | |
networkOffice.endLunch(); | |
} | |
} | |
/* a game-clock event for lunch winding down */ | |
lunchWindingPlotEvent: ClockEvent { eventTime = [2, 13, 8] } | |
/* a game-clock event for after we've finished lunch and departed */ | |
lunchDonePlotEvent: ClockEvent { eventTime = [2, 13, 19] } | |
; | |
+ ddPassage: ThroughPassage | |
'wide arched lounge south s passage' 'wide passage' | |
"The arched passage leads south, out to the lounge. " | |
; | |
+ ddKitchenDoors: Door 'swinging kitchen pair/door/doors' 'swinging doors' | |
"The doors lead into the kitchen, to the east. " | |
isPlural = true | |
/* the doors are locked outside of meal times */ | |
dobjFor(Open) | |
{ | |
check() | |
{ | |
/* | |
* if we get this far, they're closed, which means they're | |
* locked | |
*/ | |
"The doors seem to be locked. The kitchen must be | |
closed right now, since it isn't a meal time. "; | |
exit; | |
} | |
} | |
dobjFor(Unlock) | |
{ | |
verify() | |
{ | |
if (isOpen) | |
illogicalAlready('The doors are already unlocked and open. '); | |
else | |
illogical('The doors have no obvious locking mechanism, | |
at least not on this side. '); | |
} | |
} | |
dobjFor(Lock) | |
{ | |
preCond = [touchObj, objClosed] | |
verify() | |
{ | |
if (!isOpen) | |
illogical('The doors have no obvious locking mechanism, | |
at least not on this side. '); | |
} | |
} | |
/* the doors can only be closed by staff */ | |
dobjFor(Close) | |
{ | |
check() | |
{ | |
/* | |
* if we get this far, they're open, which means they're | |
* locked in the open position | |
*/ | |
"The doors seem to be locked in the open position. "; | |
exit; | |
} | |
} | |
/* | |
* going through the doors at lunchtime doesn't really go anywhere, | |
* but it seems to | |
*/ | |
dobjFor(TravelVia) | |
{ | |
action() | |
{ | |
/* | |
* if we make it this far, the doors are open; we don't | |
* actually go anywhere, but pretend we do | |
*/ | |
moreFood.doScript(); | |
} | |
} | |
moreFood: StopEventList { [ | |
'You go back into the kitchen, hoping to find something more | |
palatable to eat, but a thorough scan of the offerings turns | |
up nothing better. Disappointed, you return to the dining room. ', | |
'You go back to the kitchen and take one more look, but you | |
find nothing more palatable. You return to the dining room. ', | |
'There\'s really no point in visiting the kitchen again. ' ] } | |
; | |
+ Fixture, Surface | |
'dark long wooden wood dining table/tables/row/rows' 'table' | |
"The tables are made of a dark, varnished wood. They've seen | |
plenty of use. " | |
disambigName = 'long wooden table' | |
; | |
/* our lunch - present only during the lunch hour */ | |
++ myLunch: PresentLater, Thing | |
'greasy tray/food/puck/pucks/lunch' 'tray of food' | |
"Your tray has a selection of the greasy, non-descript foodstuffs | |
known here as <q>pucks,</q> for their resemblance in appearance | |
and flavor to the eponymous hockey accoutrement. " | |
owner = me | |
disambigName = 'your food' | |
smellDesc = "The food is surprisingly odorless. It must be | |
relatively fresh from the can, or whatever sort of receptacle | |
they get this stuff out of. " | |
tasteDesc = "You test a little bit, bracing yourself something | |
alarming, like mold or gangrene. But it's mostly just dry | |
and flavorless. " | |
dobjFor(Eat) | |
{ | |
preCond = [touchObj] | |
verify() { } | |
action() { "You choke down a little of one of the pucks. "; } | |
} | |
dobjFor(ThrowAt) | |
{ | |
check() | |
{ | |
/* FOOD FIGHT! ...or maybe not */ | |
if (gIobj.ofKind(Actor)) | |
"You fantasize for a moment about standing on the | |
table, yelling FOOD FIGHT!, and joining in as the | |
dining room explodes into pandemonium. But, alas, | |
Dabney is too mellow a place for that sort of thing, | |
and anyway, these pucks could put out an eye. "; | |
else | |
"Better not throw the pucks around; they could put | |
out an eye. "; | |
exit; | |
} | |
} | |
; | |
/* aaron's lunch, erin's lunch, etc */ | |
++ PresentLater, Decoration | |
'someone else\'s aaron\'s erin\'s lunch/food/puck/pucks/tray' 'food' | |
"No one else's food looks any more appetizing than yours. " | |
disambigName = 'someone else\'s food' | |
plKey = 'lunch' | |
; | |
+ ddChair: CustomImmovable, MultiChair | |
'simple dark wood wooden (dining) (room) chair/chairs' 'chair' | |
"The chairs are unpadded wood. There are enough chairs to seat | |
about seventy people. " | |
disambigName = 'simple wood chair' | |
cannotTakeMsg = 'You have no reason to carry around a dining room chair. ' | |
/* because we represent many objects, customize the status messages */ | |
actorInName = 'in one of the chairs' | |
chooseChairSitMsg() | |
{ | |
if (location.isLunchtime) | |
"You sit down with Erin and Aaron. "; | |
else | |
inherited(); | |
} | |
; | |
/* | |
* The students eating lunch - present only during the lunch hour. | |
* Don't actually put them in the chair, as the collective nature of the | |
* chair makes that work not terribly well. Instead, we'll just create | |
* the general impression that various people are sitting in the chairs. | |
*/ | |
+ PresentLater, Person 'dining other student*students' 'students' | |
"The students are eating and talking. " | |
specialDesc = "The room is about half full of students, sitting | |
around the tables eating lunch. " | |
disambigName = 'other students' | |
isPlural = true | |
plKey = 'lunch' | |
; | |
++ SimpleNoise 'conversations' 'conversations' | |
"There's a continuous babble of conversation and cliking of | |
silverware. " | |
isPlural = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alcove off Dabney Courtyard | |
*/ | |
dabneyCourtyardAlcove: Room 'Pinball Room' 'the pinball room' | |
"You don't know what this small room off the courtyard was | |
originally intended for, but to you it's the pinball room, | |
since there was always an old 1960s-vintage pinball machine or | |
two in here. It looks like they've finally gotten with the 80s: | |
in place of a pinball machine is Positron, a classic video game. | |
<.p>The courtyard is outside, to the north. " | |
vocabWords = 'pinball alcove/room' | |
north = dabneyCourtyard | |
out asExit(north) | |
; | |
+ Enterable ->(location.out) 'courtyard' 'courtyard' | |
"The courtyard is outside, to the north. " | |
; | |
+ posKey: PresentLater, Key 'small brass (positron) key' 'small brass key' | |
"It's a small brass key; it's the one Scott gave you for the | |
Positron game. " | |
; | |
+ posGame: Heavy, ComplexContainer | |
'positron classic video game black machine/cabinet/letters' | |
'Positron game' | |
"You remember Positron: it's one of those classic 80s video | |
games, from the generation right after the vector graphics machines. | |
The game is pretty abstract; you fly a spaceship around a maze | |
of 2D caves, blasting aliens and collecting power crystals. | |
<.p>The machine's cabinet is about five feet tall, painted black, | |
with <q>Positron</q> down the side in elaborate silver letters. The | |
display is recessed and slanted back, and under it is the console | |
with its big plastic buttons. An instruction card is affixed | |
to the console. Below the console is a coin slot | |
and a service door<<doorStat>>, and around the side from the | |
door is the power switch (currently <<posSwitch.onDesc>>). | |
<<practiceNote>> " | |
/* | |
* We don't want to show our contents as part of the 'status' part of | |
* EXAMINE, so don't show anything in our examineStatus(). Instead, | |
* we'll count on the compartment's specialDesc to show its contents. | |
*/ | |
examineStatus() { } | |
/* | |
* mention that this would be a good thing to practice on, if we | |
* know we need practice and haven't mentioned it before | |
*/ | |
practiceNote() | |
{ | |
if (gRevealed('need-ee-practice') && practiceNoteCount++ == 0) | |
"<.p>This might be just the sort of thing you need to | |
get back in practice for working on Brian's stack. These | |
old video games are pretty low-tech, so you shouldn't have | |
too much trouble finding your way around it. "; | |
} | |
practiceNoteCount = 0 | |
/* our door status message */ | |
doorStat = (posInterior.isOpen ? " (currently open)" : "") | |
/* our complex-container subcontainer */ | |
subContainer = posInterior | |
/* turn on/off go to the switch */ | |
dobjFor(TurnOn) remapTo(TurnOn, posSwitch) | |
dobjFor(TurnOff) remapTo(TurnOff, posSwitch) | |
/* the closest thing we have to a surface is the display */ | |
iobjFor(PutOn) remapTo(PutOn, DirectObject, posDisplay) | |
/* USE POSITRON == play it */ | |
dobjFor(Use) remapTo(Push, posButtons) | |
dobjFor(Play) remapTo(Push, posButtons) | |
/* are we working? */ | |
isWorking = (goodXtal.isIn(xtalSocket)) | |
/* our on/off state is controlled via the power switch */ | |
isOn = nil | |
; | |
/* | |
* a class for most of the Positron parts that happen to be elecrical in | |
* nature - these can be tested with the scope and signal generator, but | |
* not much happens when they are | |
*/ | |
class PosElectricalPart: TestGearAttachable | |
/* | |
* don't worry about attachment locations, as we don't actually | |
* allow attaching anything - just use everything's current location | |
* to avoid trying to put anything anywhere, and use an elevated | |
* priority to make sure that our non-condition prevails | |
*/ | |
getNearbyAttachmentLocs(other) { return [location, other.location, 100]; } | |
probeWithSignalGen() | |
{ | |
/* | |
* we can only probe if the cabinet is open, and even then, | |
* don't bother | |
*/ | |
if (posInterior.isOpen) | |
"You don't need to attach the signal generator there; | |
a quick probe with the scope ought to be sufficient to | |
check <<thatObj>>. "; | |
else | |
"You'll have to open the cabinet before you can access | |
any of the component wiring. "; | |
/* don't allow attaching it in any case */ | |
detachFrom(signalGen); | |
} | |
probeWithScope() | |
{ | |
/* only probe when the cabinet is open */ | |
if (posInterior.isOpen) | |
probeWithScopeMsg; | |
else | |
"You'll have to open the cabinet before you can access | |
any of the component wiring. "; | |
} | |
; | |
++ posSwitch: PosElectricalPart, Switch, Component | |
'(positron) (video) (game) power switch' 'power switch' | |
disambigName = 'video game power switch' | |
/* coordinate our on/off state with our parent */ | |
makeOn(val) | |
{ | |
inherited(val); | |
location.isOn = isOn; | |
} | |
turnOnOff() | |
{ | |
/* show what happens */ | |
"You flip the switch"; | |
if (location.isWorking) | |
{ | |
/* we're working; the display goes on or off as appropriate */ | |
if (isOn) | |
{ | |
", and a power-up test screen gradually fades in on the | |
display. After a few moments, the display switches | |
to the game's <q>attract mode.</q> "; | |
/* if this is the first time, mention it specially */ | |
if (scoreMarker.scoreCount == 0) | |
{ | |
"<.p>That must have done the trick! You fiddle | |
with the internal controls to give yourself a free | |
game, and you start playing. The aliens quickly | |
clobber you, but it looks like everything's working | |
properly.<.reveal positron-repaired> "; | |
/* score this the first time it happens */ | |
scoreMarker.awardPointsOnce(); | |
/* this is a clock-significant plot event */ | |
repairPlotEvent.eventReached(); | |
} | |
} | |
else | |
". A brief white flash shows on the display screen, | |
then the screen goes dark. "; | |
} | |
else | |
", but there's no obvious effect. "; | |
} | |
/* do some extra work on turning on or off */ | |
dobjFor(TurnOn) { action() { inherited(); turnOnOff(); } } | |
dobjFor(TurnOff) { action() { inherited(); turnOnOff(); } } | |
/* it's a reasonable thing to do, so at least acknowledge it */ | |
probeWithScopeMsg = "You give the switch's electrical contacts a | |
cursory examination. Everything looks fine there. " | |
/* | |
* sore the repair when we turn it on for the first time with the | |
* machine working | |
*/ | |
scoreMarker: Achievement { +10 "repairing Positron" } | |
/* a game-clock event for having fixed the game */ | |
repairPlotEvent: ClockEvent { eventTime = [2, 11, 41] } | |
; | |
++ CustomFixture 'instruction card/instructions' 'instruction card' | |
"At the top of the card, printed in bold type: | |
<table><tr><td align=center> | |
<font face='tads-sans'><b> | |
\b\t25¢ Per Game - Quarters Only | |
\n\tFor Amusement Only | |
\n\t<i>Manufactured by ARITA GAMES</i> | |
\n\t<font size=-1>©1982 All Rights Reserved</font> | |
\b | |
</b></font> | |
</table> | |
Under this, handwritten: | |
\b\t<i>Lost quarter? See Scott, Dabney rm 39 (Alley 7)</i> | |
<.reveal find-scott> " | |
cannotTakeMsg = 'The instruction card is permanently affixed to | |
the console. ' | |
; | |
++ posButtons: PosElectricalPart, Component | |
'(positron) (video) (game) big plastic buttons/console' | |
'video game console' | |
"The console consists of several big plastic buttons that control | |
the spaceship. " | |
dobjFor(Push) | |
{ | |
verify() { } | |
action() | |
{ | |
if (location.isWorking) | |
{ | |
if (posInterior.isOpen) | |
"You fiddle with the internal controls to give yourself | |
a free game, then play for a few minutes to verify | |
that everything's working. It's actually kind of a | |
fun game. You play until you get clobbered by the | |
aliens; GAME OVER flashes on the screen for a few | |
moments, then the machine goes back to attract mode. "; | |
else | |
"The game displays INSERT QUARTER on the screen for | |
a few moments, and then returns to attract mode. "; | |
} | |
else | |
"You tap on the buttons, but nothing happens. "; | |
} | |
} | |
/* it's not interesting to test this part, but allow it */ | |
probeWithScopeMsg = "You test the button wiring with the scope, | |
and everything to be in order. " | |
; | |
++ RestrictedContainer, Component | |
'(positron) (video) (game) coin quarter slot' 'coin slot' | |
"It's a slot for quarters. Under the slot is a coin return. " | |
cannotPutInMsg(obj) { return 'The slot only accepts quarters. '; } | |
; | |
++ RestrictedContainer, Component | |
'(positron) (video) (game) small coin return/recess' 'coin return' | |
"It's a small recess where coins are returned when rejected | |
by the coin slot. " | |
cannotPutInMsg(obj) { return 'The recess is too small to hold | |
anything but a coin or two. '; } | |
lookInLister: thingLookInLister { | |
showListEmpty(pov, parent) { defaultDescReport('You check the | |
coin return, but you find no stray quarters. '); } | |
} | |
; | |
++ posDisplay: PosElectricalPart, Component, Surface | |
'(positron) (video) (game) display/screen' 'video game display' | |
desc() | |
{ | |
if (location.isWorking) | |
"The display is showing the game's <q>attract mode,</q> | |
which is just a section of the game being played in a loop. "; | |
else | |
"The display is currently dark. "; | |
} | |
iobjFor(PutOn) | |
{ | |
action() | |
{ | |
if (gDobj == oooSign) | |
{ | |
"You put the sign back on the display. "; | |
inherited(); | |
} | |
else | |
"The screen is too steeply slanted to hold anything. "; | |
} | |
} | |
/* it's not interesting to test this part, but allow it */ | |
probeWithScopeMsg = "You check the display connections, and | |
find that everything looks to be hooked up properly. " | |
; | |
+++ oooSign: Readable 'yellow out-of-order note' 'yellow note' | |
"It's a yellow note reading <q>Out of Order.</q> " | |
useSpecialDesc = (location == posDisplay) | |
specialDesc = "A yellow note reading <q>Out of Order</q> is on the | |
Positron display. " | |
; | |
++ posDoor: ContainerDoor | |
'(positron) (video) (game) service door' 'service door' | |
"The service door takes up roughly the bottom half of the front of | |
the machine; it provides access to the game's electronics. | |
<<posInterior.isOpen ? "It's currently open, providing access | |
to the service compartment inside the cabinet." : "">> " | |
; | |
++ posInterior: KeyedContainer, Component | |
'(positron) (video) (game) service compartment' 'service compartment' | |
keyList = [posKey] | |
knownKeyList = [posKey] | |
initiallyLocked = true | |
initiallyOpen = nil | |
dobjFor(Close) | |
{ | |
check() | |
{ | |
local obj; | |
/* do the normal work */ | |
inherited(); | |
/* | |
* we can't close it if anything's attached to the circuits | |
* that's not in the compartment | |
*/ | |
obj = posCircuits.attachedObjects.valWhich({x: !x.isIn(self)}); | |
if (obj != nil) | |
{ | |
"You can't close the compartment while <<obj.nameIs>> still | |
attached to the circuit board. "; | |
exit; | |
} | |
} | |
} | |
; | |
+++ posCircuits: TestGearAttachable, CustomImmovable | |
'video amp amps amplifiers main logic large circuit | |
board/boards/circuits/circuitry' | |
'circuit board' | |
"These old games were built on primitive chips by today's | |
standards, so rather than a few big integrated circuits, these | |
boards have lots of small chips and discrete components. | |
<<xtalSocket.boardDesc>> " | |
cannotTakeMsg = 'The boards aren\'t easily removable. ' | |
/* | |
* Show a special description in descriptions of our immediate | |
* container (the service compartment), but not as part of anything | |
* above that level. This is too much detail until we're looking | |
* right at the compartment. | |
*/ | |
useSpecialDescInRoom(room) { return nil; } | |
useSpecialDescInContents(cont) { return cont == location; } | |
specialDesc = "Several large circuit boards are inside the | |
compartment. " | |
/* | |
* When things are attached to me, put them in the Positron's | |
* location, and leave me where I am. This is as safe as the | |
* standard both-in-same-location pattern, since the positron game | |
* can't be moved. | |
*/ | |
getNearbyAttachmentLocs(other) | |
{ return [location, posGame.location, 100]; } | |
/* putting a part into the board maps to the socket */ | |
iobjFor(PutIn) remapTo(PutIn, DirectObject, xtalSocket) | |
/* probe with the oscilloscope */ | |
probeWithScope() | |
{ | |
local aThe = (townsendBook.moved ? 'the' : 'a'); | |
/* if the game isn't on, not much happens */ | |
if (!posGame.isOn) | |
{ | |
"You probe the circuits in a few places, and find that | |
all you get is a flat line, just as you'd expect with | |
the game powered off. "; | |
return; | |
} | |
/* if the machine is repaired, this is easy */ | |
if (posGame.isWorking) | |
{ | |
"You make a few quick checks, and everything looks | |
like it's working properly. "; | |
return; | |
} | |
/* if the crystal socket is empty, we know what to do */ | |
if (xtalSocket.contents.length() == 0) | |
{ | |
"You're pretty sure that you need to put a new crystal | |
in socket X-7. "; | |
return; | |
} | |
/* check how far we've traced the problem */ | |
switch (traceLevel) | |
{ | |
case 0: | |
/* we haven't started yet, so say how we're starting */ | |
"Since the game's display isn't showing anything, you | |
decide to start with the video amplifiers. It would | |
be nice to have a set of schematics, but you figure | |
it's actually better practice for Stamer's stack without | |
them.<.p> "; | |
/* advance to the video amp trace level */ | |
++traceLevel; | |
/* go check the video amps */ | |
goto checkVideoAmps; | |
case 1: | |
"You go back and look at the video amplifiers again. "; | |
checkVideoAmps: | |
/* check to see if we know what we're doing */ | |
if (gRevealed('read-video-amp-lab')) | |
{ | |
/* we can finish with this part */ | |
"You carefully step through the video amp circuits, | |
and after a bit you're convinced that they're okay | |
and that the problem lies elsewhere. | |
<.p>The next thing to check is probably the main | |
logic board. "; | |
/* move to the next trace level */ | |
++traceLevel; | |
/* go check the main logic board */ | |
goto checkCPU; | |
} | |
else | |
{ | |
"You probe around a little with the scope, but it's | |
been a while since you looked at a video amp in this | |
much detail. Maybe if you spent a little time reading | |
about video amps in <<aThe>> lab manual, you'd have a | |
better idea how to proceed.<.reveal need-video-amp-lab> "; | |
} | |
break; | |
case 2: | |
/* move on to the CPU */ | |
"You look again at the main logic board. "; | |
checkCPU: | |
if (gRevealed('read-1a80-lab')) | |
{ | |
/* we can finish this part */ | |
"You check around the CPU. After a bit of | |
probing, it becomes apparent that the problem is | |
on the clock signal coming into the CPU, and once | |
you know that, you quickly identify a bad part, which | |
is probably the source of the problem: a crystal with | |
the faded marking <q>9.8304MHZ,</q> indicating | |
its frequency. | |
<.p>The crystal will need to be replaced with a | |
working one. Fortunately, it's not soldered into | |
the board---it's in a socket for easy | |
replacement.<.reveal need-pos-xtal> "; | |
/* bring the bad crystal and its socket into the game */ | |
xtalSocket.makePresent(); | |
/* move to the next trace level */ | |
++traceLevel; | |
} | |
else | |
{ | |
"You identify the CPU as a 1A80. You built a few | |
projects with these many years ago, but you certainly | |
don't remember all the little details of pin layout | |
and so forth, so you'll need to look up the 1A80 | |
in <<aThe>> lab manual before you can test the logic | |
board properly.<.reveal need-1a80-lab> "; | |
} | |
break; | |
case 3: | |
"You've traced the problem to a bad crystal---the one | |
labeled <q>9.8304MHz</q>---on the main logic board. You | |
simply need to replace it. Luckily, it's in a socket, | |
not soldered in place, making it easily removable. "; | |
break; | |
} | |
} | |
/* how far have we traced the problem? */ | |
traceLevel = 0 | |
; | |
++++ randomComponents: GenericObject, CustomImmovable | |
'small small-scale discrete 1A80 cpu/crystal/crystals/chip/chips/ | |
components/transistors/resistors/capacitors' | |
'chips' | |
"The boards have lots of discrete components---transistors, | |
resistors, capacitors, and small-scale chips. " | |
disambigName = 'chips on the circuit boards' | |
isPlural = true | |
cannotTakeMsg = 'Most of the components are soldered in place. ' | |
/* trying to attach something here redirects to the board */ | |
iobjFor(AttachTo) remapTo(AttachTo, DirectObject, location) | |
dobjFor(TestWith) remapTo(TestWith, location, IndirectObject) | |
iobjFor(PlugInto) remapTo(PlugInto, DirectObject, location) | |
; | |
++++ xtalSocket: PresentLater, Component, | |
SingleContainer, RestrictedContainer | |
'crystal socket x-7/x7' 'socket X-7' | |
"It's a socket for a certain type of crystal. " | |
aName = 'socket X-7' | |
/* mention it as part of the board description if appropriate */ | |
boardDesc() | |
{ | |
/* if I'm not here, say nothing */ | |
if (location == nil) | |
return; | |
/* mention me and my contents */ | |
if (badXtal.isIn(self)) | |
"<.p>One of the components is the bad 9.8304 MHz crystal you've | |
identified as the probable source of the game's problem. "; | |
else | |
"<.p>Among the board's components is an empty socket | |
labeled X-7. The socket is for a crystal. "; | |
} | |
/* don't show my contents in listings of containing objects */ | |
contentsListed = nil | |
/* allow only the crystals */ | |
validContents = [badXtal, goodXtal] | |
cannotPutInMsg(obj) { return 'The only thing that fits the socket | |
is a certain kind of crystal. '; } | |
/* turn off positron when inserting or removing parts */ | |
turnOffGameFirst() | |
{ | |
if (posGame.isOn) | |
{ | |
extraReport('You turn off the Positron game first, to | |
make sure you don\'t damage any parts. '); | |
posSwitch.makeOn(nil); | |
} | |
} | |
notifyRemove(obj) { turnOffGameFirst(); } | |
notifyInsert(obj, newCont) { turnOffGameFirst(); } | |
; | |
+++++ badXtal: Thing | |
'blackened tiny metal 9.8304mhz 9.8304 mhz old bad crystal/(box)' | |
'old 9.8304 MHz crystal' | |
"It's a tiny metal box, shaped like a can of sardines but shrunk | |
down to the size of a dime. <q>9.8304MHZ</q> is stamped on it | |
in faded letters. | |
<<isIn(xtalSocket) | |
? "Fortunately, it's in a socket, not soldered into the board, | |
making it easy to replace it." : "">> " | |
disambigName = 'old 9.8304 MHz crystal' | |
; | |
/* | |
* The bag of spare parts | |
*/ | |
+++ RestrictedContainer, Consultable | |
'crumpled brown paper bag/sack' 'brown paper sack' | |
"It's a brown paper bag, the kind that might at some point have | |
held a half gallon of milk from the grocery store. " | |
cannotPutInMsg(obj) { return 'You don\'t want to get anything | |
mixed up with the assorted spare parts. '; } | |
lookInDesc = "The bag is full of random spare parts. If you | |
needed a particular part, you might be able to find it if | |
you looked for it. " | |
/* GET X FROM BAG -> SEARCH BAG FOR X */ | |
iobjFor(TakeFrom) remapTo(ConsultAbout, self, DirectObject) | |
/* this isn't something we want to put new objects into */ | |
iobjFor(PutIn) { verify() { logicalRank(50, 'special bag'); } } | |
/* | |
* When we LOOK IN or SEARCH the bag, there are three possibilities. | |
* First, if we haven't found out that we need the crystal yet, we | |
* see only random parts, because we don't know what we need yet. | |
* Second, if we do know we need the crystal, but we haven't found | |
* it yet, we find the crystal. Third, if we've already found the | |
* crystal, we again see only random spare parts, but this time | |
* because we've already found everything we need so far. | |
*/ | |
dobjFor(LookIn) | |
{ | |
action() | |
{ | |
if (!gRevealed('need-pos-xtal')) | |
{ | |
/* we don't even know what we need yet */ | |
"The bag is full of random spare parts. If you manage | |
to track down the problem with Positron to a particular | |
bad part, maybe you can find a replacement here. "; | |
} | |
else if (goodXtal.location == nil) | |
{ | |
/* we know we need the crystal, and we don't have it yet */ | |
replaceAction(ConsultAbout, self, goodXtal); | |
} | |
else | |
{ | |
/* we've already found everything we need */ | |
"You sift through the parts a bit, but you don't see | |
anything you need at the moment. "; | |
} | |
} | |
} | |
; | |
++++ GenericObject, CustomImmovable | |
'(random) (positron) spare part/parts/crystal/crystals/chip/chips/ | |
components/transistors/resistors/capacitors' | |
'spare parts' | |
"The bag is full of random spare parts for the Positron | |
game---chips, transistors, capacitors, crystals, and so on. " | |
isPlural = true | |
cannotTakeMsg = 'There are a lot of parts here, so it\'s better | |
to keep them together in the bag. If you needed a particular | |
part, you might be able to find it if you looked for it. ' | |
/* show a special description in our immediate container only */ | |
useSpecialDescInRoom(room) { return nil; } | |
useSpecialDescInContents(cont) { return cont == location; } | |
specialDesc = "The bag is full of random spare parts for | |
the Positron machine. " | |
/* map LOOK IN and CONSULT ABOUT to our container */ | |
dobjFor(LookIn) remapTo(LookIn, location) | |
dobjFor(ConsultAbout) remapTo(ConsultAbout, location, IndirectObject) | |
iobjFor(TakeFrom) remapTo(ConsultAbout, location, DirectObject) | |
; | |
/* | |
* Find the good crystal. Use an elevated match score for this so that | |
* we match it if at all possible, even on vague things like "find | |
* crystal". We'll then turn around and ignore this if we don't know | |
* what we're looking for yet. | |
* | |
* We have three goals that we meet with this weird handling. First, if | |
* we know what we're looking for, we want to be liberal about finding | |
* it: we want vague things like FIND CRYSTAL IN BAG to find it, which | |
* we do by ensuring this topic is highly ranked and thus matches on any | |
* correct vocabulary. Second, if we *don't* know what we're looking | |
* for, because we haven't gotten to that point in the game yet, we | |
* *don't* want such liberal matching. We avoid the liberal matching in | |
* this case by matching on the vocabulary, but then just replacing the | |
* action with FIND GENERIC PARTS instead if we don't know what we need | |
* yet. Third, if we look for *exactly* the right part, we want to find | |
* it regardless of whether we know about it yet. We accomplish this | |
* third bit by allowing the match if the command contains the specific, | |
* unique identifier for this part that can't be confused with the | |
* generic parts. | |
*/ | |
++++ ConsultTopic +110 @goodXtal | |
handleTopic(actor, topic) | |
{ | |
/* | |
* Until we know we need the crystal, only find the crystal if | |
* we asked for exactly the right one, identified by the unique | |
* "9.8304" vocabulary. | |
*/ | |
if (gRevealed('need-pos-xtal') | |
|| rexSearch('9<dot>8304', topic.getTopicText(), 1) != nil) | |
{ | |
/* we know what we want, or the exact name was given - find it */ | |
inherited(actor, topic); | |
} | |
else | |
{ | |
/* don't know exactly what we want; find generic parts instead */ | |
replaceAction(ConsultAbout, location, randomComponents); | |
} | |
} | |
topicResponse() | |
{ | |
/* move the good crystal into the bag, then take it */ | |
goodXtal.makePresent(); | |
nestedAction(Take, goodXtal); | |
/* mention that we found it */ | |
"You sift through the parts for a few moments, coming | |
across a brand new-looking 9.8304 MHz crystal<< | |
goodXtal.isIn(me) ? ", which you take" : "" >>. "; | |
/* make the crystal 'it' */ | |
gActor.setIt(goodXtal); | |
} | |
/* this is only active until we find the crystal */ | |
isActive = (goodXtal.location == nil) | |
; | |
++++ ConsultTopic @randomComponents | |
"The bag is full of random parts. You might be able to find | |
a specific part if you look for exactly the part you need. " | |
; | |
++++ DefaultConsultTopic | |
"There are a lot of parts, but you don't come across the one | |
you're looking for. " | |
; | |
++++ goodXtal: PresentLater, Thing | |
'brand new new-looking tiny metal 9.8304mhz 9.8304 mhz good | |
replacement crystal/(box)' | |
'new 9.8304 MHz crystal' | |
"It's a tiny metal box, about the size of a dime, stamped | |
with <q>9.8304MHZ.</q> It looks shiny and new. " | |
disambigName = 'new 9.8304 MHz crystal' | |
/* make it known so that we can look it up */ | |
isKnown = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 3 - entrance | |
*/ | |
alley3main: AlleyRoom 'Alley Three Entry' 'the Alley Three entry' | |
"This is the middle of Alley Three, which runs east and | |
west from here. The exit to the courtyard is to the north, | |
and next to it a stairway leads up. The door to room 13 is | |
to the south. " | |
vocabWords = '3 alley three entry/hallway/hall' | |
north = dabneyCourtyard | |
out asExit(north) | |
northeast asExit(north) | |
up = a3StairsUp | |
east = alley3E | |
west = alley3W | |
south = room13door | |
roomParts = static (inherited + [alleySouthWall, alleyNorthWall]) | |
; | |
+ ExitPortal ->(location.out) 'courtyard/exit' 'courtyard' | |
"The courtyard lies to the north. " | |
dobjFor(Enter) asDobjFor(TravelVia) | |
; | |
+ room13door: AlleyDoor '13 -' 'door to room 13' | |
"The wooden door is labeled <q>13.</q> " | |
; | |
+ Graffiti 'small abstract designs' 'graffiti' | |
"There a couple of small abstract designs apparently saved | |
in the last repainting, and not much else. " | |
; | |
+ a3StairsUp: StairwayUp 'stairs/stair/stairway' 'stairway' | |
"The stairs lead up to Alley Four. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 3 east | |
*/ | |
alley3E: AlleyRoom 'Alley Three East' 'the east end of Alley Three' | |
"The hallway ends here, continuing west. It's partially | |
blocked by an elaborately decorated, refrigerator-sized cardboard | |
box outside the door to room 12, on the south side of the hall. | |
The door to room 10 is across the hall on the north side, and room | |
11 is at the end of the hall. " | |
vocabWords = '3 alley three' | |
west = alley3main | |
north = room10door | |
east = room11door | |
south = room12door | |
atmosphereList: ShuffledEventList { [ | |
'A couple of the chickens depart down the hall, probably | |
leaving on a mission. ', | |
'The chickens mill about, flapping their wings. ', | |
'A couple of chickens arrive from the down the hall. ', | |
'The chickens flap their wings excitedly. '] | |
eventPercent = 66 | |
} | |
; | |
+ Graffiti '-' 'graffiti' | |
"Unlike most of the rest of the house, there's no graffiti here. " | |
; | |
+ room10door: AlleyDoor '10 -' 'door to room 10' | |
"The door is labeled <q>10.</q> " | |
; | |
+ room11door: AlleyDoor '11 -' 'door to room 11' | |
"The wooden door is labeled <q>11.</q> " | |
; | |
+ room12door: StackDoor '12 -' 'door to room 12' | |
"The door is labeled <q>12.</q> " | |
; | |
++ chickenNotebook: CustomImmovable, Readable | |
'gray notebook/string' 'notebook' | |
"The notebook is hanging from a string. Fancy calligraphic | |
letters adorn the cover: <q><i>The Laboratory Notebook and | |
Philosophical Manifesto of Dr.\ Klaus W.D. von Geflügel, | |
Super-Genius</i>.</q> " | |
readDesc = "The notebook is filled with the same sort of flowery | |
cursive as the cover. | |
<.p><i> | |
“The Laboratory Notebook and Philosophical Manifesto | |
of Dr.\ Klaus W.D. von Geflügel, Super-Genius | |
<.p>“I, Klaus von Geflügel, now commit to ink | |
and parchment my Greatest Findings, truly an Historic | |
Achievement. Since the time of my earliest Childhood | |
memories, it has Been my Dream and Sacred Quest to create | |
what I have now created. You, the reader, an | |
Ordinary Man, cannot Possibly know or appreciate the great | |
Tribulation of my quest. You cannot know What it is to be | |
called Mad, to be laughed at by Rivals when Your early | |
experiments ‘fail.’ But those Rivals will soon | |
see that they are Mere Fools, for they did not Know that | |
my past experiments Were no failures, only Gradual and | |
Deliberate Steps on a Great Journey that is now, Finally, | |
reaching its Inevitable conclusion and Fruition. | |
<.p>“For I have created that which all Philosophers have | |
dreamt of since the Earliest days of Science. I have created | |
the Means to produce a creature vastly Superior to any that | |
have walked this Earth heretofore: a Creature that combines | |
the Stature and Great Size of Man, with the Intelligence | |
and Graceful Elegance of the Chicken...\ a race of giant | |
Super-Chickens. With this Discovery, I will create An army | |
of unstoppable Super-Chickens, and together we will Rule the | |
Earth, and Under my Benevolent Dominion, we will create a | |
True paradise for Man and Chicken alike.” | |
</i> | |
<.p>It goes on like this, laying out the rules of the stack. | |
It seems that stack participants have to <q>become</q> giant | |
chickens using the Chickenator, and then carry out a number | |
of missions as part of the Super-Chicken Army---basically a | |
series of stunts around campus. The key rule is that, | |
once turned into a chicken, a participant can only talk to | |
non-chickens with clucking noises. " | |
disambigName = 'gray notebook' | |
specialDesc = "A gray notebook is hanging on a string on the | |
door to room 12. " | |
cannotTakeMsg = 'The notebook is tethered to the door, presumably | |
to prevent anyone from walking off with it. ' | |
dobjFor(Open) asDobjFor(Read) | |
dobjFor(LookIn) asDobjFor(Read) | |
; | |
+ chickenator: Immovable, EntryPortal -> insideChickenator | |
'colored large decorated cardboard narrow | |
box/booth/chickenator/light/lights/opening' | |
'cardboard box' | |
"The box must have originally contained a refrigerator or a large | |
piece of furniture: it's over six feet tall and almost as wide | |
and deep. It's been painted and decorated with colored lights | |
to look like something out of an old science fiction movie, and | |
on the front is written <q>Chickenator Mark III.</q> On one side | |
is a narrow opening, just large enough for a person to enter. " | |
disambigName = 'decorated cardboard box' | |
dobjFor(Board) asDobjFor(Enter) | |
; | |
+ superChickens: DisambigDeferrer, Person | |
'giant chicken suit/suits/costumes/chickens/super-chickens' | |
'giant chickens' | |
"They're really people wearing chicken suits, but the costumes are | |
pretty good. " | |
isPlural = true | |
/* defer in disambiguation to the chicken suit I'm wearing */ | |
disambigDeferTo = [chickenSuit] | |
; | |
/* | |
* A class for actors who are being chickens. As participants in the | |
* stack, they can't interact with us unless we're wearing the chicken | |
* suit. | |
*/ | |
class ChickenActorState: ActorState | |
handleConversation(otherActor, topic, convType) | |
{ | |
/* | |
* if the other actor is wearing the chicken suit, proceed as | |
* normal; otherwise, we can't talk to them | |
*/ | |
if (chickenSuit.isWornBy(otherActor)) | |
inherited(otherActor, topic, convType); | |
else | |
"<q>Cluck! Cluck! Cluck!</q> The chickens flap their wings | |
in agitation and point at the notebook on the door. "; | |
} | |
; | |
++ ChickenActorState | |
isInitState = true | |
specialDesc = "Several giant chickens are standing around | |
the hall, talking quietly among themselves. " | |
; | |
+++ AskTellShowTopic, StopEventList | |
[stackTopic, chickenSuit, chickenator, chickenNotebook, superChickens] | |
['<q>So how does this stack work?</q> you ask through the suit. | |
<.p><q>We are the super-chicken army!</q> one of the chickens | |
says. <q>We are on a mission from Dr.\ von Geflügel!</q> ', | |
'<q>How many super-chickens are there?</q> you ask. | |
<.p><q>There were about eight poultriform matrixizers when we | |
started,</q> one of the chickens says. ', | |
'<q>How\'s the stack coming?</q> you ask. | |
<.p>The chickens confer. <q>The super-chicken army is making | |
excellent progress!</q> one of them says. '] | |
; | |
+++ AskTellTopic [scottTopic, posGame] | |
topicResponse() | |
{ | |
"<q>Has anyone seen Scott around?</q> you ask, your voice | |
muffled through the suit. | |
<.p>One of the chickens, a colorful red one, waves. <q>I'm | |
Scott,</q> he says. "; | |
/* bring scott into play, and drop his Unthing */ | |
scott.makePresent(); | |
unScott.moveInto(nil); | |
} | |
; | |
+++ AskForTopic @scottTopic | |
topicResponse() { replaceAction(AskAbout, gDobj, scottTopic); } | |
; | |
++++ AltTopic | |
"<q>Have you guys seen Scott around?</q> Your voice is muffled | |
through the chicken suit. | |
<.p>The chickens look around, tilting their heads to see out | |
through the suits. <q>I think he's out on one of the missions,</q> | |
one of them says. <q>He'll be back later.</q> " | |
/* we gate scott's appearance on starting with stamer's stack */ | |
isActive = (labKey.isIn(erin)) | |
; | |
++++ AltTopic | |
"<q>Is Scott here?</q> you ask. | |
<.p>One of the chickens, a colorful red one, waves. <q>Right | |
here,</q> he says. " | |
/* this one fires when scott is already here */ | |
isActive = (scott.isIn(alley3E)) | |
; | |
+++ DefaultAnyTopic, ShuffledEventList | |
['A chicken arrives just as you start to talk, sending the | |
flock into a minor commotion. ', | |
'The chickens just cluck. ', | |
'The chickens just flap their wings. ', | |
'<q>The super-chicken army is very busy!</q> one of the | |
chickens says. '] | |
; | |
/* an unthing for scott, until he's here */ | |
+ unScott: Unthing 'scott' 'Scott' | |
isProperName = true | |
isHim = true | |
notHereMsg = 'One of the chickens might be Scott, but you don\'t | |
know which one. ' | |
; | |
/* the owner of the positron game */ | |
+ scott: DisambigDeferrer, PresentLater, Person | |
'colorful red giant chicken suit/scott' 'Scott' | |
"He's wearing an especially colorful chicken suit, mostly red. " | |
/* | |
* he's just one of the crowd of chickens, so don't mention him | |
* separately | |
*/ | |
specialDesc = "" | |
isProperName = true | |
isHim = true | |
noResponseFromMsg(actor) { return 'Scott just clucks and flaps | |
his wings. '; } | |
/* | |
* defer in disambiguation to the chicken suit I'm wearing, and to | |
* the other giant chicken people | |
*/ | |
disambigDeferTo = [chickenSuit, superChickens] | |
; | |
++ ChickenActorState | |
isInitState = true | |
; | |
++ AskTellGiveShowTopic [posGame, lostQuarterTopic, oooSign] | |
"<q>Are you the owner of the Positron machine?</q> you ask. | |
<.p><q>Yeah,</q> he says. | |
<.convnode repair-positron> " | |
; | |
+++ AltTopic | |
"<q>Do you have any more details about what's wrong with | |
Positron?</q> you ask. | |
<.p><q>Not really,</q> he says. <q>It's just completely dead.</q> " | |
/* this topic is active after we've obtained the key */ | |
isActive = (posKey.moved) | |
; | |
+++ AltTopic, StopEventList | |
['<q>I just wanted to let you know I managed to repair Positron,</q> | |
you say. | |
<.p><q>That\'s great!</q> Scott says. <q>Thanks!</q> ', | |
'<q>Did I mention I fixed Positron?</q> you ask. | |
<.p><q>Yeah, thanks again,</q> Scott says. '] | |
isActive = gRevealed('positron-repaired') | |
; | |
++ DefaultAnyTopic, ShuffledEventList | |
['<q>I\'m kind of busy with the stack,</q> Scott says. ', | |
'Scott just clucks. ', | |
'He seems busy with the stack. '] | |
; | |
++ ConvNode 'repair-positron'; | |
+++ SpecialTopic 'ask what\'s wrong with it' | |
['ask','scott','him','what','is','what\'s','wrong','with','it', | |
'the','positron','machine'] | |
"<q>Do you know what\'s wrong with it?</q> you ask. | |
<.p>He scratches his head with his wing. <q>Not really. I've been | |
trying to get someone to take a look at it.</q> | |
<.convstay><.topics> " | |
/* only offer this question at this node one time */ | |
isActive = (talkCount == 0) | |
; | |
+++ SpecialTopic 'offer to repair it' | |
['offer','to','repair','it','the','positron','machine'] | |
topicResponse() | |
{ | |
"<q>I know a bit about electronics,</q> you say. <q>I'd be | |
happy to try fixing it.</q> | |
<.p><q>Have you repaired anything like this before?</q> | |
<.convnode ask-experience> "; | |
} | |
; | |
++ ConvNode 'ask-experience' | |
commonReply = "<.p><q>So why are you so interested in fixing an | |
old video game?</q> he asks, skepticism in his voice. <q>I | |
usually have to harangue my double-E friends for weeks to | |
get anyone to help.</q> | |
<.convnode why-repair> " | |
; | |
+++ YesTopic | |
"<q>Sure,</q> you say. <q>It's been a while, but I've done lots | |
of work on computers and video displays before.</q> | |
<<location.commonReply>> " | |
; | |
+++ NoTopic | |
"<q>Not anything exactly like it,</q> you admit. <q>But I've | |
done lots of work on computers and video displays, which are | |
basically the same thing.</q> | |
<<location.commonReply>> " | |
; | |
++ ConvNode 'why-repair' | |
commonReply = "<q>I'm working on Brian Stamer's stack,</q> you say. | |
<q>It's some kind of electronics debugging puzzle, and I really | |
need some practice on something simpler before I tackle it.</q> | |
<.p><q>Practice?</q> He sounds a little annoyed. <q>You want | |
to practice on my video game? I mean, I appreciate the offer | |
and all, but how do I know you're not going to make it worse?</q> | |
<.convnode why-not-worse> " | |
; | |
+++ SpecialTopic 'explain about Stamer\'s stack' | |
['explain','about','brian','stamer\'s','the','stack'] | |
"<<location.commonReply>> " | |
; | |
+++ SpecialTopic 'say you need the practice' | |
['say','i','you','need','the','some','practice'] | |
"<<location.commonReply>> " | |
; | |
++ ConvNode 'why-not-worse' | |
commonReply() | |
{ | |
"<.p>He pauses for a long time. <q>Okay,</q> he says, | |
<q>sounds fair.</q> He searches around in his chicken suit | |
for a while, then fishes out a key and hands it to you. | |
<q>Here's the key to the machine.</q> | |
<.p>You take the key, which is difficult with the suit on, "; | |
if (myKeyring.isIn(me)) | |
{ | |
"and fumble through the suit to find your own keyring, | |
which is even more difficult. After a few tries you | |
manage to put the key on your keyring and put the | |
keyring in your pocket. "; | |
posKey.moveInto(myKeyring); | |
myKeyring.moveInto(myPocket); | |
} | |
else | |
{ | |
"and fumble through the suit to find your pocket, | |
which is even more difficult. You finally manage to | |
pocket the key. "; | |
posKey.moveInto(myPocket); | |
} | |
/* aware some points */ | |
scoreMarker.awardPointsOnce(); | |
"<.p><q>Thanks!</q> you say. <q>I'll let you know if | |
I can fix it.</q> "; | |
} | |
scoreMarker: Achievement { +5 "getting the Positron key" } | |
; | |
+++ SpecialTopic 'promise not to break it' | |
['promise','not','to','break','it','the','positron','machine'] | |
"<q>I promise I won't make it any worse,</q> you say. <q>I'll just | |
take a look to see if I can figure out what's wrong, and if I'm | |
not sure I can fix it, I won't try.</q><<location.commonReply>> " | |
; | |
+++ SpecialTopic 'say you\'ll pay for repairs if you break it' | |
['say','you','will','you\'ll','i','will','i\'ll','offer','to', | |
'pay','for','repairs', | |
'if','you','i','break','it','the','positron','machine'] | |
"<q>If I make it worse, I'll pay to get it repaired,</q> you offer. | |
<<location.commonReply>> " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Inside the chickenator. We could have made this a nested room, but | |
* there's no real relationship to the enclosing alley 3 area other than | |
* the in/out connection, so for simplicity it's just a separate room. | |
*/ | |
insideChickenator: Room 'Chickenator' 'the Chickenator' 'Chickenator' | |
"The box is as decorated inside as it is outside. An instruction | |
sign is posted above a huge knife switch, and colored lights all | |
along the walls and ceiling twinkle. Several coat hooks are arranged | |
along one wall, near the ceiling. A narrow opening in the side of | |
the box leads back out. " | |
vocabName_ = 'cardboard chickenator/box' | |
out = alley3E | |
/* | |
* the box is described in the alley description as being on the | |
* south side of the hall, so map 'north' to 'out' even though we | |
* don't mention it as an exit; likewise, some people might | |
* visualize the exit as being west, since we're at the east end of | |
* the hall, or east, as the hall continues a bit further east, so | |
* allow these as equivalents for 'out' as well | |
*/ | |
north asExit(out) | |
east asExit(out) | |
west asExit(out) | |
; | |
+ Decoration 'christmas-tree colored light/lights' 'colored lights' | |
"They're probably Christmas-tree lights. " | |
; | |
+ ExitPortal | |
'narrow 3 alley opening/exit/three/alleyway/hall/hallway' 'opening' | |
"The opening leads back outside to the alleyway. " | |
; | |
+ Fixture, Surface 'coat hook/hooks' 'coat hooks' | |
"Several coat hooks are arranged along the wall near the ceiling. " | |
isPlural = true | |
/* only allow the chicken suit to hang here */ | |
iobjFor(PutOn) | |
{ | |
verify() | |
{ | |
if (gDobj != nil && gDobj != chickenSuit) | |
illogical('The hooks are only for chicken suits. '); | |
} | |
} | |
/* HANG obj ON HOOK -> PUT obj ON HOOK */ | |
iobjFor(HangOn) remapTo(PutOn, DirectObject, self) | |
/* customize some default messages */ | |
alreadyPutOnMsg = '{The dobj/he} {is} already on a hook. ' | |
/* | |
* customize the way we describe our contents, so that we describe | |
* things as hanging on the hooks rather than being in the hooks | |
*/ | |
contentsLister: HookContentsLister, surfaceContentsLister { } | |
descContentsLister: HookContentsLister, surfaceDescContentsLister { } | |
lookInLister: HookContentsLister, surfaceLookInLister { | |
showListEmpty(pov, parent) | |
{ "Nothing is currently hanging from the hooks. "; } | |
} | |
; | |
/* | |
* Contents lister mix-in for our hooks. This is a mix-in that can be | |
* combined with the normal surface contents lister subtypes to create | |
* the various specialized kinds of listers we need. | |
*/ | |
class HookContentsLister: object | |
showListPrefixWide(itemCount, pov, parent) | |
{ "\^"; } | |
showListSuffixWide(itemCount, pov, parent) | |
{ | |
if (itemCount == 1) | |
" is hanging from one of the hooks. "; | |
else | |
" are hanging from hooks. "; | |
} | |
; | |
++ chickenSuit: Wearable | |
'white chicken poultriform suit/feathers/matrixizer' 'chicken suit' | |
"It's a full-body chicken suit, big enough for you to put on. | |
The feathers are mostly white. " | |
bulk = 4 | |
beforeAction() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* don't allow picking anything up while in the chicken suit */ | |
if (isWornBy(gActor)) | |
{ | |
if (gActionIs(Take) || gActionIs(TakeFrom)) | |
{ | |
"It's too hard to grasp anything with the chicken | |
suit on. "; | |
exit; | |
} | |
} | |
} | |
beforeTravel(traveler, conn) | |
{ | |
if (traveler.isActorTraveling(me) && isWornBy(me)) | |
{ | |
if (conn.getDestination(traveler.location, traveler) | |
not in (alley3main, alley3E, alley3W, insideChickenator)) | |
{ | |
"It's too hard to see where you're going in the | |
chicken suit. If you were to leave the alley, you | |
might not be able to find your way back. "; | |
exit; | |
} | |
else | |
"It's really hard to see where you're going with the | |
chicken suit on; you have to go slowly and feel your | |
way along the wall.<.p>"; | |
} | |
} | |
dobjFor(Wear) | |
{ | |
preCond = (inherited() + handsEmptyForSuit) | |
check() | |
{ | |
if (!isIn(insideChickenator)) | |
{ | |
"You really should play along with the stack and | |
use the Chickenator to make the transformation. "; | |
exit; | |
} | |
} | |
} | |
okayWearMsg = 'It takes a bit of doing, especially in this | |
cramped space, but you manage to wrestle your way into | |
the suit. It covers you head to toe, and it\'s not very | |
easy to see out of it. ' | |
/* a customized hands-empty condition for wearing the suit */ | |
handsEmptyForSuit: handsEmpty { | |
failureMsg = 'The suit is so big and bulky that you\'ll have to | |
put everything down before you can put it on. ' | |
/* | |
* Require dropping everything except the chicken suit (we | |
* obviously wouldn't want to require dropping the suit itself, | |
* since we need to be holding it to put it on). Include even | |
* worn items that aren't AlwaysWorn's. | |
*/ | |
requireDropping(obj) | |
{ | |
/* | |
* We have to drop everything that we're either holding or | |
* wearing, except for the chicken suit itself and any | |
* AlwaysWorn items. | |
*/ | |
return (obj != chickenSuit | |
&& (obj.isHeldBy(gActor) | |
|| (obj.ofKind(Wearable) | |
&& !obj.ofKind(AlwaysWorn) | |
&& obj.isWornBy(gActor)))); | |
} | |
/* | |
* don't try auto-bagging anything; since we need to drop | |
* everything, including the tote bag, there's no point in trying | |
* to free up space that way | |
*/ | |
autoBag = nil | |
} | |
dobjFor(Doff) | |
{ | |
check() | |
{ | |
if (!isIn(insideChickenator)) | |
{ | |
"You really should play along with the stack and use | |
the Chickenator to transform back into a human. "; | |
exit; | |
} | |
} | |
} | |
okayDoffMsg = 'You clumsily extract yourself from the suit. It\'s | |
a relief to get out of it. ' | |
/* this is a bit too large to stuff in the tote */ | |
dobjFor(PutIn) | |
{ | |
check() | |
{ | |
if (gIobj == toteBag.subContainer) | |
{ | |
"The tote is capacious, but the chicken suit is just | |
too large. "; | |
exit; | |
} | |
} | |
} | |
; | |
+ Fixture, Readable 'instruction sign/instructions' 'instruction sign' | |
"<font face=tads-sans><b>Chickenator Mark III Operating Instructions</b> | |
<br><br><b>Chickenation:</b> Select poultriform matrixizer from shelf. | |
Wear. Pull switch lever. Chickenation process is automatic. | |
<br><br><b>De-Chickenation:</b> Pull switch lever. Dechickenation | |
process is automatic. When done, subject will be coated with vestige | |
of poultriform matrixizer. Remove matrixizer and return to | |
hook.</font> " | |
; | |
+ SpringLever, Fixture | |
'huge electrical knife switch hinged bare metal | |
(pair)/lever/handle/blade/blades' | |
'knife switch' | |
"It's the standard Mad Scientist type of electrical switch: a hinged | |
pair of bare metal blades with a foot-long handle. It's in the down | |
position. " | |
dobjFor(Pull) | |
{ | |
action() | |
{ | |
"You grasp the handle and pull the blades up. The sound | |
of electric sparks comes from a hidden speaker, and the | |
lights in the booth dim. As soon as you release the handle, | |
it springs back down and the special effects end. "; | |
if (!seenBefore) | |
{ | |
"<.p>Not surprisingly, it appears that you haven't | |
actually been transformed into a chicken. "; | |
seenBefore = true; | |
} | |
} | |
} | |
seenBefore = nil | |
dobjFor(Push) asDobjFor(Pull) | |
dobjFor(Raise) asDobjFor(Pull) | |
dobjFor(Lower) { verify() { illogicalAlready('It\'s already in the | |
down position. '); } } | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 3 west | |
*/ | |
alley3W: AlleyRoom 'Alley Three West' 'the west end of Alley Three' | |
"This is the west end of Alley Three; the hallway continues | |
to the east. The door to room 14 is on the south side, 15 | |
at the west end, and 16 on the north side of the hall. " | |
vocabWords = '3 alley three' | |
east = alley3main | |
north = room16door | |
west = room15door | |
south = room14door | |
; | |
+ room14door: AlleyDoor '14 -' 'door to room 14' | |
"The door is labeled <q>14.</q> " | |
; | |
+ room15door: AlleyDoor '15 -' 'door to room 15' | |
"The wooden door is labeled <q>15.</q> " | |
; | |
+ room16door: AlleyDoor '16 -' 'door to room 16' | |
"The door is labeled <q>16.</q> " | |
; | |
+ Graffiti 'odd comments' 'graffiti' | |
"There's not much here; just a couple of odd comments | |
scribbled on the wall in small letters: <q>He is a fun God! He | |
is the Sun God! Ra! Ra! Ra!</q> And: <q>Subtlety is a crutch.</q> " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 4 center | |
*/ | |
alley4main: AlleyRoom 'Alley Four' 'Alley Four' | |
"This is the middle of Alley Four, where the hallway widens into | |
a good-sized lounge area. The two wings of the alley lead off | |
to the east and west, respectively, and to the north, a stairway | |
leads down. A large round table and some chairs are set up here. " | |
vocabWords = '4 alley four' | |
down = a4StairsDown | |
north asExit(down) | |
west = alley4W | |
east = alley4E | |
roomParts = static (inherited + [alleyNorthWall, alleySouthWall, | |
alleyEastWall, alleyWestWall]) | |
; | |
+ Graffiti 'twisty leafy gecko/geckos/mural/vine/vines' 'graffiti' | |
"A mural of twisty, leafy vines covers one wall. Among the | |
vines are a number of well-hidden geckos. " | |
; | |
+ a4StairsDown: StairwayDown -> a3StairsUp | |
'stairs/stairs/stairway' 'stairway' | |
"The stairs lead down to Alley Three. " | |
; | |
+ EntryPortal ->(location.west) | |
'west w (4) (four) narrow hall/hallway/wing/(alley)' 'west wing' | |
"The narrow hallway leads west. " | |
; | |
+ EntryPortal ->(location.east) | |
'east e (4) (four) narrow hall/hallway/wing/(alley)' 'east wing' | |
"The narrow hallway leads east. " | |
; | |
+ a4mChair: CustomImmovable, MultiChair | |
'occupied empty simple oak kitchen chair/chairs' 'oak chair' | |
"There are several of the simple oak kitchen chairs. Most of | |
them are occupied; a few are empty. " | |
cannotTakeMsg = 'You have no need to carry around any of the chairs. ' | |
/* because we represent many objects, customize the status messages */ | |
actorInName = 'on one of the oak chairs' | |
; | |
+ a4Table: Heavy, Surface | |
'large kitchen round oak wood wooden table' 'round table' | |
"It's the kind of table you'd find in a kitchen. It's round | |
and looks to be made of oak. " | |
iobjFor(PutOn) | |
{ | |
action() { "You're afraid you'd mess up the careful organization | |
of papers on the table if you added anything. "; } | |
} | |
; | |
/* a class for the immovable items on the table, with a custom message */ | |
class A4TableImmovable: CustomImmovable | |
cannotTakeMsg = 'Everything\'s carefully laid out; you don\'t want | |
to disturb it. ' | |
; | |
/* a class for the Turbo Power Animals action figures */ | |
class TurboPowerAnimal: Thing | |
/* don't list these when on the table */ | |
isListed = (!isIn(a4Table)) | |
moveInto(obj) | |
{ | |
/* don't allow these to be taken when on the alley 4 table */ | |
if (isIn(a4Table)) | |
{ | |
"The action figure is important to the stack; you don't | |
want to interfere. "; | |
exit; | |
} | |
/* otherwise, do the normal stuff */ | |
inherited(obj); | |
} | |
; | |
++ a4Materials: A4TableImmovable | |
'pile/papers/paper/materials' 'pile of papers' | |
"The table is piled with papers. A large blueprint map of | |
the campus is in the center of the table, and all around it | |
are envelopes and index cards. The envelopes and cards seem | |
carefully arranged. " | |
/* | |
* use a specialDesc in our container's description only (not in the | |
* room description, since we mention the materials as part of the | |
* students' specialDesc instead) | |
*/ | |
specialDesc = "A pile of papers is spread out on the table. " | |
useSpecialDescInRoom(room) { return nil; } | |
; | |
++ a4Envelopes: A4TableImmovable, Readable | |
'index envelope/envelopes/card/cards/handwriting' | |
'envelopes and cards' | |
"Some of the envelopes have been opened, others are sealed. | |
Most of the index cards are covered with handwriting. " | |
isPlural = true | |
readDesc = "You scan a few of the index cards, but none of | |
them are very meaningful to you. " | |
dobjFor(Open) | |
{ | |
verify() { } | |
action() { "You don't want to disturb anything. "; } | |
} | |
dobjFor(Close) asDobjFor(Open) | |
dobjFor(LookIn) asDobjFor(Open) | |
dobjFor(Search) asDobjFor(Open) | |
; | |
++ A4TableImmovable | |
'large campus blueprint | |
map/outline/outlines/(building)/(buildings)/(campus)' | |
'blueprint map' | |
"The map shows the outlines of the campus's buildings, printed | |
in light blue ink on a poster-sized piece of paper. It looks | |
almost like a battle-planning map: it's marked up with numerous | |
lines, squiggles, circles, and other annotations, and little | |
tokens are scattered around the map. " | |
dobjFor(ConsultAbout) | |
{ | |
verify() { logicalRank(50, 'decoration only'); } | |
action() { "It's too crowded in here, and the table is too | |
cluttered, to get a good look at the map. "; } | |
} | |
; | |
+++ a4Map: Fixture | |
'map other mark/marks/marking/markings/line/lines/arrow/ | |
arrows/squiggle/squiggles/circle/circles/annotation/annotations' | |
'map annotations' | |
"It's not obvious what the markings mean. Some of the buildings | |
are circled, others are crossed out; in other places, lines or | |
arrows are drawn between buildings. " | |
isPlural = true | |
; | |
+++ A4TableImmovable | |
'map little plastic token/tokens/pawn/pawns' 'map tokens' | |
"The tokens are mostly little plastic pawns, probably from a board | |
game. They're placed around the map to mark certain locations, | |
although it's not obvious what they indicate. " | |
isPlural = true | |
; | |
+ Person 'student/students' 'students' | |
"Some are sitting at the table, others are standing near it. | |
They're all working on the materials spread out on the table. " | |
specialDesc = "About a half dozen students<< | |
jay.isIn(alley4main) ? ", including Jay, " : "" | |
>> are sitting or standing around the table, working on | |
a pile of papers spread out over the surface. " | |
isPlural = true | |
; | |
++ AskTellShowTopic | |
[stackTopic, a4Materials, a4Envelopes, a4Map, turboTopic] | |
"<q>Which stack are you guys working on?</q> you ask. | |
<.p>One of them points down the hall to the west. <q>The | |
Turbo Power Animals stack. You should read the sign down | |
the hall if you want to help out.</q> " | |
; | |
+++ AltTopic, StopEventList | |
['<q>Are you guys working on the Turbo Power Animals stack?</q> | |
you ask. | |
<.p>One of them turns around and makes an elaborate series of | |
jerky arm gestures, first crossing his arms, then holding one arm | |
straight up and the other one sideways, then making a pulling-down | |
motion with both fists, and on and on. <q>The turbo power Caltech | |
adjunct is ready!</q> he says. The others smile and shake | |
their heads at the display. He finally finishes the salute and | |
returns to the table. ', | |
'<q>How does this stack work?</q> you ask. | |
<.p>One of the students looks up from the papers. <q>We have | |
these clues,</q> he says, indicating the envelopes, <q>hinting | |
where the five Power Animals are hidden. The clues are all | |
pretty obscure, so we\'re trying to piece things together.</q> ', | |
'<q>How\'s the stack coming?</q> | |
<.p>A couple of the students just look up at you and shrug. '] | |
isActive = gRevealed('tpa-stack') | |
; | |
++ AskTellTopic @jayTopic | |
topicResponse() | |
{ | |
"<q>Excuse me,</q> you say, <q>but I'm looking for Jay | |
Santoshnimoorthy.</q> | |
<.p>Everyone looks at one of the students, who looks up | |
at you. <q>I'm Jay,</q> he says. | |
<.p>With the aloha shirt, his build, and his long hair, | |
Jay looks like a surfer dude. "; | |
/* bring Jay into the game, and drop his Unthing */ | |
jay.makePresent(); | |
unJay.moveInto(nil); | |
/* transition jay directly to conversation */ | |
jay.autoEnterConv(); | |
} | |
; | |
+++ AltTopic | |
"<q>Has anyone seen Jay Santoshnimoorthy around?</q> you ask. | |
<.p>One of the students looks up. <q>He went over to Bridge, | |
I think, following one of the clues,</q> she says. <q>He should | |
be back in a little while.</q> " | |
/* jay won't show up until we at least start on stamer's stack */ | |
isActive = (labKey.isIn(erin)) | |
; | |
+++ AltTopic | |
"<q>Is Jay still here?</q> you ask. | |
<.p>Jay leans back from the table. <q>Right here,</q> he says. | |
<<jay.autoEnterConv>> " | |
/* use this one when jay is already here */ | |
isActive = (jay.isIn(alley4main)) | |
; | |
+++ AltTopic | |
"<q>Is Jay still here?</q> you ask. | |
<.p>One of the students points to him. He's still sitting there | |
reading the journal you gave him. " | |
isActive = (jay.curState == jayReading) | |
; | |
++ AskTellTopic [scottTopic, posGame] | |
"<q>Has anyone seen Scott around?</q> you ask. | |
<.p>One of the students answers without looking up from | |
the table. <q>I saw him down in Alley Three,</q> she says. " | |
; | |
++ AskForTopic @scottTopic | |
topicResponse() { replaceAction(AskAbout, gDobj, scottTopic); } | |
; | |
++ DefaultAnyTopic, ShuffledEventList | |
['You try to get someone\'s attention, but they\'re all too | |
involved in other conversations. ', | |
'Everyone seems too focused on the stack to respond. ', | |
'They\'re probably mostly interested in the stack right now. '] | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 4 west | |
*/ | |
alley4W: AlleyRoom 'Alley Four West' 'the west end of Alley Four' | |
"The narrow hallway leads off to the east and ends here. | |
The door to room 20 is to the south, 21 to the north, and | |
22 to the west. " | |
vocabWords = '4 alley four' | |
south = room20door | |
north = room21door | |
west = room22door | |
east = alley4main | |
roomParts = static (inherited + [alleyNorthWall, alleySouthWall, | |
alleyWestWall]) | |
; | |
+ room20door: AlleyDoor '20 -' 'door to room 20' | |
"The door is labeled <q>20.</q> " | |
; | |
+ room21door: AlleyDoor '21 -' 'door to room 21' | |
"The number 21 is painted on the door. " | |
; | |
+ room22door: StackDoor '22 -' 'door to room 22' | |
"The wooden door is numbered <q>22.</q> " | |
; | |
++ Fixture | |
'colorful illustrated turbo power | |
sign/drawing/drawings/animal/animals/illustration/illustrations' | |
'illustrated sign' | |
"The large sign is illustrated with colorful drawings of | |
characters from the Turbo Power Animals, the popular Japanese | |
cartoon/video game series/action figure collection/media empire. | |
Large, bold, action-packed lettering flows around the | |
illustrations: | |
<.p> | |
<.blockquote> | |
<font face='tads-sans'><b><i> | |
<center>Invoking Turbo Power Animals!</center> | |
<.p>Dispatch Alert! The nefarious GENERAL XI has kidnapped | |
the five Power Friends in a plot to cause environmental | |
catastrophe! | |
<.p>Knowing only the Power Animals can stop his | |
evil plan to pollute the PASADENA SEA with the deadly | |
INDUSTRIA VIRUS, the nefarious General has lured the | |
Turbo Friends to CALTECH under the guise of a Do Gooders | |
Convention. Unwary of the danger, the Power Friends went to | |
their good-doing seminars, only to find the wicked General's | |
dim-witted but brawny FROGFISH ARMY waiting. The Turbo Animals | |
fought bravely using the latest fighting moves, such as | |
two-arm cross-over flip and right-elbow joint snap, but | |
the Frogfish were too many in number, and prevented the | |
Power Friends from forming TURBO POWER COMBINE GIANT ANIMAL! | |
<.p>The Turbo Power Animals need YOUR help! With the help of | |
your friends, you must form the CALTECH TURBO ADJUNCT. You | |
must find where General Xi is holding each of the five Turbo | |
Power Animals, and rescue each one. Only when all five are | |
rescued can they form TURBO POWER COMBINE GIANT ANIMAL, and, | |
with your help, confront General Xi in battle to defeat his | |
evil plot! | |
<.p>But it won't be easy! General Xi is very crafty, and he | |
has left only the clues in the envelopes below. Furthermore, | |
he has protected many of the clues with time locks, so they | |
can be opened only at the designated hour. | |
</i></b></font> | |
<./blockquote> | |
The sign goes on with some additional rules for the stack; | |
it looks like a scavenger-hunt type of stack. <<extraComment>> | |
<.reveal tpa-stack> " | |
specialDesc = "A colorful, illustrated sign is posted on the | |
door to room 22. " | |
extraComment() | |
{ | |
if (commentCount++ == 0) | |
"You don't see the envelopes the sign mentions; | |
presumably that's what's on the table down the hall. "; | |
} | |
commentCount = 0 | |
; | |
+ Graffiti | |
'lovecraftian dripping horror/monster/tentacles/eyes/eye/teeth/ectoplasm' | |
'graffiti' | |
"A drawing of a rather Lovecraftian monster dominates one wall: | |
tentacles, eyes by the dozen, teeth dripping with ectoplasm. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 4 east | |
*/ | |
alley4E: AlleyRoom 'Alley Four East' 'the east end of Alley Four' | |
"This is a narrow section of hallway, continuing west and | |
ending here to the east. A door numbered 18 is to the south, | |
and the door to room 19 is to the north. To the northeast, a | |
door that looks like an ordinary room door leads into the | |
house library. " | |
vocabWords = '4 alley four' | |
north = room19door | |
south = room18door | |
northeast = a4LibDoor | |
west = alley4main | |
roomParts = static (inherited + [alleyNorthWall, alleySouthWall, | |
alleyEastWall]) | |
; | |
+ room18door: AlleyDoor '18 -' 'door to room 18' | |
"The door is labeled <q>18.</q> " | |
; | |
+ room19door: AlleyDoor '19 -' 'door to room 19' | |
"The number 19 is painted on the door. " | |
; | |
+ Graffiti 'long involved math tiny small story/letters' 'graffiti' | |
"A long, involved story is written on the wall in tiny letters. | |
It's some kind of extended math joke. " | |
; | |
+ a4LibDoor: Door 'library door' 'library door' | |
"It looks like the door to any student room in the house, but | |
<q>Library</q> is painted on the door in place of a room number. " | |
isOpen = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The Dabney library | |
*/ | |
dabneyLib: Room 'Library' 'the house library' 'house library' | |
"This room was probably once a regular double, but it was converted | |
a long time ago into the house library. It's a bit of a mess; | |
books are piled randomly not only on the shelves lining the walls, | |
but also on the floor, the large table, and the pair of couches. | |
<.p>A large casement window to the north overlooks the | |
courtyard. The room has two doors leading out, one to the | |
southeast and one to the southwest. " | |
vocabWords = 'dabney house library' | |
southeast = libA6Door | |
southwest = libA4Door | |
/* "out" is ambiguous here */ | |
out: AskConnector { | |
/* don't show this as an obvious direction; just show se/sw */ | |
isConnectorApparent(origin, actor) { return nil; } | |
/* "out" could mean going through either door */ | |
travelAction = GoThroughAction | |
travelObjs = [libA6Door, libA4Door] | |
/* customize the parser prompts when we go this way */ | |
askDisambig(targetActor, promptTxt, curMatchList, fullMatchList, | |
requiredNum, askingAgain, dist) | |
{ "Which way do you want to go, southeast or southwest? "; } | |
askMissingObject(targetActor, action, which) | |
{ "Which way do you want to go, southeast or southwest? "; } | |
} | |
; | |
+ Graffiti 'graffiti/drawing/drawings/quote/quotes/remark' 'graffiti' | |
"There are lots of little drawings and quotes on the walls. | |
Your favorite is a little remark near the northeast | |
door: <q>The Library: Where <b>ANYTHING</b> can happen. | |
<font size=-1>(But probably won't.)</font></q> " | |
; | |
+ libA4Door: Door ->a4LibDoor | |
'alley four 4 sw southwest door*doors' 'southwest door' | |
"The door leads out to the southwest. " | |
; | |
+ libA6Door: Door ->a6LibDoor | |
'alley six 6 se southeast door*doors' 'southeast door' | |
"The door leads out to the southeast. " | |
; | |
+ libBookPiles: CustomImmovable | |
'library worn well-worn old science fiction sf sci-fi paperback | |
comic instruction paperbacks/book/pile/piles/textbook/manual/ | |
book/books*books*textbooks*manuals' | |
'piles of books' | |
"Most of the books seem to be well-worn science fiction paperbacks, but | |
there are also old textbooks, comic books, phone books, instruction | |
manuals, and who knows what else mixed in. They're stacked up | |
everywhere, practically in knee-deep piles in some places. " | |
isPlural = true | |
cannotTakeMsg = 'You can\'t have them all, obviously, and it would | |
take too long to sift through them for something interesting. ' | |
lookInDesc = "You poke through the books a bit, but you find | |
nothing particularly interesting. " | |
dobjFor(Search) asDobjFor(LookIn) | |
dobjFor(LookUnder) asDobjFor(LookIn) | |
dobjFor(LookThrough) asDobjFor(LookIn) | |
dobjFor(LookBehind) asDobjFor(LookIn) | |
dobjFor(ConsultAbout) | |
{ | |
verify() { } | |
action() { "The books are just piled randomly, so there's | |
no chance of finding anything specific. "; } | |
} | |
/* we're nominally on anything that's a LibUnderBooks */ | |
isNominallyIn(obj) { return inherited(obj) || obj.ofKind(LibUnderBooks); } | |
; | |
/* a class for one of our items buried under books */ | |
class LibUnderBooks: object | |
dobjFor(Search) remapTo(Search, libBookPiles) | |
lookInDesc = "{The dobj/he} {is} buried under piles of books. " | |
iobjFor(PutOn) | |
{ | |
verify() { } | |
action() { "There are too many books piled on {the iobj/him}; | |
anything you add would probably get lost in the piles. "; } | |
} | |
; | |
+ LibUnderBooks, Fixture 'built-in shelves/shelf' 'shelves' | |
"The built-in shelves line the walls. Books are piled on the | |
shelves horizontally, vertically, diagonally, and every way in | |
between. " | |
isPlural = true | |
; | |
+ LibUnderBooks, Fixture, Chair 'couch/couches/sofa/sofas' 'couches' | |
"Even the couches are buried under piles of books, leaving | |
no place you can easily sit down. " | |
isPlural = true | |
dobjFor(SitOn) | |
{ | |
check() | |
{ | |
"There are too many books; there's no space left over | |
to sit. "; | |
exit; | |
} | |
} | |
dobjFor(LieOn) asDobjFor(SitOn) | |
dobjFor(StandOn) asDobjFor(SitOn) | |
; | |
+ LibUnderBooks, Fixture 'large table' 'large table' | |
"The table isn't itself very visible; all you can see is the | |
books piled on top. " | |
; | |
+ Openable, Fixture 'large casement window' 'casement window' | |
"The window looks out onto the courtyard and the model mountain. " | |
dobjFor(LookThrough) remapTo(Examine, libCourtyard) | |
initiallyOpen = nil | |
dobjFor(Open) | |
{ | |
check() | |
{ | |
"The window seems to be stuck. It was probably painted | |
shut in the last remodel. "; | |
exit; | |
} | |
} | |
; | |
+ libCourtyard: Distant | |
'dabney house hovse paper-mache papier-mache | |
papier-m\u00E2ch\u00E9 courtyard/mountain/peak' 'courtyard' | |
"The window overlooks the courtyard from one story up. The | |
model mountain in the courtyard rises up a little higher than | |
this level. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 5 main | |
*/ | |
alley5main: AlleyRoom 'Alley Five Entry' 'the Alley Five entry' | |
"This is an oddly-angled intersection of hallways at the | |
entrance of Alley Five. To the south is a stairway leading | |
up, and across from the stairway is the exit to the courtyard | |
to the north. One hallway leads west, and another turns a | |
corner and leads southeast. " | |
vocabWords = '5 alley five entry/entrance/hallway/hall' | |
north = dabneyCourtyard | |
out asExit(north) | |
northwest asExit(north) | |
up = a5StairsUp | |
south asExit(up) | |
west = alley5M | |
southeast = alley5N | |
roomParts = static (inherited | |
+ [alleyNorthWall, alleySouthWall, alleyEastWall]) | |
; | |
+ ExitPortal ->(location.out) 'courtyard/exit' 'courtyard' | |
"The courtyard lies to the north. " | |
; | |
+ Graffiti 'math mathematical formula' 'graffiti' | |
"It's a math formula: e to the pi i equals negative one. | |
That relationship among those three special numbers is always | |
mind-blowing when you first learn it; evidently someone was | |
amazed enough to write it on the wall. " | |
; | |
+ a5StairsUp: StairwayUp ->a6StairsDown | |
'stairs/stair/stairway' 'stairway' | |
"The stairs lead up to Alley Six. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 5 north | |
*/ | |
alley5N: AlleyRoom 'Alley Five North' 'the north end of Alley Five' | |
"This is the north end of a north/south section of Alley Five. | |
The door to room 25 is on the east side of the hall, and across | |
from it on the west side is the door to room 26. The hallway | |
continues south, and turns a corner to the northwest. " | |
vocabWords = '5 alley five hall/hallway' | |
northwest = alley5main | |
south = alley5S | |
east = room25door | |
west = room26door | |
roomParts = static (inherited | |
+ [alleyEastWall, alleyWestWall, alleySouthWall]) | |
; | |
+ room25door: AlleyDoor '25 -' 'door to room 25' | |
"The number 25 is painted on the door. " | |
; | |
+ room26door: AlleyDoor '26 -' 'door to room 26' | |
"The wooden door has the number 26 painted on it. " | |
; | |
+ Graffiti 'poem' 'graffiti' | |
"It's a little snippet of a poem: | |
\b\tIt matters not how strait the gate, | |
\n\thow charged with punishment the scroll | |
\n\tI am the master of my fate: | |
\n\tI am the captain of my soul. | |
\n\tNow, about that midterm... " | |
; | |
/* we can hear the music from down the hall */ | |
+ Noise 'loud honolulu 10-4 theme theme/song/music' 'loud music' | |
aName = 'a song' | |
/* LISTEN TO ALLEY */ | |
sourceDesc = (hereWithSource) | |
/* LISTEN TO MUSIC */ | |
descWithSource = "It's coming from down the alley to the south. | |
It's pretty loud even from here; you recognize it as the | |
theme from <i>Honolulu 10-4</i>, being played in a loop. " | |
hereWithSource = "You can hear music from down the hall to the south. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 5 south | |
*/ | |
alley5S: AlleyRoom 'Alley Five South' 'the south end of Alley Five' | |
"This is the south end of Alley Five. On the east side of the | |
hall is a door numbered 23, and across the hall to the west | |
is the door to room 24. The hallway ends here, and stretches | |
to the north. " | |
vocabWords = '5 alley five hall/hallway' | |
north = alley5N | |
east = room23door | |
west = room24door | |
roomParts = static (inherited | |
+ [alleyEastWall, alleyWestWall, alleyNorthWall]) | |
; | |
+ room23door: AlleyDoor '23 -' 'door to room 23' | |
"The number 23 is painted on the door. " | |
; | |
+ room24door: StackDoor '24 -' 'door to room 24' | |
"The wooden door has the number 24 painted on it. " | |
; | |
++ CustomImmovable, Readable | |
'large honolulu 10-4 promotional rick palm hula | |
poster/burl/tree/trees/ocean/dancer/dancers' | |
'<i>Honolulu 10-4</i> poster' | |
"It's a promotional poster for the <i>Honolulu 10-4</i> TV series. | |
It shows star Rick Burl in an action stance, gun at the ready, | |
against a backdrop of palm trees, ocean, and hula dancers. | |
At the bottom, in cheesy script lettering, is the series' | |
catch-phrase, <i><q>Cuff 'em, Kiko!</q></i> " | |
cannotTakeMsg = 'That\'s not yours; you should leave that where it is. ' | |
; | |
++ CustomImmovable, Readable 'blue paper sign' 'blue sign' | |
"The sign is hand-lettered on blue paper: | |
<.p><.blockquote> | |
Jacob's Honolulu 10-4 Surfin' Marbles Stack | |
<.p>This is your chance for one more try at my Surfin' Marbles | |
game. All you have to do to defeat my stack is to solve it | |
fully---you just have to get one marble to each corner. You've | |
seen me solve it, so you know it's possible. To help get you | |
in the mood, I've set up my stereo to play the <q>Honolulu 10-4</q> | |
theme continuously all day long. I'm sure you'll enjoy it! | |
<./blockquote> " | |
specialDesc = "The door to room 24 is decorated with a large | |
promotional poster, and under the poster is a blue paper | |
sign. " | |
cannotTakeMsg = 'You should leave that where it is. ' | |
; | |
+ Person 'student/students/group' 'group of students' | |
"The students are huddled around a wooden marble-maze game. | |
One of them is playing the game, trying to steer several | |
marbles through the playfield maze by turning handles that | |
tilt the playfield. The rest are watching. Most of them | |
seem to be wearing earplugs or headphones. " | |
specialDesc = "A group of students are huddled around a wooden | |
marble-maze game, watching one of them play. " | |
; | |
++ InitiallyWorn | |
'hearing earplug/earplugs/headphone/headphones/protection' 'earplugs' | |
"Most of the students are wearing some kind of hearing protection. | |
Some have earplugs, others are wearing headphones. " | |
isListedInInventory = nil | |
isPlural = true | |
; | |
++ DefaultAnyTopic, ShuffledEventList | |
['You tap one of the students on the shoulder to get her | |
attention. She looks up at you, but before you can finish | |
talking, she mouths <q>WHAT?</q> You realize that she can\'t | |
hear you over the music any better than you can hear her. ', | |
'You try to get someone\'s attention, but the music is | |
too loud; no one seems to hear a word you\'re saying. ', | |
'No one can seem to hear anything you say over the music. '] | |
; | |
+ CustomImmovable | |
'wood (honolulu) (10-4) wooden marble marble-maze maze | |
surfin surfin\' marbles surfing game/box' | |
'marble-maze game' | |
"It's a wooden box about two feet square and six inches high. | |
The playfield has a network of little ridges that form a maze | |
for a set of marbles. The playfield tilts; the player controls | |
the tilt by turning a couple of handles on the side of the game. | |
The goal is to steer each marble to a different corner. | |
<.p>The game has a <i>Honolulu 10-4</i> theme. An illustration | |
of the characters from the series is painted on the playfield, | |
although it's somewhat worn from long use. " | |
cannotTakeMsg = 'The students would certainly not want you to | |
run off with the centerpiece of the stack while they\'re | |
still working on it. ' | |
dobjFor(Play) | |
{ | |
verify() { } | |
action() { playScript.doScript(); } | |
} | |
dobjFor(Use) asDobjFor(Play) | |
playScript: StopEventList { [ | |
'You squeeze into the group of students, and wait for | |
an opening. The student who\'s been playing finishes | |
his attempt and lets you take over. You put the marbles | |
in the starting position, and start turning the handles. | |
It\'s a lot harder than it looks, though, and you almost | |
immediately lose two of the marbles in the <q>Honolulu | |
Jail</q> trap. You stand up and let one of the students | |
try it. This game clearly takes a lot of practice. ', | |
'You squeeze into the group and take another try at the | |
game. You set up the marbles and start working the handles. | |
This time you do a little better; you manage to get one of | |
the marbles into its corner. But as you start working on | |
the next corner, the first marble rolls out of its corner | |
and lands in the <q>Shark Attack</q> trap. You let someone | |
else take another turn. ', | |
'You squeeze in for another try. You get the first marble | |
into its corner pretty quickly this time, and you manage to | |
back it away from the Shark trap just as it\'s about to | |
fall in. But while you\'re doing that, a third marble | |
falls into the <q>Tiki Taboo</q> trap. You hand over the | |
game to the next player. ', | |
'You feel like you should be getting good at the game after | |
all the practice you\'ve had, so you give it another go. | |
You feel like you\'re doing well, but then one marble | |
falls into the Jail trap at the same time another falls | |
into the Shark trap. You shake your head and let someone | |
else take a turn. ', | |
'You give the game another try, but the throbbing music is | |
really getting to you; you just can\'t concentrate. You | |
let someone else have a turn. '] | |
} | |
; | |
++ Component 'worn (playfield) (honolulu) (10-4) | |
illustration/character/characters/playfield' | |
'playfield illustration' | |
"The playfield is illustrated with characters from the | |
<i>Honolulu 10-4</i> TV series. The illustration is slightly | |
worn from having marbles roll over it. " | |
; | |
++ Component | |
'steel ball marble/marbles/bearing/bearings/ball-bearing/ball-bearings' | |
'marbles' | |
"The marbles look like they're made of steel; they're probably | |
just ball bearings. " | |
isPlural = true | |
; | |
++ Component 'handle/handles' 'handle' | |
"You can control the tilt of the playfield by turning the | |
handles. " | |
/* TURN HANDLES -> PLAY GAME */ | |
dobjFor(Turn) remapTo(Play, location) | |
; | |
+ xyzzyGraffiti: Graffiti 'odd wiggly handwriting' 'graffiti' | |
"The word <q>xyzzy</q> is written on the wall in odd, wiggly | |
handwriting. " | |
; | |
+ Noise 'honolulu 10-4 theme theme/song/music' '<i>Honolulu 10-4</i> theme' | |
aName = 'a theme song' | |
/* LISTEN TO ALLEY shows this; just show our normal emanation */ | |
sourceDesc = (hereWithSource.doScript()) | |
/* LISTEN TO MUSIC shows this message */ | |
descWithSource = "It's the incredibly catchy and repetitive | |
theme music to <i>Honolulu 10-4</i>, being played at | |
high volume and in a seemingly endless loop. " | |
/* | |
* this is used in generic LISTEN, in X ALLEY, and in our background | |
* daemon that runs as long as we're within hearing range | |
*/ | |
hereWithSource: ShuffledEventList { [ | |
'Driving, fast-tempo music blares through the hall at | |
an almost deafening volume. You immediately recognize it | |
as the incredibly catchy theme music from <i>Honolulu 10-4</i>, | |
the popular TV cop show from the late 60\'s. ', | |
'The <i>Honolulu 10-4</i> music plays through its brassy | |
crescendo. You think it\'s about to end, but almost | |
seamlessly, it switches to the rolling percussion and trumpet | |
blast of the song\'s opening, and the whole thing starts | |
over again. ', | |
'The <i>Honolulu 10-4</i> theme music keeps playing, obviously | |
on a tight loop. You\'re sure you\'re going to have this song | |
stuck in your head all day. '] | |
['KEY CHANGE! The <i>Honolulu 10-4</i> music subtly shifts | |
keys and repeats the catchy melody yet again. ', | |
'The <i>Honolulu 10-4</i> music keeps blasting through the | |
alley. ', | |
'DRUM ROLL! The theme music starts yet another cycle. ', | |
'The blaring theme music continues to assault the alley. ', | |
'TRUMPET BLAST! The theme music reaches another climactic | |
crescendo, and loops back to the beginning. '] | |
} | |
/* remind about the music pretty frequently */ | |
displaySchedule = [1, 2, 2, 3] | |
; | |
/* better define a 'xyzzy' verb, now that we've mentioned the word... */ | |
DefineIAction(Xyzzy) | |
execAction() | |
{ | |
if (!dabneyBreezeway.seen) | |
{ | |
"It would be great if a magic word could whisk you away, | |
but nothing seems to happen here. "; | |
} | |
else if (xyzzyGraffiti.described) | |
{ | |
if (gActor.isIn(xyzzyGraffiti.location)) | |
"You say the magic word aloud, but alas, nothing | |
happens here. "; | |
else | |
"You say the magic word aloud, but alas, you're not | |
magically transported to Alley Five. "; | |
} | |
else | |
"You seem to recall a graffito to that effect somewhere | |
in Alley Five. "; | |
} | |
; | |
VerbRule(Xyzzy) 'xyzzy' : XyzzyAction | |
verbPhrase = 'xyzzy/xyzzying' | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 5 middle | |
*/ | |
alley5M: AlleyRoom 'Middle of Alley Five' 'the middle of Alley Five' | |
"This is the middle of an east/west section of Alley Five. | |
A door on the north is numbered 31, and across the hall on | |
the south side is the door to room 27. " | |
east = alley5main | |
west = alley5W | |
north = room31door | |
south = room27door | |
vocabWords = '5 alley five/hall/hallway' | |
roomParts = static (inherited + [alleyNorthWall, alleySouthWall]) | |
; | |
+ room27door: AlleyDoor '27 -' 'door to room 27' | |
"The number 27 is painted on the door. " | |
; | |
+ room31door: AlleyDoor '31 -' 'door to room 31' | |
"The wooden door has the number 31 painted on it. " | |
; | |
+ Graffiti 'chinese character/character/calligraphy' 'graffiti' | |
"There's a passage of what looks like Chinese calligraphy. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 5 west | |
*/ | |
alley5W: AlleyRoom 'Alley Five West' 'the west end of Alley Five' | |
"This is the west end of Alley Five; the hallway continues | |
off to the east. To the west is the door to room 29. A | |
door numbered 28 is on the south side of the hall, and across | |
from it on the north is the door to room 30. " | |
east = alley5M | |
south = room28door | |
west = room29door | |
north = room30door | |
vocabWords = '5 alley five/hall/hallway' | |
roomParts = static (inherited + | |
[alleyNorthWall, alleySouthWall, alleyWestWall]) | |
; | |
+ room28door: AlleyDoor '28 -' 'door to room 28' | |
"The number 28 is painted on the door. " | |
; | |
+ room29door: AlleyDoor '29 -' 'door to room 29' | |
"The wooden door has the number 29 painted on it. " | |
; | |
+ room30door: AlleyDoor '30 -' 'door to room 30' | |
"It's a wooden door labeled with the number 30. " | |
; | |
+ Graffiti 'comic stuffed strip character/characters/boy/tiger' 'graffiti' | |
"A very nice rendition of some comic strip characters | |
is drawn here. It's a boy and his stuffed tiger, from a | |
comic strip from years past. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 6 center | |
*/ | |
alley6main: AlleyRoom 'Middle of Alley Six' 'the middle of Alley Six' | |
"This is the middle of Alley Six. A stairway to the south | |
leads down. The hallway continues east and west. " | |
vocabWords = '6 alley six hall/hallway' | |
down = a6StairsDown | |
south asExit(down) | |
east = alley6E | |
west = alley6W | |
roomParts = static (inherited + [alleyNorthWall, alleySouthWall]) | |
; | |
+ a6StairsDown: StairwayDown 'stairs/stair/stairway' 'stairway' | |
"The stairs lead down to Alley Five. " | |
; | |
+ Graffiti 'escher-like drawing/snake' 'graffiti' | |
"An Escher-like drawing of a snake eating its own tail | |
is on the wall. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 6 west | |
*/ | |
alley6W: AlleyRoom 'Alley Six West' 'the west end of Alley Six' | |
"This is the west end of Alley Six. To the northwest is | |
a door leading into the house library. The door to room | |
33 is to the southwest, and room 34 is to the southeast. | |
The hallway continues to the east. " | |
vocabWords = '6 alley six hall/hallway' | |
northwest = a6LibDoor | |
southwest = room33door | |
southeast = room34door | |
east = alley6main | |
roomParts = static (inherited + [alleyNorthWall, alleySouthWall]) | |
; | |
+ a6LibDoor: Door 'library door' 'library door' | |
"It looks like the door to any student room in the house, but | |
<q>Library</q> is painted on the door in place of a room number. " | |
isOpen = true | |
; | |
+ room33door: AlleyDoor '33 -' 'door to room 33' | |
"It's a wooden door labeled <q>33.</q> " | |
; | |
+ room34door: AlleyDoor '34 -' 'door to room 34' | |
"It's a wooden door labeled <q>34.</q> " | |
; | |
+ Graffiti 'crude spray-painted cow/outline' 'graffiti' | |
"A rather crude outline of a cow is drawn on the wall | |
in spray paint. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 6 east | |
*/ | |
alley6E: AlleyRoom 'Alley Six East' 'the east end of Alley Six' | |
"This is the east end of Alley Six. The alley ends at the | |
door to room 35 to the east, and continues west. Another, | |
narrower hallway leads south. " | |
vocabWords = '6 alley six' | |
south = alley6S | |
west = alley6main | |
east = room35door | |
roomParts = static (inherited | |
+ [alleyEastWall, alleySouthWall, alleyNorthWall]) | |
; | |
+ Vaporous 'small black diffuse fruit fly/flies/drosophila/cloud' | |
'cloud of fruit flies' | |
"<i>Drosophila melanogaster</i>, you presume: the geneticist's | |
favorite. The flies are lazily drifting around the hall in a | |
diffuse cloud. Some of them move freely in and out of the | |
plastic tube in the door; about equal numbers seem to be | |
coming and going. " | |
specialDesc = "A diffuse cloud of small black flies fills the | |
alley. " | |
; | |
+ CustomImmovable 'pile/fruit/banana/peach/orange' 'pile of fruit' | |
"Several pieces of fruit are piled on the floor: a banana, an | |
orange, a peach, and several others. They've all been partially | |
peeled, and they're all crawling with fruit flies. " | |
/* show the initial description the first time only */ | |
isInInitState = (!described) | |
initDesc = "It looks like someone's working on the stack, even if | |
they're not here at the moment. <<desc>> " | |
cannotTakeMsg = 'You don\'t want the fruit badly enough to | |
compete for it with the flies. ' | |
/* list it as though it were portable */ | |
isListed = true | |
isListedInContents = true | |
dobjFor(Eat) | |
{ | |
preCond = [] | |
verify() { } | |
action() { "You can't be serious. "; } | |
} | |
; | |
++ Decoration 'small black ruit fly/flies/drosophila' 'fruit flies' | |
"They're happily ingesting the fruit, micrograms at a time. " | |
disambigName = 'flies on the fruit' | |
notImportantMsg = 'The flies are too numerous and too small to | |
do anything with. ' | |
isPlural = true | |
; | |
+ room35door: StackDoor '35 -' 'door to room 35' | |
"It's a door painted with the number 35. " | |
; | |
++ CustomImmovable | |
'(fruit) (fly) bioventics model 77 insect small electronic | |
densitometer/instrument/device/wire/wires' | |
name = (described ? 'fly densitometer' : 'small electronic device') | |
desc = "It's a small electronic device labeled <q>Bioventics | |
Model 77 Insect Densitometer.</q> The only feature is a numeric | |
display, which currently reads <q><tt><<dispVal>></tt>.</q> | |
The device is sitting at the bottom of the door, and some wires | |
run out the back and under the door. " | |
cannotTakeMsg = 'The wiring prevents the device from being moved. ' | |
dispVal = (toString(7500 + rand(1000)) + '.' + toString(10 + rand(80)) | |
+ ' /m^3') | |
; | |
+++ Component, Readable | |
'(fly) densitometer numeric display' 'fly densitometer display' | |
"The display is currently reading <q><tt><<location.dispVal>></tt>.</q> " | |
; | |
++ Fixture 'hard white plastic tube' 'plastic tube' | |
"The tube is about an inch in diameter and is made of hard | |
white plastic. It goes through the hole in the door where, | |
presumably, the doorknob used to be. Fruit flies lazily drift | |
in and out of the tube. " | |
dobjFor(LookIn) asDobjFor(LookThrough) | |
dobjFor(LookThrough) { action() { "You peer into the tube, but | |
you can't see past a bend that looks like it's just past | |
the door. "; } } | |
specialDesc = "An orange sign is affixed to the door of room 35. | |
Under the sign, a black plastic tube sticks out slightly | |
from the door. On the floor at the bottom of the door is | |
a small electronic device. " | |
; | |
++ CustomImmovable | |
'orange broken interlocking international biohazard | |
ring/rings/sign/symbol' | |
'orange sign' | |
"The sign is hand-lettered in black marker, over a background | |
graphic showing the three broken, interlocking rings of the | |
international Biohazard symbol. | |
<.p><.blockquote> | |
Stan's World of Drosophila Stack | |
<.p>Help me prove the cleaning people wrong. They say my room | |
is too dirty to clean. That's right, it's the room so dirty, | |
it can't be cleaned. So say they. I say their problem is my | |
highly successful drosophila breeding program. | |
<.p>My stack is this: you have to liberate the approximately | |
fifty thousand fruit flies in my room. Notice the key word is | |
liberate, not exterminate. Help show them the way into the | |
wild. You'll know you're done when the Fly Densitometer shows | |
a reading of 5 or less. | |
<.p>As an added incentive to work quickly, the bribe waiting | |
inside includes generous quantities of delicious fresh fruit, | |
plus a couple of open tubs of corn syrup. The sooner you show | |
the flies the way out, the more there'll be left. | |
<./blockquote> " | |
cannotTakeMsg = 'You should leave that where it is. ' | |
; | |
+ EntryPortal ->(location.south) | |
'another south s narrow narrower hall/hallway' 'narrow hallway' | |
"The narrow hallway joins the main east/west hall in a | |
T-intersection here. It leads south. " | |
; | |
+ Graffiti 'bright yellow leafy green painted flower/flowers/stalk/stalks' | |
'graffiti' | |
"The wall is painted near the floor with life-sized | |
bright-yellow flowers on leafy green stalks. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 6 south | |
*/ | |
alley6S: AlleyRoom 'Alley Six South' 'the south end of Alley Six' | |
"This is the south end of the narrow north/south section of | |
Alley Six. The door to room 36 is on the west side of the | |
hall, and across from it on the east side is the door to | |
room 37. The alley continues north, and ends here in a | |
paned-glass door, which leads outside to the south. " | |
vocabWords = '6 alley six hall/hallway' | |
north = alley6E | |
east = room37door | |
west = room36door | |
south = a6PorchDoor | |
roomParts = static (inherited | |
+ [alleySouthWall, alleyEastWall, alleyWestWall]) | |
; | |
+ room36door: AlleyDoor '36 -' 'door to room 36' | |
"It's a wood door numbered 36. " | |
; | |
+ room37door: AlleyDoor '37 -' 'door to room 37' | |
"It's a door painted with the number 37. " | |
; | |
class SleepingPorchDoor: Door | |
'wrought iron wrought-iron paned glass paned-glass | |
one-foot-square foot-square door/frame/pane/panes/glass' | |
'paned-glass door' | |
"The door has a wrought iron frame set with one-foot-square | |
panes of glass. <<whereDesc>> " | |
dobjFor(LookThrough) { action() { "You can't see much; the glass | |
is a little hazy. "; } } | |
; | |
+ a6PorchDoor: SleepingPorchDoor | |
whereDesc = "It leads outside to the south. " | |
; | |
+ Graffiti 'painting/vampire/window' 'graffiti' | |
desc = "The wall here features a painting of what seems to be | |
a vampire escaping a window. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Sleeping Porch | |
*/ | |
sleepingPorch: DabneyOutdoorRoom 'Sleeping Porch' 'the sleeping porch' | |
"This wide terrace is known as the Sleeping Porch. Students | |
actually do sleep out here on occasion, especially during the | |
summer when it's too warm indoors. Traffic busily zips by on | |
California Boulevard below. A paned-glass door leads inside to | |
the north. " | |
north = spDoor | |
vocabWords = 'wide sleeping porch/terrace' | |
dobjFor(JumpOff) | |
{ | |
verify() { } | |
action() { "It's much too long a drop to attempt. "; } | |
} | |
roomBeforeAction() | |
{ | |
if (gActionIn(Jump, JumpOffI)) | |
replaceAction(JumpOff, self); | |
} | |
; | |
+ spDoor: SleepingPorchDoor ->a6PorchDoor | |
whereDesc = "It leads north, back inside. " | |
; | |
/* add California Blvd and its consituent parts */ | |
+ Distant 'california boulevard/blvd/blvd./street/road' | |
'California Boulevard' | |
"California Boulevard is a busy four-lane street that forms | |
the south border of the campus. " | |
isProperName = true | |
; | |
++ CalBlvdTraffic; | |
+++ CalBlvdNoise; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Alley 7 top of stairs | |
*/ | |
alley7main: AlleyRoom 'Alley Seven' 'Alley Seven' | |
"This is the hub of Alley Seven. The long, narrow expanse of | |
Upper Seven, with its array of monastic cells, stretches north; | |
Lower Seven is down a few steps to the east. A long, steep | |
stairway descends to the courtyard. " | |
vocabWords = '7 alley seven' | |
down = a7CourtyardStairs | |
out asExit(down) | |
north = upper7S | |
east = a7lowerStairs | |
south asExit(down) | |
roomParts = static (inherited + [alleySouthWall, alleyWestWall]) | |
; | |
+ a7CourtyardStairs: StairwayDown -> dcStairsUp | |
'long steep concrete courtyard stairs/stairway' 'courtyard stairway' | |
"It's about two normal stories worth of stairs leading down to | |
the courtyard. " | |
; | |
+ EntryPortal ->(location.north) | |
'long narrow monastic 7 upper seven/cell/cells/(array)/hall/hallway' | |
'Upper Seven' | |
"The long, narrow hallway extends north. " | |
isProperName = true | |
; | |
+ a7lowerStairs: StairwayDown -> l7Stairs | |
'lower seven 7 step/steps/stair/stairs/stairway' 'Lower Seven' | |
"Lower Seven is down a few steps to the east. " | |
isProperName = true | |
; | |
+ Graffiti 'gigantic black "welcome to alley 7" "welcome" sign' 'graffiti' | |
"There are lots of bits of graffiti here, including a gigantic | |
black <q>Welcome to Alley 7</q> sign. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Lower 7 West | |
*/ | |
lower7W: AlleyRoom 'Lower Seven West' 'the west end of Lower Seven' | |
"This is the west end of Lower Seven. A half flight of stairs to | |
the west leads up to Upper Seven. The hallway continues east, | |
but the way is thoroughly blocked by a pile of concrete rubble | |
and broken pieces of re-bar, and a collection of heavy power tools. | |
The rubble is evidently what's been chipped off the huge concrete | |
block to the south, filling the space where the door to room 50 | |
would normally be. Across the hall to the north, the door to | |
room 49 is still intact. " | |
west = l7Stairs | |
up asExit(west) | |
north = room49door | |
south: NoTravelMessage { "There's a huge concrete block in the way. " } | |
east: NoTravelMessage { "The hallway does continue east, but there's | |
no way you can get past the rubble and all of the tools. " } | |
vocabWords = '7 alley lower seven/hallway' | |
roomParts = static (inherited + [alleyNorthWall, alleySouthWall]) | |
; | |
+ Graffiti | |
'psychedelic bright neon black-and-white | |
swirl/swirls/spiral/spirals/color/colors/landscape/checkerboard/mural' | |
'graffiti' | |
"An extensive mural starts with a black-and-white checkerboard | |
at one end that transforms into a psychedelic landscape of | |
swirls and spirals of bright, neon colors at the other end. " | |
; | |
+ l7Stairs: StairwayUp | |
'upper seven 7 step/steps/stair/stairs/stairway' 'stairs' | |
"The stairs lead up to Upper Seven, to the west. " | |
; | |
+ room49door: AlleyDoor '49 -' 'door to room 49' | |
"It's a wooden door labeled <q>49.</q> " | |
; | |
+ CustomImmovable | |
'construction heavy power circular masonry huge air-powered air | |
tool/tools/equipment/jackhammer/jackhammers/saw/drill/drills/ | |
compressor/compressors' | |
'power tools' | |
"There are a bunch of serious construction tools stacked along | |
the wall: a couple of jackhammers, a circular masonry saw, | |
huge air-powered drills, air compressors, and lots of things | |
you don't even recognize. Several of the students are attacking | |
the block of concrete with one of the jackhammers. " | |
isPlural = true | |
cannotTakeMsg = 'Even if the equipment weren\'t so large and | |
unwieldy, the students would undoubtedly object to your | |
making off with it. ' | |
; | |
++ Noise '(jackhammer) (jackhammering) construction noise' | |
'construction noise' | |
/* LISTEN TO TOOLS */ | |
sourceDesc = "The jackhammer is making a lot of noise as it | |
chips away at the concrete. " | |
/* LISTEN TO CONSTRUCTION NOISE */ | |
descWithSource = (sourceDesc) | |
/* LISTEN, X ALLEY, background daemon */ | |
hereWithSource: ShuffledEventList { [ | |
'The jackhammer makes a horrible metal-on-metal scraping sound. ', | |
'The jackhammer blasts away at the concrete block. ', | |
'The air compressor makes a loud hiss. ', | |
'The whole building seems to rumble with the jackhammer noise. '] | |
} | |
displaySchedule = [2] | |
; | |
+ Person 'dusty group/student/students' 'group of students' | |
"The students are dusty from the demolition, like everything | |
else around here. They're working together to jackhammer the | |
block of concrete. " | |
specialDesc = "A group of students are attacking the block | |
of concrete with a jackhammer. " | |
; | |
++ DefaultAnyTopic | |
"You try to get someone's attention, but everyone's too | |
busy with the jackhammering. " | |
; | |
+ Fixture 'room 50 door doorframe/frame' 'room 50 doorframe' | |
"The doorframe is completely filled in with a block of concrete. " | |
; | |
++ Heavy 'concrete block/concrete' 'concrete block' | |
"The block of concrete entirely fills the room 50 doorframe, | |
and sticks out a couple of feet into the hall. About half of | |
the front face is unevenly pitted to a depth of a few inches, | |
evidently from the efforts of the students to break through. | |
Twisted bits of re-bar stick out in places. " | |
/* show the initial description the first time only */ | |
isInInitState = (!described) | |
initDesc = "This is a fine example of the classic Brute Force | |
Stack: not the sort of fancy intellectual puzzle that's so | |
popular these days, but an actual physical barrier. The | |
point is just to blast through it with raw physical force. | |
There's no need for rules; the only rules are the laws of | |
physics. That, and a decent respect for the structural | |
integrity of the building. | |
<.p><<desc>> " | |
; | |
+++ Decoration 'twisted re-bar/rebar' 'twisted re-bar' | |
"The re-bar is exposed in a few places where the concrete has | |
been chipped away. " | |
dobjFor(Pull) | |
{ | |
verify() { } | |
action() { "It's literally set in concrete. There's no way | |
you can move it with your bare hands. "; } | |
} | |
dobjFor(Push) asDobjFor(Pull) | |
dobjFor(Move) asDobjFor(Pull) | |
dobjFor(Turn) asDobjFor(Pull) | |
dobjFor(PushTravel) asDobjFor(Pull) | |
; | |
+ CustomImmovable | |
'broken concrete demolition rubble/pile/piles/re-bar/piece/pieces/dust' | |
'rubble' | |
"Considering how much rubble is piled in the hallway, it's | |
surprising that the block isn't already gone. The rubble is | |
piled at least a couple of feet deep, effectively blocking the | |
way east. Needless to say, the demolition dust is everywhere. " | |
cannotTakeMsg = 'There\'s far too much rubble to take with you, | |
and even the smallest pieces are inconveniently large and | |
heavy and sharp-edged. The most you could do is shift the | |
piles around a little, which wouldn\'t accomplish anything. ' | |
lookInDesc = "All you find in the rubble is even more rubble. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Upper 7 South | |
*/ | |
upper7S: AlleyRoom 'Upper Seven South' 'south end of Upper Seven' | |
"This is the south end of Upper Seven. The narrow hallway | |
stretches north, lined on both sides with graffiti and | |
closely-spaced doors. Most of the notorious Upper Seven singles | |
are so small that you can stand in the middle of one and touch | |
opposite walls at the same time. | |
<.p>The door to room 43 is northwest, 44 northeast, 45 west, | |
46 east, 47 southwest, and 48 southeast. The hallway continues | |
to more dense-packed rooms to the north, and opens into a larger | |
area to the south. " | |
vocabWords = '7 upper alley seven' | |
south = alley7main | |
north = upper7N | |
northwest = room43door | |
northeast = room44door | |
west = room45door | |
east = room46door | |
southwest = room47door | |
southeast = room48door | |
roomParts = static (inherited + [alleyEastWall, alleyWestWall]) | |
; | |
+ room43door: AlleyDoor '43 -' 'door to room 43' | |
"It's a wooden door labeled <q>43.</q> " | |
; | |
+ room44door: AlleyDoor '44 -' 'door to room 44' | |
"It's a wooden door labeled <q>44.</q> " | |
; | |
+ room45door: AlleyDoor '45 -' 'door to room 45' | |
"It's a wooden door labeled <q>45.</q> " | |
; | |
+ room46door: AlleyDoor '46 -' 'door to room 46' | |
"It's a wooden door labeled <q>46.</q> " | |
; | |
+ room47door: AlleyDoor '47 -' 'door to room 47' | |
"It's a wooden door labeled <q>47.</q> " | |
; | |
+ room48door: AlleyDoor '48 -' 'door to room 48' | |
"It's a wooden door labeled <q>48.</q> " | |
; | |
+ Graffiti '-' 'graffiti' | |
"<font face='tads-sans'><b>You know you're a nerd when | |
you start dreaming in <tab id=lang><s> FORTRAN </s> | |
\n<tab to=lang><s> Pascal </s> | |
\n<tab to=lang><s> C </s> | |
\n<tab to=lang><s> C++ </s> | |
\n<tab to=lang><s> Java </s> | |
\n<tab to=lang> QUBITS | |
</b></font> | |
\bEach programming language name has been crossed out and | |
another has been written below, and then that replacement | |
has been itself crossed out and replaced. Only the | |
last entry, <q>QUBITS,</q> is left standing. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Upper 7 North | |
*/ | |
upper7N: AlleyRoom 'Upper Seven North' 'north end of Upper Seven' | |
"Closely-spaced doors to the monastic cells that are Upper Seven | |
singles line this stretch of narrow hallway. Room 38 is northeast, | |
39 is northwest (you recall it's the one double room in Upper 7), | |
40 is east, 41 is southwest, and 42 is southeast. | |
The alley extends away to the south, ending here at a wall to | |
the north. | |
<.p>An ancient personal computer is on the floor outside the | |
door of room 42. A blue paper sign is attached to the door. " | |
vocabWords = '7 upper alley seven' | |
south = upper7S | |
northeast = room38door | |
northwest = room39door | |
east = room40door | |
southwest = room41door | |
southeast = room42door | |
roomParts = static (inherited | |
+ [alleyEastWall, alleyWestWall, alleyNorthWall]) | |
; | |
+ room38door: AlleyDoor '38 -' 'door to room 38' | |
"It's a wooden door labeled <q>38.</q> " | |
; | |
+ room39door: AlleyDoor '39 -' 'door to room 39' | |
"It's a wooden door labeled <q>39.</q> " | |
dobjFor(Knock) | |
{ | |
action() | |
{ | |
/* do the normal no-reply business */ | |
inherited(); | |
/* if Aaron's here, mention where he went */ | |
if (aaron.isIn(location)) | |
"<.p>Aaron glances over. <q>If you're looking for | |
Scott, I think he's working on the giant chicken | |
stack in alley 3.</q> "; | |
} | |
} | |
; | |
+ room40door: AlleyDoor '40 -' 'door to room 40' | |
"It's a wooden door labeled <q>40.</q> " | |
; | |
+ room41door: AlleyDoor '41 -' 'door to room 41' | |
"It's a wooden door labeled <q>41.</q> " | |
; | |
+ room42door: StackDoor '42 -' 'door to room 42' | |
"A blue paper sign is attached to the door, and an old personal | |
computer is sitting on the floor in front of it. The door is | |
labeled <q>42.</q> " | |
; | |
++ CustomImmovable, Readable 'blue paper sign' 'sign' | |
"<font face='tads-sans'>Welcome to Paul's Ditch Day Stack! | |
My stack consists of | |
just one puzzle, contained in the Commandant 64 below. All you have | |
to do is type in the correct <q>password.</q> You'll know | |
you've typed the right thing when the output is the exact same string | |
as the input. (The password has a non-zero number of characters. | |
You can't win by claiming that if you type zero characters, the | |
computer responds with the same zero characters.) | |
<.p>A couple of hints. First, the password is from one to | |
sixteen characters long. Second, it only contains the digits 0 to 9 | |
and the letters A through F. Third, if you type in something other | |
than the password, the computer might show you a response that | |
looks like gibberish---but that gibberish is actually useful | |
information, so don't ignore it. | |
<.p>Good luck!</font> " | |
disambigName = 'blue paper sign' | |
cannotTakeMsg = 'You should leave the sign where it is. ' | |
; | |
+ commandant64: Keypad, CustomImmovable, Readable | |
'white plastic ancient old personal 64 display | |
computer/commandant/machine/keyboard/display/monitor/screen/pc' | |
'old computer' | |
"The computer is a Commandant 64, one of those machines from | |
the first generation or two of personal computers. It looks like | |
a white plastic suitcase, lying on its side, with a keyboard | |
embedded on the top near the front edge. A small display sits | |
on top. | |
<.p><<readDesc>> " | |
cannotTakeMsg = 'You shouldn\'t disturb the stack. ' | |
readDesc() | |
{ | |
"The monitor is currently showing:\b | |
<tt><<monitorDesc>></tt> "; | |
/* note that this program has been observed */ | |
progSeen = true; | |
} | |
/* a few interesting programs */ | |
interestingProgs = | |
[ | |
'123456789abcdef0', /* just a random first try */ | |
'eeee', /* displays 0000... forever */ | |
'21ed0', /* display 1111... forever */ | |
'22ed0', /* display 2222... forever */ | |
'1ea6d11', /* display 0123... repeatedly forever */ | |
'1ea6d12', /* display 02468... repeatedly forever */ | |
'1ea9c8d101', /* display 0123...DEF once */ | |
'1b9ec8d101', /* display FEDC...210 once */ | |
'3feae5fbdccd0b11' /* count from 1 to 10 (0xA) once */ | |
] | |
nextInteresting = 2 | |
monitorDesc = static (processInput(interestingProgs[1])) | |
successStr = 'SUCCESS!!!' | |
/* flag: the current program has been examined */ | |
progSeen = nil | |
/* | |
* When the PC arrives, and Aaron is present, change the program if | |
* we've seen the one that was here before. This makes it seem as | |
* though Aaron is actively trying different entries. | |
*/ | |
afterTravel(trav, conn) | |
{ | |
/* | |
* if Aaron is here, and the current program has been observed, | |
* select a new program | |
*/ | |
if (aaron.isIn(location) && progSeen) | |
selectNewProg(); | |
} | |
/* select and enter a program */ | |
selectNewProg() | |
{ | |
local str; | |
/* | |
* if we have more interesting programs to enter, enter one of | |
* those | |
*/ | |
if (nextInteresting <= interestingProgs.length()) | |
{ | |
/* we have more programs to enter, so enter one */ | |
str = interestingProgs[nextInteresting++]; | |
} | |
else | |
{ | |
/* | |
* we have no more interesting programs, so make one up | |
* randomly - just choose a string of random hex digits, of | |
* random length (but always from 5 to 16 characters) | |
*/ | |
for (local len = rand(12) + 5, str = '' ; len != 0 ; --len) | |
str += '01234567890abcdef'.substr(rand(16) + 1, 1); | |
} | |
/* enter the selected program */ | |
monitorDesc = processInput(str); | |
/* this new program hasn't been examined by the player yet */ | |
progSeen = nil; | |
} | |
dobjFor(TypeLiteralOn) | |
{ | |
verify() { } | |
action() | |
{ | |
/* process the input (from the literal phrase typed) */ | |
monitorDesc = processInput(gLiteral); | |
/* show the result */ | |
"You type in <q><tt><<gLiteral.htmlify()>></tt></q> and press | |
enter. After a few moments, the monitor changes to display: | |
\b<tt><<monitorDesc>></tt> "; | |
/* if it's the winning password, say so */ | |
if (monitorDesc.endsWith(successStr)) | |
{ | |
if (room42door.isSolved) | |
{ | |
"<.p>You've solved it again! You discreetly type in | |
a new random string to avoid spoiling the puzzle | |
for anyone else. "; | |
} | |
else | |
{ | |
"<.p>You found the password! You feel inordinantly | |
pleased with yourself, but that's tempered by the | |
knowledge that this doesn't get you any further | |
with Stamer's stack. "; | |
if (aaron.isIn(location)) | |
"<.p>It doesn't look like Erin or Aaron were | |
paying attention. As much as you'd like to show | |
off your solution, you figure they'd probably | |
be happier solving the stack on their own, so | |
you quietly type in a random string to clear | |
the solution from the monitor. "; | |
else | |
"<.p>You don't want to spoil the puzzle for | |
anyone else, so much as you'd like to show off | |
your solution, you quietly type in a random | |
string to clear the solution from the monitor. "; | |
/* mark the stack as solved */ | |
room42door.isSolved = true; | |
/* award extra-credit points for solving it */ | |
extraCreditMarker.awardPointsOnce(); | |
} | |
/* reset the display */ | |
monitorDesc = processInput('123456'); | |
} | |
else | |
{ | |
/* mention that this isn't important once in a while */ | |
timeWasterWarning.doScript(); | |
/* mention that aaron and erin notice, if they're here */ | |
if (aaron.isIn(location)) | |
aAndENotice.doScript(); | |
} | |
/* this counts as seeing the program */ | |
progSeen = true; | |
} | |
} | |
dobjFor(EnterOn) asDobjFor(TypeLiteralOn) | |
/* an extra-credit achievement for solving the stack */ | |
extraCreditMarker: ExtraCreditAchievement { +50 "solving the | |
Commandant 64 stack" } | |
timeWasterWarning: StopEventList { [ | |
nil, nil, nil, | |
'<.p>It occurs to you that this sort of puzzle can be awfully | |
addictive; better be careful not to get sidetracked too much here. ', | |
nil, nil, nil, nil, nil, | |
'<.p>Interesting as this is, you know you really should be | |
getting back to Stamer\'s stack before too long. ', | |
'<.p>You keep reminding yourself not to get too sucked into | |
this stack, since you have more important things you should | |
be working on. ', | |
nil] } | |
aAndENotice: ShuffledEventList { [ | |
'<.p>Erin and Aaron look at what you typed and mutter a | |
few comments to each other. ', | |
'<.p>Aaron looks at the display and makes a little <q>hmm</q> | |
noise. ', | |
'<.p>Erin looks carefully at what you typed. ', | |
'<.p><q>Interesting,</q> Aaron says, looking at the display. ', | |
'<.p>Erin looks at what you typed and nods. '] } | |
/* process an input string and update the monitor with the result */ | |
processInput(str) | |
{ | |
local prefix; | |
/* get the upper-case version of the input */ | |
str = str.toUpper(); | |
/* the monitor always shows the input first */ | |
prefix = '\tIN:\ ' + str.htmlify() + '\n\t'; | |
/* | |
* if the string contains non-hex, or it's longer than 16 | |
* characters, show an error | |
*/ | |
if (rexMatch('[0-9A-F]+$', str) == nil) | |
{ | |
/* invalid keys entered */ | |
return prefix + 'ERR:0-9+A-F ONLY!!!'; | |
} | |
else if (str.length() > 16) | |
{ | |
/* too long */ | |
return prefix + 'ERR:16 CHARS MAX!!!'; | |
} | |
else | |
{ | |
local result, outStr; | |
/* run the program */ | |
outStr = runProgram(str); | |
/* add the result to the prefix */ | |
result = prefix + 'OUT:' + outStr; | |
/* | |
* If it's a winner, add a mention of this. We can tell | |
* that we have a winner when the output string equals the | |
* input string. | |
* | |
* Note that a great feature of this puzzle is that you can | |
* read the source code, and it still doesn't give away the | |
* solution. You'll notice that there's no hard-coded | |
* string anywhere in here that says what the solution is; | |
* it's simply the input that yields itself as the output. | |
* It probably helps a little that you can see exactly what | |
* each input does without having to infer it from | |
* trial-and-error observation, but reverse-engineering the | |
* mechanism is only half the game - there's still a fairly | |
* challenging problem to solve even after you know exactly | |
* how the input is transformed into the output. | |
*/ | |
if (str.length() > 0 && str == outStr) | |
result += '\b\t' + successStr; | |
/* return the result */ | |
return result; | |
} | |
} | |
/* | |
* Run the program contained in the given string, returning the | |
* display string that results. | |
*/ | |
runProgram(str) | |
{ | |
local iter; | |
local pc; | |
local disp; | |
local r0; | |
local mem; | |
/* start out with no display results */ | |
disp = ''; | |
/* start with R0 set to zero */ | |
r0 = 0; | |
/* set up a vector from the string */ | |
mem = new Vector(16); | |
for (local i = 1, local len = str.length() ; i <= 16 ; ++i) | |
{ | |
/* | |
* fill from this string element, or with zero if the string | |
* isn't long enough | |
*/ | |
mem[i] = (i <= len ? toInteger(str.substr(i, 1), 16) : 0); | |
} | |
/* run an arbitrary maximum of 1024 instructions */ | |
runLoop: | |
for (iter = 0, pc = 1 ; iter < 1024 ; ++iter) | |
{ | |
/* fetch the current instruction half-byte */ | |
local instr = mem[pc]; | |
/* | |
* advance the program counter, wrapping if we've reached | |
* the top of memory | |
*/ | |
if (++pc > 16) | |
pc = 1; | |
/* process the current instruction */ | |
switch (instr) | |
{ | |
case 0: | |
/* HALT */ | |
break runLoop; | |
case 1: | |
/* CLR - load zero into R0 */ | |
r0 = 0; | |
break; | |
case 2: | |
/* LOADC - load immediate operand into R0 */ | |
r0 = mem[pc++]; | |
break; | |
case 3: | |
/* LOAD - load value from address operand */ | |
r0 = mem[mem[pc++] + 1]; | |
break; | |
case 4: | |
/* LOADIDX - load value from address pointer operand */ | |
r0 = mem[mem[mem[pc++] + 1] + 1]; | |
break; | |
case 5: | |
/* STORE - store R0 into address operand */ | |
mem[mem[pc++] + 1] = r0; | |
break; | |
case 6: | |
/* STOREIDX - store R0 into address pointer operand */ | |
mem[mem[mem[pc++] + 1] + 1] = r0; | |
break; | |
case 7: | |
/* AND - bitwise AND address operand into R0 */ | |
r0 &= mem[mem[pc++] + 1]; | |
break; | |
case 8: | |
/* OR - bitwise OR address operand into R0 */ | |
r0 |= mem[mem[pc++] + 1]; | |
break; | |
case 9: | |
/* NOT - bitwise negate R0 */ | |
r0 = ~r0; | |
r0 &= 0xF; | |
break; | |
case 0xA: | |
/* ADD - add address operand into R0 */ | |
r0 += mem[mem[pc++] + 1]; | |
r0 &= 0xF; | |
break; | |
case 0xB: | |
/* SUB - subtract address operand from R0 */ | |
r0 -= mem[mem[pc++] + 1]; | |
r0 &= 0xF; | |
break; | |
case 0xC: | |
/* JZ - jump to operand address if R0 is zero */ | |
if (r0 == 0) | |
pc = mem[pc] + 1; | |
else | |
++pc; | |
break; | |
case 0xD: | |
/* JMP - jump to operand address */ | |
pc = mem[pc] + 1; | |
break; | |
case 0xE: | |
/* | |
* PRINT - display contents of R0. Only allow up to 64 | |
* characters in the output; if we already have 64, | |
* simply stop running now. | |
*/ | |
if (disp.length() >= 64) | |
{ | |
disp += '...'; | |
break runLoop; | |
} | |
/* add the contents of R0 to the display */ | |
disp += toString(r0, 16); | |
break; | |
case 0xF: | |
/* NOP - no operation */ | |
break; | |
} | |
/* wrap the PC if necessary, in case we read an operand */ | |
if (pc > 16) | |
pc = 1; | |
} | |
/* return the display result */ | |
return disp; | |
} | |
; | |
+ Graffiti 'Polly Nomial story' 'graffiti' | |
"<q>...When Polly got home that night, her mother noticed | |
that she was no longer piecewise continuous, but had been | |
truncated in several places. But it was too late to | |
differentiate now. As the months went by, Polly's | |
denominator increased monotonically. Finally, she went | |
to l'Hôpital and generated a small but pathological | |
function which left surds all over the place, and drove | |
Polly to deviation...</q> " | |
; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2005 Michael J. Roberts | |
* | |
* Return to Ditch Day - general header definitions. This file contains a | |
* few miscellaneous things that we need in multiple source files, such as | |
* object template definitions. | |
*/ | |
#ifndef DITCH3_H | |
#define DITCH3_H | |
/* object template for the Mitachron movers in and around Dabney Hovse */ | |
MitaMovers template "npcDesc" "specialDesc"; | |
#endif /* DITCH3_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# TADS 3 makefile | |
# | |
# Warning: this file was mechanically generated. You may edit this file | |
# manually, but your changes might be modified or discarded when | |
# you load this file into a TADS development tool. The TADS tools | |
# generally retain the comment at the start of the file and the | |
# comment marked "##sources" below, but other comments might be | |
# discarded, the formatting might be changed, and some option | |
# settings might be modified. | |
# Return to Ditch Day - tads 3 makefile | |
# | |
-o ditch3.t3 | |
-pre | |
-nodef | |
-D LANGUAGE=en_us | |
-D MESSAGESTYLE=neu | |
-D INSTRUCTIONS_MENU | |
-Fy obj | |
-Fo obj | |
-w1 | |
-we | |
##sources | |
-lib system | |
-source reflect | |
-lib adv3/adv3 | |
-x footnote | |
-source main | |
-source me | |
-source intro | |
-source campus | |
-source dabney | |
-source jay | |
-source stack | |
-source bridge | |
-source library | |
-source bookstore | |
-source career | |
-source synclab | |
-source frosst | |
-source about | |
-source hints | |
-source mods | |
-source pathfind | |
-source elev | |
-source subtime | |
-res | |
gameinfo.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - elevator class. This is a set of classes for | |
* implementing a typical modern elevator. | |
* | |
* These classes are meant to be reusable in a range of games; they're a | |
* little too specialized for the main system library, but other authors | |
* might find them helpful in creating their own elevators. Read the | |
* comments carefully, since the various objects make a certain number of | |
* assumptions about their location/contents relationships to one | |
* another; you can override most of these assumptions with methods near | |
* the start of each class, but the default assumptions are probably | |
* suitable for many situations. | |
* | |
* To improve playability, we by default travel between any pair of | |
* floors, no matter how far apart, in a single turn. You can override | |
* this to take one turn per floor, or any other pace you like, but the | |
* standard pacing is probably the best compromise between playability | |
* and realistic timing, at least for most cases. There's still some | |
* feeling of a real elevator's cadence, since it always takes an extra | |
* turn for doors to close. Also, since the elevator moves around in the | |
* background when it has work to do, it'll take extra time when it has | |
* extra stops to make; for example, if you push all of the buttons and | |
* get out on the first stop, then call the elevator, it'll take a while | |
* to get there because it'll actually have to service all of those other | |
* stops. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
/* | |
* An elevator. This is a mix-in class that should be combined with | |
* Room or a Room subclass to create the elevator's interior. Place an | |
* ElevatorInnerDoor directly within this object to serve as the | |
* elevator door. | |
*/ | |
class Elevator: object | |
/* | |
* the standard amount of time (in turns) to leave the doors open | |
* each time we respond to a call | |
*/ | |
doorOpenTime = 2 | |
/* | |
* Continue the elevator's motion. 'curFloorNum' is the current | |
* floor number, and 'targetFloorNum' is the target floor number. | |
* This method should move the elevator as far as we can go in one | |
* turn, and return the *actual* new floor number - this need not be | |
* the same as the target floor, since the elevator can limit its | |
* pace as it sees fit. For example, you could implement this | |
* routine so that it moves the elevator only one floor per turn. | |
* | |
* In addition to moving the elevator, this routine should comment on | |
* any changes that occur in the course of the travel. For example, | |
* if the elevator has a display showing the current floor number, | |
* this routine should comment on the change in the displayed floor | |
* number. We don't assume a display by default, so we don't say | |
* anything here. | |
* | |
* By default, we'll move the elevator the entire distance to the new | |
* floor. Some people might find it more realistic to move the | |
* elevator one turn per floor, since then the time required in turns | |
* would scale with the length of the journey; but I like the instant | |
* travel for two reasons. | |
* | |
* First, it's simply more playable; I think it's better in general | |
* not to let reality trump playability for reality's sake alone, and | |
* in this regard, gratuitous 'wait' commands are as bad as | |
* gratuitous sleeping and eating. The only reason I'd slow down the | |
* elevator is for the sake of the game design; for example, this | |
* could be useful if you have a timed puzzle that depends on | |
* visiting floors in an optimized order. | |
* | |
* Second, even though one-floor-per-turn seems superficially | |
* "realistic," I don't think it actually is. Trying to tie 'turns' | |
* to any concrete measure of real-world time almost always makes a | |
* game less realistic, not more; to a player, different commands | |
* ought to take radically different amounts of time to be realistic. | |
* Game time is almost always better handled as a "narrative" or | |
* "subjective" time, where the amount of time that passes depends on | |
* the amount of *interesting* stuff that happens, not on the number | |
* or types of commands entered. | |
*/ | |
continueElevatorMotion(curFloorNum, targetFloorNum) | |
{ | |
/* | |
* by default, travel directly - all in this one turn - to the | |
* target floor | |
*/ | |
return targetFloorNum; | |
} | |
/* | |
* Announce that we're starting to move. 'dir' is the direction of | |
* movement: 1 is up, -1 is down. | |
*/ | |
announceStart(dir) | |
{ | |
"The elevator starts moving. "; | |
} | |
/* | |
* Announce that we're stopping at a floor and opening our doors. | |
* 'moving' indicates whether or not we were moving before this stop; | |
* if not, it means we're simply opening our doors at the floor where | |
* we already were located. 'newDir' is the direction we'll be going | |
* after this floor: 1 is up, -1 is down, and 0 means we're just | |
* opening our doors with nowhere to go. | |
*/ | |
announceStop(moving, newDir) | |
{ | |
/* if we were moving, we're moving no longer */ | |
if (moving) | |
"The elevator stops, and the doors slide open. "; | |
else | |
"The elevator doors slide open. "; | |
} | |
/* | |
* Get my door. By default, this is simply the ElevatorDoor object | |
* directly within our contents. Override this if the relationship | |
* to the elevator door is different (if it's in an intermediate | |
* container, for example). | |
*/ | |
getElevatorDoor() | |
{ return contents.valWhich({x: x.ofKind(ElevatorDoor)}); } | |
/* | |
* The direct container of my buttons. By default, that's just me. | |
* You can override this if you want to create an intermediate | |
* object to hold the buttons - a control panel object, for example. | |
*/ | |
buttonContainer = (self) | |
/* get a list of my floor buttons */ | |
getButtonList() | |
{ | |
/* | |
* return all of the ElevatorButton objects among the contents | |
* of the container of my buttons | |
*/ | |
return buttonContainer.contents.subset({x: x.ofKind(ElevatorButton)}); | |
} | |
/* get the current floor number */ | |
getFloorNum() | |
{ | |
/* read the floor number from the outer door where we're parked */ | |
return getElevatorDoor().otherSide.floorNum; | |
} | |
/* | |
* get the current floor name - we'll get this from the button for | |
* the current floor | |
*/ | |
getFloorName() | |
{ | |
/* return the name for the current floor */ | |
return getNameForFloor(getFloorNum); | |
} | |
/* get the name for the given floor */ | |
getNameForFloor(floor) | |
{ | |
/* get the button for this floor, and return its name */ | |
return getButtonList().valWhich({x: x.floorNum == floor}).floorName; | |
} | |
/* note that a floor button inside the elevator has been lit */ | |
noteButtonLit() | |
{ | |
local door = getElevatorDoor(); | |
/* | |
* If the doors are open, speed up the close timer. In real | |
* elevators, pushing a floor button tends to close the doors | |
* more or less right away. | |
*/ | |
if (door.isOpen && doorTimer > 1) | |
doorTimer = 1; | |
/* if the elevator daemon isn't running, start it up */ | |
startElevatorDaemon(); | |
} | |
/* extend the door timer to the maximum timeout */ | |
extendDoorTimer() | |
{ | |
/* set the door timer to the standard open time */ | |
doorTimer = doorOpenTime; | |
} | |
/* note a call from a floor button */ | |
noteFloorCall(floor, dir, lit) | |
{ | |
local btn; | |
/* | |
* find the floor button corresponding to this floor, and set | |
* the call for the given direction | |
*/ | |
btn = getButtonList().valWhich({x: x.floorNum == floor}); | |
/* mark it as called or not, according to the new status */ | |
if (dir == 1) | |
btn.calledGoingUp = lit; | |
else | |
btn.calledGoingDown = lit; | |
/* if the daemon isn't running, start it up */ | |
startElevatorDaemon(); | |
} | |
/* cancel our call button for the current floor in the given direction */ | |
cancelCallButton(door, dir) | |
{ | |
local btn; | |
/* find the appropriate button on the outside of the elevator */ | |
btn = door.otherSide.buttonContainer.contents.valWhich( | |
{x: x.ofKind(ElevatorCallButton) && x.buttonDir == dir}); | |
/* turn off this button's floor call */ | |
btn.setFloorCall(nil); | |
} | |
/* start the elevator daemon, if it's not already running */ | |
startElevatorDaemon() | |
{ | |
if (!elevatorDaemonRunning) | |
{ | |
/* start the daemon */ | |
new SenseDaemon(self, &elevatorDaemon, 1, self, sight); | |
/* note that it's running so that we don't start it again */ | |
elevatorDaemonRunning = true; | |
} | |
} | |
/* | |
* The main elevator daemon. We keep this running whenever we have | |
* any pending work: specifically, our door is open, or we have any | |
* active floor call pending. When we have no work, we cancel the | |
* daemon. | |
*/ | |
elevatorDaemon() | |
{ | |
local door = getElevatorDoor(); | |
local lst; | |
local len; | |
local i; | |
local btn; | |
local lastCallBtn; | |
local floor; | |
local iter; | |
/* note our current floor */ | |
floor = door.otherSide.floorNum; | |
/* if the door is open, consider closing it */ | |
if (door.isOpen) | |
{ | |
/* decrement the timer; close the doors if it's reached zero */ | |
if (doorTimer-- <= 0) | |
{ | |
/* close the doors */ | |
door.makeOpen(nil); | |
/* mention it */ | |
"The elevator doors slide shut. "; | |
} | |
/* | |
* When the doors are open, we can't move; even if we just | |
* closed them, wait a turn before considering any motion, | |
* to simulate the timing of real elevators. | |
*/ | |
return; | |
} | |
/* get a list of my floor buttons, in order of floor number */ | |
lst = getButtonList.sort(SortAsc, {a, b: a.floorNum - b.floorNum}); | |
/* get the list's length */ | |
len = lst.length(); | |
/* | |
* Keep going until we either find a floor-call to respond to, | |
* or figure out that we have no pending work. We have to loop | |
* because we might have to reverse our direction and try again. | |
*/ | |
for (iter = 1 ; ; ++iter) | |
{ | |
local newDir; | |
/* if we have no pending work, we're done */ | |
if (lst.indexWhich({x: x.isCalling}) == nil) | |
{ | |
/* | |
* Since we have no pending work, we can cancel the | |
* daemon. Any sort of floor call will start the daemon | |
* again, so we don't need it as long as we're idle. | |
*/ | |
eventManager.removeCurrentEvent(); | |
elevatorDaemonRunning = nil; | |
/* we're not going in any direction now */ | |
currentDir = 0; | |
/* we're done */ | |
return; | |
} | |
/* | |
* if we're not going in any direction, select one according | |
* to any available call | |
*/ | |
if (currentDir == 0) | |
{ | |
/* | |
* Find any button on another floor with a call. If the | |
* current floor has a call, we won't need a direction, | |
* as we'll start with the current floor's called | |
* direction. | |
*/ | |
btn = lst.valWhich({x: x.isCalling && x.floorNum != floor}); | |
/* go in that direction */ | |
if (btn != nil) | |
currentDir = (btn.floorNum < floor ? -1 : 1); | |
} | |
/* we don't know the new direction after the call yet */ | |
newDir = nil; | |
/* | |
* We have a pending call somewhere, so find the next one to | |
* service and go service it. Starting at the current | |
* floor, and working in the current direction, check each | |
* floor until we find one where we want to stop. | |
*/ | |
for (i = lst.indexWhich({x: x.floorNum == floor}), | |
lastCallBtn = nil ; | |
i >= 1 && i <= len ; i += currentDir) | |
{ | |
/* get the current floor button */ | |
btn = lst[i]; | |
/* | |
* If this is the first pass, and we have a direction, | |
* ignore the current floor. This ensures that we don't | |
* get called back to the current floor by a button push | |
* right after we close the doors and prepare to leave. | |
* Real elevators tend to ignore calls to the current | |
* floor as long as they have somewhere else to go. | |
*/ | |
if (iter == 1 && btn.floorNum == floor && currentDir != 0) | |
continue; | |
/* | |
* If the button has a floor call in our current | |
* direction, we want to stop here. Note that if we | |
* don't have a direction, we'll take either one. | |
*/ | |
if (currentDir >= 0 && btn.calledGoingUp) | |
{ | |
/* | |
* we're answering a going-up call, so the new | |
* direction after the call is definitely 'up' | |
*/ | |
newDir = 1; | |
break; | |
} | |
if (currentDir <= 0 && btn.calledGoingDown) | |
{ | |
/* we're answering a going-down call */ | |
newDir = -1; | |
break; | |
} | |
/* if the button is lit, we want to stop here */ | |
if (btn.isLit) | |
break; | |
/* | |
* If this button is on floor call in the *opposite* | |
* direction, we don't necessarily want to stop here, | |
* since we could have another floor further in the | |
* current direction to visit first. However, if we | |
* eventually discover that we *don't* have any more | |
* calls in the current direction, we'll want to proceed | |
* to this floor to handle its call the other way. So, | |
* note it as the last on-call floor, so that we can | |
* visit it if we don't find anything else in this | |
* direction. | |
*/ | |
if (btn.calledGoingUp || btn.calledGoingDown) | |
lastCallBtn = btn; | |
/* this button isn't on call, so forget it */ | |
btn = nil; | |
} | |
/* | |
* If we found anything, and we haven't already decided on | |
* the new direction, determine the direction we'll be going | |
* after answering the call. If we have a direction, and | |
* there's any call in the current direction beyond the floor | |
* we're going to, we'll continue in the current direction. | |
* Otherwise, we have no direction. | |
*/ | |
if (newDir == nil && (btn != nil || lastCallBtn != nil)) | |
{ | |
if (btn != nil) | |
{ | |
local c; | |
/* | |
* We're answering an in-elevator call. These are by | |
* themselves non-directional; they just ask us to go | |
* to the given floor and stop. | |
* | |
* If we have no direction, we must be stopping at | |
* the same floor, so this establishes no new | |
* direction. | |
* | |
* Otherwise, if there are any other floors called in | |
* the same direction, we want to continue in the | |
* same direction. If not, we have no new direction | |
* on reaching the called floor. | |
*/ | |
if (currentDir == 0) | |
{ | |
/* no direction now, so none after the call */ | |
newDir = 0; | |
} | |
else | |
{ | |
local target = btn.floorNum; | |
/* scan for calls above/below */ | |
if (currentDir == 1) | |
c = lst.indexWhich( | |
{x: x.floorNum > target && x.isCalling}); | |
else | |
c = lst.indexWhich( | |
{x: x.floorNum < target && x.isCalling}); | |
/* | |
* if we found anything, continue in the same | |
* direction; if not, there's no new direction | |
*/ | |
newDir = (c != nil ? currentDir : 0); | |
} | |
} | |
else | |
{ | |
/* | |
* We're answering a last-call button, so the new | |
* direction is the always reverse of the current | |
* one. (We only answer last-call buttons when we | |
* have no one calling us for travel in the same | |
* direction, so this always requires a reversal.) | |
*/ | |
newDir = -currentDir; | |
} | |
} | |
/* if we're going to the last-call button, use it */ | |
if (btn == nil) | |
btn = lastCallBtn; | |
/* if we found a floor to visit, go there now */ | |
if (btn != nil) | |
{ | |
/* | |
* travel to this floor, answering the call in the | |
* current direction | |
*/ | |
goTowardFloor(door, btn, newDir, lst); | |
/* we're done */ | |
return; | |
} | |
/* | |
* We didn't find any floors further along in the current | |
* direction that are calling us. So, reverse direction, | |
* un-light all of the buttons (which real elevators always | |
* do on a direction reversal), and loop back for another | |
* try with the new direction. | |
*/ | |
currentDir = -currentDir; | |
unlightAllButtons(lst); | |
} | |
} | |
/* | |
* Move in the current direction, with the given floor button as the | |
* goal. We're answering the button's call in the direction | |
* 'answerDir', which might not be the same as the direction we're | |
* moving; for example, if we're answering a 'going down' call on a | |
* floor above us, we'll have to travel up to get there, but we'll be | |
* going down as soon as we answer the call. | |
*/ | |
goTowardFloor(door, btn, answerDir, btnList) | |
{ | |
local newFloorNum; | |
/* if we're not already at the target floor, move the elevator */ | |
if (btn.floorNum != door.otherSide.floorNum) | |
{ | |
/* if we're not already in motion, start moving */ | |
if (!isInMotion) | |
{ | |
/* mention that we're now moving */ | |
announceStart(currentDir); | |
/* we're now in motion */ | |
isInMotion = true; | |
} | |
/* proceed as far as possible toward the new floor */ | |
newFloorNum = continueElevatorMotion( | |
door.otherSide.floorNum, btn.floorNum); | |
} | |
else | |
{ | |
/* we're already there, so the new floor is the current floor */ | |
newFloorNum = btn.floorNum; | |
} | |
/* | |
* Set our door's other-side property to point to the outer door | |
* on the new floor. To do this, find an ElevatorDoor object | |
* that has our door as its other side and which has the new | |
* floor number. | |
*/ | |
forEachInstance(ElevatorDoor, new function(obj) { | |
if (obj.otherSide == door && obj.floorNum == newFloorNum) | |
door.otherSide = obj; | |
}); | |
/* if we've reached the new floor, stop moving and open the doors */ | |
if (newFloorNum == btn.floorNum) | |
{ | |
/* announce that we're stopping */ | |
announceStop(isInMotion, answerDir); | |
/* we're no longer moving, if we were */ | |
isInMotion = nil; | |
/* un-light the button for this floor inside the elevator */ | |
btn.isLit = nil; | |
/* also remove the floor call for our current direction */ | |
if (answerDir != 0) | |
cancelCallButton(door, answerDir); | |
/* if we're changing directions, unlight the buttons */ | |
if (answerDir != currentDir) | |
{ | |
/* put the new direction into effect */ | |
currentDir = answerDir; | |
/* unlight all of the internal buttons */ | |
unlightAllButtons(btnList); | |
} | |
/* open the doors */ | |
door.makeOpen(true); | |
} | |
} | |
/* unlight all of the buttons that are currently lit */ | |
unlightAllButtons(lst) | |
{ | |
/* check to see if any are lit */ | |
if (lst.indexWhich({x: x.isLit}) != nil) | |
{ | |
/* mention it */ | |
"All of the buttons go dark. "; | |
/* turn them all off */ | |
lst.forEach({x: x.isLit = nil}); | |
} | |
} | |
/* flag: our daemon is running */ | |
elevatorDaemonRunning = nil | |
/* the number of turns remaining until we close the door */ | |
doorTimer = nil | |
/* our current direction: 1 is up, -1 is down, 0 is neutral */ | |
currentDir = 0 | |
/* flag: we're currently in motion */ | |
isInMotion = nil | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* A class for standard sliding elevator doors. The master object must | |
* be set to the interior door of the associated elevator. | |
*/ | |
class ElevatorDoor: Door | |
/* | |
* Get our associated elevator room. This is the object that | |
* contains the interior door of the elevator. By default, this is | |
* simply the direct container of the other side of the door; you | |
* can override this if there's some other relationship to the | |
* elevator room. | |
*/ | |
getElevator() { return otherSide.location; } | |
/* | |
* Get the direct container of our up/down call buttons. By | |
* default, this is simply our direct location. Override this if | |
* you have some other arrangement, such as an intermediate object | |
* that contains the buttons (a control panel, for example). | |
*/ | |
buttonContainer = (location) | |
/* describe our buttons - look for our buttons in our container */ | |
buttonDesc() | |
{ | |
local btns; | |
/* scan our container's contents for our buttons */ | |
btns = buttonContainer.contents.subset( | |
{x: x.ofKind(ElevatorCallButton)}); | |
/* if we have just one, say which kind; otherwise, say we have both */ | |
if (btns.length() == 1) | |
"Next to the doors is <<btns[1].aDesc>>. "; | |
else if (btns.length() == 2) | |
"Next to the doors are <q>up</q> and <q>down</q> buttons. "; | |
} | |
/* start out closed by default */ | |
initiallyOpen = nil | |
/* | |
* we're only open if the master side is open AND we're the master | |
* side's other side (i.e., the elevator is at this floor) | |
*/ | |
isOpen = (inherited() | |
&& (masterObject == self || masterObject.otherSide == self)) | |
/* we can't manually open or close the doors */ | |
dobjFor(Open) { action() { "The elevator doors are automatic; | |
you can't open them manually. "; } } | |
dobjFor(Close) { action() { "The elevator doors open and close | |
automatically. "; } } | |
/* | |
* since we can't open the doors manually, don't make opening the | |
* doors a precondition of travel | |
*/ | |
getDoorOpenPreCond() { return nil; } | |
/* if we can't travel, it's because the doors are closed */ | |
cannotTravel() { "The elevator doors are closed. "; } | |
/* | |
* Customize the message for remote opening and closing. This is | |
* the message that will always be used for the outside doors (i.e., | |
* the doors to the elevator on a floor outside the elevator), | |
* because the interior side of the door always controls the door. | |
*/ | |
noteRemoteOpen(stat) | |
{ | |
/* note the elevator's direction */ | |
local dir = getElevator().currentDir; | |
callWithSenseContext(self, sight, | |
{: announceRemoteOpen(stat, dir) }); | |
} | |
/* announce that the doors are opening automatically */ | |
announceRemoteOpen(stat, dir) | |
{ | |
"The elevator doors slide <<stat ? 'open' : 'shut'>>. "; | |
} | |
/* the floor number on which this door is located */ | |
floorNum = 0 | |
/* "get in elevator" is the same as "go through doors" */ | |
dobjFor(Board) asDobjFor(GoThrough) | |
; | |
/* | |
* Elevator call button. This is a button positioned alongside an | |
* elevator door, outside the elevator, to summon the elevator to this | |
* floor. | |
* | |
* You must locate these buttons in the same immediate location as the | |
* associated elevator door. Other parts of the elevator will find | |
* these buttons by looking in the door's location. | |
*/ | |
class ElevatorCallButton: Button, Fixture | |
'(elevator) (lift) button*buttons' 'elevator button' | |
"It's <<isLit ? 'lit' : 'unlit'>>. " | |
dobjFor(Push) | |
{ | |
action() | |
{ | |
local door; | |
/* | |
* If one of our doors is already open on this floor, simply | |
* extend its timer. Otherwise, if we're not already lit, | |
* light up and notify the elevator of the call; otherwise, | |
* do nothing. | |
*/ | |
if ((door = getElevatorDoors().valWhich( | |
{x: x.otherSide.otherSide == x && x.isOpen})) | |
!= nil | |
&& door.otherSide.getElevator.currentDir is in (0, buttonDir)) | |
{ | |
/* extend the timer on the inner door */ | |
door.otherSide.getElevator.extendDoorTimer(); | |
/* nothing really happens */ | |
"The button lights up momentarily and then goes out. "; | |
} | |
else if (!isLit) | |
{ | |
/* light it up */ | |
"You tap the button, and it lights up. "; | |
/* notify our elevator(s) */ | |
setFloorCall(true); | |
} | |
else | |
{ | |
/* | |
* we're already lit, and no elevator is here, so this | |
* has no effect at all | |
*/ | |
"You tap the button again, but it's already lit, so | |
this has no obvious effect. "; | |
} | |
} | |
} | |
/* clear our floor call */ | |
setFloorCall(lit) | |
{ | |
/* note our new 'lit' status */ | |
isLit = lit; | |
/* notify each elevator in my list */ | |
foreach (local door in getElevatorDoors()) | |
{ | |
/* let this door's elevator know about the change */ | |
door.getElevator().noteFloorCall(door.floorNum, buttonDir, lit); | |
} | |
} | |
/* | |
* Get my associated elevator door(s). This returns a list, to | |
* allow for elevator lobbies where a single call button summons any | |
* of a bank of available elevators. By default, we return a list | |
* of all of the ElevatorDoor objects in our direct location. | |
*/ | |
getElevatorDoors() | |
{ | |
/* find the elevator door among our location's contents */ | |
return location.contents.subset({x: x.ofKind(ElevatorDoor)}); | |
} | |
/* the direction of travel we request - 1 for up, -1 for down */ | |
buttonDir = nil | |
/* flag: we're currently lit */ | |
isLit = nil | |
; | |
/* an "up" call button */ | |
class ElevatorUpButton: ElevatorCallButton '"up" -' '<q>up</q> button' | |
"It depicts an arrow pointing up. " | |
aDesc = "an <q>up</q> button" | |
buttonDir = 1 | |
; | |
/* a "down" call button */ | |
class ElevatorDownButton: ElevatorCallButton '"down" -' '<q>down</q> button' | |
"It depicts an arrow pointing down. " | |
buttonDir = -1 | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Elevator inner door - this is a subclass of the elevator door for use | |
* on the *inside* of an elevator. | |
*/ | |
class ElevatorInnerDoor: ElevatorDoor | |
/* | |
* Each instance must initialize otherSide to point to the outer | |
* door on the floor where the elevator starts. | |
*/ | |
otherSide = nil | |
/* our elevator - this is by default simply our location */ | |
getElevator = (location) | |
/* open the door */ | |
makeOpen(flag) | |
{ | |
local wasOpen = isOpen; | |
local elev = getElevator; | |
/* do the normal work */ | |
inherited(flag); | |
/* | |
* if we just opened, set the elevator's door timer to its | |
* standard open timeout | |
*/ | |
if (!wasOpen && isOpen) | |
elev.extendDoorTimer(); | |
} | |
/* | |
* Because our destination changes a lot, make it not apparent what | |
* our destination is when our doors are closed. | |
*/ | |
getApparentDestination(loc, actor) | |
{ | |
/* | |
* use the inherited apparent destination if we're open; when | |
* we're closed, our destination is not apparent at all, so | |
* simply return nil | |
*/ | |
return isOpen ? inherited(loc, actor) : nil; | |
} | |
/* | |
* Our 'otherSide' changes dynamically as we move between floors; we | |
* must initialize it explicitly in each instance to the starting | |
* floor. The different floors will try to tell us about | |
* themselves, so ignore the initMasterObject() and just use our | |
* fixed initial value. | |
*/ | |
initMasterObject(other) { } | |
; | |
/* | |
* An elevator button. This is for floor buttons with in the elevator. | |
*/ | |
class ElevatorButton: Button, Fixture 'elevator lift button*buttons' | |
/* button name - by default, this is just 'button' and the floor name */ | |
name = ('elevator button ' + floorName) | |
theName = (name) | |
aName = (name) | |
/* | |
* The floor number we're associated with - this must be set for | |
* each button instance. The floor numbers should always be | |
* contiguous integers ascending in order of the 'up' direction. | |
* Note that you can give your floors names different from the floor | |
* numbers; simply use floorName to give the floor its name. | |
*/ | |
floorNum = 0 | |
/* | |
* The name of the floor. By default, this simply returns the floor | |
* number as a string. This can be overridden if the floor names | |
* are different than their numbers; for example, you might want to | |
* use 'G' for the ground floor and 'B' for the basement. | |
*/ | |
floorName = (toString(floorNum)) | |
/* | |
* Our elevator room object - by default, this is simply our | |
* location. Override this if the button has some other | |
* relationship to the room (for example, you'll need to override | |
* this if you create an intermediate container, such as a control | |
* panel object that contains the buttons). | |
*/ | |
getElevator = (location) | |
/* flag: we're lit */ | |
isLit = nil | |
/* | |
* flags: this floor is on call from the floor itself (so we're not | |
* lit, but the elevator is on call for the floor), with a request | |
* to go up or down | |
*/ | |
calledGoingUp = nil | |
calledGoingDown = nil | |
/* | |
* are we calling the elevator to our floor, either by being lit | |
* inside the elevator or having an up or down button lit on our | |
* floor? | |
*/ | |
isCalling = (isLit || calledGoingUp || calledGoingDown) | |
dobjFor(Push) | |
{ | |
action() | |
{ | |
if (isLit) | |
{ | |
/* we're already lit, so just say so */ | |
"You tap on the button again, but it's already lit. "; | |
/* | |
* Let the elevator know a button is lit. Do this even | |
* though the button was already lit, as pressing a | |
* button has the side effect of closing the doors | |
* earlier. | |
*/ | |
getElevator.noteButtonLit(); | |
} | |
else if (getElevator.getFloorNum() == floorNum | |
&& getElevator.getElevatorDoor().isOpen) | |
{ | |
/* | |
* the door is already open at our floor; just extend the | |
* door-open timer | |
*/ | |
getElevator.extendDoorTimer(); | |
/* the visible effects are minimal, though */ | |
"The button lights momentarily as you tap on it, | |
and then goes out. "; | |
} | |
else | |
{ | |
/* note that we're lit */ | |
isLit = true; | |
/* mention it */ | |
"The button lights as you touch it. "; | |
/* tell the elevator about the change */ | |
getElevator.noteButtonLit(); | |
} | |
} | |
} | |
; | |
/* | |
* It's often nice to use a collective group to field references to an | |
* unspecified button. Elevator button groups tend to be large, so | |
* disambiguation questions involving them can get unwieldy; the | |
* collective object avoids that by using a single, unambiguous object | |
* to represent the whole collection when no particular button is called | |
* out by name. | |
* | |
* Always put the group in the same location as the individual buttons; | |
* we'll look for our individual buttons among our direct container's | |
* contents. | |
*/ | |
class ElevatorButtonGroup: CollectiveGroup, Fixture | |
'elevator lift button*buttons' 'elevator buttons' | |
/* list the list buttons */ | |
listLit() | |
{ | |
local lst; | |
/* get the list of lit call buttons */ | |
lst = location.contents.subset( | |
{x: x.ofKind(ElevatorButton) && x.isLit}); | |
/* show the list */ | |
litLister.showList(nil, nil, lst, 0, 0, nil, nil); | |
} | |
/* a lister for the lit buttons */ | |
litLister: Lister { | |
showListEmpty(pov, parent) { } | |
showListPrefixWide(cnt, pov, parent) | |
{ "Button<<cnt == 1 ? '' : 's'>> "; } | |
showListSuffixWide(cnt, pov, parent) | |
{ " <<cnt == 1 ? 'is' : 'are'>> lit. "; } | |
isListed(obj) { return true; } | |
showListItem(obj, options, pov, info) { say(obj.floorNum); } | |
} | |
/* take over all actions */ | |
isCollectiveAction(action, whichObj) { return true; } | |
/* take over all singular and unspecified quantities */ | |
isCollectiveQuant(np, requiredNum) { return requiredNum is in (nil, 1); } | |
/* for Push, don't ask which button; just say they need to say */ | |
dobjFor(Push) | |
{ | |
verify() { } | |
action() { "You'll have to say which button you mean. "; } | |
} | |
; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - Frosst Belker. Frosst is a fairly substantial | |
* NPC, so it's convenient to give him his own, separate source module. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Frosst Belker | |
*/ | |
frosst: Person 'frosst belker/man*men' 'Belker' | |
"He's a slight man of medium height and rather pale complexion. | |
You have a hard time guessing how old he is; he looks like he | |
could be anywhere from twenty-five to forty-five. He's dressed | |
in white slacks and a white double-breasted jacket. " | |
isProperName = true | |
isHim = true | |
/* | |
* because we sometimes talk on the phone, delegate our sound | |
* description to the state, if it defines a soundDesc method | |
*/ | |
soundDesc | |
{ | |
if (curState.propDefined(&soundDesc)) | |
curState.soundDesc; | |
else | |
inherited; | |
} | |
/* | |
* List frosst just after other actors. There are times when we | |
* refer to other actors, so it's best to list us after them. | |
*/ | |
specialDescOrder = 220 | |
; | |
+ InitiallyWorn 'white double-breasted breasted jacket/coat' | |
'white double-breasted jacket' | |
"It's an immaculately tailored double-breasted white suit coat. " | |
isListedInInventory = nil | |
; | |
+ InitiallyWorn 'white slacks/pants' 'white slacks' | |
"It's a well-tailored pair of white dress slacks. " | |
isPlural = true | |
isListedInInventory = nil | |
; | |
+ frosstCellPhone: PresentLater, Thing 'cell phone' 'cell phone' | |
"It's small enough that it must be a recent model, but you | |
can't really get a good look at it while he's using it. " | |
/* | |
* don't list in inventory, as we'll mention the phone specially | |
* while it's in use | |
*/ | |
isListedInInventory = nil | |
; | |
+ PresentLater, Container | |
'mitachron logo large black plastic shopping bag' 'plastic bag' | |
"It's a large plastic shopping bag with the Mitachron logo on | |
the side. " | |
plKey = 'logo-wear' | |
; | |
+ ActorState | |
isInitState = true | |
stateDesc = "He's standing here with an amused expression. " | |
specialDesc = "Frosst Belker is standing here. He has an amused | |
expression. " | |
; | |
++ DefaultAnyTopic | |
"Belker just ignores you and looks over toward the woman | |
on the phone. " | |
; | |
+ frosstTalking: ActorState | |
stateDesc = "He's here talking with Ms.\ Dinsdale. " | |
specialDesc = "Frosst Belker is here, talking with Ms.\ Dinsdale. " | |
; | |
++ DefaultAnyTopic | |
"He glances over at you, but ignores you and turns back | |
to Ms.\ Dinsdale. " | |
; | |
+ goingToStack: HermitActorState | |
stateDesc = "He's walking briskly, and talking to someone | |
on a cell phone. " | |
specialDesc = "Frosst Belker is walking briskly through while | |
talking to someone on a cell phone. " | |
noResponse = "Belker just ignores you and keeps walking. " | |
soundDesc = "He's talking too quickly and quietly; you can't | |
make out what he's saying. " | |
takeTurn() | |
{ | |
local path = [ccOffice, cssLobby, holliston, sanPasqual, | |
sanPasqualWalkway, quad, orangeWalk, | |
dabneyBreezeway, alley1S, alley1N]; | |
local idx; | |
/* wherever we are, head off to our next location */ | |
switch (frosst.location) | |
{ | |
default: | |
/* | |
* find our current location in our path, then increment it | |
* to get the next location to visit | |
*/ | |
idx = path.indexOf(frosst.location) + 1; | |
/* travel via the next connector */ | |
frosst.scriptedTravelTo(path[idx]); | |
/* | |
* if this is our final destination, switch Frosst to the | |
* state where he gets ready to start solving the stack | |
*/ | |
if (idx == path.length()) | |
frosst.setCurState(stackSetup); | |
break; | |
} | |
} | |
sayDepartingThroughPassage(conn) | |
{ | |
/* | |
* if we're just leaving the Career Center Office, say nothing, | |
* since we've already mentioned that; otherwise, use the | |
* standard handling | |
*/ | |
if (frosst.location != ccOffice) | |
inherited(conn); | |
} | |
activateState(actor, prv) | |
{ | |
/* inherit the base handling */ | |
inherited(actor, prv); | |
/* take out the cell phone */ | |
frosstCellPhone.makePresent(); | |
} | |
; | |
+ stackSetup: HermitActorState | |
stateDesc = "He's pacing around in little circles, talking | |
rapidly into his cell phone. " | |
specialDesc = "Belker is here, pacing in little circles | |
as he talks into his cell phone. " | |
noResponse = "He just ignores you and keeps talking into his phone. " | |
soundDesc = "He's talking too quickly and quietly; you can't | |
make out what he's saying. " | |
; | |
+ frosstUnpacking: HermitActorState | |
stateDesc = "He's standing in the middle of the swarm of | |
movers directing their movements. " | |
specialDesc = "Belker is standing in the middle of the swarm | |
of movers, pointing and issuing orders. " | |
noResponse = "He pays no attention to you as he orders the | |
movers around. " | |
soundDesc = "He's just issuing orders to the movers—put | |
this here, put that there, move this, unpack that. " | |
alley1Atmosphere: ShuffledEventList { [ | |
'A mover with a huge box negotiates his way around you, forcing | |
you to press yourself against the wall for a moment. ', | |
'A mover comes up behind you with a crate, waits for you | |
to get out of the way, then goes past. ', | |
'A couple of movers come through with a crate that barely fits | |
the alley\'s width. You have to duck down and let them carry it | |
over your head. ', | |
'One of the movers drops a crate to the floor with a crash. | |
Belker looks over. The mover picks up the crate and keeps going. ', | |
'A mover goes past carrying a round box. ', | |
'You have to stand aside as a couple of movers carrying | |
large boxes try to get by you. ', | |
'One of the departing movers bumps shoulders with you, but just | |
keeps going past without even glancing at you. '] | |
eventPercent = 75 | |
} | |
; | |
/* | |
* Frosst's state while initially figuring out the stack: his gang of | |
* technicians is using the MitaTest Pro 3000 to reverse-engineer the | |
* black box. | |
*/ | |
+ frosstSolving: HermitActorState | |
stateDesc = "He's slowly pacing, closely watching his technicians | |
as they operate the test equipment. " | |
specialDesc = "Belker is slowly pacing back and forth, supervising | |
the technicians as they operate the test equipment. " | |
noResponse = "He glances at you, but goes back to his pacing | |
without responding. " | |
alley1Atmosphere: ShuffledEventList { [ | |
'The technicians continue to tend to the MegaTester 3000.', | |
'Belker confers with one of the technicians for a few moments.', | |
'Three of the technicians gather around one of the MegaTester | |
monitors and talk to one another excitedly.', | |
'A loud alarm bell sounds from the MegaTester, and the technicians | |
start slapping at the controls in a panic. They manage to stop | |
the alarm after a few seconds.', | |
'The technicians busily adjust controls on the MegaTester.', | |
'A couple of the technicians tending the MegaTester switch places, | |
forcing them to squeeze past each other in the narrow space | |
alongside the machine.', | |
'One of the technicians gets on his hands and knees and looks | |
carefully under the table with the black box. He gets up and | |
returns to the MegaTester after a minute.', | |
/* | |
* include the special sub-list - we'll get a special message | |
* each time we run this entry | |
*/ | |
testOpsSubList, | |
'A technician holds out a wand-like apparatus connected by a | |
thick cable to the MegaTester, running it slowly around the | |
outside of the black box. After a couple of passes around | |
the box, he returns to the MegaTester and puts away the wand.', | |
'Belker speaks in hushed tones to a couple of the technicians.', | |
'Belker gets out his phone, says a few words into it, and returns | |
it to his pocket.', | |
'One of the technicians calls out a few numbers.', | |
'The MegaTester starts emitting a deep, rumbling sound. You can | |
feel it in the floor and you can see the walls shaking. Ten | |
seconds later the sound abruptly stops.', | |
'Several of the technicians switch places on the MegaTester, | |
squeezing past one another in the crowded hallway. ', | |
'A bright, erratic light like the sparks from a welding torch | |
flashes from somewhere behind the MegaTester for several moments.', | |
/* | |
* include the special sub-list again - this way we get a special | |
* message twice in each round of the ordinary messages | |
*/ | |
testOpsSubList] | |
eventPercent = 80 | |
} | |
; | |
/* | |
* Frosst's state for computing the answer to the stack's puzzle: his | |
* gang of technicians is using the Mitavac 3000 to figure out the | |
* Hovarth number. | |
*/ | |
+ frosstComputing: HermitActorState | |
stateDesc = "He's walking around slowly, watching the technicians | |
operate the computer. " | |
specialDesc = "Belker is walking around slowly, supervising the | |
technicians. " | |
noResponse = "He ignores you, his attention fixed on the technicians. " | |
alley1Atmosphere: ShuffledEventList { [ | |
'The technicians make adjustments on the computer. ', | |
'Belker confers quietly with one of the technicians for a | |
few moments. ', | |
'A couple of the technicians trade places on the Mitavac. ', | |
'The Mitavac\'s throbbing seems to get louder. ', | |
'The Mitavac beeps softly a few times, and one of the | |
technicians reacts by making several rapid adjustments. ', | |
'One of the technicians points something out to Belker, | |
who nods and returns to his pacing. ', | |
'Belker leans in between two of the technicians and makes | |
an adjustment to the computer. ', | |
'The Mitavac\'s throbbing seems to get a little quieter. '] | |
eventPercent = 80 | |
eventReduceAfter = 5 | |
eventReduceTo = 60 | |
} | |
beforeAction() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* check for pointlessly adjusting the dial */ | |
if (gActionIs(TurnTo) | |
&& gDobj == signalGenKnob | |
&& !(signalGen.isOn && signalGen.isAttachedTo(blackBox)) | |
&& gRevealed('hovarth-solved') | |
&& !gRevealed('black-box-solved')) | |
{ | |
/* remark on the third digit */ | |
if (++pointlessDialing == 3) | |
"You notice Belker is watching you. <q>This dialing | |
of yours, it seems quite pointless,</q> he says. <q>But | |
perhaps you have a reason to manipulate this device while | |
it is inertly << | |
signalGen.isOn ? 'turned off' : 'disconnected'>>.</q> | |
One of the technicians attracts his attention and he | |
turns away. "; | |
} | |
} | |
/* count of times we've seen pointless signal generator dialing */ | |
pointlessDialing = 0 | |
; | |
/* | |
* An atmosphere sub-list for some special one-time-only events. One | |
* entry of the main list points to this sublist, so once in a while, the | |
* sublist will come up in the random rotation. At that point, we'll | |
* move on to the next item in the sub-list, working sequentially through | |
* the items until we've exhausted the list. | |
*/ | |
+ testOpsSubList: StopEventList | |
['<q>Clear!</q> someone shouts. The technicians all rush to the | |
end of the hall and huddle with their backs to the MegaTester, | |
which is making a sound like a jet engine starting up. The sound | |
abruptly stops, and nothing happens for several moments, then | |
the hall fills with a flash of blinding light for an instant. | |
Everything seems to turn dark blue with yellow spots. You blink | |
and squint, and your vision gradually returns to normal, by which | |
time the technicians are back at their stations like nothing | |
happened.', | |
'All of the monitors on the MegaTester start flashing in unison, | |
in a blue-white-blue-white pattern that repeats several times. | |
The technicians furiously pound at the controls, and the monitors | |
return to normal.', | |
'Two of the technicians approach the black box, dragging | |
cables from the MegaTester. They carefully attach the | |
elaborate connectors on the cables to the connector on | |
the black box, then give some sort of hand signal to the | |
other technicians, who punch busily at the MegaTester | |
controls. The operators at the controls finally stop | |
and give another hand signal to the technicians with the | |
cables, who detach their cables and retract them back | |
into the MegaTester. ', | |
'<q>Prepare for reboot!</q> one of the technicians shouts. All | |
of the technicians stand back from the MegaTester and hold up their | |
arms to cover their eyes. The MegaTester makes a sound like a | |
camera flash charging, a whine that climbs in pitch until it\'s | |
too high to hear; then rumbles like distant thunder. The | |
MegaTester plays a little piezo-electric <q>welcome</q> tune, and | |
the technicians drop their poses and return to the controls.', | |
'A technician goes up to the black box with what looks | |
like a simple voltmeter. She delicately touches the | |
connector pins one at a time with the voltmeter contacts, | |
watching the meter intently. She returns to the MegaTester | |
as soon as she\'s done. ', | |
'Someone yells <q>Clear!</q> The technicians rush behind the | |
MegaTester and huddle with their backs to it. You remember | |
the last time this happened, so you tightly shut your eyes and | |
cover them with your hand. Just like last time, a jet-engine | |
screech fills the alleyway, then goes quiet. You wait for | |
the flash. Nothing for a really long time. Then something | |
happens, but it\'s not a flash of light---it\'s some kind of | |
ghostly glow that goes right through your hand and right through | |
your eyelids, and for a moment you see everything around you | |
in a near-black photo-negative, the bones in your hand black | |
against a not-quite-black alley wall, diffuse black outlines of | |
the huddled technicians in the corner, glowing black gears and | |
wheels inside the MegaTester, Belker\'s bright black cell phone | |
hanging in mid-air alongside his glowing black skeleton. The | |
X-ray vision fades and you feel a blast of heat on your face | |
and the hand that\'s still in front of your eyes. You cautiously | |
peek through your fingers and see that the technicians are all | |
back at their stations.', | |
'Two of the technicians come over and carefully look all around | |
the box, then go back to the MegaTester. '] | |
; | |
+ frosstAlleyConv: InConversationState | |
specialDesc = (stateDesc) | |
stateDesc = "Belker is standing in the alley watching you. " | |
/* don't time out of this conversation */ | |
attentionSpan = nil | |
; | |
+ ConvNode 'meet-xojo' | |
npcGreetingMsg = "<.p><q>Ah, Mr.\ Mittling.</q> You look over | |
and see that Frosst Belker has come up alongside you. | |
<q>Have you previously made acquaintance with, er, um...</q> | |
<.p><q>Probationary employee 119297, sir,</q> Xojo says | |
sheepishly, cringing like Belker's going to slap him. | |
<.p><q>Yes, of course. So, I take it you two know one | |
another?</q> " | |
noteLeaving() | |
{ | |
/* end the conversation with both Frosst and Xojo */ | |
frosst.endConversation(); | |
xojo2.endConversation(); | |
} | |
; | |
++ YesTopic | |
"<q>Xojo and I met while I was working on the Government Power | |
Plant 6 demo,</q> you say. | |
<.p><q>How nice,</q> Belker says. <q>As reluctant as I am to | |
interrupt your reminiscences, I must remind, er, 119297 of the | |
elevated standard of productivity to which we at Mitachron must | |
always strive.</q> | |
<.p><q>I am most grateful for this helpful episode of reminding, | |
sir,</q> Xojo says, then slowly moves away, working the controls. | |
Belker smiles and goes back to his pacing. " | |
; | |
++ NoTopic | |
"<q>No, I don't think so,</q> you say, hoping to help Xojo avoid | |
getting in trouble. | |
<.p>Frosst pauses and smiles faintly. <q>Perhaps you are curious | |
about our progress, then. I am sorry to have to insist on a degree | |
of secrecy because of our friendly rivalry; I am certain you | |
understand.</q> He looks at Xojo, who's slowly slinking away. | |
<q>And I am equally certain my colleagues here would more than | |
agree.</q> He shakes his head and goes back to his pacing. " | |
; | |
++ DefaultAnyTopic | |
"<q>I am very much more interested in your acquaintance with | |
my colleague here. Do you in fact know one another?</q> | |
<.convstay> " | |
; | |
/* | |
* Frosst's state for near the end of the game, when we're entering | |
* digits into the black box. This is an in-conversation state because | |
* we want to revert to our prior state when the PC wanders off or stops | |
* interacting with us. | |
*/ | |
+ frosstWatchingDigits: InConversationState | |
specialDesc = "Frosst Belker is standing near the black box, | |
watching what you're doing. " | |
stateDesc = "He's standing near the black box watching you. " | |
/* always revert back to the 'computing' state when we give up */ | |
nextState = frosstComputing | |
/* we get bored in this state quickly */ | |
attentionSpan = 2 | |
/* note that we're entering a digit */ | |
noteDigitEntry() | |
{ | |
/* | |
* if the PC is here, and frosst isn't already in this state, | |
* switch frosst to this state | |
*/ | |
if (me.isIn(alley1N) && frosst.curState != self) | |
{ | |
/* set this state */ | |
frosst.setCurState(self); | |
/* note Frosst's approach */ | |
"<.p>Frosst Belker wanders over to watch what you're doing. "; | |
} | |
} | |
/* generate our random comments */ | |
takeTurn() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* if we're still in this state, do some more work */ | |
if (frosst.curState == self) | |
{ | |
/* if the signal generator is off or detached, wander off */ | |
if (!signalGen.isOn || !signalGen.isAttachedTo(blackBox)) | |
{ | |
/* end the conversation */ | |
endConversation(me, endConvBoredom); | |
/* we're done */ | |
return; | |
} | |
/* if we haven't conversed yet this turn, generate a comment */ | |
if (!frosst.conversedThisTurn()) | |
commentScript.doScript(); | |
} | |
/* reset our boredom counter */ | |
frosst.boredomCount = 0; | |
} | |
/* our random comments */ | |
commentScript: ShuffledEventList { | |
/* we'll go through this first list once, in this order */ | |
['<q>So, Mr.\ Mittling,</q> Belker says, <q>you have deduced | |
the method by which digits are entered, I see.</q> ', | |
'Belker clucks his tongue. <q>If I were you, Mr.\ Mittling, | |
I would not be wasting the scant time remaining trying digits | |
at random.</q> ', | |
'Belker shakes his head. <q>Your determination is admirable,</q> | |
he says, <q>but surely you realize the impossibility of solving | |
Mr.\ Stamer\'s riddle by exhaustive enumeration.</q> ', | |
'<q>At this pace,</q> Belker says, <q>you might find the number | |
you are seeking in, perhaps, thirty years.</q> ', | |
'<q>You know,</q> Belker says, <q>my engineers will have the | |
correct number very soon. Perhaps I could persuade them to | |
allow you to use their equipment afterwards. It would, of | |
course, be too late for you to win, but at least you might have | |
the satisfaction of solving the riddle for yourself.</q> ', | |
'Belker chuckles. <q>Mr.\ Mittling, I begin to wonder if you | |
even know what riddle you are attempting to solve. Given the | |
lateness of the hour, I think it safe to offer you a small | |
hint: it has something to do with a mathematician named | |
Hovarth.</q><.reveal frosst-hovarth-hint> '] | |
/* ...then we'll show this list in shuffled order, if we need more */ | |
['<q>Mr.\ Mittling,</q> Belker says, <q>I find it nearly | |
unbearable to watch such an earnest display of futility.</q> ', | |
'Belker sighs. <q>I cannot bear to watch these pointless | |
exertions of yours for much longer.</q> ', | |
'<q>This futile effort of yours is exhausting to watch,</q> | |
Belker says. ', | |
'<q>Surely you must be tiring of this hopeless, random | |
flailing,</q> Belker says. '] | |
} | |
/* | |
* our atmosphere list - we'll provide atmosphere with our comments, | |
* not through random background messages, so we don't need to do | |
* anything here | |
*/ | |
alley1Atmosphere: Script { } | |
endConversation(actor, reason) | |
{ | |
/* do the normal work */ | |
inherited(actor, reason); | |
/* mention that we're returning to work */ | |
"<.p>Frosst goes back to supervising the technicians. "; | |
} | |
; | |
/* | |
* There's no point in having too many responses in this state, as most | |
* players at this point will be highly motivated to plow on through and | |
* enter the number without interruption. However, provide a few | |
* responses just in case... | |
*/ | |
++ AskTellTopic @hovarthTopic | |
"You assume that Belker's technicians have managed to read the | |
message from the black box, but you don't want to give anything | |
away in case they haven't. " | |
isConversational = nil | |
; | |
+++ AltTopic | |
"<q>I know all about Hovarth numbers,</q> you boast. | |
<.p>Belker raises his eyebrows. <q>Indeed,</q> he says. | |
<q>Then I should think you would be marshalling the services of a | |
large supercomputer, as I have, rather than persisting in this | |
random tinkering.</q> " | |
isActive = gRevealed('frosst-hovarth-hint') | |
; | |
++ DefaultAnyTopic, ShuffledEventList | |
['Before you can start talking, Belker turns away to talk | |
momentarily to one of the technicians. ', | |
'Something on the Mitavac diverts Belker\'s attention as you | |
try to talk to him. ', | |
'Belker seems distracted by the black box; he just ignores you. '] | |
isConversational = nil | |
; | |
/* state when in room 4 during the endgame */ | |
+ frosstRoom4: ActorState | |
specialDesc = "Frosst Belker is standing next to you. " | |
stateDesc = "He's standing next to you. " | |
; | |
++ DefaultAnyTopic | |
"Belker waves his hand at you dismissively. <q>Not now, | |
Mr.\ Mittling,</q> he says. " | |
; | |
/* in any communicative state, we should at least recognize Galvani-2 */ | |
+ AskTellTopic [efficiencyStudy37, galvaniTopic] | |
"<q>What do you know about Project Galvani-2?</q> you ask. | |
<.p>Belker raises his eyebrows slightly. <q>Project | |
Galvani-2?</q> he asks. He strokes his chin. <q>I am afraid | |
this means nothing to me.</q> " | |
isActive = (efficiencyStudy37.seen) | |
; | |
+ GiveShowTopic @efficiencyStudy37 | |
"<q>How do you explain this?</q> you ask, holding up the binder. | |
<.p>Belker gives it a cursory glance, then waves it away. | |
<q>I am unfamiliar with this,</q> he says dismissively. | |
<q>If I were you, though, I would be careful with such matters. | |
You might arouse unwelcome curiosity about how you came into | |
possession of such things.</q> " | |
; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - Hints. This implements the on-line hints | |
* provided with the game. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The main hint menu | |
*/ | |
topHintMenu: TopHintMenu | |
'Select the general area of your question' | |
; | |
/* | |
* Provide help on the hint system itself | |
*/ | |
+ HintLongTopicItem 'Hint System Overview' | |
'''It's tricky to give good hints for IF games. Hints can spoil | |
the fun if they give away too much, but at the same time, it's | |
no frustrating being stuck. We've tried to strike a balance here | |
by putting you in control of how strong a hint you receive. | |
<.p> | |
Hints are presented in a question-and-answer format, with the | |
questions grouped into story areas. To avoid revealing potential | |
plot developments too early, the system tries to show you only | |
the topics you ought to know about already, based on where | |
you are in the story. | |
<.p> | |
Select the general area where you're stuck, and you'll be given | |
a list of questions. Select the question closest to what you're | |
trying to figure out, and the game will start showing you | |
hints for that question, one hint at a time. The first hint | |
or two will be very vague and general and usually won't give | |
anything away. Each additional hint will get more specific, and | |
the last hint for each question usually gives away the exact solution. | |
You can stop after any hint---just press <b>Q</b> and you'll be | |
returned to the story. Try to stop at the first hint that gives | |
you any new ideas, to avoid revealing too much at once; you can | |
always come back to the same topic later and see more hints if | |
you need to. | |
<.p> | |
Be aware that most people enjoy IF a lot more when they solve all | |
of the puzzles for themselves, without any hints. I've tried to | |
design the puzzles in this game to be fair, so that most players | |
can keep the story moving without any impossible leaps of logic, | |
random guesswork, or feats of telepathy (that is, reading the | |
author's mind). If you do get horribly stuck, though, the hints | |
are here for you; just keep in mind that hints belong at the very | |
top of the IF dietary pyramid---<q>use sparingly.</q> ''' | |
/* make sure this is the first item in the menu */ | |
topicOrder = 1 | |
; | |
+ HintLongTopicItem 'A Few Words of Advice' | |
'''This game is designed to be fun to play, not to frustrate you. | |
If you've played other text adventures, especially older | |
<q>classic</q> games, you might have gotten the impression that | |
IF authors take cruel delight in stumping players and randomly | |
killing main characters. Some do, certainly, but this isn't | |
that kind of game. | |
<.p> | |
There are two things I'd like you to know and to keep in mind | |
while you play. | |
<.p> | |
First, you might encounter some engineering and physics jargon | |
in the course of the game. Don't let it scare you---just think | |
of it as <q>technobabble,</q> the way they're always going on | |
about <q>nutating the shield harmonics</q> and what not in <i>Star | |
Trek</i>. You don't need a background in physics to play - in | |
fact, you're probably better off without it, since the <q>science</q> | |
in the story is mostly made up anyway. You can rely on your | |
character to figure things out and fill you in. | |
<.p> | |
Second, you don't have to worry about <q>losing</q> the game. | |
There are no random deaths for the main character, and there | |
shouldn't be any way to make the game unwinnable. You don't | |
have to worry that a seemingly innocuous action will turn out, | |
hours later, to have ruined the game. So feel free to explore | |
and experiment---no matter what you do, the game should always | |
remain winnable. ''' | |
/* keep this just after the 'hint system overview' topic */ | |
topicOrder = 10 | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Miscellanous general topics | |
*/ | |
+ HintMenu 'General Questions' | |
/* keep this just after the 'a few words of advice' topic */ | |
topicOrder = 20 | |
; | |
++ Goal 'What does this have to do with Ditch Day?' | |
['''Don't worry; you'll get to that part soon enough.'''] | |
goalState = OpenGoal | |
/* stop showing this hint as soon as we get the ditch day lecture */ | |
closeWhenRevealed = 'ditch-day-explained' | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Hints for the prologue | |
*/ | |
+ HintMenu 'Government Power Plant #6'; | |
/* | |
* A general hint topic for the main goals right now. In any adventure | |
* game, one of the big problems some players experience is that they | |
* don't know what they're supposed to be doing in general, or they | |
* misunderstand what they're supposed to be doing. A high-level goals | |
* topic like this can help avoid that sort of frustrating aimlessness. | |
* | |
* The "->" indicates that this goal is achieved as soon as the "->" | |
* expression is true; in this case, we point to the scoreMarker for the | |
* scu1100dx object, which means that we'll consider this goal achieved | |
* as soon as that achievement is scored. When the goal is achieved, | |
* it'll be automatically "closed," which means it won't show up in the | |
* hint menus any longer. Once we've accomplished a goal, there's no | |
* reason to give hints for that goal any longer. | |
*/ | |
++ Goal ->(scu1100dx.scoreMarker) | |
'What am I supposed to be doing?' | |
['''You're here to give the customer a demo of the SCU-1100DX.''', | |
'''You can't demo it until you get it working.''', | |
'You have to repair the SCU-1100DX.'] | |
/* make this goal open from the start of the game */ | |
goalState = OpenGoal | |
/* put this at the top of the hint list */ | |
topicOrder = 100 | |
; | |
/* | |
* explain how to repair the SCU; close this goal as soon as we've | |
* awarded the points for repairing the SCU | |
*/ | |
++ Goal ->(scu1100dx.scoreMarker) | |
'How do I repair the SCU-1100DX?' | |
['Have you looked at it carefully?', | |
'Did you notice the empty slot?', | |
'What goes in the empty slot?', | |
'Have you looked at the CT-22 carefully?', | |
'''There's an incorrect component in the CT-22.''', | |
hint772lv, | |
'Once you put the XT772-LV in the CT-22, just put the CT-22 | |
back in the SCU-1100DX and turn on the SCU-1100DX.'] | |
goalState = OpenGoal | |
; | |
/* | |
* A 'Hint' object is a line item with a list of answers for a Goal. In | |
* most cases, we don't bother with a separate Hint object, but just use | |
* a string. In this particular case, though, we want to make sure that | |
* we make the XT772-LV hints available as soon as we mention the | |
* XT772-LV in a hint (we otherwise wouldn't have mentioned it until the | |
* player saw the ct-22 description). Using a separate Hint object lets | |
* us do this via our 'referencedGoals' property, which points to one or | |
* more goals that we need to mark as open as soon as we show this hint. | |
*/ | |
+++ hint772lv: Hint | |
hintText = 'You need to replace the XT772-HV with an XT772-LV.' | |
referencedGoals = [goal772lv] | |
; | |
/* | |
* A hint topic for finding the xt772-lv. Don't make this hint | |
* available until the player has discovered that they need the XT772-LV | |
* in the first place - they should be able to realize this as soon as | |
* they've seen the xt772-hv. | |
*/ | |
++ goal772lv: Goal ->(scu1100dx.scoreMarker) | |
'Where can I find an XT772-LV?' | |
['Have you looked at everything here carefully?', | |
'Have you looked closely at your own equipment?', | |
'Have you looked closely at your circuit tester?', | |
'Did you read the warning labels?', | |
'Did you notice that the circuit tester has a removable back cover?', | |
'''You're supposed to be an engineer, right? Would an engineer | |
pay attention to a warning not to open the back cover?''', | |
'''Try opening the circuit tester's back cover.'''] | |
closeWhenSeen = xt772lv | |
/* open the goal when the CT-22 is examined or the LV is mentioned */ | |
openWhenTrue = (ct22.described || xt772lv.isMentioned) | |
; | |
++ Goal 'What do I do now?' | |
['You were supposed to find someone.', | |
'You were supposed to find Colonel Magnxi.', | |
'Someone was going to take you to the Colonel.', | |
'''Just stick with Xojo; he'll lead you to the Colonel.'''] | |
openWhenAchieved = (scu1100dx.scoreMarker) | |
closeWhenSeen = adminLobby | |
; | |
++ Goal 'I found Colonel Magnxi. Now what?' | |
['You were supposed to get the contract signed, right?', | |
'Try showing her the contract.', | |
'Or, you can just ask her about anything, or even just TALK TO her.'] | |
openWhenSeen = adminLobby | |
closeWhenAchieved = (magnxi.scoreMarker) | |
; | |
++ Goal '''I can't get Colonel Magnxi's attention.''' | |
['Maybe you just have to try harder.', | |
'Or maybe you just have to try again.', | |
'''Keep trying to talk to her, and you'll eventually get her attention.'''] | |
openWhenSeen = adminLobby | |
closeWhenAchieved = (magnxi.scoreMarker) | |
; | |
++ Goal 'Uh, now what?' | |
['Just read the print-out Xojo gave you.'] | |
openWhenTrue = (!adminEmail.isIn(nil)) | |
closeWhenTrue = (adminEmail.isIn(nil)) | |
; | |
++ Goal 'Is there a way to avoid getting stuck in the elevator?' | |
['Did you see any stairs?', | |
'''There are no stairs; the elevator is the only way down. | |
You can't avoid it, and you can't keep it from getting stuck, | |
so you'll just have to find a way out once it's stuck.'''] | |
openWhenTrue = (plantElevator.seen && plantElevator.isAtBottom) | |
closeWhenAchieved = (powerElevPanel.scoreMarker) | |
; | |
++ Goal 'The elevator is stuck. What do I do now?' | |
['Have you looked around carefully?', | |
'''The gate doesn't open here, but maybe there\'s another way out.''', | |
'Do you see anything interesting on the ceiling?', | |
'How about the service panel?', | |
'Maybe you could escape through the service panel.', | |
'The only problem is the service panel is too high to reach.', | |
'''Maybe there's something you could stand on, or something you | |
could climb.''', | |
'Try standing on the handrail, or climbing the gate.', | |
'''Standing on the handrail and climbing the gate won't work, | |
but notice that Xojo will offer to help you up when you try.''', | |
'STAND ON HANDRAIL. When Xojo asks if you want a lift, say YES.', | |
'Once Xojo has lifted you up, just open the panel and go UP.'] | |
openWhenTrue = (plantElevator.seen && plantElevator.isAtBottom) | |
closeWhenAchieved = (powerElevPanel.scoreMarker) | |
; | |
++ Goal '''I'm out of the elevator, but now I'm stuck in the shaft.''' | |
['Did you try the door?', | |
'Okay, so the door is locked. Take a look at it more carefully.', | |
'''There's a locking mechanism on the door. Examine it.''', | |
'''The locking mechanism opens when the elevator car inserts | |
something into the slot. Maybe there's something else you | |
could insert to trip the lock.''', | |
'You need something thin enough to fit the slot but strong | |
enough to trip the lock.', | |
'Something like a piece of sheet metal.', | |
'The cover for the service match will work. Put it in the slot.', | |
'Now just go through the door.'] | |
openWhenSeen = atopPlantElevator | |
closeWhenAchieved = (doorS2outer.scoreMarker) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* General hints for the campus | |
*/ | |
+ HintMenu 'On Campus'; | |
++ Goal 'How do I find my way around campus?' | |
['Have you looked in your tote bag yet?', | |
'Did you notice the campus map?', | |
'Look at the campus map.', | |
'''The campus map shows the locations of the buildings on campus. | |
To find your way to a building, just type FIND <i>building</i> | |
ON MAP. Note that you have to be outdoors to do this, because | |
the map doesn't include interior floor plans of any buildings. | |
The map also won't help you find your way around inside any | |
building; you'll have to rely on exploration for that.'''] | |
openWhenSeen = sanPasqual | |
; | |
++ Goal 'Just how big is the campus?' | |
['''The campus has quite a few locations. If you're not sure | |
what to do next, you might want to just spend a little time | |
wandering around, exploring. Most of the buildings aren't | |
too large inside, but a couple---particularly Dabney | |
House---are big enough that you might want to make a map.'''] | |
openWhenSeen = spWalkway | |
; | |
++ Goal 'What am I doing at Caltech?' | |
['''You're here to interview a student for a job at your company.'''] | |
openWhenSeen = sanPasqual | |
; | |
++ Goal 'Is there any way to get the Netbisco 9099?' | |
['Did you talk to the workers yet?', | |
'''Ask them something, then WAIT for a few turns. You'll overhear | |
some things they say to one another.''', | |
'Notice that they have a co-worker down in the shaft.', | |
'Notice that they hand stuff to their co-worker from time to time.', | |
'Did you look at the shaft carefully?', | |
'The shaft reportedly connects to the steam tunnels. Maybe you could | |
reach the bottom of the shaft from the Bridge steam tunnels.', | |
'From the Bridge steam tunnel entrance, go eastward (jogging north | |
when necessary) until you find Plisnik.', | |
'Maybe if you can get rid of Plisnik, you can get the workers on | |
the surface to hand you the network analyzer.', | |
'Once you get rid of Plisnik, all you have to do is ask the workers | |
for the Netbisco 9099.'] | |
openWhenTrue = (quadAnalyzer.described) | |
closeWhenTrue = (netAnalyzer.moved) | |
; | |
++ Goal 'How do I get past the counter in the Physical Plant office?' | |
['Think about what made you want to get back there.', | |
'Come to think of it, did anything actually make you think you | |
had any business back there?', | |
'''You really don't need to get back there.'''] | |
openWhenSeen = ppOffice | |
; | |
++ Goal 'How do I communicate with the electrician?' | |
['''He only seems to speak German, which your character doesn't | |
speak, so there's not much you can talk about with him.''', | |
'You could at least show him things, though.', | |
'There are a couple of things lying around meant just for him.', | |
'Have you been inside the Physical Plant office yet?', | |
'Did you look at the job cards?', | |
'A couple of the job cards are meant for an electrician. You could | |
try giving him one of those.'] | |
openWhenDescribed = ernst | |
closeWhenTrue = (ernst.location == nil) | |
; | |
++ Goal 'Can I get the gardener to let me use the cherry picker?' | |
['You could always just try asking him for it.', | |
'''Okay, so he won't willingly let you have it. Have you been | |
inside the Physical Plant office yet?''', | |
'Did you look at the job cards?', | |
'A couple of the job cards are meant for a gardener. You could | |
try giving him one of those.', | |
'Did you read the memo in the Physical Plant office?', | |
'Have you run into anyone named Ernst yet?', | |
'What does the memo say you should never allow to happen?', | |
'Maybe you could arrange for that to happen.', | |
'''Unfortunately, the shift supervisor who created the job cards | |
paid attention to the memo and didn't arrange for any gardening | |
jobs to coincide with any electrical jobs. But there's another | |
thing that you're supposed to avoid.''', | |
'Maybe you can make the Gunther and Ernst cross paths on the | |
way to new jobs.', | |
'It takes a little care with timing, but you can make them both | |
cross the Quad at the same time.', | |
'Did you notice that Ernst always takes a little while to get going?', | |
'Ernst takes long enough to get going that you can dispatch him | |
to a new job, then immediately go find Gunther and dispatch him. | |
If you time things properly, the two will reach the Quad at | |
the same time.', | |
'''It's important to get the timing right.''', | |
'''The Quad is too far away from where Gunther and Ernst start out, | |
so you can't make the timing work from there.''', | |
'Gunther and Ernst both have two locations you can send them. | |
First send them both to their other location, then send them | |
back. You can make the timing work out on the return trip.' | |
] | |
openWhenSeen = cherryPicker | |
closeWhenTrue = (gunther.location == nil) | |
; | |
++ Goal 'How can I get to the Guggenheim wind tunnel?' | |
['''It's on the roof of the building. But you can't seem to | |
get inside to take the stairs.''', | |
'''Maybe there's a way to get up there without going inside | |
the building.''', | |
'Have you looked carefully at the building?', | |
'Have you noticed that Guggenheim is attached to another building?', | |
'Did you look carefully at Firestone?', | |
'That latticework on Firestone might be worth another look.', | |
'People have been known to climb that latticework.', | |
'''The only catch is that you need a way to reach the latticework, | |
since it doesn't start until well above ground level.''', | |
'You need something that will get you up far enough to reach | |
the grill.', | |
'Something that lifts people a little ways above the ground.', | |
'''You should explore around the campus grounds a little more if | |
you haven't seen anything that might help.''', | |
'Look up Beckman Auditorium on the campus map and go look around.', | |
'Maybe that cherry picker would be of use.'] | |
openWhenSeen = wowWindTunnel | |
closeWhenSeen = guggenheimRoof | |
; | |
++ Goal 'How do I get into the wind tunnel?' | |
['Did you look carefully at the structure?', | |
'Did you examine the small panel?', | |
'Try opening the panel.'] | |
openWhenSeen = guggenheimRoof | |
closeWhenSeen = windTunnel | |
; | |
++ Goal 'How do I get into the Campus Network Office?' | |
['Did you read the sign?', | |
'It says the office will be open again at 1:00 pm. Maybe you | |
should take it at face value.', | |
'You have plenty of other stuff to do until then; try working | |
on your stack for a while.', | |
'''Lunch is usually at noon. Once you finish with lunch, it | |
should be around one o'clock; check back then.'''] | |
openWhenSeen = jorgensenHall | |
closeWhenSeen = networkOffice | |
; | |
++ Goal '''What's the deal with Mr.\ Happy Gear?''' | |
['''It's just a little in-joke reference for people who remember | |
the original <i>Ditch Day Drifter</i>.''', | |
'Mr.\ Happy Gear was one of the random objects you had to find | |
to solve the Ditch Day stack in the original <i>DDD</i>.', | |
'''Other than that, it's completely unimportant.'''] | |
openWhenDescribed = mrHappyGear | |
; | |
++ Goal '''What's with all the <q>Hovse</q> misspellings?''' | |
['Did you look at the decorative stonework outside Dabney on | |
the Orange Walk?', | |
'''It's just a little joke about the student hovses.''', | |
'''The hovses feature stonework with inscriptions designed | |
to look like the inscriptions on classical Roman buildings. The | |
Roman alphabet in classical times didn't have a U; V had roughly | |
the phonetic value of the modern U. So the hovse builders substituted | |
V for U in all of the instructions, thus HOVSE for HOUSE.''', | |
'''It's always seemed like a silly affectation to me; I mean, | |
why worry about one letter when the entire word is historically | |
inaccurate? If they had <i>really</i> wanted to be historically | |
accurate, they might have written something like DOMVS DABNEIIS.'''] | |
openWhenSeen = orangeWalk | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Hints for the Sync Lab | |
*/ | |
+ HintMenu 'The Sync Lab'; | |
++ Goal 'How do I get into the Sync Lab?' | |
['''There doesn't seem to be any way of unlocking the door in | |
the parking lot.''', | |
'Maybe you need an indirect approach.', | |
'''Don't worry too much about this for now; something might | |
occur to you after you've explored more.'''] | |
openWhenSeen = syncLot | |
closeWhenSeen = firestoneRoof | |
; | |
++ Goal 'How do I get into the Sync Lab?' | |
['You should do some more exploring in the general area.', | |
'What else seems nearby?', | |
'Firestone is nearby, but it blocks the way in.', | |
'Maybe you could get past Firestone somehow.', | |
'Think three-dimensionally.', | |
'Try exploring some rooftops more thoroughly.', | |
'Specifically, the roof of Firestone.', | |
'Check to see where that ladder goes.'] | |
openWhenSeen = firestoneRoof | |
closeWhenSeen = syncLabRoof | |
; | |
++ Goal 'How do I get into the Sync Lab?' | |
['''You've already technically been to the Sync Lab.''', | |
'''You just haven't been inside yet.''', | |
'''You've been on the roof of the building. Maybe there's a | |
way inside from there.''', | |
'Look at the rectangular fixture.', | |
'Open the door and go inside.'] | |
openWhenSeen = syncLabRoof | |
closeWhenSeen = syncCatwalkSouthWest | |
; | |
++ Goal 'How do I cross the gap in the catwalk?' | |
['''It looks like there's a drawbridge, but unfortunately it seems | |
to be inoperable.''', | |
'''Maybe there's something else you could use to bridge the gap.''', | |
'Explore the lab a bit.', | |
'Did you notice anything standing out among all the crates and | |
boxes?', | |
'Maybe one unusual crate?', | |
'Look at the metal crate in the Open Area.', | |
'Did you notice anything unusual about, other than its size?', | |
'''It's raised off the floor slightly; maybe you should look | |
under it to find out why.''', | |
'''It's on wheels, so maybe you can move it.''', | |
'''You can't move it from the Open Area, but maybe you can get | |
around behind it and dislodge it.''', | |
'Go north and southwest from the Open Area. There it is again.', | |
'Try pushing it.', | |
'Now that you have it free, maybe you could use it somehow.', | |
'It looks pretty large---maybe even big enough to bridge that gap.', | |
'Try pushing it north from the Open Area.'] | |
openWhenSeen = syncCatwalkGapWest | |
closeWhenTrue = (metalCrate.isIn(syncLab3)) | |
; | |
++ Goal 'How do I get into the office?' | |
['It looks like you need a combination to open the door.', | |
'''Don't worry too much about it for now; should you find | |
out you need to get in there, you'll also learn more | |
about how to do so.'''] | |
openWhenSeen = syncCatwalkEast | |
closeWhenSeen = syncLabOffice | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Hints for the initial campus section: finding our way to the career | |
* center office | |
*/ | |
+ HintMenu 'The Interview'; | |
++ Goal 'Where am I now?' | |
['''You're not anywhere near the power plant.''', | |
'''You're on the Caltech campus in Pasadena, California. Three weeks | |
have passed since the previous scene. You're here to interview a | |
candidate for a job at your company.'''] | |
openWhenSeen = sanPasqual | |
closeWhenSeen = ccOffice | |
; | |
++ Goal '''How do I find the person I'm supposed to interview?''' | |
['''You're supposed to go to the Career Center Office first. | |
They'll put you in touch with the candidate.'''] | |
openWhenSeen = sanPasqual | |
closeWhenSeen = ccOffice | |
; | |
++ Goal 'How do I find the Career Center Office?' | |
['The Career Center Office is in the Student Services building on | |
Holliston Avenue.', | |
'''You can't get too lost at this point; just go north from | |
San Pasqual, east off of Holliston, and south from the lobby.'''] | |
openWhenSeen = sanPasqual | |
closeWhenSeen = ccOffice | |
; | |
++ Goal '''How do I get the woman's attention?''' | |
['It would be rude to interrupt her phone call.', | |
'''Don't worry about it; just hang out a while and she'll eventually | |
finish her call and come talk to you.''', | |
'''Why not read some of the brochures (on the literature rack) | |
while you're waiting?'''] | |
openWhenSeen = ccOffice | |
closeWhenTrue = (ccDinsdale.introduced) | |
; | |
++ Goal 'What do I do now?' | |
['Just talk to Ms.\ Dinsdale for a bit.'] | |
openWhenRevealed = 'ditch-day-explained' | |
closeWhenRevealed = 'stack-accepted' | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Hints for the stack | |
*/ | |
+ HintMenu 'Ditch Day'; | |
++ Goal '''What's this Ditch Day thing about again?''' | |
['''It's an annual event where all of the seniors ditch their | |
classes for the day and go to the beach, leaving behind <q>stacks</q> | |
outside their rooms to keep out the underclassmen.'''] | |
openWhenRevealed = 'stack-accepted' | |
topicOrder = 100 | |
; | |
++ Goal 'What should I be doing?' | |
['You\'re trying to solve Stamer\'s stack, so that you can hire | |
him into Omegatron.'] | |
openWhenRevealed = 'stack-accepted' | |
topicOrder = 110 | |
; | |
++ Goal 'Where\'s this so-called <q>stack</q> I\'m supposed to find?' | |
['Ms.\ Dinsdale mentioned where Stamer lives. That\'s where | |
you\'re going.', | |
'You\'re going to Dabney House, room 4.', | |
'If you\'re not sure where Dabney House is, have you looked inside | |
your tote bag?', | |
'You have a map of the campus, so just FIND DABNEY HOUSE ON MAP. | |
(You\'ll need to be outdoors before this will work, though, because | |
the map doesn\'t show any interior floor plans.)'] | |
openWhenRevealed = 'stack-accepted' | |
closeWhenSeen = alley1N | |
; | |
++ Goal 'How do I get the key from Erin?' | |
['Have you asked her for it?', | |
'She won\'t give it to you as long as she\'s working on the stack, | |
but don\'t worry: she won\'t be working on the stack for long. | |
Just stay near her for a couple of turns.'] | |
openWhenRevealed = 'erin-has-key' | |
closeWhenTrue = (!labKey.isIn(erin)) | |
; | |
++ Goal 'What am I supposed to do with the <q>black box</q>?' | |
['Did you look at it?', | |
'When you examined the black box, the story gave you some ideas | |
for test gear that you\'ll need.', | |
'You need an oscilloscope and a signal generator.', | |
'Did you read the sign on Brian Stamer\'s door?', | |
'Brian left the equipment you\'ll need in his lab.'] | |
openWhenSeen = alley1N | |
closeWhenTrue = (blackBox.equipGathered) | |
; | |
++ Goal 'How do I figure out what the <q>black box</q> does?' | |
['You gathered the test equipment, right?', | |
'Maybe you should use the test equipment.', | |
'You might start by plugging the oscilloscope into the black box.', | |
'It would also help to turn on the oscilloscope.', | |
'Does that suggest anything else you could try?', | |
'Try plugging the signal generator into the black box while the | |
oscilloscope is still attached.', | |
'Turn on the signal generator, of course. <b>Then</b> plug | |
in the oscilloscope.', | |
'Okay, so that didn\'t exactly solve the stack, but at least it | |
gives you an idea of what to do next.', | |
'You need to do some background reading and get some practice | |
working with circuitry.', | |
'Once you do all that, just try probing with the signal generator | |
and oscilloscope again.'] | |
openWhenTrue = (blackBox.equipGathered) | |
closeWhenTrue = (blackBox.timesRead != 0) | |
; | |
++ Goal 'Where would I find a <q>good EE textbook</q>?' | |
['What\'s a good place to find books in general?', | |
'Maybe a big building devoted to housing lots of books...', | |
'The library, for example.', | |
'If you haven\'t been to the library yet, go outside and | |
find the library on your map.', | |
'You might need someone to recommend a book (see the separate | |
hints on that.)'] | |
openWhenRevealed = 'need-ee-text' | |
closeWhenSeen = morgenBook | |
; | |
++ Goal 'Where can I get a recommendation for a good EE textbook?' | |
['Maybe there\'s someone on campus who could help.', | |
'Maybe a student.', | |
'Have you met any electrical engineering students?', | |
'Erin and Aaron mentioned they were EE students. Go ask them.', | |
'If you\'re not sure where they are, just wander around Dabney | |
a bit.', | |
'They\'re working on a stack in Alley 7.', | |
'From the courtyard, go up the stairs, then north, and north again.'] | |
openWhenRevealed = 'need-ee-text' | |
closeWhenSeen = morgenBook | |
; | |
++ Goal 'Where can I get some practice debugging circuitry?' | |
['Explore Dabney a little; maybe you\'ll come across something.', | |
'Check the locations near the courtyard.', | |
'You\'re looking for some kind of electronic device that\'s in | |
need of repairs.', | |
'Something out of order.', | |
'Go into the alcove off the courtyard.', | |
'The Positron game needs repairs.', | |
'Be sure to look carefully at the machine. In particular, take | |
note of the instruction card.'] | |
openWhenRevealed = 'need-ee-text' | |
closeWhenRevealed = 'positron-repaired' | |
; | |
++ Goal 'How do I find Scott?' | |
['You might start by going to his room.', | |
'The instruction card mentioned that he\'s in room 39.', | |
'Room 39 is in Alley 7.', | |
'From the courtyard, go up the stairs, then go north, and north again.', | |
'Try knocking on his door.', | |
'If there\'s no one else around right now, you should hold off | |
for a while---go back and work on Stamer\'s stack for a bit.', | |
'If Erin and Aaron are around, just ask them about Scott.'] | |
openWhenRevealed = 'find-scott' | |
closeWhenTrue = (scott.moved) | |
; | |
++ Goal 'Where can I find electronic test equipment?' | |
['Did you read the sign on Brian Stamer\'s door?', | |
'Brian left the needed equipment in his lab.', | |
'The sign mentions that his lab is in Bridge, room 022. (Bridge | |
is a building on campus---you can find it on your campus map | |
if you\'re not sure how to get there.)'] | |
openWhenRevealed = 'needed-equip-list' | |
closeWhenTrue = (blackBox.equipGathered) | |
; | |
++ Goal 'Oscilloscopes? Signal generators? I\'m not an EE major!' | |
['Relax---you don\'t need to know anything about electronics | |
to play the game. Your <i>character</i> might have to know | |
some electronics, but you don\'t.', | |
'You don\'t even have to worry about the details of running | |
the test equipment; your character will take care of that.'] | |
openWhenTrue = (gRevealed('needed-equip-list') | |
|| oscilloscope.moved | |
|| signalGen.moved) | |
closeWhenRevealed = 'black-box-solved' | |
; | |
++ Goal 'How can I get past the smoke in Alley One?' | |
['Maybe you just need to wait a while.', | |
'Did you run into anyone in the breezeway?', | |
'What time did Erin say it was?', | |
'Erin said it\'s lunchtime. You might as well get some lunch | |
while you\'re waiting for the fire to be put out.', | |
'Go to the Dabney dining room and have some lunch.'] | |
openWhenTrue = (bwSmoke.location != nil) | |
closeWhenRevealed = 'after-lunch' | |
; | |
++ Goal 'What\'s a Hovarth function?' | |
['You know where to look for information like this.', | |
'You just need a copy of the DRD math tables.', | |
'Like most of the other books you\'ve needed, this one is | |
probably available in the library.', | |
'It\'s a math book, so try the math floor.', | |
'Once you find the book, just look up Hovarth functions in it.'] | |
openWhenRevealed = 'hovarth' | |
closeWhenRevealed = 'drd-hovarth' | |
; | |
++ Goal 'How do I calculate that Hovarth number?' | |
['The DRD doesn\'t list the number, so you need to calculate it | |
somehow.', | |
'The DRD mentioned that these numbers are computationally | |
intractable.', | |
'Have you run into anything other mention of computationally | |
intractable problems?', | |
'Did you read the background information on Stamer\'s research | |
project, as referenced in the report in his lab?', | |
'Read the report, and then go find the references in the library. | |
Then go back and read the report again. That will point you to | |
one more reference you should look at.', | |
'Now, this should give you some ideas about how to proceed.', | |
'Hovarth functions are computationally intractable, but maybe | |
a <q>quantum computer</q> could handle the job.', | |
'Have you looked at the equipment in Stamer\'s lab carefully?', | |
'Have you played with it?', | |
'Have you figured out what\'s going on when you push down on the | |
Dice-O-Matic while the equipment is running?', | |
'The equipment is creating a <q>decoherence</q> field. Anything | |
you put inside will start showing <q>quantum</q> behavior.', | |
'Putting a computing device of some kind inside the equipment | |
could make it into a quantum computer.', | |
'A calculator is a kind of computer that might fit in the | |
equipment.', | |
'If you could properly program a calculator, you could put it | |
in the equipment to turn it into a quantum calculator, which | |
could then solve the problem.', | |
'Your skills at programming calculators aren\'t up to the task, | |
but maybe there\'s someone else who could help out.', | |
'Have you been to the bookstore yet?', | |
'Did you read the newspaper?', | |
'Did you see the part about Jay?', | |
'Maybe Jay could program the calculator as needed.'] | |
openWhenRevealed = 'drd-hovarth' | |
closeWhenTrue = (calculator.isProgrammed) | |
; | |
++ Goal 'Where can I find a calculator?' | |
['There\'s one in plain view, in one of the places you\'ve | |
already been.', | |
'There\'s a calculator in Stamer\'s lab in Bridge.'] | |
openWhenRevealed = 'qc-calculator-technique' | |
closeWhenTrue = (calculator.moved) | |
; | |
++ Goal 'How do I find Jay?' | |
['Have you been to the bookstore yet?', | |
'Did you read the newspaper?', | |
'Jay is a student in Dabney. Maybe he\'s working on a stack.', | |
'Go ask Aaron about Jay.', | |
'Jay is in Alley Four. Go ask the people there about him.'] | |
openWhenRevealed = 'dvd-hovarth' | |
closeWhenSeen = jay | |
; | |
++ Goal 'How do I get Jay to help me?' | |
['First, you need to explain what you need him to do.', | |
'There are two main things you need to cover: Hovarth numbers, | |
and quantum computers.', | |
'You can just ask Jay about Hovarth numbers.', | |
'Quantum computers are a bit hard to explain, though. Maybe | |
you can just let him read about them for himself.', | |
'Give Jay the <i>Quantum Review Letters</i> number 73:9a.', | |
'When he\'s done reading, you can just give him the calculator.', | |
'But before he\'ll program the calculator for you, you have to | |
do something for him, which he\'ll explain.'] | |
openWhenTrue = (gRevealed('dvd-hovarth') && jay.seen) | |
closeWhenRevealed = 'jay-ready-to-program' | |
; | |
++ Goal 'How do I find Turbo Power Squirrel?' | |
['Jay tells you where he thinks it is.', | |
'You just need to get into the Guggenheim wind tunnel.'] | |
openWhenRevealed = 'squirrel-assigned' | |
closeWhenRevealed = 'squirrel-returned' | |
; | |
++ Goal 'Jay programmed the calculator; now what?' | |
['Remember the Dice-O-Matic? If not, go back to Stamer\'s lab | |
and have a closer look.', | |
'The equipment in Stamer\'s lab turns ordinary things into weird | |
quantum things. So maybe it could turn your ordinary calculator | |
into a quantum computer.', | |
'Put the calcalator in the experiment and turn it on. Push the | |
green button. Follow Jay\'s instructions to enter the number | |
from the black box.'] | |
openWhenTrue = (calculator.isProgrammed) | |
closeWhenRevealed = 'hovarth-solved' | |
; | |
++ Goal 'Okay, Stamer\'s door is open; what now?' | |
['It\'s traditional after solving a stack to enter the senior\'s room. | |
You broke past their defenses, after all.', | |
'Just go into room 4.'] | |
openWhenTrue = (room4Door.isOpen) | |
closeWhenTrue = (me.isIn(room4)) | |
; | |
++ Goal 'I\'m in Stamer\'s room. What now?' | |
['Help yourself to the bribe---you earned it.', | |
'Just hang out for a while. Brian will show up shortly.', | |
'When Brian shows up, talk to him.'] | |
openWhenTrue = (me.isIn(room4)) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The library | |
*/ | |
+ HintMenu 'Millikan Library'; | |
++ Goal 'How do I get past the receptionist?' | |
['Did you read the sign?', | |
'What does the receptionist ask you for?', | |
'You might be thinking you have to trick him somehow, but you don\'t.', | |
'After all, you\'re an alum, so you\'re allowed to use the library.', | |
'Maybe you have an ID card proving that.', | |
'Did you check your wallet?', | |
'Show your alumni ID card to the receptionist.'] | |
openWhenSeen = millikanLobby | |
closeWhenSeen = millikanElevator | |
; | |
++ Goal 'How do I find the book I\'m looking for?' | |
['You might have noticed that the library\'s floors are dedicated | |
to different subjects, so you need to find the right floor first.', | |
'There\'s a directory outside the elevator on the first floor | |
(look at the elevator if you haven\'t found it yet).', | |
'Once you know which floor your book is on, just go there and | |
look for it by title (FIND title IN SHELVES).'] | |
openWhenSeen = millikanElevator | |
closeWhenTrue = (LibBook.allLibBooksFound) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Hints for Bridge Lab | |
*/ | |
+ HintMenu 'Bridge Lab'; | |
++ Goal 'Stamer\'s lab is locked! How do I get in?' | |
['Erin gave you the key when Belker bribed her to go work | |
on another stack. You simply need to use that key.'] | |
openWhenTrue = (!labKey.isIn(erin)) | |
closeWhenSeen = basementLab | |
; | |
++ Goal 'How do I get into the display case?' | |
['Did you find the alarm system yet? Stop with this topic until you do.', | |
'I thought I said not to continue with this topic until you found the | |
alarm system!', | |
'Okay, so there\'s no alarm system. The simple fact is, you don\'t | |
need to get into the display case.', | |
'Stop worrying about it already; it\'s just scenery.'] | |
openWhenSeen = bridgeEntry | |
; | |
++ Goal 'How do I get the SPY-9 camera?' | |
['You don\'t actually need to take the camera, but there might be | |
other interesting things about it.', | |
'Did you look carefully at how it\'s wired?', | |
'Maybe you should try figuring out where the wire goes.'] | |
openWhenTrue = (spy9.described) | |
; | |
++ Goal 'How do I trace the wire from the SPY-9 camera?' | |
['Did you look at it carefully?', | |
'You should go find the wiring closet mentioned when you looked at | |
the wire.', | |
'The wiring closet is in the sub-basement, down a level from the lab.', | |
'Once you\'ve found the wiring closet, you need to figure out which | |
wire connects to the camera.', | |
'Have you tried searching the mass of wiring?', | |
'That suggested a way to identify the wire.', | |
'Go upstairs and pull the wire connected to the camera, then go | |
downstairs and look at mass of wiring again.'] | |
openWhenTrue = (spy9Wire.described) | |
closeWhenTrue = (bwirBlueWire.described) | |
; | |
++ Goal 'Does the tag on the SPY-9 wire mean anything?' | |
['You don\'t have to worry about this for a while.'] | |
openWhenTrue = (spy9Tag.described) | |
closeWhenTrue = (!plisnik.inOrigLocation) | |
; | |
++ Goal 'What does the tag on the SPY-9 wire mean?' | |
['Did you check around Plisnik\'s work area yet?', | |
'Have a look at the three-ring binder.', | |
'The number on the tag is a job number in the binder.'] | |
openWhenTrue = (spy9Tag.moved && !plisnik.inOrigLocation) | |
closeWhenRevealed = 'spy9-ip' | |
; | |
++ Goal 'Is there any way to trace the wire beyond the wiring closet?' | |
['You noticed that it goes through a hole in the north wall, right?', | |
'So, maybe you should find out what\'s on the other side of the wall.', | |
'There\'s no way north from the wiring closet, but maybe there\'s a | |
way north from somewhere nearby.', | |
'Have you been to the storage room yet?', | |
'Have you looked at everything there carefully?', | |
'Have you looked at the pile of scrap wood in particular?', | |
'There\'s a door behind the scrap wood.', | |
'The wood is too heavy to take, but maybe you could move it out | |
of the way.', | |
'Just MOVE WOOD.', | |
'The wiring closet is west of the storage room, so maybe if you | |
go through the door and then west, you\'ll find what you\'re looking | |
for.', | |
'Once you\'re in <q>Narrow Tunnel,</q> try searching the pipes.'] | |
openWhenTrue = (bwirBlueWire.described) | |
closeWhenTrue = (st5Wire.described || steamTunnel8.seen) | |
; | |
++ Goal 'Where can I find the books listed in the bibliography?' | |
['Let\'s see...\ where can one find books? Think, think, think...', | |
'If only there were a big building full of all sorts of different | |
books...', | |
'Have you tried the library?', | |
'If you\'re not sure where the library is, try going outside | |
and finding the library on your map.'] | |
openWhenRevealed = 'bibliography' | |
closeWhenTrue = (LibBook.anyLibBookFound) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Steam tunnels | |
*/ | |
+ HintMenu 'The Steam Tunnels'; | |
++ Goal 'What was the combination to the supply room again?' | |
['What, you aren\'t keeping careful notes?', | |
'It\'s ' + st8Door.showCombo + '. ', | |
'If that doesn\'t work, you might try pushing the Reset | |
button before entering the combination---the lock remembers | |
button presses until the door is opened.', | |
'Also note that once you\'ve successfully opened the door once, | |
your character will remember how to do it in the future; all you | |
need to type from then on is UNLOCK DOOR, or even just OPEN DOOR.'] | |
openWhenRevealed = 'supplies-combo' | |
; | |
++ Goal 'How do I get into the "Supplies" room?' | |
['You don\'t even want to think about going in there while anyone | |
else is around.', | |
'You can listen to the door to see if anyone\'s in the room.', | |
'If you just wait a little bit, everyone will leave the room.', | |
'But you still need to know the combination.', | |
'Who would know the combination?', | |
'The people already in the room probably know the combination.', | |
'If you\'ve avoided the people in the room by leaving each time | |
they show up, you\'ve probably noticed that they come and go | |
frequently.', | |
'Maybe if you could spy on them when they\'re arriving, you could | |
see the combination they enter.', | |
'But if you leave every time they show up, you won\'t be able to | |
spy on them.', | |
'Maybe you could stay, and find a place to hide.', | |
'Have you looked at everything in the <q>Dead End</q> location?', | |
'Have you noticed the nook?', | |
'If you STAND IN THE NOOK before the workers show up, they won\'t | |
notice you, giving you a chance to spy on them.'] | |
openWhenSeen = steamTunnel8 | |
closeWhenRevealed = 'supplies-combo' | |
; | |
++ Goal 'How do I avoid getting kicked out of the tunnels by the workers?' | |
['You do get a little warning when they\'re about to show up.', | |
'You could just leave before they arrive.', | |
'Or perhaps you could stay, and find a place to hide.', | |
'Have you looked at everything in the <q>Dead End</q> location?', | |
'Have you noticed the nook?', | |
'If you stand in the nook before the workers show up, they won\'t | |
notice you.'] | |
openWhenRevealed = 'escorted-out-of-tunnels' | |
closeWhenRevealed = 'supplies-combo' | |
; | |
++ Goal 'How do I trace the camera past the network router?' | |
['You\'ve traced the physical wiring to the router, but the | |
router is connected to the whole campus network, so tracing | |
the physical connection is useless beyond this point.', | |
'You need instead to trace the network connection.', | |
'Each device on the network has an <q>IP address</q> that | |
identifies it. If you could analyze the data on the network, | |
you could find out the IP address where the packets are going.', | |
'Your character happens to know that a <q>network analyzer</q> | |
would let you trace where the data packets are going.', | |
'You need to find a network analyzer, plug it into the | |
router, and trace the packets coming from the camera.', | |
'To trace the right packets, you have to know where the | |
packets are coming from.', | |
'Like all devices on the network, the camera has an IP address.', | |
'You need to find the camera\'s address, and trace packets | |
from that address. That will tell you the address of the | |
computer that\'s receiving data from the camera.'] | |
openWhenRevealed = 'need-net-analyzer' | |
closeWhenRevealed = 'spy9-dest-ip' | |
; | |
++ Goal 'How do I find a computer if I know its IP address?' | |
['You must have seen a few Network Installer Company | |
job-order forms by now. Did you notice the boilerplate | |
text at the top of each form?', | |
'Each form says that IP address assignments have to be | |
obtained through the Campus Network Office, in Jorgensen.', | |
'Maybe you could visit that office and ask them to look | |
up the IP address for you.', | |
'If you don\'t know where Jorgensen is, just check your | |
campus map.'] | |
openWhenRevealed = 'spy9-dest-ip' | |
closeWhenSeen = syncLabOffice | |
; | |
++ Goal 'I got the work order for the IP address, but now what?' | |
['You\'ve looked up work orders before.', | |
'Look up the work order in the binder you got from Plisnik.', | |
'That will tell you where to go and how to get in.'] | |
openWhenRevealed = 'sync-job-number-available' | |
closeWhenSeen = syncLabOffice | |
; | |
++ Goal 'Where can I find a <q>network analyzer</q>?' | |
['Have you run into anyone else around campus working on networks?', | |
'Someone dressed similarly to the workers you saw in the tunnels, | |
perhaps?', | |
'The workers out on the Quad are wearing similar uniforms.', | |
'Look at the equipment the workers on the Quad have.'] | |
openWhenRevealed = 'need-net-analyzer' | |
closeWhenTrue = (netAnalyzer.moved) | |
; | |
++ Goal 'What do I do with the network analyzer?' | |
['Did you find the instructions on how to use the analyzer yet?', | |
'Have you run into any other networking gear?', | |
'Down in the steam tunnels somewhere, perhaps?', | |
'You need to find your way into the room marked <q>Supplies</q> | |
before you need the network analyzer.', | |
'Once you\'re in the network room (the room marked <q>Supplies</q>), | |
take a closer look at the router.', | |
'You should notice two things. First, note where the blue wire goes | |
(the one you\'ve been tracing). Second, note the special jack on | |
the router.', | |
'You\'ve probably figured out that you can plug the network analyzer | |
into the router\'s special jack.', | |
'What do you want to know about the router?', | |
'You want to trace what\'s connected to the camera, which requires | |
that you trace the data packets from the camera through the router. | |
The network analyzer can do this.', | |
'Once you figure out how to work the network analyzer, you just | |
need to type the special code to trace packets from the camera. | |
You need to know the camera\'s IP address to do this.'] | |
openWhenTrue = (netAnalyzer.moved) | |
closeWhenRevealed = 'spy9-dest-ip' | |
; | |
++ Goal 'I don\'t understand the network analyzer instructions!' | |
['They do seem to be poorly translated. It might help to | |
know that tracing a data packet on a network is often known | |
by the jargon <q>packet sniffing.</q>', | |
'What you\'re after here is a way to trace a packet from | |
one computer to another. Computers are identified by their | |
<q>IP addresses,</q> so if you know the IP address of the | |
computer sending data, you want to find out the IP address | |
of the computer receiving those packets.', | |
'Note that the instructions are trying to warn you that the | |
network analyzer model you have uses different code numbers | |
than most of the other models. The hand-written bits at the | |
bottom of the page might be important.'] | |
openWhenDescribed = netAnInstructions | |
closeWhenRevealed = 'spy9-dest-ip' | |
; | |
++ Goal 'What\'s an <q>IP address</q>?' | |
['It\'s just some computer jargon for a special number assigned to | |
each computer or other device that\'s hooked up to a computer network. | |
Each device has its own unique IP address, which identifies it so | |
that the other devices on the network can send information to it. | |
<q>IP</q> stands for <q>internet protocol,</q> and it\'s called | |
an <q>address</q> because it\'s kind of like your street address, | |
in that it tells other machines where you are so that they can send | |
you information. There\'s nothing meaningful about the actual numbers | |
in an IP address, at least in this story; it\'s just an arbitrary | |
identification number, kind of like a social security number. | |
IP addresses are usually written like this: 89.99.100.101---four | |
decimal numbers, ranging from 0 to 255, separated by periods.'] | |
openWhenTrue = (netAnalyzer.moved) | |
; | |
++ Goal 'How do I convert decimal to hexadecimal (or vice versa)?' | |
['Hexadecimal is just base 16. 0-9 are the same as in decimal; | |
10 in decimal is written as A in hex, 11 as B, 12 as C, 13 as D, | |
14 as E, and 15 as F. 16 in decimal is written as 10 in hex. | |
To read a hex number, treat each digit as a power of 16: the | |
last digit is the units, the second-to-last is 16\'s, the | |
third-to-last is 256\'s, and so on. So 100 hex is 1x256=256, | |
200 hex is 2x256=512, 123 hex is 1x256+2x16+3x1=291.', | |
'Of course, the <i>easy</i> way to convert is to use a hex | |
calculator. The Windows CALC program can show hex in its | |
<q>Scientific</q> mode, for example. '] | |
openWhenTrue = (netAnalyzer.moved) | |
; | |
++ Goal 'What\'s <q>hexadecimal</q>?' | |
['It\'s a base-16 number system. Remember the stuff from | |
elementary school about using different arithmetic bases? | |
The idea is that each digit can hold 16 different values | |
rather than just the usual 10. You need more than just | |
the digits 0 through 9 to represent the extra values, | |
so in hexadecimal, you add A through F---A stands for | |
the decimal value 10, B for 11, C for 12, D for 13, E | |
for 14, and F for 15. So the hexadecimal (or just <q>hex</q>) | |
number 25 means 2 times 16, plus 5, or 37 in decimal. 1F | |
means 1 times 16, plus 15, which adds up to 31 in decimal. | |
100 in hex is 1 times 16 times 16, or 256 in decimal. 123 | |
in hex is 1 times 16 times 16, plus 2 times 16, plus 3, | |
which is 291 in decimal.', | |
'Computer programmers tend to use hex numbering a lot | |
because it\'s relatively easy to convert hex numbers to | |
and from binary. The exact reasons for this will be left | |
as an exercise to the reader, but it has to do with powers | |
of 2.'] | |
openWhenTrue = (netAnalyzer.moved) | |
; | |
++ Goal 'Where do I find instructions for the network analyzer?' | |
['Who was supposed to be using the network analyzer?', | |
'It\'s not the workers on the quad who use the network analyzer---it\'s | |
Plisnik.', | |
'Have you looked at the other things where Plisnik was?', | |
'Did you look at the three-ring binder?', | |
'Did you read it?', | |
'The piece of paper that falls out of the binder has instructions | |
on using the network analyzer.', | |
'The instructions are kind of hard to read, since they\'re poorly | |
translated from some other language, but you should be able to make | |
sense of them.', | |
'You just need the part about <q>packet smelling</q> (which really | |
means <q>packet sniffing,</q> computer jargon for tracing the flow | |
of data on a network from one computer to another).', | |
'Note the hand-written comments at the bottom of the page.', | |
'To trace packets on the network, type 09 plus the IP address | |
you want to trace. For example, to trace packets from a device | |
with IP address 8.9.10.11, you\'d type in 0908090A0B.'] | |
openWhenTrue = (netAnalyzer.moved) | |
closeWhenRevealed = 'spy9-dest-ip' | |
; | |
++ Goal 'How do I get the IP address for the SPY-9?' | |
['Did you notice the tag on the wire connected to the camera?', | |
'Did you look around Plisnik\'s work area after he left?', | |
'Did you find the three-ring binder?', | |
'Did you look in it?', | |
'The number on the wire tag is a work order number. Look it up | |
in the binder.', | |
'The work order referenced on the wire tag refers to another | |
work order. Look that one up too.', | |
'The dotted four-part number on the work order is the camera\'s | |
IP address.'] | |
openWhenTrue = (netAnalyzer.moved && nrRouter.seen) | |
closeWhenRevealed = 'spy9-dest-ip' | |
; | |
++ Goal 'How do I get rid of Plisnik?' | |
['Try talking to him a bit.', | |
'You\'ve undoubtedly noticed he has a favorite topic.', | |
'Something he\'s scared of.', | |
'Plisnik is scared of rats. Maybe a rat would scare him away.', | |
'Have you found the plush rat toy?', | |
'Maybe you could try showing it to Plisnik.', | |
'Okay, so he\'s not scared of a toy rat. But what if you made | |
it look alive?', | |
'Did you notice it\'s a hand-puppet?', | |
'Try putting it on your hand and showing it to him.', | |
'So that doesn\'t convince him either. Maybe you need to make the | |
toy rat look like it\'s moving around without your help.', | |
'Have you been inside Thomas yet?', | |
'Did you find anything interesting around the robotics competition?', | |
'Have a look at the toy car.', | |
'The toy car appears to move around on its own, if you drive it | |
with the remote.', | |
'Maybe you could use the toy car to make the toy rat look alive.', | |
'Put the rat on the toy car. Go into the steam tunnel next to | |
where Plisnik is working, and drive the rat in remotely.'] | |
openWhenSeen = plisnik | |
closeWhenTrue = (!plisnik.inOrigLocation) | |
; | |
++ Goal 'Is there anything I need to look up in the three-ring binder?' | |
['Notice the job number format: 1234-5678.', | |
'Have you seen anything else in that format?', | |
'How about something in the lab in Bridge?', | |
'Something related to the SPY-9.', | |
'Try looking up the number on the tag on the SPY-9 wire.'] | |
openWhenTrue = (workOrders.described && spy9Tag.described) | |
closeWhenRevealed = 'spy9-ip' | |
; | |
++ Goal 'How do I get through the north-south crawl?' | |
['Do you have any reason to go that way?', | |
'You don\'t have to worry about getting through. There\'s | |
nothing you need on the other side.'] | |
openWhenSeen = steamTunnel15 | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Other stacks | |
*/ | |
+ HintMenu 'The Other Stacks'; | |
++ Goal 'How do I solve Paul\'s Stack (room 42)?' | |
['Before you get sucked into this too much, you might note | |
the warnings the story shows every so often about how this stack | |
isn\'t very important. (Try typing in a few random passwords if | |
you haven\'t seen the warnings yet.) ', | |
'You don\'t need to solve this stack to win the game. ', | |
'This stack is entirely optional, so we\'re going to leave it | |
as an exercise for the reader. Be assured that there is a solution, | |
though (possibly even more than one). ', | |
'For extra bonus nerdliness, try finding another password that | |
the computer repeats back to you in <i>reverse order</i>. (This | |
doesn\'t do anything special in the game, but it\'s an interesting | |
extra challenge once you figure out how the puzzle works.) '] | |
openWhenSeen = commandant64 | |
closeWhenTrue = (room42door.isSolved) | |
; | |
++ Goal 'How do I solve the Giant Chicken Stack (room 12)?' | |
['If you\'ve been looking for someone, you might be able to | |
find them here.', | |
'You might try talking to the chickens.', | |
'If they\'re not very responsive, it might have to do with the | |
rules of the stack.', | |
'You might want to read the notebook (on the door to room 12) | |
for information on the stack.', | |
'The chickens can only talk to other chickens.', | |
'Maybe you could find a way to join them.', | |
'Have you checked out the Chickenator?', | |
'Go into the Chickenator and put on the suit. That\'ll let you | |
talk to the chickens and ask about the person you\'re looking | |
for.', | |
'Beyond that, there\'s nothing you have to do with this stack. | |
You don\'t have time for a scavenger hunt dressed as a giant | |
chicken.'] | |
openWhenSeen = alley3E | |
; | |
++ Goal 'Can I help with the Turbo Power Animals stack (room 22)?' | |
['You have plenty to do on your own stack. You probably | |
shouldn\'t worry too much about this one, unless a good reason | |
comes up.'] | |
openWhenSeen = alley4W | |
closeWhenRevealed = 'squirrel-assigned' | |
; | |
++ Goal 'Can I help with the Turbo Power Animals stack (room 22)?' | |
['You could use Jay\'s help, so it seems worth your time to go | |
retrieve Turbo Power Squirrel.', | |
'Beyond that, you should probably stay focused on your own task | |
of solving Brian Stamer\'s stack.'] | |
openWhenRevaled = 'squirrel-assigned' | |
; | |
++ Goal 'How do I solve the <i>Honolulu 10-4</i> stack (room 24)?' | |
['Have you tried playing the game?', | |
'Practice might help.', | |
'But even with practice, the music is an intolerable distraction.', | |
'Unfortunately, there\'s no way to turn off the music.', | |
'You probably shouldn\'t worry too much about this stack for now. | |
Brian Stamer\'s stack is much more important to you.'] | |
openWhenSeen = alley5S | |
; | |
++ Goal 'How do I solve the fruit fly stack (room 35)?' | |
['You probably shouldn\'t let this stack take time away from | |
your main task of solving Brian Stamer\'s stack, unless a good | |
reason comes up.'] | |
openWhenSeen = alley6E | |
; | |
++ Goal 'How do I solve the brute-force stack (room 50)?' | |
['The students seem to have this one under control. You | |
should probably focus your efforts on Brian Stamer\'s stack.'] | |
openWhenSeen = lower7W | |
; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - Prologue. This introductory sequence takes | |
* place away from campus - "somewhere in south Asia" - and gives us | |
* a chance to set up some of the characters and back-story. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The plot event describing the time at the start of the game | |
*/ | |
ClockEvent eventTime = [1, 10, 35]; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Some generic topic items, for conversation | |
*/ | |
/* omegatron (the player character's employer) */ | |
omegatronTopic: Topic 'my omegatron/company'; | |
/* mitachron (the rival company) */ | |
mitachronTopic: Topic 'mitachron'; | |
/* phone call */ | |
phoneCallTopic: Topic 'cell cellular phone telephone call'; | |
/* elevators in general */ | |
elevatorTopic: Topic 'elevator/lift'; | |
/* some resume-related topics */ | |
hiringFreezeTopic: Topic 'omegatron hiring freeze'; | |
kowtuan: Topic 'kowtuan technical institute/university/college'; | |
chipTechNo: Topic 'semiconductor chemical applicator | |
chiptechno/manufacturer/operations'; | |
/* the power plant itself */ | |
powerPlant6: Topic '6 government power plant'; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* A special object for the power plant itself. This object will exist | |
* everywhere in the plant, so that the plant can always be referred to | |
* while we're in it. | |
*/ | |
MultiInstance | |
/* we're in all power plant rooms */ | |
initialLocationClass = PowerPlantRoom | |
instanceObject: SecretFixture { | |
'6 government power plant' 'Government Power Plant #6' | |
/* examine the plant by doing an ordinary "look" */ | |
dobjFor(Examine) | |
{ | |
action() { replaceAction(Look); } | |
} | |
/* | |
* reduce my likelihood slightly for disambiguation in all | |
* commands, so that if there's another object present that | |
* responds to the same vocabulary, we'll pick the other object - | |
* the other one is almost certainly more specific than this | |
* generic object | |
*/ | |
dobjFor(All) { verify() { logicalRank(55, 'generic'); } } | |
} | |
; | |
/* the background plant noise */ | |
+ SimpleNoise | |
desc = "The normal low rumblings and industrial noises of | |
a power plant are constantly in the background. " | |
; | |
MultiInstance | |
initialLocationClass = PowerPlantRoom | |
instanceObject: Decoration { 'concrete' 'concrete' | |
"Essentially all of the plant structure is built out of concrete. " | |
} | |
; | |
/* a simple marker class for our power plant rooms */ | |
class PowerPlantRoom: Room; | |
/* misty cloud of insect repellent */ | |
deetCloud: Vaporous 'misty powerful noxious insect | |
spray/repellent/mist/cloud/vapor/insecticide' | |
'cloud of insect repellent' | |
"The misty vapor hangs in the air through the whole room. " | |
; | |
+ Odor | |
isAmbient = true | |
sourceDesc = "It has a foul chemical odor. " | |
hereWithSource = "A noxious mist of insect repellent fills the air. " | |
; | |
/* the tank */ | |
deetTank: Thing 'powerful suitcase-sized metal insect tank/hose/repellent' | |
'tank of insect repellent' | |
"It's about the size of a large suitcase, and has a hose attached. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Power Plant Control Room | |
*/ | |
/* group lister for Xojo and Guanmgon while standing in the doorway */ | |
standingInDoorway: ListGroupPrefixSuffix | |
groupPrefix = "\^" | |
groupSuffix = " are standing in the doorway watching you work. " | |
createGroupSublister(parentLister) { return plainActorLister; } | |
; | |
/* | |
* The control room itself. | |
*/ | |
powerControl: PowerPlantRoom 'Control Room' 'the control room' | |
"This is the cramped control room of Government Power Plant #6. | |
Even before you arrived, this room was so stuffed with equipment | |
that it was barely possible to turn around. Now that you've | |
added the refrigerator-sized SCU-1100DX to the mix, the room has | |
about as much open space as the <q>budget economy class</q> | |
airline seat you were wedged into for fifteen hours on the | |
flight here. The only exit is west. " | |
vocabWords = 'control room' | |
/* going west takes us to the power control hallway */ | |
west = powerControlDoorway | |
out asExit(west) | |
/* some atmospheric messages */ | |
atmosphereList: ShuffledEventList { | |
[ | |
'You notice a mosquito land on your arm, and manage to swat | |
it before it can bite. ', | |
'You feel something on your neck, and realize too late that | |
it\'s a mosquito just flying away. ', | |
'A mosquito buzzes past within inches of your ear. ', | |
'Several of your mosquito bites start itching. ', | |
'A mosquito flies lazily past your head. ', | |
'The humidity is definitely getting worse. ', | |
'A distant creaking sound echoes through the building. ' | |
] | |
eventPercent = 80 | |
eventReduceAfter = 20 | |
eventReduceTo = 25 | |
} | |
; | |
/* | |
* The doorway. This is a Fixture, which means that it's a permanent | |
* part of the room (so it can't be taken or moved, for example), and | |
* it's a ThroughPassage, which means that it connects this location to | |
* another location for actor travel. | |
*/ | |
+ powerControlDoorway: Fixture, ThroughPassage | |
'west w door/doorway' 'doorway' | |
"It's just a doorway without a door, leading out to the west. " | |
/* | |
* Dont't allow the player to go this way until the SCU-1100DX is | |
* repaired. | |
*/ | |
canTravelerPass(travler) { return scu1100dx.isOn; } | |
explainTravelBarrier(traveler) | |
{ | |
"You simply can't leave until you get the SCU-1100DX working. "; | |
} | |
dobjFor(StandOn) | |
{ | |
verify() { } | |
action() { mainReport('There\'s no reason to do that. '); } | |
} | |
; | |
/* | |
* our first assistant | |
*/ | |
+ xojo: TourGuide, Person 'xojo/man/bureaucrat*men' 'Xojo' | |
"He's a low-level bureaucrat assigned to assist you with the | |
SCU-1100DX installation. He looks young, maybe in his mid-twenties. | |
He's a bit taller than you and very thin. " | |
isProperName = true | |
isHim = true | |
checkTakeFromInventory(actor, obj) | |
{ | |
/* allow taking the resume; for others, use default handling */ | |
if (obj != xojoResume) | |
inherited(actor, obj); | |
} | |
/* begin an errand */ | |
beginErrand(state) | |
{ | |
/* remove us from power control */ | |
trackAndDisappear(xojo, powerControl.west); | |
/* transition to the errand state */ | |
setCurState(state); | |
/* show the message */ | |
state.beginErrand(); | |
} | |
/* | |
* End an errand. This can be called to cause us to return from an | |
* errand immediately. The errand state objects will automatically | |
* call this after enough turns have elapsed. | |
*/ | |
endErrand() | |
{ | |
local st = curState; | |
/* if we're not in an errand state, ignore it */ | |
if (!st.ofKind(XojoErrandState)) | |
return; | |
/* return us to power control */ | |
moveIntoForTravel(powerControl); | |
/* return to the our main state */ | |
setCurState(xojoInit); | |
/* let the errand state describe our return */ | |
"<.p>"; | |
st.endErrand(); | |
"<.p>"; | |
} | |
dobjFor(Climb) | |
{ | |
verify() | |
{ | |
/* Xojo is certainly not the most obvious thing to climb */ | |
nonObvious; | |
} | |
action() | |
{ | |
if (gActor.location == plantElevator && plantElevator.isAtBottom) | |
{ | |
/* | |
* we're stuck in the elevator; we can use Xojo's help to | |
* reach the escape hatch | |
*/ | |
"<q>Do you think you could try lifting me?</q> you ask. | |
<.p><q>Very well,</q> Xojo says. "; | |
boostPlayerChar(); | |
} | |
else if (gActor.isIn(xojoBoost)) | |
"It doesn't look like Xojo can lift you any higher. "; | |
else | |
"Xojo probably wouldn\'t like that. "; | |
} | |
} | |
dobjFor(ClimbUp) asDobjFor(Climb) | |
dobjFor(StandOn) asDobjFor(Climb) | |
dobjFor(SitOn) asDobjFor(Climb) | |
dobjFor(Board) asDobjFor(Climb) | |
/* observe the player trying to climb something in the elevator */ | |
observeClimb(obj) | |
{ | |
if (plantElevator.isAtBottom) | |
{ | |
if (boostCount == 0) | |
"<.p>Xojo watches what you're doing. <q>I think | |
<<obj.theName>> is too difficult to climb.</q> he says. | |
<q>Perhaps, though, I could lift you. Do you want me to | |
try?</q> "; | |
else | |
"<.p><q>Would you perhaps like me to lift you | |
once again?</q> Xojo asks. "; | |
xojo.initiateConversation(nil, 'offer-boost'); | |
} | |
} | |
/* give the player a boost (in the elevator) */ | |
boostPlayerChar() | |
{ | |
/* describe it */ | |
"Xojo bends down to let you climb onto his shoulders, | |
which you manage to do with some awkwardness, then he | |
shakily stands up. This puts you in easy reach of the | |
ceiling. "; | |
/* if this is the first time, add a little something extra */ | |
if (boostCount++ == 0) | |
"<.p><q>I hope you can see what a high-initiative employee | |
I would be,</q> Xojo says, straining a bit under your weight. "; | |
/* move me to the special secret "boosted by xojo" location */ | |
me.moveIntoForTravel(xojoBoost); | |
/* xojo has a special state for this situation */ | |
xojo.setCurState(xojoElevatorBoosting); | |
} | |
boostCount = 0 | |
dobjFor(GetOffOf) maybeRemapTo(gActor.isIn(xojoBoost), | |
GetOffOf, xojoBoost); | |
; | |
++ xojoResume: PresentLater, Readable | |
'cirriculum resume/r\u00E9sum\u00E9/cv/c.v./vitae/piece/paper' | |
'r\u00E9sum\u00E9' | |
"The format is slightly unusual, probably a matter of local | |
conventions, but you don't have much trouble finding the important | |
bits of information about Xojo's professional qualifications: | |
a bachelor's degree in electrical engineering | |
from Kowtuan Technical Institute; a year in an entry-level | |
position with a semiconductor manufacturer called ChipTechNo; | |
and two years here at Government Power Plant #6, rising from | |
Junior Technical Administrative Rank 3 to Junior Administrative | |
Technical Rank 7. " | |
/* it's xojo's, no matter where it's located */ | |
owner = xojo | |
/* take the resume before reading or examining it */ | |
dobjFor(Read) { preCond = (inherited() + [objHeld]) } | |
dobjFor(Examine) { preCond = (inherited() + [objHeld]) } | |
; | |
++ AskTellTopic @magnxi | |
"<q>What can you tell me about the Colonel?</q> you ask. | |
<.p><q>I am taking you to her now,</q> he replies. " | |
/* this is active as soon as we're on our way to see the colonel */ | |
isActive = (scu1100dx.isOn) | |
; | |
++ GiveTopic @xojoResume | |
"You offer the r\u00E9sum\u00E9 back to Xojo, but he just waves | |
his hands. <q>Please,</q> he says, <q>keep it for your eventual | |
consideration.</q> " | |
; | |
++ DefaultCommandTopic | |
"Xojo politely declines, saying his rank is too low to | |
be of assistance in this manner. " | |
isConversational = nil | |
; | |
++ AskTellTopic @xojo | |
"<q>Tell me about yourself,</q> you say. | |
<.p><q>I am here to assist you,</q> Xojo replies. " | |
; | |
++ AskTellShowTopic +90 @guanmgon | |
"<q>Tell me about Guanmgon,</q> you say. | |
<.p>Xojo pauses for a moment. <q>He is very qualified to | |
assist you,</q> he finally says. " | |
; | |
++ HelloTopic | |
"<q>How may I assist?</q> Xojo asks. " | |
; | |
++ DefaultAnyTopic, ShuffledEventList | |
['<q>My superior, Guanmgon, would be better qualified than | |
myself to assist you in this matter,</q> Xojo says. ', | |
'<q>I must defer on such matters to my superiors,</q> Xojo says. ', | |
'Xojo thinks for a moment. <q>Perhaps you should consult my superior, | |
Guanmgon,</q> he says. ' | |
] | |
/* don't use an implied greeting with this topic */ | |
impliesGreeting = nil | |
; | |
++ AskTellTopic @contract | |
"You know for a fact that Xojo is too far down in the bureaucracy to | |
have anything to do with contracts. " | |
/* we're not conversational, so don't use a greeting */ | |
isConversational = nil | |
; | |
++ GiveShowTopic @contract | |
"There's no point in doing that; Xojo is about forty-five | |
bureaucracy levels too low to have anything to do with contracts. " | |
isConversational = nil | |
; | |
/* when asking about the SCU, change the response once we've fixed it */ | |
++ GiveShowTopic @scu1100dx | |
"You don't have anything to show him until you get it working. " | |
isConversational = nil | |
; | |
+++ AltTopic | |
"You'd love to give Xojo a full demo, but you really should go | |
see Colonel Magnxi right away. " | |
isActive = (scu1100dx.isWorking && scu1100dx.isOn) | |
isConversational = nil | |
; | |
++ GiveShowTopic [ct22, s901, xt772hv, tester, testerProbe, xt772lv] | |
"You think he'd really like to help, but it's become clear that | |
all the bizarre red tape here prevents Xojo from actually doing | |
anything that would resemble repair work. " | |
isConversational = nil | |
; | |
++ AskTellShowTopic, SuggestedAskTopic [helicopter1, helicopter5] | |
"<q>Do you know anything about those helicopters?</q> you ask. | |
<.p><q>I am not sure,</q> he says, <q>but my superior, Guanmgon, | |
intimated that a representative of Mitachron was to | |
approach. Perhaps the representative is performing arrival.</q> " | |
isActive = (helicopter1.seen) | |
/* suggestion name */ | |
name = 'the helicopters' | |
; | |
/* | |
* the rope bridge - start with the generic response, then define some | |
* specific responses for asking about it in different locations | |
*/ | |
++ AskTellShowTopic [platformBridge] | |
"<q>I really don't want to cross that rope bridge,</q> you say. | |
<.p><q>It is our most expedient course,</q> Xojo | |
says, <q>but perhaps we could wait for the repair of the | |
elevator, if you prefer.</q> " | |
; | |
/* Rope Bridge response for when we're on the platform */ | |
+++ AltTopic, StopEventList | |
['<q>Are you serious?</q> you ask. <q>You actually want to go across | |
the rope bridge?</q> | |
<.p><q>I am sorry,</q> he says, <q>but it the most expedient course. | |
The only possibility of an alternative is awaiting the completion of | |
repairs to the elevator.</q> ', | |
'<q>Why is this thing even here?</q> you ask incredulously. | |
<.p><q>The Peon Grade staff are not normally allowed to leave | |
the sub-basement levels during the working hours,</q> Xojo says. | |
<q>A group of sub-peons erected this in secret, to enable departure | |
and later return without detection by superior staff functionaries.</q> ', | |
'<q>This bridge doesn\'t look very safe,</q> you say. | |
<.p><q>I have used it personally, during my own Peon Grade period | |
of employment,</q> Xojo assures you. <q>Simply use caution to make | |
the crossing relatively free of excess hazard.</q> ', | |
'<q>You\'re really sure this is the only way across?</q> you ask. | |
<.p><q>I am regrettably forced to respond in the affirmative,</q> | |
Xojo says. '] | |
isActive = (xojo.isIn(s2Platform)) | |
; | |
/* Rope Bridge response for when we're starting across the bridge */ | |
+++ AltTopic | |
"<q>You're really sure this thing is safe?</q> you ask. | |
<.p><q>I have crossed it several times,</q> he says. <q>Please, | |
we should be quick.</q> " | |
isActive = (xojo.isIn(ropeBridge1) || xojo.isIn(ropeBridge2)) | |
; | |
/* Rope Bridge response after the bridge collapses */ | |
+++ AltTopic | |
"<q>You said this thing was safe!</q> you yell. | |
<.p><q>Sorry,</q> Xojo says. <q>I've only had this happen once | |
or twice before. Come, we can climb up from here.</q> " | |
isActive = (xojo.isIn(ropeBridge3)) | |
; | |
/* Rope Bridge response once we're past it */ | |
+++ AltTopic | |
"<q>I still can't believe you made me cross that rope | |
bridge,</q> you say. | |
<.p><q>Admittedly, the danger level was non-zero,</q> Xojo says. " | |
isActive = (me.hasSeen(canyonNorth)) | |
; | |
/* in-conversation state for xojo */ | |
++ InConversationState | |
stateDesc = "He's waiting attentively. " | |
specialDescListWith = [standingInDoorway] | |
attentionSpan = nil | |
; | |
/* initial state - waiting for repairs to the SCU */ | |
+++ xojoInit: ConversationReadyState | |
isInitState = true | |
specialDesc = "Xojo is watching you from the doorway. " | |
specialDescListWith = [standingInDoorway] | |
stateDesc = "He's been watching you attentively. " | |
; | |
++++ HelloTopic, ShuffledEventList | |
['You look over at Xojo. <q>Excuse me...</q> | |
<.p><q>How may I assist?</q> ', | |
'You make eye contact. <q>Yes?</q> he asks.<.p>', | |
'<q>Xojo?</q> | |
<.p><q>What can I do to help?</q> '] | |
; | |
++++ ByeTopic "<q>Thanks,</q> you say. Xojo nods. " | |
; | |
++++ ImpByeTopic "" | |
; | |
/* a state class for when xojo is running an errand */ | |
class XojoErrandState: ActorState | |
takeTurn() | |
{ | |
/* if we've been gone long enough, return */ | |
++turnCount; | |
if (turnCount > turnsNeeded) | |
xojo.endErrand(); | |
} | |
activateState(actor, oldState) | |
{ | |
/* inherit the default handling */ | |
inherited(actor, oldState); | |
/* reset our counter of turns in this state */ | |
turnCount = 0; | |
} | |
/* the number of turns we've been gone */ | |
turnCount = 0 | |
/* the expected number of turns for the errand */ | |
turnsNeeded = 2 | |
; | |
/* fetching Koffee */ | |
++ koffeeErrand: XojoErrandState | |
beginErrand() | |
{ | |
"<q>Xojo! Go fetch Mr.\ Mittling some Koffee. Hurry!</q> | |
Xojo rushes off through the door. "; | |
/* we're done with the agenda item for the koffee */ | |
guanmgon.removeFromAgenda(koffeeAgenda); | |
} | |
endErrand() | |
{ | |
"Xojo returns with a can of Koffee, and squeezes | |
into the room long enough to hand it to you. "; | |
koffee.moveInto(me); | |
} | |
; | |
/* fetching Deet */ | |
++ deetErrand: XojoErrandState | |
beginErrand() | |
{ | |
"<q>The mosquito insects are a nuisance for you,</q> Guanmgon | |
says. <q>Xojo!</q> he yells. <q>Go fetch powerful insect repellent! | |
Go now!</q> Xojo walks off, muttering something. "; | |
/* we're done with the dispatching agenda item */ | |
guanmgon.removeFromAgenda(deetAgenda); | |
} | |
endErrand() | |
{ | |
"Xojo returns, lugging a suitcase-sized metal tank with a | |
hose attached. He turns a valve on the tank and points the | |
hose into the room. A misty spray issues from the hose, and | |
the room is quickly filled with a cloud of noxious vapor. | |
<.p>The mosquitoes carry on with their business unperturbed. "; | |
/* add the tank and deet cloud */ | |
deetTank.moveInto(xojo); | |
deetCloud.moveInto(powerControl); | |
/* add the agenda item for returning the tank */ | |
guanmgon.addToAgenda(tankReturnAgenda); | |
} | |
; | |
++ tankReturnErrand: XojoErrandState | |
beginErrand() | |
{ | |
/* get rid of the deet tank */ | |
deetTank.moveInto(nil); | |
/* this agenda item is finished now */ | |
guanmgon.removeFromAgenda(tankReturnAgenda); | |
} | |
endErrand = "Xojo returns and stands next to Guanmgon in the doorway. " | |
; | |
++ koffee: Food | |
'koffee brand business (man\'s) aluminum aluminium 12-ounce 12 ounce | |
beverage/koffee/coffee/can' | |
'can of Koffee' | |
"Yes, it's Koffee with a K: Koffee brand Business Man's Beverage, | |
the 100% inorganic choice, packaged in a 12-ounce aluminum can. | |
It seems that you run into this horrid ToxiCola Corporation product | |
every time you visit a customer outside the US. " | |
dobjFor(Taste) asDobjFor(Eat) | |
dobjFor(Drink) asDobjFor(Eat) | |
dobjFor(Eat) { action() { "You don't want Xojo to feel put out, | |
so you manage to choke down a little. It's as awful as ever. "; }} | |
smellDesc = "It has a strong chemical odor, although nothing | |
you can place exactly; paint thinner mixed with chlorine, | |
perhaps, with just a hint of 3-methoxy-4-hydroxybenzaldehyde. " | |
dobjFor(Pour) | |
{ | |
preCond = [objHeld] | |
verify() { } | |
action() { "That would make a mess. "; } | |
} | |
dobjFor(PourInto) asDobjFor(Pour) | |
dobjFor(PourOnto) asDobjFor(Pour) | |
iobjFor(PutIn) | |
{ | |
verify() { } | |
check() | |
{ | |
if (gDobj == xt772hv) | |
"It'd be an interesting experiment, but the noxious | |
fluid would probably just dissolve the chip, and it's | |
still possible that you'll need it for something. "; | |
else | |
"Better not; who knows what the noxious fluid would | |
do to {that dobj/him}. "; | |
exit; | |
} | |
} | |
cannotOpenMsg = 'No need; Xojo already opened it for you. ' | |
/* we know about it from the start */ | |
isKnown = true | |
/* | |
* we're not actually part of the state tree - we just want to | |
* define ourselves here for convenience | |
*/ | |
location = nil | |
; | |
/* a special "escort" state class, for our trip to see the colonel */ | |
class XojoEscortState: GuidedTourState | |
stateDesc = "He's waiting for you to accompany him. " | |
showGreeting(actor) { "You already have Xojo's attention. "; } | |
justFollowed(success) | |
{ | |
local st; | |
/* | |
* if they made us follow them, it means they're not going where | |
* we're trying to guide them; let them know | |
*/ | |
"He looks a little impatient. <q>Please, this way.</q> "; | |
/* make sure we're in the right state for the new location */ | |
st = instanceWhich(XojoEscortState, {x: x.stateLoc == xojo.location}); | |
if (st != nil) | |
xojo.setCurState(st); | |
} | |
; | |
/* waiting in the control room to escort us to the colonel */ | |
++ xojoEscortControl: XojoEscortState | |
stateLoc = powerControl | |
stateDesc = "He's waiting for you to accompany him through the doorway. " | |
specialDesc = "Xojo is waiting for you to accompany him through | |
the doorway. " | |
escortDest = (powerControl.west) | |
stateAfterEscort = xojoHallEast | |
; | |
/* waiting at the east end of the hallway to escort us */ | |
++ xojoHallEast: XojoEscortState | |
stateLoc = powerHallEast | |
arrivingWithDesc = "Xojo holds his hand out to indicate the far | |
end of the hall, and waits for you to proceed. " | |
stateDesc = "He's waiting for you to accompany him down the hall | |
to the west. " | |
specialDesc = "Xojo is waiting for you to follow him down the | |
hall to the west. " | |
escortDest = (powerHallEast.west) | |
stateAfterEscort = xojoHallWest | |
; | |
/* waiting at west end of hallway to escort us */ | |
++ xojoHallWest: XojoEscortState | |
stateLoc = powerHallWest | |
arrivingWithDesc = "" | |
arrivingTurn() | |
{ | |
if (plantElevator.isOnCall) | |
"Xojo stop and waits with you for the elevator. "; | |
else if (plantElevator.isAtTop) | |
{ | |
"Xojo pulls open the elevator door and pushes aside | |
the gate, and waits for you to get in. "; | |
plantElevatorGate.makeOpen(true); | |
} | |
else | |
{ | |
/* the elevator hasn't even been called yet - call it */ | |
"Xojo reaches out and pushes the elevator call button. | |
The neon lamp above the button lights dimly. "; | |
plantElevator.callToTop(); | |
} | |
} | |
stateDesc() | |
{ | |
if (plantElevatorGate.isOpen) | |
"He's waiting for you to get in the elevator. "; | |
else | |
"He's waiting with you for the elevator to arrive. "; | |
} | |
specialDesc() | |
{ | |
if (plantElevator.isAtTop) | |
"Xojo is here, holding the elevator door open for you. "; | |
else | |
"Xojo is here, waiting with you for the elevator. "; | |
} | |
escortDest = (plantElevatorGate.isOpen ? powerHallWest.west : nil) | |
stateAfterEscort = xojoElevator | |
; | |
+++ AskTellShowTopic @plantHallElevatorDoor 'stair(s|way)?' | |
"<q>Couldn\'t we take the stairs instead?</q> you ask, worried | |
about the time. | |
<.p>Xojo laughs nervously. <q>Stairs, no,</q> he says. <q>For fire | |
safety reasons, only the elevator is provided in this sector.</q> " | |
; | |
++ xojoElevator: InConversationState | |
arrivingWithDesc = "" | |
arrivingTurn() | |
{ | |
/* | |
* if the elevator isn't at the top, we must have re-entered the | |
* elevator through the service panel; don't repeat this | |
* conversation (and the elevator descent) in this case | |
*/ | |
if (!plantElevator.isAtTop) | |
return; | |
"Xojo lets the door swing shut and closes the gate, then | |
pushes the <q>G</q> button. The elevator lurches and starts | |
slowly descending. "; | |
/* kick off Xojo's resume offer after we get going */ | |
xojo.scheduleInitiateConversation(nil, 'xojo-resume', 1); | |
/* close the door and start the descent */ | |
plantElevatorGate.makeOpen(nil); | |
plantElevator.startDescent(); | |
} | |
/* we have no limit on our attention span */ | |
attentionSpan = nil | |
/* just stay in this state at end of conversation */ | |
nextState = self | |
; | |
+++ AskTellShowTopic @powerElevPanel | |
"<q>Is that a service panel up there?</q> you ask. | |
<.p><q>The answering of such questions is not within the scope | |
of my responsibilities,</q> Xojo says. <q>Our very fine department | |
of vertical conveyance is in command of such matters.</q> " | |
; | |
++++ AltTopic | |
"You point to the service panel. <q>Do you think we could get | |
out through that panel?</q> | |
<.p>Xojo looks at it appraisingly. <q>Perhaps, but the height is | |
too far above for me to reach it. Perhaps I could lift you, and | |
you could attempt reaching. Shall we try?</q><.convnode offer-boost> " | |
isActive = (plantElevator.isAtBottom) | |
; | |
++++ AltTopic | |
"<q>I need to get up to the service panel again,</q> you say. | |
<.p><q>Would you like me to lift you again?</q> Xojo asks. | |
<.convnode offer-boost> " | |
isActive = (xojo.boostCount != 0) | |
; | |
++++ AltTopic | |
"<q>Thanks for your help with the service panel,</q> you say. | |
<.p><q>It is a duty and a pleasure to assist,</q> he says. " | |
isActive = (me.isIn(atopPlantElevator)) | |
; | |
++ xojoElevatorBoosting: InConversationState | |
stateDesc = "He's holding you on his shoulders. " | |
specialDesc = "Xojo is holding you on his shoulders. " | |
attentionSpan = nil | |
afterAction() | |
{ | |
/* if the PC is back in the elevator, go back to the elevator state */ | |
if (gPlayerChar.location == plantElevator) | |
xojo.setCurState(xojoElevator); | |
} | |
; | |
++ ConvNode 'offer-boost'; | |
+++ YesTopic | |
topicResponse() | |
{ | |
"<q>Okay,</q> you say, <q>let's give it a try.</q> "; | |
xojo.boostPlayerChar(); | |
} | |
; | |
+++ NoTopic | |
"<q>No, thanks,</q> you say. <q>I'll try to think of something else.</q> " | |
; | |
/* ask about the elevator once broken */ | |
++ AskTellShowTopic @elevatorTopic | |
"<q>What's wrong with the elevator?</q> you ask. | |
<.p><q>The elevator is sometimes faulty in halting as requested,</q> | |
Xojo replies. <q>But no need for concern. It reliably halts, in due | |
course.</q> " | |
isActive = (plantElevator.curFloor <= 2) | |
; | |
+++ AltTopic, SuggestedAskTopic, StopEventList | |
['<q>How can we get out of here?</q> you ask. | |
<.p><q>Yes, our facility\'s very efficient department of vertical | |
conveyance will soon observe our mishap, then to notify office of | |
elevator rescue. Many times before when I have been similarly | |
detained in the elevators, the department responded within several | |
hours only, in typical experiences. </q> ', | |
'<q>Are you sure we can\'t find a way out of here?</q> you ask, hoping | |
to avoid waiting for help---you definitely can\'t afford the <q>several | |
hours</q> Xojo said that could take. | |
<.p>Xojo shakes his head. <q>The standard procedure for such | |
elevator mishaps is the waiting for rescue.</q> '] | |
isActive = (plantElevator.isAtBottom) | |
name = 'the elevator' | |
; | |
+++ AltTopic | |
"<q>I'm glad we were able to get out of that elevator,</q> you say. | |
<.p><q>Yes, your extrication plan was most well conceived | |
and executed,</q> Xojo says. " | |
isActive = (plantElevator.isAtBottom && me.hasSeen(atopPlantElevator)) | |
; | |
++ ConvNode 'xojo-resume' | |
npcGreetingMsg() | |
{ | |
"Xojo clears his throat. <q>I was wondering,</q> he says, | |
<q>if your fine company might consider myself for a position | |
of employment.</q> He produces a piece of paper and holds | |
it out for you. <q>My résumé,</q> he says. <q>I | |
would most appreciate your kind consideration.</q> | |
<.topics><.reveal xojo-job> "; | |
xojoResume.makePresent(); | |
} | |
npcContinueList: StopEventList { | |
['<q>If anything is unclear on my résumé,</q> | |
Xojo says, <q>I would be pleased to clarify.</q> ', | |
'', | |
'<q>I would appreciate the opportunity to be considered | |
for opportunities with your wonderful company,</q> Xojo says. ', | |
'', | |
'Xojo looks at you expectantly. ', | |
'Xojo watches you expectantly. ' | |
] | |
} | |
; | |
++ AskTellShowTopic +90 @xojoResume | |
"<q>This is your résumé?</q> you ask. | |
<.p><q>Yes,</q> Xojo says, <q>please allow me to clarify any | |
questions you wonder about.</q> " | |
isActive = (xojoResume.isIn(xojo)) | |
; | |
++ TopicGroup | |
isActive = (xojoResume.described) | |
; | |
+++ AskTellShowTopic, StopEventList @xojoResume | |
['<q>This looks very nice,</q> you say. <q>Is there anything more | |
you wanted to mention about it?</q> | |
<.p><q>Yes, perhaps it is useful to know that my rank, rank 7, | |
is considered advanced for an employee of my duration.</q> ', | |
'<q>Is there anything else about your résumé you wanted | |
to add?</q> you ask. | |
<.p><q>Thank you,</q> he responds, <q>but I believe it is | |
inclusive.</q> '] | |
; | |
+++ AskTellTopic, SuggestedAskTopic, StopEventList @kowtuan | |
['<q>Forgive me,</q> you say, <q>but I haven\'t heard of your | |
university before. Is it a good school?</q> | |
<.p><q>Oh, yes,</q> Xojo says earnestly, nodding his head rapidly. | |
<q>It is often known as the <q>MIT</q> of the tri-province region. | |
The program is very rigorous.</q> ', | |
'<q>Is there anything else about your university you can tell me?</q> | |
<.p>Xojo replies, <q>The program is considered very excellent.</q> '] | |
name = 'Kowtuan Technical Institute' | |
; | |
+++ AskTellTopic, SuggestedAskTopic, StopEventList @chipTechNo | |
['<q>Tell me about your job at ChipTechNo,</q> you say. | |
<.p><q>It was an excellent experience of learning,</q> he says, | |
<q>but the promotability was limited due to non-local ownership.</q> ', | |
'<q>What did you do at ChipTechNo?</q> you ask him. | |
<.p><q>My primary duties were in the chemical applicator | |
operations,</q> he says. <q>But this was only after | |
considerable training and apprenticeship, of course.</q> ', | |
'<q>Is there anything else about ChipTechNo you can tell me?</q> | |
you ask. | |
<.p><q>I believe you have the main facts now,</q> he says. ' | |
] | |
name = 'ChipTechNo' | |
; | |
+++ AskTellTopic, SuggestedAskTopic, StopEventList @powerPlant6 | |
['<q>Why do you want to leave your job here at the plant?</q> you ask. | |
<.p><q>As you have surely observed,</q> he says, <q>my duties are | |
considerably variable, at the whim of my very capable superiors. | |
This has provided many excellent opportunities for frustration.</q> ', | |
'<q>Tell me more about your job at the plant,</q> you say. | |
<.p><q>I feel my growth opportunities at the Government Power | |
Plant are limited,</q> he says. <q>I would hope to find an | |
opportunity that is more robust.</q> ', | |
'<q>You\'re not happy with your job at the plant?</q> you ask. | |
<.p><q>My happiness is perhaps less here than it might be elsewhere,</q> | |
he says. '] | |
name = 'the power plant' | |
; | |
+++ AskTellTopic, SuggestedAskTopic, StopEventList @guanmgon | |
['<q>Guanmgon seems to keep you busy,</q> you say. | |
<.p><q>Yes,</q> Xojo says, looking a bit distant for a moment. | |
<q>He is most capable at providing numerous small tasks of | |
considerable import to himself.</q> ', | |
'<q>Is Guanmgon a good manager?</q> you ask. | |
<.p><q>He is undoubtedly assigned the role of manager,</q> | |
Xojo replies. '] | |
name = 'Guanmgon' | |
; | |
++ TellTopic, SuggestedTellTopic, StopEventList @hiringFreezeTopic | |
['<q>I\'m sorry to say that there\'s a hiring freeze at Omegatron | |
right now,</q> you explain. <q>That means we can\'t hire anyone.</q> | |
<.p>Xojo can\'t quite conceal his disappointment. <q>Oh. Well, | |
perhaps in the future, then.</q> You shrug slightly and | |
nod noncommittally. ', | |
'<q>Sorry about the hiring freeze,</q> you say. | |
<.p><q>Oh, that is not your fault,</q> Xojo says. <q>Still, it | |
is unfortunate.</q> '] | |
name = 'the Omegatron hiring freeze' | |
isActive = gRevealed('xojo-job') | |
; | |
/* waiting at the east end of the hallway to escort us */ | |
++ xojoS2West: XojoEscortState | |
stateLoc = s2HallWest | |
arrivingWithDesc = "Xojo points down the hall. " | |
stateDesc = "He's waiting for you to accompany him down the hall | |
to the east. " | |
specialDesc = "Xojo is waiting for you to follow him down the | |
hall to the east. " | |
escortDest = (s2HallWest.east) | |
stateAfterEscort = xojoS2East | |
; | |
++ xojoS2East: XojoEscortState | |
stateLoc = s2HallEast | |
arrivingWithDesc() | |
{ | |
/* open the door if it's not already */ | |
if (s2HallEastDoor.isOpen) | |
"Xojo indicates the door. <q>This way,</q> he says. "; | |
else | |
{ | |
s2HallEastDoor.makeOpen(true); | |
"Xojo opens the door. <q>This way, please,</q> he says, | |
indicating the door. "; | |
} | |
} | |
stateDesc = "He's waiting for you to accompany him through | |
the door. " | |
specialDesc = "Xojo is waiting for you to accompany him through | |
the door. " | |
escortDest = (s2HallEast.north) | |
stateAfterEscort = xojoS2Storage | |
; | |
++ xojoS2Storage: XojoEscortState | |
stateLoc = s2Storage | |
arrivingWithDesc = "<q>The clutter is formidable,</q> Xojo says, | |
<q>but the way north is passable.</q> " | |
stateDesc = "He's waiting for you to accompany him through | |
the junk to the north. " | |
specialDesc = "Xojo is waiting for you to accompany him through | |
the junk to the north. " | |
escortDest = (s2Storage.north) | |
stateAfterEscort = xojoS2Utility | |
; | |
++ xojoS2Utility: XojoEscortState | |
stateLoc = s2Utility | |
arrivingWithDesc = "Xojo points to the opening. <q>My apologies, | |
but this is the way for proceeding,</q> he | |
says. " | |
stateDesc = "He's waiting for you to go through the opening | |
in the wall. " | |
specialDesc = "Xojo is by the opening in the wall, waiting for | |
you to go through. " | |
escortDest = (s2Utility.north) | |
stateAfterEscort = xojoS2Platform | |
; | |
++ xojoS2Platform: XojoEscortState | |
stateLoc = s2Platform | |
arrivingWithDesc = "<q>Apologies once again,</q> Xojo says, | |
indicating the rope bridge. <q>It is relatively safe, | |
in contrast to appearances.</q> " | |
stateDesc = "He's waiting by the rope bridge for you. " | |
specialDesc = "Xojo is waiting for you to start across the | |
rope bridge. " | |
escortDest = (s2Platform.north) | |
stateAfterEscort = xojoRopeBridge1 | |
/* | |
* this escort state class is special, because we don't want to | |
* bother with the usual "you let xojo lead the way" message here - | |
* we have a custom message for this travel instead, provided by the | |
* room | |
*/ | |
escortStateClass = SilentGuidedInTravelState | |
; | |
/* | |
* a "silent" version of the guided tour travel state - this simply | |
* doesn't say anything when we follow our escort | |
*/ | |
class SilentGuidedInTravelState: GuidedInTravelState | |
sayDeparting(conn) { } | |
; | |
++ xojoRopeBridge1: XojoEscortState | |
stateLoc = ropeBridge1 | |
arrivingWithDesc = "Xojo waits for you to catch up. " | |
stateDesc = "He's waiting for you to continue across the bridge. " | |
specialDesc = "Xojo is waiting for you to keep going across | |
the bridge. " | |
escortDest = (ropeBridge1.north) | |
stateAfterEscort = xojoRopeBridge2 | |
; | |
++ xojoRopeBridge2: XojoEscortState | |
stateLoc = ropeBridge2 | |
arrivingWithDesc = "Xojo waits for you to catch up. " | |
stateDesc = "He's waiting for you to continue across the bridge. " | |
specialDesc = "Xojo is waiting for you to continue across the bridge. " | |
escortDest = (ropeBridge2.north) | |
stateAfterEscort = xojoRopeBridge3 | |
; | |
++ xojoRopeBridge3: XojoEscortState | |
stateLoc = ropeBridge3 | |
arrivingWithDesc = "Xojo is just above, hanging on to the ropes. | |
<q>I think we can climb up from here,</q> he says. " | |
stateDesc = "He's waiting for you to try climbing up the ropes. " | |
specialDesc = "Xojo is waiting for you to try climbing up the ropes. " | |
escortDest = (ropeBridge3.up) | |
stateAfterEscort = xojoCanyonNorth | |
; | |
++ xojoCanyonNorth: XojoEscortState | |
stateLoc = canyonNorth | |
arrivingWithDesc = "" | |
arrivingTurn() | |
{ | |
if (firstTime) | |
{ | |
"<.p>Xojo sits on the ground, catching his breath. <q>Ha, | |
ha,</q> he forces a laugh. <q>Perhaps I should have mentioned. | |
The senior leaders sometimes find it is useful to partially cut | |
the ropes anchoring the bridge, as a surprise to those crossing | |
it. This reminds Junior Peons of the importance of remaining | |
alert.</q> He gets to his feet. <q>This way,</q> he says, | |
pointing to the path. "; | |
firstTime = nil; | |
} | |
else | |
"Xojo points to the path. <q>This way,</q> he says. "; | |
} | |
firstTime = true | |
stateDesc = "He's waiting by the path. " | |
specialDesc = "Xojo is waiting by the path. " | |
escortDest = (canyonNorth.northeast) | |
stateAfterEscort = xojoCourtyard | |
; | |
++ xojoCourtyard: XojoEscortState | |
stateLoc = plantCourtyard | |
arrivingWithDesc = "Xojo points to the doors into the building. " | |
stateDesc = "He's waiting for you to go into the building. " | |
specialDesc = "Xojo is waiting for you to enter the building. " | |
escortDest = (plantCourtyard.in) | |
stateAfterEscort = xojoAdmin | |
; | |
++ xojoAdmin: HermitActorState | |
arrivingWithDesc = "" | |
arrivingTurn() | |
{ | |
"<.p>Xojo taps you on the shoulder. <q>Behold, Colonel | |
Magnxi!</q> he shouts, pointing to the Colonel. You hadn't | |
picked her out of the crowd yet, but now you see her standing | |
nearby. | |
<.p>So this is your chance. You wish you didn't look like | |
you'd just been hit by a bus, but this is no time to worry | |
about trivial details such as looking presentable. Besides, | |
the Colonel looks a little ridiculous herself---the military | |
uniform she's wearing is funny-looking enough, but she's worn | |
that every other time you've met with her. It's the hat she | |
has on that crosses the line from eccentric to bizarre. "; | |
/* no need to follow the player any longer */ | |
xojo.followingActor = nil; | |
/* move the colonel here */ | |
magnxi.moveIntoForTravel(adminLobby); | |
} | |
stateDesc = "He's watching the band. " | |
specialDesc = "Xojo is here, watching the band. " | |
noResponse = "Xojo can't seem to hear you over the music. " | |
; | |
++ xojoEmailAgenda: ConvAgendaItem, DelayedAgendaItem | |
invokeItem() | |
{ | |
"<.p>Xojo taps you on the shoulder and hands you a piece of | |
paper---a print-out from an old-style line printer, with | |
alternating green and white horizontal stripes across the | |
page. <q>This arrived in the nightly batch of electronic | |
e-mail,</q> he says. <q>I express my sympathy for your | |
unfortunate non-success,</q> he adds. <q>I wish you improvement | |
of fortune for your future endeavors. Now, I must return | |
to assist Guanmgon in dismantling your very wonderful | |
SCU-1100DX product.</q> You shake hands, and he disappears | |
into the crowd. "; | |
/* move the email */ | |
adminEmail.moveInto(me); | |
/* I'm outta here */ | |
xojo.moveIntoForTravel(nil); | |
/* we're done */ | |
isDone = true; | |
} | |
/* we need the PC to be present to proceed */ | |
isReady = (inherited() && xojo.canSee(me)) | |
; | |
/* | |
* Our other assistant. | |
*/ | |
+ guanmgon: Person 'guanmgon/guan/man/bureaucrat*men' 'Guanmgon' | |
"He's a mid-level bureaucrat assigned to assist you with the | |
installation. He's wearing a suit that looks a bit too small | |
for him. Guanmgon looks like he's in his forties. " | |
isProperName = true | |
isHim = true | |
/* | |
* ensure we start the phone call on this turn, if we haven't | |
* already started it | |
*/ | |
ensureStartPhoneCall() | |
{ | |
if (phoneState < 16) | |
phoneState = 16; | |
} | |
/* flag: we've received the phone call */ | |
didGetCall = nil | |
/* the phone daemon handler */ | |
phoneState = 0 | |
phoneScript() | |
{ | |
/* check our state */ | |
switch (phoneState++) | |
{ | |
case 16: | |
/* start the phone call */ | |
"<.p>You hear the opening notes of Beethoven's Fifth as rendered | |
in piezo-electric square waves: Guanmgon's cell phone ringing. | |
He frantically pulls out the phone, drops it, catches it in | |
mid-air, drops it again, picks it up, pokes at the keypad, | |
and finally puts it to his ear. His conversation isn't in | |
English, so you don't have any idea what's being said, but | |
you can tell it's not good news. You'd bet it's his | |
superiors calling for yet another update. Needless to | |
say, the bill of goods they were sold didn't include six | |
long weeks just to get the demo working, and they haven't | |
been hiding their impatience lately. "; | |
/* switch to my 'on the phone' state */ | |
setCurState(guanmgonOnPhone); | |
setConvNode(nil); | |
break; | |
case 17: | |
/* continue the call */ | |
"<.p>Guanmgon continues talking on the phone. More listening | |
than talking, actually; even without understanding the language, | |
you can tell that every time he starts to say something he gets | |
interrupted. "; | |
break; | |
case 18: | |
/* make sure xojo is back from any errand, since we mention him */ | |
xojo.endErrand(); | |
/* finish the call */ | |
"<.p>Guanmgon fumbles with the phone, almost dropping it, and | |
puts it away. Xojo asks him something, and you hear | |
<q>Mitachron</q> a couple of times in Guanmgon's reply, | |
accompanied by heavy sighs. "; | |
/* switch back to the base state */ | |
setCurState(guanmgonInit); | |
/* note that we got the call */ | |
didGetCall = true; | |
/* the script is done, so cancel the daemon */ | |
eventManager.removeCurrentEvent(); | |
break; | |
} | |
} | |
; | |
++ InitiallyWorn | |
'narrow vertical broad striped checked small brown slightly mismatched | |
suit/slacks/pants/jacket/stripe/stripes/check/checks/pattern' | |
'Guanmgon\'s suit' | |
"Apart from looking a little too small, the thing that makes | |
Guanmgon's suit look odd is that the jacket and slacks don't | |
quite match. They're both brown, but the jacket has a narrow | |
vertical striped pattern, and the pants have broad checks. " | |
isQualifiedName = true | |
isListedInInventory = nil | |
; | |
/* our initial state - standing nearby watching us try to repair the SCU */ | |
++ guanmgonInit: ActorState, EventList | |
isInitState = true | |
stateDesc = "It seems to you that Guanmgon has grown increasingly | |
nervous as your work here has dragged on; lately he's | |
become almost frantic. " | |
specialDesc = "Guanmgon is watching from the doorway, craning his | |
neck to see what you're doing. " | |
specialDescListWith = [standingInDoorway] | |
/* our background script steps */ | |
eventList = | |
[ | |
nil, | |
&initKoffee, | |
'Guanmgon taps his fingers on the wall. <q>No reason for panic,</q> | |
he says to himself. <q>Success is still possible.</q> ', | |
&initDeet | |
] | |
doScript() | |
{ | |
/* | |
* only perform a scripted action if we haven't engaged in | |
* conversation on this turn, so that we're not overly active; to | |
* perform a scripted action, just inherit the default handling | |
*/ | |
if (!guanmgon.conversedThisTurn()) | |
inherited(); | |
} | |
initKoffee() | |
{ | |
/* start offering "koffee" on the next turn */ | |
guanmgon.addToAgenda(koffeeAgenda); | |
/* just show a background message on this turn */ | |
"Guanmgon tiptoes into the room, trying to find a path through | |
the clutter, but he bumps something and knocks a pile of equipment | |
onto the floor. <q>Sorry, sorry!</q> he whispers. He hastily | |
puts everything back as it was and works his way back to the | |
doorway. "; | |
} | |
initDeet() | |
{ | |
/* add the item for fetching deet */ | |
guanmgon.addToAgenda(deetAgenda); | |
} | |
; | |
++ HelloTopic, ShuffledEventList | |
['You glance over at Guanmgon. As soon as you make eye contact, | |
he eagerly leans into the room. <q>I am to your service,</q> | |
he says. ', | |
'Guanmgon sees that you\'re about to speak. <q>Whatever you need, | |
it is I who is here to provide,</q> he says nervously. ', | |
'You look over at Guanmgon and clear your throat. <q>I am | |
here to be of help,</q> he says. '] | |
; | |
++ ByeTopic "<q>Thanks,</q> you say. Guanmgon nods. " | |
; | |
++ ImpByeTopic "" | |
; | |
/* | |
* A class for guanmgon's "conversational agenda" items. These are | |
* things guanmgon wants to do when he has an opening in the | |
* conversation. | |
*/ | |
class GuanmgonAgendaItem: ConvAgendaItem | |
/* | |
* We can optionally set a delay, so that we don't run at the first | |
* opportunity but wait the given number of turns. | |
*/ | |
delayBy = 0 | |
/* cancel these items after the SCU is repaired */ | |
isDone = (scu1100dx.isWorking && scu1100dx.isOn) | |
/* | |
* These items all dispatch xojo, so make sure xojo is present; only | |
* perform these items in our base state. | |
*/ | |
isReady = (inherited() | |
&& xojo.location == guanmgon.location | |
&& guanmgon.curState == guanmgonInit | |
&& deferCnt++ >= delayBy) | |
/* | |
* number of times we've been able to run but haven't, to satisfy | |
* our delay | |
*/ | |
deferCnt = 0 | |
; | |
/* | |
* an "agenda item" - this lets guanmgon keep pursuing the koffee | |
* question until we get an answer | |
*/ | |
++ koffeeAgenda: GuanmgonAgendaItem | |
invokeItem() | |
{ | |
"<q>Do you need anything?</q> Guanmgon asks. <q>Perhaps some | |
Koffee?</q> "; | |
guanmgon.setConvNode('Koffee?'); | |
me.noteConversation(guanmgon); | |
} | |
; | |
++ deetAgenda: GuanmgonAgendaItem | |
delayBy = 2 | |
invokeItem() | |
{ | |
xojo.beginErrand(deetErrand); | |
} | |
; | |
++ tankReturnAgenda: GuanmgonAgendaItem | |
delayBy = 2 | |
invokeItem() | |
{ | |
"<.p>Guanmgon waves his hand to clear the air around him. | |
<q>Xojo! You must not keep the powerful insect repellent | |
vessel for so long, for our cost account will receive the | |
excess internal late fee! Go quickly to return the vessel!</q> | |
Xojo lugs the tank away. "; | |
/* start the tank return errand */ | |
xojo.beginErrand(tankReturnErrand); | |
} | |
; | |
++ ConvNode 'Koffee?' | |
npcContinueMsg() | |
{ | |
/* start the errand */ | |
"<q>Yes, Koffee is just what you need,</q> Guanmgon says. | |
<.convnode> "; | |
/* send xojo off for Koffee */ | |
xojo.beginErrand(koffeeErrand); | |
} | |
; | |
+++ YesTopic | |
topicResponse() | |
{ | |
"<q>Sure, thanks,</q> you say.<.p> "; | |
xojo.beginErrand(koffeeErrand); | |
} | |
; | |
+++ NoTopic | |
topicResponse() | |
{ | |
"<q>No, thanks, that's okay,</q> you say. You want nothing to | |
do with that Koffee stuff. | |
<.p><q>Yes, I insist,</q> Guanmgon says, <q>I would be pleased to | |
bring you some.</q> He turns to Xojo. "; | |
xojo.beginErrand(koffeeErrand); | |
} | |
; | |
+++ AskTellAboutForTopic @koffee | |
topicResponse() | |
{ | |
"<q>Do you mean Koffee with a K?</q> you ask. | |
<.p><q>Yes, of course,</q> Guanmgon says. "; | |
xojo.beginErrand(koffeeErrand); | |
} | |
; | |
/* | |
* while guanmgon is on the phone, he's a "hermit" - he doesn't respond | |
* to any conversational commands | |
*/ | |
++ guanmgonOnPhone: HermitActorState | |
stateDesc = "He's currently talking to someone on his cell phone. " | |
specialDesc = "Guanmgon is talking to someone on his cell phone. " | |
/* don't respond to any conversation while on the phone */ | |
noResponse = "You don't want to disturb him while he's on the phone. " | |
; | |
++ DefaultCommandTopic | |
"Guanmgon politely declines, explaining that, much as he'd like | |
to help, administrative rules prohibit someone of his high rank | |
from assisting in this manner. " | |
isConversational = nil | |
; | |
++ AskTellAboutForTopic @koffee | |
"<q>Do you have any coffee around here?</q> you ask. | |
<.p>Guanmgon smiles and nods. <q>Koffee, yes.</q> " | |
; | |
+++ AltTopic | |
"<q>You wouldn't have any real coffee, with a C, would you?</q> | |
<.p><q>We have the very delicious Koffee with a K kind only,</q> | |
Guanmgon says, nodding. " | |
isActive = (koffee.location != nil) | |
; | |
++ AskTellShowTopic [mosquitoes, deetCloud] | |
"<q>There are sure a lot of mosquitoes around here,</q> you | |
say, stating the obvious. | |
<.p><q>Mosquitoes, many, yes,</q> Guanmgon says, laughing | |
nervously. " | |
; | |
+++ AltTopic | |
"<q>I don't think the insecticide is working,</q> you say, | |
swatting another mosquito. | |
<.p>Guanmgon laughs nervously. <q>We must be patient in the | |
extreme, and trust that working will eventually occur, just | |
as with the very nice SCU product.</q> " | |
isActive = (deetCloud.location != nil) | |
; | |
++ AskTellTopic @contract | |
"Guanmgon is only a mid-level functionary; he doesn't know anything | |
about contract matters. " | |
isConversational = nil | |
; | |
++ GiveShowTopic @contract | |
"There's no point; Guanmgon is only a mid-level functionary, not | |
the sort of deluxe grand high administrator who could help you | |
with a contract matter. " | |
isConversational = nil | |
; | |
/* change the response once we fix the SCU */ | |
++ GiveShowTopic @scu1100dx | |
"You don't have anything to show him until you get it working. " | |
isConversational = nil | |
; | |
+++ AltTopic | |
"Guanmgon looks a little busy; besides, you're in a hurry to | |
see the Colonel. " | |
isActive = (scu1100dx.isWorking && scu1100dx.isOn) | |
; | |
++ GiveShowTopic [ct22, s901, xt772hv, tester, testerProbe, xt772lv] | |
"It's been made clear that Guanmgon is here for administrative, | |
not technical, support. " | |
isConversational = nil | |
; | |
++ AskTellTopic, SuggestedAskTopic @phoneCallTopic | |
"<q>Do you mind my asking what that call was about?</q> you ask. | |
<.p><q>Call? What call? Oh, yes, it was nothing. No need for | |
concern at all. It will be fine. No reason for panic. Yes, there | |
was just a slight mention of a representative of the Mitachron | |
company, possibly arriving, possibly today. I am sure this is no | |
reason for worrying.</q> He pretends to smile broadly. " | |
/* this topic isn't meaningful until after the phone call */ | |
isActive = (guanmgon.didGetCall) | |
/* suggestion name */ | |
name = 'the phone call' | |
; | |
++ guanmgonChecking: HermitActorState | |
stateDesc = "He's busily checking the equipment. " | |
specialDesc = "Guanmgon is here, busily checking the equipment. " | |
noResponse = "He seems busy, and besides, you're in a bit of | |
a hurry to see the Colonel. "; | |
; | |
++ AskTellTopic @guanmgon | |
"<q>How's it going?</q> you ask. | |
<.p><q>Oh, well, very fine, thank you,</q> Guanmgon says. <q>We will | |
soon have this assignment complete, I am certain.</q> " | |
; | |
/* | |
* A pair of topics about Mitachron. The first is known from the start; | |
* the second one becomes available only after the phone call. | |
*/ | |
++ AskTellTopic, SuggestedAskTopic @mitachronTopic | |
"<q>What do you know about Mitachron?</q> you ask. | |
<.p><q>Nothing!</q> he rushes to say. <q>Certainly, your very | |
wonderful SCU-1100DX is far superior to the similiar offerings | |
that this other company provides. No need exists for worrying about | |
that company! I am sure we can be successful at completing this | |
assignment, so there will be no motivation for considering | |
alternatives.</q> " | |
/* suggestion name */ | |
name = 'mitachron' | |
; | |
+++ AltTopic, SuggestedAskTopic | |
"<q>Have you heard anything about Mitachron?</q> you ask. | |
<.p><q>No!</q> he replies. <q>Only on my phone call, and just a | |
small mention, very brief, probably not important. Something about | |
a representative of that company arriving, that is all it was. | |
I am sure we will be successful before any reason to worry arises.</q> " | |
isActive = (guanmgon.didGetCall) | |
name = 'mitachron' | |
; | |
++ AskTellTopic [omegatronTopic, me] | |
"<q>What do you think about Omegatron so far?</q> you ask. | |
<.p><q>Excellent, very good,</q> he says, nodding rapidly. <q>I am | |
sure we can be successful. I am impressed especially with how | |
very greatly challenging you have made this project appear.</q> " | |
; | |
++ AskTellShowTopic [ct22, xt772hv, xt772lv] | |
"You really don't think Guanmgon knows much about | |
the inner workings of this equipment. " | |
isConversational = nil | |
; | |
/* | |
* if we haven't found the xt772-lv yet, override the more general | |
* answer about the equipment - use an elevated score to override other | |
* answers | |
*/ | |
++ AskTellAboutForTopic +110 @xt772lv | |
"Not knowing where else to turn, you ask Guanmgon if he | |
has any idea where you can find the chip you need. | |
<q>No, sorry, we do not have anything of this kind,</q> he | |
says. <q>Perhaps we can order it on the internet web? But | |
that would take too long. We must be successful with our | |
assignment much too soon for that!</q> " | |
/* this one is used only if we haven't already found the xt772-lv */ | |
isActive = (!xt772lv.isFound) | |
; | |
++ AskTellTopic @magnxi | |
"<q>What can you tell me about Colonel Magnxi?</q> you ask. | |
<.p><q>The Colonel?</q> he says, getting flustered. <q>There's | |
no need to talk to the Colonel! I have great confidence in | |
our success with this assignment. Very great confidence!</q> " | |
; | |
/* | |
* a pair of topics about the SCU - which one we get depends on whether | |
* or not we've fixed it yet | |
*/ | |
++ AskTellShowTopic @scu1100dx | |
"<q>How do you like the SCU-1100DX so far?</q> you ask. | |
<.p><q>Very wonderful!</q> he says. <q>It will be more wonderful | |
when it is working, certainly, but it is very wonderful already.</q> " | |
; | |
+++ AltTopic | |
"<q>How do you like the SCU-1100DX so far?</q> you ask. | |
<.p><q>I am relieved very much to see that your excellent product | |
has begun functioning properly,</q> he says. <q>Very, very, very | |
relieved.</q> " | |
isActive = (scu1100dx.isWorking && scu1100dx.isOn) | |
; | |
++ AskTellShowTopic @xojo | |
"<q>Tell me about Xojo,</q> you say. | |
<.p><q>Xojo is here to assist you,</q> he says. <q>I hope he has | |
been adequate in his assistance. There has been no mistake in | |
assigning him, I am sure. He is most capable.</q> " | |
; | |
++ AskTellTopic @powerPlant6 | |
"<q>What else can you tell me about the power plant?</q> | |
you ask. | |
<.p><q>I am very committed to its proper operation!</q> he | |
says. <q>Yes, I very much like it here.</q> " | |
; | |
++ DefaultAskTellTopic, ShuffledEventList | |
['Guanmgon doesn\'t seem to understand the question. ', | |
'Guanmgon smiles but does not respond. ', | |
'<q>I am sorry,</q> Guanmgon says, <q>perhaps | |
I could answer a different question?</q> '] | |
; | |
++ Decoration 'cell cellular phone/telephone/cellphone' 'cell phone' | |
"You're as technophilic as the next engineer, but really, you | |
haven't been very impressed by devices that can play synthesized | |
music with square waves since the early PC days. " | |
; | |
/* | |
* Decorations for the control room equipment. Decorations are objects | |
* that exist for descriptive purposes only, and generally have no other | |
* function in the game. The default response of a decoration to any | |
* action is to say "that's not important," to make it clear to the | |
* player that they shouldn't waste any time trying to figure out the | |
* object's purpose. We customize some of these decorations (via | |
* notImportantMsg) with more specific responses, but the gist is the | |
* same. | |
*/ | |
+ Decoration '(power) (plant) equipment/systems/pile' 'equipment' | |
"It's the usual complement of breaker panels, gauges, load | |
control boards, and internal comm systems that you'd expect | |
to find in any plant built in the 1960s. Right now it's all | |
opened up and taken apart, thanks to your efforts to integrate | |
the 1100DX into the existing systems. " | |
notImportantMsg = 'You\'ve already jury-rigged it pretty | |
thoroughly; it\'d probably be best not to | |
mess with it now that things are nearly working. ' | |
/* | |
* It's the power plant's systems. Setting 'owner' like this | |
* explicitly means that the player can refer to this using a | |
* possessive that refers to the owner, as in "the plant's | |
* equipment" or "the power plant's systems". | |
*/ | |
owner = powerPlant6 | |
; | |
+ Decoration 'breaker panel/panels/switch/switches/breakers' 'breaker panels' | |
"There are dozens of breaker switches controlling the distribution | |
of power out of the generators and providing some protection against | |
overloads. " | |
isPlural = true | |
notImportantMsg = 'That\'s not your domain; best to leave it alone. ' | |
; | |
+ Decoration 'power levels/gauge/guage/gauges/guages/voltages/amperages' | |
'gauges' | |
"The gauges show voltages, amperages, and power levels for the many | |
circuits. " | |
notImportantMsg = 'You should probably leave the gauges alone. ' | |
isPlural = true | |
; | |
+ Decoration 'load control board/boards/panel/panels' 'load control boards' | |
"These controls adjust the power generation capacity to match the | |
load. Every day you've been here, technicians have interrupted you | |
several times to adjust these settings. " | |
notImportantMsg = 'The controls are all delicately balanced; you | |
definitely don\'t want to touch them. ' | |
isPlural = true | |
; | |
/* | |
* The internal communications systems. Note that we define the | |
* vocabulary words "system" and "systems" in parentheses; this makes | |
* them "weak" vocabulary words, which means that they can't be used to | |
* refer to this object without also including one of the other | |
* ("strong") words. We do this because we don't want any ambiguity if | |
* the player refers to simply "system" or "systems" - we want those | |
* words by themselves to refer to the main "equipment" decoration that | |
* models all of the plant's systems. | |
*/ | |
+ Decoration 'internal comm communications (system)/(systems)' 'comm systems' | |
"The comm system lets the operators talk to technicians in other | |
parts of the plant. " | |
notImportantMsg = 'Best to leave the comm systems alone. ' | |
isPlural = true | |
; | |
/* | |
* Mosquitoes. These are purely a decoration, but we mention them a lot | |
* in the room's atmospheric messages, so it's good to be able to refer | |
* to them. | |
*/ | |
+ mosquitoes: Decoration | |
'mosquito/mosquitos/mosquitoes/fly/flies/bug/bugs/insect/insects' | |
'mosquitoes' | |
"They're incessant. " | |
isPlural = true | |
notImportantMsg = 'The mosquitoes are extremely annoying, but | |
they\'re not important. ' | |
dobjFor(Attack) | |
{ | |
verify() { } | |
action() { "It's futile; there are more bugs in this | |
room than in the SCU-1100DX. "; } | |
} | |
dobjFor(Take) | |
{ | |
verify() { } | |
action() { "Try as you might, you can't catch any of them. "; } | |
} | |
; | |
/* the SCU-1100DX */ | |
+ scu1100dx: TestableCircuit, Immovable, OnOffControl | |
'omegatron supplemental control unit model big metal | |
box/scu/1100/1100dx/scu-1100/scu-1100dx/scu1100/scu1100dx' | |
'SCU-1100DX' | |
"An Omegatron Supplemental Control Unit model 1100DX. You ought | |
to know it like the back of your hand after sitting through all | |
of those engineering meetings and design reviews, but the extensive | |
cost-cutting and jury-rigging in the manufacturing process | |
changed it into something strangely unfamiliar. | |
<<isOn ? "Fortunately, you finally managed to get it working. " | |
: "Regardless, your job is to get it working. " >> | |
<.p> | |
The 1100 is essentially a big metal box open on one side, like a | |
refrigerator without a door (and about the same size), filled | |
with stacked electronics modules. Bundles of wires and cables connect | |
the modules to the control boards and other plant equipment. | |
It's currently powered <<onDesc>>. | |
<<isWorking ? "" : | |
"<.p>There's one empty slot, which is where the CT-22 module you | |
removed goes." >> " | |
/* customize the messages for various ways of trying to move me */ | |
cannotTakeMsg = 'Are you kidding? It\'s the size of a refrigerator, | |
and it weighs as much as a car. ' | |
cannotMoveMsg = 'There\'s no room to move it anywhere. ' | |
cannotPutMsg = 'It\s far too heavy. ' | |
/* keep track of when I start working */ | |
isWorking = nil | |
/* we're not really a container, but make it look like we are */ | |
lookInDesc = "The unit is stuffed full of stacked electronics modules. " | |
dobjFor(Open) { verify() { illogical('The 1100DX doesn\'t have a | |
door, to keep the modules inside easily accessible. '); } } | |
dobjFor(Close) asDobjFor(Open) | |
dobjFor(TurnOn) | |
{ | |
check() | |
{ | |
/* allow activation only after repair */ | |
if (!isWorking) | |
{ | |
"You have no reason to turn it on until you've fixed | |
the problem with the CT-22. "; | |
exit; | |
} | |
} | |
action() | |
{ | |
/* remember our activated status */ | |
makeOn(true); | |
/* make sure xojo is back from any errand, since we mention him */ | |
xojo.endErrand(); | |
/* success! */ | |
"You cross your fingers and flip the switch. | |
The SCU-1100DX clicks and hums as it starts its | |
power-on self-test sequence. Many seconds pass. | |
You hold your breath. Guanmgon and Xojo lean in | |
from the doorway, as though there were something | |
to see. Then, four short beeps: successful | |
start-up! You carefully scan the modules, and | |
see that everything's working. After six weeks, | |
you\'ve finally done it; at last, you can give | |
the demo and, hopefully, get a contract signed. | |
<.p> | |
Guanmgon sees the SCU power up, and squeezes into | |
the room to check the control boards. <q>It is | |
working,</q> he says with amazement. <q>This is | |
so wonderful! Dreadful disgrace is not for us now! | |
But we must act quickly. Xojo! Escort Mr.\ Mittling | |
to see the Colonel at once. Hurry with maximum haste! | |
Do not delay! Quickly, quickly, hurry!</q> | |
<.p> | |
Xojo nods, then turns to you and indicates the | |
doorway. "; | |
/* set xojo to follow us around from now on */ | |
xojo.followingActor = me; | |
/* update xojo's and guanmgon's states for the change */ | |
xojo.setCurState(xojoEscortControl); | |
guanmgon.setCurState(guanmgonChecking); | |
guanmgon.setConvNode(nil); | |
/* count the score */ | |
scoreMarker.awardPoints(); | |
} | |
} | |
scoreMarker: Achievement { +10 "repairing the SCU-1100DX" } | |
/* once it's on, don't allow turning it off */ | |
dobjFor(TurnOff) | |
{ | |
check() | |
{ | |
"Are you out of your mind? There's no way you're going to | |
risk going through a power cycle now that you've got it | |
ready to demo. "; | |
exit; | |
} | |
} | |
dobjFor(Repair) | |
{ | |
action() | |
{ | |
mainReport(isWorking && isOn | |
? 'But it\'s already repaired! ' | |
: 'It sure would be nice if it were that easy, | |
wouldn\'t it? Sadly, you don\'t have any magical | |
powers of electronics repair, so you\'ll have to do | |
something besides just willing it to start working. '); | |
} | |
} | |
/* map 'put x in scu' to 'put x in slot' */ | |
iobjFor(PutIn) maybeRemapTo(scuSlot.isIn(self), | |
PutIn, DirectObject, scuSlot) | |
; | |
++ scuSlot: TestableCircuit, Component, RestrictedContainer | |
'empty module slot' 'empty slot' | |
"It's the slot where the CT-22 diagnostic module goes. " | |
/* only allow the CT-22 to go in the slot */ | |
validContents = [ct22] | |
iobjFor(PutIn) | |
{ | |
action() | |
{ | |
if (gDobj != ct22) | |
{ | |
/* only allow my module to go in the slot */ | |
reportFailure('{That dobj} do{es}n\'t belong in the slot. '); | |
} | |
else if (!xt772lv.isIn(ct22)) | |
{ | |
reportFailure('There\'s no reason to re-install the module | |
until you\'ve fixed it. '); | |
} | |
else | |
{ | |
/* | |
* once the module is back in place, get rid of the empty | |
* slot, and get rid of the module itself as a separate | |
* entity | |
*/ | |
self.moveInto(nil); | |
ct22.moveInto(nil); | |
/* | |
* in case they want to refer to 'it' again, refer them | |
* to the entire of components, since we're essentially | |
* merged into the stack now | |
*/ | |
gActor.setIt(moduleStack); | |
/* | |
* add the ct22 vocabulary to the main stack of modules, | |
* so that we can still refer to the ct22 even though | |
* it's no longer around as a separate object | |
*/ | |
moduleStack.initializeVocabWith(ct22.vocabWords); | |
/* the SCU-1100DX is now repaired! */ | |
scu1100dx.isWorking = true; | |
/* explain what happened */ | |
mainReport('The module slides into place—not quite | |
smoothly, but at this point it\'s enough that it fits | |
at all. You give it a firm push to make sure the | |
connectors are all seated properly. Now all that | |
remains is switching on the SCU, and hoping there\'s | |
not yet another problem lurking. '); | |
} | |
} | |
} | |
; | |
++ moduleStack: TestableCircuit, Immovable | |
'stacked installed electronics module/modules/stack' 'electronics modules' | |
"Each module is about the shape of a pizza box, and the modules | |
are stacked one on top of another inside the 1100DX. | |
<< scu1100dx.isWorking | |
? "The repaired CT-22 is back in place. " | |
: "There's one empty slot, where the CT-22 module goes. " >> " | |
isPlural = true | |
dobjFor(Take) | |
{ | |
/* this is slightly less likely for 'take' than portable modules */ | |
verify() | |
{ | |
inherited(); | |
logicalRank(80, 'do not disturb'); | |
} | |
} | |
dobjFor(Open) asDobjFor(LookIn) | |
lookInDesc = "The modules are all kind of jury-rigged into place | |
at the moment; you don't want to risk breaking something by | |
taking them apart. " | |
/* | |
* For disambiguation purposes, refer to this object as the | |
* "installed modules" to more clearly differentiate it from the | |
* single uninstalled module (the ct-22). | |
*/ | |
disambigName = 'installed modules' | |
cannotTakeMsg = 'It took a while to get everything plugged in | |
properly, so you\'d rather leave everything where | |
it is. ' | |
cannotMoveMsg = 'The modules are all seated properly, so it\'s best | |
not to jiggle them. ' | |
cannotPutMsg = 'You don\'t want to move the modules anywhere, since | |
it took a while to get them all positioned properly. ' | |
; | |
+ ct22: TestableCircuit, Thing | |
'ct ct-22 ct22 diagnostic module' 'CT-22 diagnostic module' | |
"<< xt772lv.isIn(self) | |
? "It's the diagnostic module that's responsible for many of | |
your woes here. Fortunately, it seems at last to be repaired: an | |
XT772-LV chip is installed in the module's S901 socket. " | |
: "This module is a big part of why this job has taken | |
so long. The CT-22's function is to diagnose errors and defects | |
in the other modules; naturally, it turned out be defective | |
itself. It was working just well enough to send you on weeks | |
of wild goose chases fixing the fake problems it reported in | |
other modules. Once it occurred to you to take it out and check its | |
own operation with your circuit tester, you quickly found the problem: | |
manufacturing installed the wrong chip in one of the key circuits. | |
They installed an XT772-HV, the high-power version, when they should | |
have installed the low-power version, the XT772-LV. The voltage | |
sensitivity differences made for all sorts of false error readings. | |
Unfortunately, the CT-22 is a critical component of the SCU, so | |
you can't just leave it out. ">> | |
<< xt772hv.isIn(self) | |
? "<.p>The errant XT772-HV chip is currently seated in the module's | |
S901 socket. " | |
: xt772lv.isIn(self) | |
? "" : "<.p>The module's S901 socket is current empty. " >> " | |
dobjFor(Examine) | |
{ | |
verify() | |
{ | |
/* | |
* use a slightly elevated rank for disambiguating 'examine | |
* module'; we're the more special of the modules, so it's | |
* more likely we're the one they intend to examine | |
*/ | |
logicalRank(110, 'more special'); | |
} | |
} | |
dobjFor(LookIn) asDobjFor(Examine) | |
/* map 'put x in ct22' to 'put x in socket' */ | |
iobjFor(PutIn) remapTo(PutIn, DirectObject, s901) | |
dobjFor(Repair) | |
{ | |
action() | |
{ | |
if (xt772lv.isIn(self)) | |
"You already have! "; | |
else if (gActor.canSee(xt772lv)) | |
{ | |
"A simple matter of putting the XT772-LV in the socket. "; | |
nestedAction(PutIn, xt772lv, self); | |
if (xt772lv.isIn(self)) | |
"Done. "; | |
} | |
else | |
{ | |
"That would be trivial if you had an XT772-LV chip. | |
Without one, there's no obvious fix. "; | |
/* this counts as a mention of the lv */ | |
xt772lv.isMentioned = true; | |
} | |
} | |
} | |
; | |
++ s901: TestableCircuit, Component, SingleContainer, RestrictedContainer | |
's901 chip 97-pin medium-density hybrid square/square socket' | |
'S901 socket' | |
"It's a 97-pin medium-density hybrid square/square socket. " | |
/* this is a special case for a/an because of the leading initial */ | |
aName = 'an S901 socket' | |
/* don't show my contents in listings of containing objects */ | |
contentsListed = nil | |
/* only allow the XT772 chips */ | |
validContents = [xt772lv, xt772hv] | |
; | |
+++ xt772hv: TestableCircuit, Thing | |
'errant xt772 hv xt772-hv high-power high power chip/version' | |
'XT772-HV chip' | |
"It's a high-power version of the XT772 chip. It's the right kind | |
of chip for most of the other modules, but the wrong kind for the | |
CT-22. <<xt772lv.isIn(nil) | |
? "Unfortunately, this is a spare part you didn't think to | |
bring, and it could take several days to get one shipped here. " | |
: "" >> " | |
/* | |
* override the 'aName', because the standard rules that figure out | |
* whether to use 'a' or 'an' can't handle this kind of weird | |
* special case | |
*/ | |
aName = 'an XT772-HV chip' | |
/* it's small; make it pocketable */ | |
okayForPocket = true | |
; | |
+ TestableCircuit, Decoration | |
'bundle/bundles/wire/wires/cable/cables' 'wires' | |
"The wires and cables connect the SCU-1100DX to the power plant's | |
systems. You've spent the last six weeks figuring out where | |
everything goes with a certain amount of trial and error, so | |
the wiring is a bit of a mess, but you think everything is mostly | |
where it should be at this point. " | |
notImportantMsg = 'It took you six weeks to get the wiring set up just | |
right. You\'re not about to mess with it now. ' | |
isPlural = true | |
; | |
/* | |
* The circuit tester. This is a "complex container" because we want it | |
* to have some components that are always on the outside, plus a secret | |
* interior container that can be opened and closed. The secret | |
* interior container is what makes it "complex." The ComplexContainer | |
* class automatically handles most of the details of the secret | |
* interior container; all we have to do is set a property, | |
* subContainer, to point to the interior container object. | |
*/ | |
+ tester: ComplexContainer 'mitachron dynatest multi-function circuit tester' | |
'circuit tester' | |
"This is your Mitachron DynaTest multi-function circuit tester. | |
You're always a little embarrassed that one of your main | |
day-to-day engineering tools is made by your chief competitor, but | |
Omegatron has never had much luck fielding its own products in this | |
market segment. The tester is about the size of a car battery; its | |
main features are a probe and a small display screen, plus the usual | |
collection of warning stickers on the back cover. It's currently | |
turned off. " | |
/* Use a secret nested container to hold our contents */ | |
subContainer = testerInterior | |
dobjFor(TurnOn) | |
{ | |
verify() { } | |
action() { noNeedToTest(nil); } | |
} | |
cannotTurnOffMsg = 'It\'s already off. ' | |
noNeedToTest(other) | |
{ | |
if (other != nil && !other.ofKind(TestableCircuit)) | |
"The tester isn't designed to test {that iobj/him}. "; | |
else if (scu1100dx.isWorking) | |
"No need; the SCU is already fixed. "; | |
else | |
"You've already narrowed down the problem, or at least | |
the latest problem, to the CT-22. No need to do more | |
testing until you figure out how to fix that. "; | |
} | |
/* "use tester" requires something to use it on */ | |
dobjFor(Use) | |
{ | |
verify() { } | |
action() { askForIobj(UseOn); } | |
} | |
/* "use tester on x" maps to "test x with probe" */ | |
dobjFor(UseOn) remapTo(TestWith, IndirectObject, testerProbe) | |
/* "test x with tester" maps to "test x with probe" */ | |
iobjFor(TestWith) remapTo(TestWith, DirectObject, testerProbe) | |
/* "plug tester into x" equals "test x with probe" */ | |
dobjFor(PlugInto) remapTo(TestWith, IndirectObject, testerProbe) | |
/* "attach tester to x" maps to "test x with probe" */ | |
dobjFor(AttachTo) remapTo(TestWith, IndirectObject, testerProbe) | |
; | |
++ Component '(circuit) (tester) flat panel flat-panel display screen/display' | |
'tester display' | |
"It's a small flat-panel screen, where the tester displays | |
its readings. The screen is currently blank. " | |
; | |
++ testerProbe: ComponentDeferrer, Component | |
'(circuit) (tester) electrical (coax) (coaxial) | |
probe/set/contacts/cable' 'probe' | |
"It's the set of electrical contacts, connected to the tester by | |
a coaxial cable, that you attach to the circuit you want to | |
test. " | |
/* handle testing */ | |
iobjFor(TestWith) | |
{ | |
verify() { } | |
action() { location.noNeedToTest(gDobj); } | |
} | |
/* treat "attach probe to x" as "test x with probe" */ | |
dobjFor(AttachTo) remapTo(TestWith, IndirectObject, self) | |
/* treat "plug probe into x" as "test x with probe" */ | |
dobjFor(PlugInto) remapTo(TestWith, IndirectObject, self) | |
/* treat "use probe on x" as "test x with probe */ | |
dobjFor(UseOn) remapTo(TestWith, IndirectObject, self) | |
; | |
/* mix-in for something we can attach the probe to */ | |
class TestableCircuit: object | |
iobjFor(AttachTo) | |
{ | |
verify() { } | |
check() | |
{ | |
if (gDobj != testerProbe) | |
{ | |
"There's no obvious way to do that. "; | |
exit; | |
} | |
} | |
} | |
/* plug <x> into <self> -> attach <x> to <self> */ | |
iobjFor(PlugInto) remapTo(AttachTo, DirectObject, self) | |
/* "test <self>" requires something to test it with */ | |
dobjFor(Test) | |
{ | |
verify() { } | |
action() { askForIobj(TestWith); } | |
} | |
/* | |
* "test <self> with <x> is allowed; but we'll count on the indirect | |
* object to do the actual action() handling | |
*/ | |
dobjFor(TestWith) { verify() { } } | |
iobjFor(PourOnto) remapTo(PourInto, DirectObject, self) | |
iobjFor(PourInto) | |
{ | |
verify() { } | |
check() | |
{ | |
"Better not; that would probably ruin {the iobj/him}. "; | |
exit; | |
} | |
} | |
; | |
/* | |
* The back cover of the tester. The tricky thing about this object is | |
* that we want to allow the player to open and close the back cover to | |
* open and close the tester; we manage this by remapping these commands | |
* from us to the tester's secret interior container. | |
*/ | |
++ testerBackCover: ComponentDeferrer, ContainerDoor | |
'circuit tester back cover' 'back cover' | |
"It's designed to provide service access to the unit's internal | |
components, and has the usual warning stickers. | |
<<testerInterior.isOpen ? "It's currently open." : "">> " | |
/* treat 'remove back cover' as 'open tester' */ | |
dobjFor(Remove) remapTo(Open, testerInterior) | |
dobjFor(TakeFrom) maybeRemapTo(gIobj == location, Open, testerInterior) | |
/* treat 'take off back cover' as 'open tester' */ | |
dobjFor(Doff) remapTo(Open, testerInterior) | |
; | |
/* | |
* The tester's secret interior container. This isn't really visible to | |
* the player as a game object, so it has no vocabulary of its own. | |
* Instead, we redirect container-oriented commands (open, close, look | |
* in, put in) from the tester to this object, so the player can refer | |
* to us in relevant commands by referring to the tester. | |
*/ | |
++ testerInterior: Component, Openable, RestrictedContainer | |
/* | |
* we don't want this object to appear to the player as a separate | |
* object from the tester, so just refer to us as the tester | |
*/ | |
name = 'tester' | |
/* only allow the xt772's to go in here */ | |
validContents = [xt772lv, xt772hv] | |
/* | |
* Use a custom contents lister. First, don't bother mentioning | |
* anything when the back cover is closed; we want to make its | |
* openability somewhat understated rather than calling it out as | |
* explicit status. Second, describe the contents as being inside | |
* the tester rather than inside the back cover. | |
*/ | |
contentsLister: thingContentsLister { | |
showListEmpty(pov, parent) | |
{ | |
if (parent.isOpen) | |
"The back cover is open, revealing the circuitry inside | |
the tester. "; | |
} | |
showListPrefixWide(itemCount, pov, parent) | |
{ | |
/* | |
* note that we'll only reach this method if we're open, | |
* since otherwise we wouldn't have any visible contents to | |
* list | |
*/ | |
"The back cover of the tester is open, revealing the circuitry | |
inside, including "; | |
} | |
} | |
descContentsLister = (contentsLister) | |
lookInLister = (contentsLister) | |
/* use a custom lister when we're opened */ | |
openingLister: openableOpeningLister { | |
showListPrefixWide(itemCount, pov, parent) | |
{ | |
"Opening the tester reveals the circuitry inside, | |
including "; | |
} | |
} | |
dobjFor(LookIn) | |
{ | |
verify() | |
{ | |
/* | |
* If we're not open, it's illogical to look in the tester. | |
* Getting this message right is a bit tricky; we want to | |
* convey that the only reason we can't look inside the | |
* tester is that it's not open, but we don't want to make | |
* it too obvious that it actually is openable, since in | |
* real life we'd tend to overlook the possibility of taking | |
* apart an everyday object like this. | |
*/ | |
if (!isOpen) | |
illogicalNow('You\'d have to take it apart to do that. '); | |
/* inherit the default handling as well */ | |
inherited(); | |
} | |
} | |
dobjFor(Open) | |
{ | |
verify() | |
{ | |
/* | |
* Mark this as non-obvious, so we don't do it implicitly. | |
* Since you have to remove the back cover to open the | |
* tester, it's not entirely obvious that the tester is | |
* openable in the first place. | |
*/ | |
nonObvious; | |
/* inherit default */ | |
inherited(); | |
} | |
action() | |
{ | |
/* run the normal action first */ | |
inherited(); | |
/* if we haven't scored before, do so now */ | |
if (scoreMarker.awardPointsOnce()) | |
{ | |
/* mention that we've found something special */ | |
reportAfter('<.p>Of course! The DynaTest is just the | |
kind of low-power application where you\'d find an | |
XT772-LV! '); | |
/* note that we've found it */ | |
xt772lv.isFound = true; | |
/* | |
* This is a bit of a "story hack," but we want to | |
* ensure that Guanmgon's phone call finishes before the | |
* PC can finish the repairs to the SCU-1100DX. It | |
* takes three turns for the phone call, and the repair | |
* will take at least three turns after this one (put | |
* xt772lv in ct-22; put ct-22 in slot; turn on | |
* scu1100dx). So, if we simply start the phone call on | |
* this turn (if it hasn't started already), and the | |
* player does the repair in the least possible number | |
* of turns, then the phone call finish on the 'put | |
* ct-22 in slot' turn. So, we'll definitely be past | |
* the phone call on the 'turn on scu1100dx' turn. | |
*/ | |
guanmgon.ensureStartPhoneCall(); | |
} | |
} | |
} | |
/* achievement object to track finding the xt772-lv chip */ | |
scoreMarker: Achievement { +2 "finding the XT772-LV chip" } | |
; | |
+++ Fixture 'tester circuitry/circuits' 'tester circuitry' | |
"The circuitry has the usual Mitachron look of chaotic design, | |
as though the parts wouldn't quite fit and had to be force into | |
place with a hammer. " | |
; | |
+++ xt772lv: TestableCircuit, Thing | |
'xt772 lv xt772-lv low-power low power chip/version' | |
'XT772-LV chip' | |
"It's the low-power version of the XT772 chip. " | |
/* we need to override the a/an name because of the leading initial */ | |
aName = 'an XT772-LV chip' | |
/* mark it as known in advance */ | |
isKnown = true | |
/* have we found it yet? */ | |
isFound = nil | |
/* have we mentioned it to the player yet? (for hint management) */ | |
isMentioned = nil | |
/* it's small; make it pocketable */ | |
okayForPocket = true | |
; | |
++ Component 'warning stickers/warnings' 'warning stickers' | |
"Lightning bolts inside yellow triangles. | |
<font color=red bgcolor=yellow><b>DO NOT OPEN!</b></font> | |
No user-serviceable parts inside! | |
<b>Opening may invalidate warranty!</b> The usual stuff. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Some objects in common to both hallways | |
*/ | |
/* class for power hall rooms */ | |
class PowerPlantHallRoom: PowerPlantRoom | |
north: NoTravelMessage { "You're five stories up; better take the | |
elevator instead. " } | |
south: NoTravelMessage { "The plant floor is five stories below; | |
better take the elevator instead. " } | |
name = 'hallway' | |
vocabWords = 'hall/hallway' | |
/* | |
* these rooms don't have normal north and south walls (we provide | |
* our own custom walls instead, to describe the waist-high nature) | |
*/ | |
roomParts = static (inherited - [defaultNorthWall, defaultSouthWall]) | |
/* share a single set of atmosphere events with both hall locations */ | |
atmosphereList: Script | |
{ | |
doScript() | |
{ | |
switch (curScriptState++) | |
{ | |
case 1: | |
"A distant rumbling sound drifts in from somewhere out in | |
the jungle. At first you think it's thunder, but it grows | |
a bit louder and you realize it's an approaching helicopter. | |
A scan of the horizon shows nothing in the air. Suddenly | |
the noise is deafening, and a black shape swoops into view | |
from overhead, racing away toward the buildings across the | |
river. As the helicopter flies past, you spot the yellow | |
Mitachron logo on the tail. "; | |
/* add the first helicopter */ | |
helicopter1.moveIntoAdd(powerHallEast); | |
helicopter1.moveIntoAdd(powerHallWest); | |
/* mark it as having been seen */ | |
me.setHasSeen(helicopter1); | |
break; | |
case 2: | |
"The sound of another approaching helicopter drifts in | |
from the jungle, and within a few moments it | |
appears—along with three others, flying in | |
formation, heading the same way as the previous one. | |
The four fly across the river and land next to the | |
first. "; | |
/* replace the single helicopter with the five */ | |
helicopter1.moveInto(nil); | |
helicopter5.moveIntoAdd(powerHallEast); | |
helicopter5.moveIntoAdd(powerHallWest); | |
/* mark it as having been seen */ | |
me.setHasSeen(helicopter5); | |
break; | |
case 3: | |
"The arrival of the Mitachron people is awfully | |
worrying; they must really want this deal to have | |
sent such a large group. At least they'll have to do | |
their own demo, so you're ahead of them on that count. | |
Even so, direct competitive situations with Mitachron | |
never seem to go well for Omegatron. Maybe if you can | |
get to the Colonel quickly enough, you'll be able to get | |
your contract signed before the Mitachron reps even | |
get a meeting with her. "; | |
break; | |
} | |
} | |
} | |
; | |
/* a dummy container for objects within the power plant */ | |
MultiInstance | |
initialLocationClass = PowerPlantHallRoom | |
instanceObject: SecretFixture { } | |
; | |
+ Fixture | |
'north n waist high waist-high wall' 'north wall' | |
"It's only waist-high, leaving the hallway open to the jungle. " | |
; | |
+ Fixture | |
'south s waist high waist-high wall' 'south wall' | |
"It's only waist-high, leaving the hallway open to the plant | |
interior. " | |
; | |
+ Fixture 'concrete widely-spaced space columns' 'columns' | |
"They're just concrete columns holding up the roof. " | |
isPlural = true | |
; | |
+ Distant 'cavernous plant interior/plant/(floor)' | |
'plant interior' | |
"The plant interior is a cavernous space to the south. This | |
hallway is essentially a balcony, five stores up, along the | |
north wall. From here, you have a good view of the giant | |
industrial equipment that runs the plant. " | |
; | |
+ Distant 'giant industrial steam age steam-age | |
equipment/turbines/transformers/boilers/pipe/pipes/cable/cables' | |
'equipment' | |
"Turbines, transformers, boilers, all connected with a vast | |
web of pipes and cables. All very steam-age looking. " | |
/* | |
* 'equipment' is used as a 'mass noun' - it refers to mutiple | |
* objects even though the word itself is singular | |
*/ | |
isMassNoun = true | |
; | |
+ Distant 'jungle' 'jungle' | |
"The scenery is great from this elevated vantage. Directly to | |
the north is the deep gorge of the Xtuyong River Canyon; the plant | |
is built right on the edge of the cliffs. On the opposite | |
rim of the canyon is the complex of administrative buildings, | |
where the Colonel's office is. Beyond that is untamed jungle | |
to the horizon. A bridge spans the canyon. " | |
; | |
+ Distant 'vegetation/plants' 'vegetation' | |
"There's quite a lot of it, this being a jungle and all. " | |
; | |
+ Distant 'xtuyong river deep canyon/gorge' 'canyon' | |
"Two hundred meters deep and a hundred across, carved out | |
over the eons by the steady flow of water from the rain forest | |
highlands to the west. A bridge spans the canyon. " | |
; | |
+ Distant 'concrete steel structure/bridge' 'bridge' | |
"You've been across it many times now; it's a broad, modern structure | |
built of steel and concrete. The bridge connects the power plant on | |
this side of the canyon with the administrative buildings on the | |
other side. " | |
; | |
+ Distant 'office administrative complex/buildings' | |
'administrative complex' | |
"It's a sprawling complex of office buildings housing the | |
power plant's extensive bureaucracy. " | |
; | |
+ Distant 'sky' 'sky' | |
"It's a deep, bright blue. " | |
; | |
class HeliTail: Distant 'helicopter tail' 'helicopter tail' | |
"It appears to be marked with the Mitachron logo. " | |
; | |
class HeliTailLogo: Distant 'yellow mitachron logo' 'Mitachron logo' | |
"You can just barely make it out from here, but you think | |
you recognize the tail markings as the Mitachron logo: a big yellow | |
<q>M</q> in a heavy sans-serif action-slant font, superimposed over | |
a light yellow outline of a globe. " | |
; | |
/* the first helicopter */ | |
helicopter1: MultiLoc, Distant 'black mitachron helicopter/chopper/copter' | |
'black helicopter' | |
"It's just setting down near the administrative complex. " | |
; | |
+ HeliTail; | |
++ HeliTailLogo; | |
/* the full group of helicopters */ | |
helicopter5: MultiLoc, Distant | |
'five black mitachron helicopters/choppers/copters/group' | |
'five black helicopters' | |
"All five helicopters are parked together near the administration | |
complex. They're too far away, and there's too much vegetation | |
in the way, to see if there's any activity near them or if anyone | |
has disembarked. " | |
isPlural = true | |
aName = (name) | |
; | |
+ HeliTail; | |
++ HeliTailLogo; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The hallway outside the control room | |
*/ | |
powerHallEast: PowerPlantHallRoom | |
'East End of Hallway' 'the east end of the hallway' | |
"Like every other part of the plant structure, this wide | |
fifth-floor hallway is built entirely of concrete. The north | |
and south walls are only waist-high, apart from a few widely-spaced | |
columns supporting the roof, leaving the hallway open | |
to the jungle to the north and to the cavernous interior of | |
the plant to the south. The hall ends in a doorway to the | |
east, and continues to the west. " | |
east = powerHallDoorway | |
in asExit(east) | |
west = powerHallWest | |
/* | |
* remove the west wall, since there isn't one, and the default | |
* north and south walls, since we want custom descriptions for | |
* these instead | |
*/ | |
roomParts = static (inherited() - defaultWestWall) | |
; | |
+ powerHallDoorway: Fixture, ThroughPassage -> powerControlDoorway | |
'east e door/doorway/sign' 'doorway' | |
"You assume that the sign says <q>Control Room,</q> although | |
it's not in an alphabet you can read. The doorway leads to the east. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The west end of the hall, at the elevator | |
*/ | |
powerHallWest: PowerPlantHallRoom | |
'West End of Hallway' 'the west end of the hallway' | |
"This wide hallway has waist-high walls open to the jungle | |
to the north, and to the enormous plant interior to the south; a | |
few widely-spaced columns support the roof. The hall ends at an | |
elevator door to the west, and continues to the east. " | |
east = powerHallEast | |
west = plantHallElevatorDoor | |
in asExit(west) | |
/* we have no east well, and we have custom north and south walls */ | |
roomParts = static (inherited() | |
- [defaultNorthWall, defaultSouthWall, defaultEastWall]) | |
; | |
+ plantHallElevatorDoor: Door, BasicContainer ->plantElevatorGate | |
'elevator lift door/elevator/lift' 'elevator door' | |
"It's one of those old-style elevators with an ordinary swinging | |
door, painted a pale blue-green. A round, black call button is | |
next to the door, and above the button is a small neon lamp (currently | |
<<plantElevator.isOnCall ? 'lit' : 'unlit'>>). " | |
/* it's initially closed - we can't open it until the elevator arrives */ | |
initiallyOpen = nil | |
dobjFor(Open) | |
{ | |
check() | |
{ | |
/* if the elevator isn't here, we can't open it */ | |
if (!plantElevator.isAtTop) | |
{ | |
"The door can't be opened until the elevator arrives. "; | |
exit; | |
} | |
/* run the default checks */ | |
inherited(); | |
} | |
} | |
dobjFor(Close) | |
{ | |
check() | |
{ | |
/* if xojo is here, don't allow the player to close it */ | |
if (xojo.isIn(location)) | |
{ | |
"Xojo is holding it open for you; no need to be rude. "; | |
exit; | |
} | |
} | |
} | |
dobjFor(Board) remapTo(TravelVia, self) | |
; | |
++ Fixture 'folding metal elevator lift gate' 'elevator gate' | |
"It's a folding metal gate that serves as the elevator's | |
inner door. " | |
; | |
+ Button, Fixture 'round black elevator lift call button' 'call button' | |
"It's a big black button sticking out from the wall about | |
a centimeter. " | |
; | |
+ Fixture 'small neon lamp' 'neon lamp' | |
"It's currently <<plantElevator.isOnCall ? 'lit' : 'unlit'>>. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The elevator interior. Note that we don't use our general Elevator | |
* class, or its various related components, to implement this elevator; | |
* this elevator's behavior is quirky, so our general elevator classes | |
* don't apply well here. | |
*/ | |
plantElevator: PowerPlantRoom 'Elevator' 'the elevator' | |
"This is a big, sturdily-built elevator, like something you'd | |
find in an old warehouse. There's been no attempt at decoration; | |
just dull metal walls, a folding metal gate to the east, | |
a handrail on the back wall, <<powerElevPanel.isOpen | |
? 'a two-by-three-foot opening' : 'a recessed service panel' | |
>> in the ceiling, a bare bulb dimly burning at the top corner of | |
the back wall. Protruding black buttons are labeled, from the | |
bottom, S2, S1, G, and 2 through 5. | |
<<isDescending ? "The bare concrete of the shaft wall slides by | |
beyond the gate." : "Beyond the gate is the bare concrete of | |
the shaft wall." >> " | |
vocabWords = 'elevator/lift' | |
east = plantElevatorGate | |
out asExit(east) | |
dobjFor(GetOutOf) | |
{ | |
verify() { } | |
remap = nil | |
action() | |
{ | |
/* | |
* if we're on xojo's shoulders, and the panel is open, | |
* climb out; otherwise, there's no obvious way out | |
*/ | |
if (gActor.isIn(xojoBoost) && powerElevPanel.isOpen) | |
replaceAction(Up); | |
else | |
replaceAction(East); | |
} | |
} | |
up = (powerElevPanel.isOpen ? powerElevPanel : inherited()) | |
/* dispense with the usual room parts, keeping only the floor */ | |
roomParts = [defaultFloor] | |
/* flag: the elevator is on call to the top floor */ | |
isOnCall = nil | |
/* flag: we're at the top floor */ | |
isAtTop = nil | |
/* flag: we're at the bottom of the shaft */ | |
isAtBottom = nil | |
/* flag: we're currently descending */ | |
isDescending = nil | |
/* current floor number */ | |
curFloor = 10 | |
/* call the elevator to the top floor */ | |
callToTop() | |
{ | |
/* note that I'm on call */ | |
isOnCall = true; | |
/* | |
* Set a fuse for arrival in a few turns. We want to make the | |
* wait long enough that we see the helicopters arrive, but not | |
* much longer. | |
*/ | |
new SenseFuse(self, &arriveAtTop, 3, plantHallElevatorDoor, sight); | |
} | |
/* start the descent */ | |
startDescent() | |
{ | |
/* set us in motion */ | |
isDescending = true; | |
isAtTop = nil; | |
/* start a daemon to run the descent */ | |
new Daemon(self, &descentDaemon, 1); | |
} | |
/* fuse - arrive at the top floor when called */ | |
arriveAtTop() | |
{ | |
"A loud buzzer sounds from the elevator, and the neon | |
lamp above the call button goes out. "; | |
/* if xojo is here, have him open the door */ | |
if (xojo.isIn(powerHallWest)) | |
{ | |
"<.p>Xojo pulls opens the door and pushes aside the elevator's | |
folding metal gate for you, and waits for you to get in. "; | |
plantElevatorGate.makeOpen(true); | |
} | |
/* we're no longer on call, and we are at the top */ | |
isOnCall = nil; | |
isAtTop = true; | |
} | |
/* descent daemon - called each turn while we're descending */ | |
descentDaemon() | |
{ | |
local btn; | |
/* descend one level */ | |
--curFloor; | |
/* find the button for our current floor, if any */ | |
btn = contents.valWhich( | |
{x: x.ofKind(PlantElevButton) && x.internalFloor == curFloor}); | |
/* mention what's going on */ | |
if (btn != nil) | |
{ | |
/* we have a button, so we just passed a door */ | |
"<.p>The elevator descends past a door marked | |
<q><<btn.nominalFloor>>.</q> "; | |
/* | |
* If we just passed floor 1, xojo notices it. If we just | |
* passed S2, we're about to crash. Otherwise, if the button | |
* is pushed, mention that we didn't stop. | |
*/ | |
if (btn.internalFloor == 2) | |
{ | |
"Strange; you thought that was your stop. | |
<.p>Xojo, looking a little alarmed, starts punching | |
buttons. <q>No reason for panic,</q> he says, not very | |
convincingly, but then he seems to calm down and stop | |
fooling with the buttons. <q>The elevator programming | |
is sometimes faulty. We should be halted soon, | |
since the shaft is nearly at bottom.</q> "; | |
/* cancel any conversation */ | |
xojo.setConvNode(nil); | |
} | |
else if (btn.internalFloor == -2) | |
{ | |
/* we've reached the bottom of the shaft */ | |
"<.p>Xojo grips the handrail tightly. <q>Be firm, the | |
stopping is ready to be abrupt.</q> You grab the handrail | |
and brace yourself. | |
<.p> | |
The shaft wall keeps going by for a few more moments, showing | |
no sign of slowing, then: thud! Not a jerk from the cable | |
above, but the sharp shock of hitting something below. | |
The whole elevator shudders. Outside the gate, the shaft | |
wall is suddenly motionless. | |
<.p> | |
<q>All is safe now,</q> Xojo says, relaxing. <q>Now we | |
need do no more than wait patiently for rescue.</q> "; | |
/* no longer descending, since we're at the bottom */ | |
isDescending = nil; | |
isAtBottom = true; | |
/* we don't need the descent script any longer */ | |
eventManager.removeCurrentEvent(); | |
/* cancel any conversation */ | |
xojo.setConvNode(nil); | |
} | |
else if (btn.isPushed) | |
"Strange that the elevator didn't stop here, given that | |
you pushed the button. "; | |
} | |
else if (curFloor == 9) | |
{ | |
/* | |
* say nothing on the first turn we're descending, as we will | |
* just have said we started descending | |
*/ | |
} | |
else if (curFloor == 5) | |
{ | |
"<.p>The elevator continues its lethargic descent. | |
Rationally, you know that the few minutes of this elevator | |
ride aren't going to cost you the deal, but that doesn't | |
stop the irrational part of your brain from pumping out | |
anxiety hormones. "; | |
} | |
else if (curFloor == 3) | |
{ | |
"<.p>The elevator plods on. Come on, come on, come on... "; | |
} | |
else | |
{ | |
/* there's no button, so we're just going by shaft wall */ | |
"<.p>The elevator continues its slow descent. "; | |
} | |
} | |
roomBeforeAction() | |
{ | |
/* | |
* if we try going UP from directly in the elevator (not sitting | |
* on xojo's shoulders), and we've climbed up before, allow it | |
*/ | |
if (gActionIs(Up) | |
&& xojo.boostCount != 0 | |
&& gActor.isDirectlyIn(self)) | |
replaceAction(Climb, xojo); | |
} | |
; | |
+ Fixture 'shaft wall' 'shaft wall' | |
"The shaft wall is plain concrete. | |
<<location.isDescending ? 'The elevator\'s motion creates the | |
illusion that the shaft wall is sliding slowly upwards.' : ''>> " | |
/* this wall is slightly more interesting than the elevator walls */ | |
dobjFor(Examine) { verify() { logicalRank(110, 'shaft'); } } | |
; | |
+ Fixture | |
'dull metal elevator lift north south west n s w back top | |
wall/corner*walls' | |
'elevator walls' | |
"The elevator walls are a bare, dull metal. A handrail is attached | |
to the back wall. " | |
isPlural = true | |
; | |
+ Fixture 'metal hand rail/handrail' 'handrail' | |
"It's a simple metal handrail. " | |
dobjFor(Hold) | |
{ | |
verify() { } | |
action() { "You hold the handrail for a moment. "; } | |
} | |
dobjFor(StandOn) | |
{ | |
verify() { } | |
action() | |
{ | |
"You try to climb onto the handrail, but it's too | |
narrow to get a good footing, and there's nothing else to | |
hold onto. You try attacking it from a few different angles, | |
but you can't seem to make it work. "; | |
xojo.observeClimb(self); | |
} | |
} | |
dobjFor(Climb) asDobjFor(StandOn) | |
dobjFor(ClimbUp) asDobjFor(StandOn) | |
dobjFor(Board) asDobjFor(StandOn) | |
; | |
+ plantElevatorGate: Door 'folding metal elevator lift door/gate' 'gate' | |
"Instead of a door, there's only this folding metal gate to | |
separate passengers from the shaft wall while the elevator is | |
in motion. " | |
dobjFor(Open) | |
{ | |
action() | |
{ | |
"You try, but it won't budge. Presumably, the gate | |
automatically locks while the elevator is in | |
motion<< location.isDescending ? "" : " (or stuck | |
between floors)" >>. "; | |
} | |
} | |
dobjFor(LookThrough) | |
{ | |
action() { "You see the shaft wall. "; } | |
} | |
/* once the elevator is moving, the apparent destination is nowhere */ | |
getApparentDestination(origin, actor) | |
{ | |
if (plantElevator.isAtTop) | |
return inherited(origin, actor); | |
else | |
return nil; | |
} | |
dobjFor(Climb) | |
{ | |
verify() { } | |
action() | |
{ | |
"You tentatively try climbing the gate the way you would | |
a chain-link fence, but it's too wobbly; you'd be sure | |
to get your fingers badly pinched, so you don't risk it. "; | |
xojo.observeClimb(self); | |
} | |
} | |
dobjFor(ClimbUp) asDobjFor(Climb) | |
; | |
/* | |
* the ceiling and its contents are out of reach (until Xojo gives us a | |
* boost) | |
*/ | |
+ OutOfReach, NestedRoom, Fixture 'ceiling' 'ceiling' | |
"Dull, bare metal like the walls; the only feature is a recessed | |
service panel. " | |
/* we can reach Xojo when he's giving us a boost */ | |
canReachFromInside(obj, dest) | |
{ return inherited(obj, dest) || dest == xojo; } | |
cannotReachFromOutsideMsg(dest) | |
{ | |
gMessageParams(dest); | |
return '{The dest/he} {is} too high up to reach from here. '; | |
} | |
cannotReachFromInsideMsg(dest) | |
{ | |
gMessageParams(dest); | |
return 'You can\'t reach {that dest/him} from up here. '; | |
} | |
/* the only way we can be inside here is to be in xojoBoost */ | |
tryRemovingFromNested() { return tryImplicitAction(GetOffOf, xojo); } | |
/* | |
* anything thrown at an object inside the out-of-reach area near the | |
* ceiling lands back on the floor | |
*/ | |
getDropDestination(objToDrop, path) { return location; } | |
; | |
++ Fixture 'bare light bulb' 'light bulb' | |
"It's providing the dull illumination in here. " | |
cannotTakeMsg = 'You have no desire to eliminate the only | |
source of light here. ' | |
; | |
/* | |
* A secret internal platform for we're on Xojo's shoulders. We can't | |
* refer to this object directly; we get here through other actions that | |
* cause Xojo to offer us a boost. | |
* | |
* Putting this platform "inside" the ceiling means we don't have to | |
* traverse the ceiling's OutOfReach containment boundary, which puts the | |
* ceiling and everything inside within reach while we're here, and puts | |
* everything outside the ceiling out of reach while we're here. | |
*/ | |
++ xojoBoost: SecretFixture, Platform | |
name = 'Xojo\'s shoulders' | |
isQualifiedName = true | |
isPlural = true | |
/* show special descriptions while here */ | |
roomActorStatus(actor) { " (on Xojo's shoulders)"; } | |
roomActorPostureDesc(actor) { "You're on Xojo's shoulders. "; } | |
/* we can't stand here */ | |
makeStandingUp() { reportFailure('You and Xojo are having a difficult | |
enough time keeping balanced as it is. '); } | |
/* ...but we can implicitly dismount if needed */ | |
tryMakingTravelReady(conn) { return tryImplicitAction(GetOffOf, self); } | |
notTravelReadyMsg = 'You\'ll have to get off Xojo\'s shoulders first. ' | |
/* | |
* obviously, we don't want xojo following us here; we can prevent | |
* this from happening by making our 'effective' follow location the | |
* same as the elevator, which will mean that being in the elevator | |
* is as good as being here for following purposes | |
*/ | |
effectiveFollowLocation = plantElevator | |
/* when we leave via "get off of", we return to the elevator */ | |
exitDestination = plantElevator | |
/* stuff dropped here lands in the elevator */ | |
getDropDestination(obj, path) { return plantElevator; } | |
roomBeforeAction() | |
{ | |
/* | |
* IF the command is an explicit OUT or GET OUT, treat the | |
* command as simply UP if the power panel is open, or GET OFF | |
* XOJO is it's closed. | |
* | |
* Note that we check to make sure the command doesn't include | |
* the word 'down', because there's at least one GET OUT | |
* phrasing that does; if DOWN is involved, it pretty clearly | |
* means GET OFF XOJO'S SHOULDERS instead of GET OUT OF | |
* ELEVATOR. We also ignore nested commands, as we have to do a | |
* GET OUT OF to get down, so we don't want to get stuck in a | |
* loop. | |
*/ | |
if (!gAction.isImplicit | |
&& gAction.getOrigText.find('down') == nil | |
&& gActionIn(GetOut, Out)) | |
{ | |
if (powerElevPanel.isOpen) | |
replaceAction(Up); | |
else | |
replaceAction(GetOffOf, xojo); | |
} | |
/* if the command is DOWN, treat it as GET OFF XOJO */ | |
if (!gAction.isImplicit && gActionIs(Down)) | |
replaceAction(GetOffOf, xojo); | |
} | |
/* | |
* we handle OUT specially, and we don't want it to show in the exits | |
* list, so we can just make this a noTravel | |
*/ | |
out = noTravel | |
; | |
++ powerElevPanel: Door | |
'recessed metal ceiling service panel/plate/hatch' 'service panel' | |
desc() | |
{ | |
if (isOpen) | |
"It's an opening in the ceiling, about two feet by | |
three feet. "; | |
else | |
"It's a recessed metal plate set into the ceiling, | |
about two feet by three feet. You'd guess it's a | |
service hatch, providing access to the roof of the | |
elevator car. "; | |
} | |
initiallyOpen = nil | |
descContentsLister = thingContentsLister | |
dobjFor(TravelVia) | |
{ | |
action() | |
{ | |
/* add a description of the traversal */ | |
"You lift yourself through the opening, first propping yourself | |
on your elbows, then using the leverage to lift the rest of | |
your body. "; | |
/* an unfortunate side effect the first time through... */ | |
if (scoreMarker.scoreCount == 0) | |
{ | |
"Just as you're almost through, you hear the sound of | |
tearing cloth---you realize you managed to snag the leg | |
of your pants on the edge of the opening, making a huge | |
rip down half the leg.<.p>"; | |
khakis.makeTorn(true); | |
} | |
/* achieve the score */ | |
scoreMarker.awardPointsOnce(); | |
/* use the normal handling */ | |
inherited(); | |
/* | |
* bring Xojo here directly; he won't be able to follow using | |
* the standard follow handling, because he can't reach the | |
* opening from the floor any more than we could | |
*/ | |
xojo.moveIntoForTravel(destination); | |
"<.p>You extend your hand to Xojo and help him climb up | |
from the elevator. "; | |
} | |
} | |
dobjFor(Board) asDobjFor(TravelVia) | |
dobjFor(ClimbUp) asDobjFor(TravelVia) | |
scoreMarker: Achievement { +5 "escaping the stalled elevator" } | |
dobjFor(Open) | |
{ | |
action() | |
{ | |
"You give the plate a push, and it lifts away easily. | |
You slide it aside, leaving a two-by-three foot opening | |
in the ceiling. "; | |
makeOpen(true); | |
/* it's now an opening */ | |
initializeVocabWith('two-by-three-foot opening'); | |
} | |
} | |
dobjFor(Push) remapTo(Open, self) | |
dobjFor(Pull) remapTo(Open, self) | |
dobjFor(Remove) remapTo(Open, self) | |
dobjFor(Move) remapTo(Open, self) | |
dobjFor(PushTravel) remapTo(Open, self) | |
dobjFor(Close) { action() { "There's no reason to do that. "; }} | |
/* | |
* to traverse this connector, we don't need a staging location, | |
* since we'll be in the right location if we can reach the panel; | |
* and we don't need to be standing, since sitting on xojo's | |
* shoulders is good enough | |
*/ | |
connectorStagingLocation = nil | |
actorTravelPreCond(actor) { return []; } | |
; | |
class PlantElevButton: Button, Fixture | |
'black protruding button*buttons' 'button' | |
"It's a round, black button protruding about a centimeter. " | |
collectiveGroups = [plantElevButtonGroup] | |
/* flag: I've been pushed */ | |
isPushed = nil | |
/* | |
* our "nominal floor" - we use two internal floors for each nominal | |
* floor, so our nominal floor is our internal floor number divided | |
* by 2 | |
*/ | |
nominalFloor = (toString(internalFloor/2)) | |
dobjFor(Push) | |
{ | |
action() | |
{ | |
if (plantElevator.isAtBottom) | |
"You push the button, hoping it'll unfreeze the elevator, | |
but nothing seems to happen. "; | |
else if (internalFloor >= plantElevator.curFloor) | |
"You push the button, but you doubt it'll have | |
any effect, since the elevator has already passed | |
that floor. Xojo gives you a quizzical look. "; | |
else if (internalFloor == 2) | |
"You push the button, which shouldn't be necessary | |
given that you already saw Xojo push it. "; | |
else if (isPushed) | |
"You push the button. You doubt it'll have any | |
effect, since you've already pushed it. "; | |
else | |
"You push the button. Xojo looks like he wants | |
to object, but it's too late. "; | |
/* note that this button has been pushed */ | |
isPushed = true; | |
} | |
} | |
; | |
+ PlantElevButton '"s2" button' '<q>S2</q> button' | |
internalFloor = -2 | |
nominalFloor = 'S2' | |
; | |
+ PlantElevButton '"s1" button' '<q>S1</q> button' | |
internalFloor = 0 | |
nominalFloor = 'S1' | |
; | |
+ PlantElevButton '"g" button' '<q>G</q> button' | |
internalFloor = 2 | |
nominalFloor = 'G' | |
; | |
+ PlantElevButton '2 -' '<q>2</q> button' internalFloor = 4; | |
+ PlantElevButton '3 -' '<q>3</q> button' internalFloor = 6; | |
+ PlantElevButton '4 -' '<q>4</q> button' internalFloor = 8; | |
+ PlantElevButton '5 -' '<q>5</q> button' internalFloor = 10; | |
/* | |
* A "collective group" object for the buttons. For most actions, when | |
* we try to operate on a 'button' without saying which one, we don't | |
* want to ask "which button do you mean...", since they're all basically | |
* the same. Instead, we just want to say why you can't do whatever it | |
* is you're trying to do. | |
*/ | |
plantElevButtonGroup: ElevatorButtonGroup | |
'black protruding elevator lift -' 'elevator buttons' | |
"The buttons are arranged in a column. From the bottom, they're | |
labeled S2, S1, G, 2, 3, 4, 5. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Atop the elevator. We get here after making it through the escape | |
* hatch. | |
*/ | |
atopPlantElevator: Room 'Elevator Shaft' 'the elevator shaft' | |
"The roof of the elevator car doesn't make a very good place to stand, | |
crowded as it is with mechanical protrusions and cable attachments. | |
A service hatch provides access to the elevator's interior. | |
The main cable hangs limply from the top of the shaft far above. | |
<.p>To the east is a door marked <q>S2</q>, just a little above | |
the top of the elevator. " | |
down = elevRoofHatch | |
east = doorS2inner | |
out asExit(east) | |
up: NoTravelMessage { "The only way of going up would be to climb | |
the cable, but it's too greasy to get a good grip. "; } | |
roomParts = [] | |
roomFloor = apeFloor | |
; | |
+ Fixture 'shaft concrete wall/walls' 'shaft walls' | |
"The shaft walls are bare concrete. Rails " | |
isPlural = true | |
; | |
+ Fixture 'elevator lift rail/rails' 'rails' | |
"The rails presumably guide the elevator car as it travels up | |
and down the shaft. " | |
isPlural = true | |
dobjFor(Climb) | |
{ | |
verify() { } | |
action() { reportFailure('They\'re too narrow; there\'s no way | |
to get a good enough grip. '); } | |
} | |
dobjFor(ClimbUp) asDobjFor(Climb) | |
; | |
+ apeFloor: Floor 'elevator lift elevator/lift/roof/car/floor' 'elevator roof' | |
"It's not a very easy place to stand because of the many | |
mechanical protrusions. " | |
; | |
+ Fixture 'mechanical cable bolts/attachments/protrusions' 'protrusions' | |
"They're just a bunch of bolts and cable attachments and the like. " | |
isPlural = true | |
; | |
+ Fixture 'elevator lift shaft/top' 'shaft' | |
"The shaft must be about seven or eight stories, but there's not | |
enough light that you can really tell by looking at it. " | |
; | |
+ elevatorCable: Fixture 'main elevator lift cable/cables' 'cable' | |
"It's just hanging limply, which is consistent with the way the | |
elevator crashed into the bottom of the shaft. " | |
dobjFor(Climb) | |
{ | |
verify() { } | |
action() { reportFailure('The cable is well oiled; there\'s | |
no way to get a good enough grip. '); } | |
} | |
dobjFor(ClimbUp) asDobjFor(Climb) | |
dobjFor(Pull) | |
{ | |
verify() { } | |
action() { "You give the cable a tug. This sends a lovely | |
sine wave propagating up the cable. You're sure you could | |
calculate several properties of the cable by observing | |
the propagation velocity of the wave, but you're in too | |
much of a hurry for such diversions right now. "; } | |
} | |
; | |
+ elevPanelCover: Thing 'service hatch opening metal plate/cover/panel' | |
'metal plate' | |
"It's a rectangular metal plate, about two feet by three feet, | |
a few millimeters thick. It serves as the cover for the elevator | |
service hatch. " | |
initSpecialDesc = "The service hatch cover is lying next to the opening. " | |
cannotUnlockWithMsg = 'You\'ll have to be more specific about | |
how you intend to do that. ' | |
; | |
/* the escape hatch, as seen from above */ | |
+ elevRoofHatch: TravelWithMessage, ThroughPassage -> powerElevPanel | |
'service access opening/hatch' 'service hatch' | |
"It's a two-by-three-foot opening in the roof of the elevator. " | |
/* | |
* our destination is the elevator itself, not the ceiling (where the | |
* other side of our passage is actually contained) | |
*/ | |
destination = plantElevator | |
/* this travel merits some extra description */ | |
travelDesc = "You carefully lower yourself through the service access | |
hatch. Once you're through, you let go, and drop | |
the several feet to the elevator floor. " | |
dobjFor(Close) | |
{ | |
verify() { } | |
action() { reportFailure('There\'s no need to do that, and it | |
would take away the main source of light here. '); } | |
} | |
iobjFor(PutOn) | |
{ | |
verify() { } | |
action() | |
{ | |
if (gDobj == elevPanelCover) | |
replaceAction(Close, self); | |
else | |
reportFailure('That\'s not a good place to put | |
{that/him dobj}. '); | |
} | |
} | |
iobjFor(PutIn) | |
{ | |
verify() { } | |
action() { "Better not; it's a long drop. "; } | |
} | |
; | |
+ doorS2inner: TravelWithMessage, Door '"s2" door' 'door' | |
"Like the other doors in this shaft, it's the kind that | |
swings outward rather than the sliding type more typical of modern | |
elevators. The bottom of the door is a couple of feet above the | |
top of the elevator. The marking <q>S2</q> has been painted on with | |
a stencil, and at the edge of the door is a locking mechanism. | |
<<isOpen ? "You're holding it open. " | |
: isLocked ? '' : "It's just slightly ajar. " >> " | |
descContentsLister = thingContentsLister | |
isLocked = true | |
dobjFor(Open) | |
{ | |
check() | |
{ | |
if (isLocked) | |
{ | |
"The door won\'t budge. You notice a locking | |
mechanism at the edge of the door: probably a | |
safety feature that keeps the door from being | |
opened when the elevator isn\'t stopped at this | |
floor. "; | |
gActor.setIt(self); | |
exit; | |
} | |
} | |
action() | |
{ | |
"You give the door a push and it swings open. It's | |
spring-loaded, so you have to hold it open. "; | |
makeOpen(true); | |
} | |
} | |
dobjFor(Push) remapTo(Open, self) | |
dobjFor(Pull) remapTo(Open, self) | |
dobjFor(Move) remapTo(Open, self) | |
dobjFor(Unlock) remapTo(Unlock, elevLockSlot) | |
cannotUnlockMsg = 'You\'ll have to be more specific about how | |
you intend to do that. ' | |
beforeTravel(traveler, connector) | |
{ | |
if (connector != self && isOpen) | |
{ | |
"The elevator door slams shut as soon as you let go.<.p> "; | |
makeOpen(nil); | |
isLocked = true; | |
} | |
} | |
travelDesc = "You climb the couple of feet over the raised threshold. " | |
; | |
++ Component 'stenciled s2 marking s2' '<q>S2</q> marking' | |
"It's just a stenciled marking reading <q>S2</q>; it's presumably | |
the floor number. " | |
; | |
++ elevLockSlot: Fixture 'narrow vertical door locking | |
safety mechanism/lock/interlock' | |
'locking mechanism' | |
"It looks like a safety interlock, to prevent anyone from opening the | |
door into the empty shaft when the elevator car isn't stopped at | |
this floor. The only exposed parts are a locking bolt and a narrow | |
vertical slot about four inches deep and a foot long; presumably | |
the elevator car has a corresponding part that slides into the slot | |
when the car is stopped at this door. " | |
dobjFor(Open) | |
{ | |
verify() | |
{ | |
if (location.isOpen) | |
illogicalNow('The bolt is already unlocked. '); | |
} | |
action() { reportFailure('You fiddle with the bolt a bit, but | |
you can\'t get it free, and your fingers don\'t fit into | |
the slot. '); } | |
} | |
dobjFor(Unlock) asDobjFor(Open) | |
lookInDesc = "The slot is too narrow to make out any detail of | |
its inner workings. " | |
iobjFor(PutIn) | |
{ | |
verify() { } | |
check() | |
{ | |
switch (gDobj) | |
{ | |
case elevPanelCover: | |
/* this one works - keep going... */ | |
break; | |
case contract: | |
case xojoResume: | |
"You try sliding the paper into the slot. It easily | |
fits, but it's too flimsy to trip the locking mechanism. "; | |
exit; | |
default: | |
"{The dobj/he} do{es}n't fit in the slot. "; | |
exit; | |
} | |
} | |
action() | |
{ | |
/* | |
* check() will ensure we only make it here if we chose the | |
* right object | |
*/ | |
"You slide the plate into the slot. "; | |
if (location.isLocked) | |
{ | |
"You move it up and down a bit, and you feel it catch | |
on something. You give it a little tug; with a | |
click, the door unlatches and pops just a little | |
ajar. You remove the plate from the slot. "; | |
location.isLocked = nil; | |
} | |
else | |
"This doesn't have any obvious effect, so you remove | |
the plate. "; | |
} | |
} | |
/* | |
* receive notification that we're the indirect object of a PUT IN | |
* involving multiple direct objects | |
*/ | |
noteMultiPutIn(dobjs) | |
{ | |
/* don't allow it */ | |
"You can only put one thing at a time in the slot. "; | |
exit; | |
} | |
/* MOVE THROUGH SLOT, MOVE INTO SLOT -> PUT IN SLOT */ | |
iobjFor(PushTravelThrough) remapTo(PutIn, DirectObject, self) | |
iobjFor(PushTravelEnter) remapTo(PutIn, DirectObject, self) | |
; | |
+++ Component 'locking mechanism vertical slot' 'slot' | |
"It's about four inches deep and a foot long, and only a few | |
millimeters wide. " | |
/* map PUT IN, LOOK IN, etc to our location */ | |
iobjFor(PutIn) remapTo(PutIn, DirectObject, location) | |
dobjFor(LookIn) remapTo(LookIn, location) | |
iobjFor(PushTravelThrough) remapTo(PutIn, DirectObject, location) | |
iobjFor(PushTravelEnter) remapTo(PutIn, DirectObject, location) | |
; | |
+++ Component 'locking mechanism bolt' 'locking bolt' | |
"It's recessed into the mechanism enough that you can't really | |
see how it works. " | |
dobjFor(Open) remapTo(Open, location) | |
dobjFor(Unlock) asDobjFor(Open) | |
dobjFor(Pull) asDobjFor(Open) | |
dobjFor(Move) asDobjFor(Open) | |
dobjFor(Push) asDobjFor(Open) | |
dobjFor(Turn) asDobjFor(Open) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The hallway on level s2 - west end | |
*/ | |
s2HallWest: Room 'West End of Hallway' 'the west end of the hallway' | |
'hallway' | |
"This is the west end of a dimly-lit corridor. The hall ends here | |
with an elevator door to the west, and continues to the east. " | |
vocabWords = 'hall/hallway' | |
west = doorS2outer | |
east = s2HallEast | |
roomParts = static (inherited() - defaultEastWall) | |
; | |
+ doorS2outer: Door ->doorS2inner | |
'elevator lift door/elevator/lift' 'elevator door' | |
"It's an old-style elevator door that opens by swinging out | |
rather than sliding sideways. A round, black call button is | |
next to the door, and above the button is a small neon lamp | |
(currently <<isOnCall ? 'lit' : 'unlit'>>). " | |
isOnCall = nil | |
dobjFor(Open) | |
{ | |
check() | |
{ | |
"The door is locked and won't budge. "; | |
exit; | |
} | |
} | |
dobjFor(Unlock) | |
{ | |
verify() { } | |
action() { "It's locked from the other side; there's no obvious | |
way to unlock it from this side. "; } | |
} | |
afterTravel(traveler, connector) | |
{ | |
if (connector == doorS2inner) | |
{ | |
"<.p>You hold the door open for Xojo, then let it swing | |
shut after he comes through. You hear the door latch as | |
it closes. "; | |
xojo.moveIntoForTravel(location); | |
makeOpen(nil); | |
/* award some points */ | |
scoreMarker.awardPointsOnce(); | |
/* have xojo start escorting us again */ | |
xojo.setCurState(xojoS2West); | |
"<.p><q>Your plan of escape was conceived and executed | |
with great excellence,</q> Xojo says. <q>The main | |
canyon bridge is unfortunately not accessible from this | |
sub-basement level, but I know of an alternative crossing. | |
This way, please.</q> He points down the hall. "; | |
} | |
} | |
scoreMarker: Achievement { +2 "escaping the elevator shaft" } | |
; | |
++ Button, Fixture 'round black elevator lift call button' 'call button' | |
"It's a big black button sticking out from the wall about | |
a centimeter. " | |
dobjFor(Push) | |
{ | |
action() | |
{ | |
if (!location.isOnCall) | |
{ | |
"The lamp above the button lights dimly. "; | |
location.isOnCall = true; | |
} | |
else | |
"<q>Click.</q> "; | |
} | |
} | |
; | |
++ Fixture 'small neon lamp' 'neon lamp' | |
"It's currently <<location.isOnCall ? 'lit' : 'unlit'>>. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* S2 hallway - east side | |
*/ | |
s2HallEast: Room 'Middle of Hallway' 'the middle of the hallway' 'hallway' | |
"This long, dimly-lit corridor extends to the east and west. | |
A low, narrow door leads north. " | |
vocabWords = 'hall/hallway' | |
west = s2HallWest | |
east: FakeConnector, StopEventList { | |
['Xojo gently but firmly takes your arm and stops you. | |
<q>Respectfully, we must not go that way,</q> he says. He | |
looks around almost conspiratorially and lowers his voice. | |
<q>That is the domain of Junior Assistant Staff Functionaries, | |
of Peon Grade and lower. They are relegated to toil here in | |
these nether regions, and in their misery they are desperate | |
for any contact with even such lowly superiors as myself. | |
Were we to venture thus, we could perhaps not escape the | |
obsequious attentions of sub-peons for many hours. Better | |
to go this way instead.</q> He indicates the door to the | |
north. ', | |
'Xojo stops you. <q>Sub-peon staffers that way lie,</q> he | |
says. <q>Better to go this way.</q> He indicates the door. '] | |
} | |
north = s2HallEastDoor | |
roomParts = static (inherited() - [defaultEastWall, defaultWestWall]) | |
; | |
+ s2HallEastDoor: Door ->s2StorageDoor 'low narrow door' 'door' | |
"It's a low, narrow door leading north. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* S2 Storage room | |
*/ | |
s2Storage: Room 'Storage Room' 'the storage room' | |
"This dark, musty room is packed with boxes, crates, and random | |
junk, jammed into every available space and stacked precariously | |
from the floor to the low ceiling. A narrow path seems to wind | |
through the junk to the north. A door leads south. " | |
vocabWords = 'storage room' | |
south = s2StorageDoor | |
north = storagePath | |
roomParts = [defaultFloor, defaultCeiling, defaultSouthWall] | |
; | |
+ s2StorageDoor: Door 'low narrow door' 'door' | |
"It's a low, narrow door leading south. " | |
; | |
+ Decoration | |
'random stack/stacks/box/boxes/crate/crates/junk/pile/piles/clutter/stuff' | |
'junk' | |
"It's just a lot of random junk. " | |
isMassNoun = true | |
; | |
+ storagePath: TravelWithMessage, ThroughPassage 'narrow path' 'path' | |
"It looks like enough of a path through the junk that you | |
could get through. " | |
travelDesc() | |
{ | |
"You carefully pick your way through the piles of junk"; | |
if (!traversedBefore) | |
{ | |
traversedBefore = true; | |
myDust.makePresent(); | |
", but the place is so cramped that you can't avoid getting | |
covered with dust and cobwebs"; | |
} | |
". "; | |
} | |
traversedBefore = nil | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* North end of storage room | |
*/ | |
s2Utility: Room 'Utility Area' 'the utility area' | |
"This is the north end of a dark, musty storage room. Junk is | |
piled almost from floor to ceiling to the south, except for a | |
narrow path that winds through the clutter. This end of the | |
room is mostly cleared of junk, probably to leave room to | |
access the pipes, conduits, and other utility equipment arrayed | |
near the north wall. A round opening in the north wall leads | |
outside; it looks like it was designed mostly for the pipes | |
and conduits, but there's enough space left over for a person | |
to fit through. " | |
vocabWords = 'utility area/room' | |
south = utilityPath | |
north = utilityOpening | |
roomParts = [defaultFloor, defaultCeiling] | |
; | |
+ Fixture 'north n wall*walls' 'north wall' | |
"A round opening leads outside. " | |
; | |
++ utilityOpening: ThroughPassage 'round opening' 'round opening' | |
"It's about a meter in diameter. A number of pipes and conduits | |
run through it, but there's enough space left over for a person | |
to fit through. " | |
; | |
+ Fixture 'utility pipe/pipes/conduits/equipment' 'utility equipment' | |
"A complex network of pipes and conduits fills most of the north | |
end of the room, some connecting to pieces of equipment installed | |
here, some going out through the round opening to the north, | |
some running out through the floor or ceiling. " | |
isMassNoun = true | |
; | |
+ Decoration | |
'random stack/stacks/pile/piles/box/boxes/crate/crates/junk/clutter/stuff' | |
'junk' | |
"It's just a lot of random junk. " | |
isMassNoun = true | |
; | |
+ utilityPath: TravelWithMessage, ThroughPassage ->storagePath | |
'narrow path' 'path' | |
"It's enough of a path that you could make it through the junk. " | |
travelDesc = "You carefully pick your way through the piles of junk. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* platform outside storage room | |
*/ | |
s2Platform: OutdoorRoom 'Utility Platform' 'the utility platform' | |
"This rickety walkway is little more than a steel grating | |
bolted to the outside of the power plant building, suspended | |
over a six-hundred foot drop into the canyon below. The plant | |
wall continues for another twenty feet below, where it reaches | |
the top of the sheer cliff wall of the canyon. The canyon | |
wall drops almost vertically to the river below. | |
<.p>A crude rope bridge over the canyon ends here. This end | |
of the bridge is tied to the steel grating of the platform, | |
and the bridge extends out over the canyon to the north. | |
<.p>Numerous pipes and conduits run up and down the wall of | |
the building. Many go in through the round opening in the | |
wall to the south, but there's just enough space left over | |
for a person to fit through the opening. | |
<.p>A short distance east, the underside of the main bridge | |
across the canyon is visible. " | |
north: TravelMessage { -> ropeBridge1 | |
"You take a deep breath and carefully follow Xojo's lead, | |
holding the main support ropes for balance and hunting | |
around for footing on each step. " } | |
south = platformOpening | |
down: NoTravelMessage { "No way; it's much too big a drop. "; } | |
roomParts = [s2PlatformFloor, defaultSky] | |
roomBeforeAction() | |
{ | |
if (gActionIn(Jump, JumpOffI)) | |
{ | |
"It's much too big a drop. "; | |
exit; | |
} | |
} | |
; | |
+ platformBridge: Enterable 'crude rope bridge/ropes' 'rope bridge' | |
"It really is a <i>rope</i> bridge---not a bridge made of | |
wooden planks supported by ropes, like you've seen before, | |
but a bridge literally made entirely of rope. The walkway | |
is formed by ropes arranged in a criss-crossed pattern to | |
create a sort of sling hanging from the main supporting ropes, | |
which could be used as handrails. It looks very makeshift. " | |
connector = (location.north) | |
dobjFor(Cross) asDobjFor(Enter) | |
dobjFor(Board) asDobjFor(Enter) | |
dobjFor(Push) | |
{ | |
verify() { } | |
action() { "The bridge sways, a bit more than you'd expect. "; } | |
} | |
dobjFor(Pull) asDobjFor(Push) | |
dobjFor(Move) asDobjFor(Push) | |
; | |
+ Distant, Decoration 'main underside/bridge/girders' 'main bridge' | |
"All you can see from here is the steel girders supporting the | |
roadway over the canyon. " | |
; | |
+ RopeBridgeCanyon 'sheer vertical cliff canyon river/wall/walls' 'canyon' | |
"The canyon walls drop away almost vertically. Judging distances | |
on this scale by eye is almost impossible, but you've been told | |
that the canyon is about six hundred feet deep. " | |
; | |
+ s2PlatformFloor: Floor 'steel metal platform/floor/grate/grating/walkway' | |
'steel grating' | |
"It's a simple steel grating, bolted to the side of the power | |
plant building. " | |
dobjFor(JumpOff) | |
{ | |
verify() { } | |
action() { "It's much too big a drop. "; } | |
} | |
dobjFor(JumpOver) asDobjFor(JumpOff) | |
; | |
+ Fixture 'utility pipe/pipes/conduits' 'pipes' | |
"There are no markings you can read, so it's hard | |
to tell what the specific purpose of any given pipe or | |
conduit is. " | |
isPlural = true | |
; | |
+ Fixture 'concrete government power plant 6 outside wall/building/side' | |
'power plant' | |
"The power plant wall towers several stories above, and ends | |
not far below, at the top of the cliff wall. A round opening | |
leads into the building. " | |
dobjFor(Enter) remapTo(TravelVia, platformOpening) | |
dobjFor(GoThrough) remapTo(TravelVia, platformOpening) | |
; | |
++ platformOpening: ThroughPassage ->utilityOpening | |
'round opening' 'round opening' | |
"It's about a meter in diameter. Pipes and conduits run through | |
it, but there's space enough for a person to fit through. " | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* helper classes for the bridge rooms | |
*/ | |
class RopeBridgeRoom: Floorless, OutdoorRoom | |
roomBeforeAction() | |
{ | |
if (gActionIs(Drop)) | |
{ | |
"There's no place to put anything down here; it | |
would probably fall through the gaps in the ropes. "; | |
exit; | |
} | |
if (gActionIs(ThrowAt) && !gIobj.ofKind(RopeBridgeCanyon)) | |
{ | |
"Better not; {it dobj/he} would probably fall into the canyon. "; | |
exit; | |
} | |
if (gActionIn(Jump, JumpOffI)) | |
{ | |
"No way; it's a very long way down. "; | |
exit; | |
} | |
if (gActionIs(Wait)) | |
{ | |
"You'd rather not spend any more time here than necessary. "; | |
} | |
} | |
; | |
class RopeBridge: Fixture | |
'rope rope/ropes/bridge/walkway/lattice' 'rope bridge' | |
"The whole bridge is swaying considerably. You don't want to | |
spend any more time on this thing than you have to. " | |
dobjFor(JumpOff) | |
{ | |
verify() { } | |
action() { "No way; it's a very long way down. "; } | |
} | |
dobjFor(JumpOver) asDobjFor(JumpOff) | |
dobjFor(SitOn) | |
{ | |
verify() { } | |
check() { "It's not a very good place to sit. "; exit; } | |
} | |
dobjFor(LieOn) | |
{ | |
verify() { } | |
check() { "It's not a very good place to lie down. "; exit; } | |
} | |
dobjFor(StandOn) | |
{ | |
verify() { } | |
check() { "You already are. "; exit; } | |
} | |
iobjFor(PutOn) remapTo(Drop, DirectObject) | |
dobjFor(Cross) | |
{ | |
verify() { } | |
action() { "(If you want to continue across, just say which way.) "; } | |
} | |
dobjFor(Push) | |
{ | |
verify() { } | |
action() { "The bridge sways alarmingly. "; } | |
} | |
dobjFor(Pull) asDobjFor(Push) | |
dobjFor(Move) asDobjFor(Push) | |
; | |
class RopeBridgeCanyon: Distant | |
'sheer vertical cliff canyon river/canyon/(wall)/(walls)/cliff/cliffs' | |
'canyon' | |
"You're sure you're missing a great view, but right now you're | |
too fixated on not falling to pay any attention. " | |
iobjFor(ThrowAt) | |
{ | |
verify() { } | |
check() | |
{ | |
"Better not; they have very harsh penalties for littering | |
around here. "; | |
exit; | |
} | |
} | |
; | |
class RopeBridgeMainBridge: Distant | |
'main bridge/underside/girders' 'main bridge' | |
"The main bridge is visible overhead and some distance east. " | |
dobjFor(Examine) | |
{ | |
/* make this less likely, like decorations */ | |
verify() | |
{ | |
inherited(); | |
logicalRank(70, 'x distant'); | |
} | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The bridge - part 1 | |
*/ | |
ropeBridge1: RopeBridgeRoom 'South End of Rope Bridge' | |
'the south end of the rope bridge' 'rope bridge' | |
"Standing on this bridge is a lot more difficult than it looked | |
from the platform. The whole thing wants to fold up under | |
your weight, and the lattice of ropes making up the walkway shifts | |
with every step. " | |
north = ropeBridge2 | |
south = s2Platform | |
atmosphereList: StopEventList { | |
[ | |
'A sharp vibration shudders through the bridge, as though | |
someone struck one of the ropes with a hammer. ', | |
'A little gust of wind sets the bridge swaying alarmingly. ', | |
'The ropes creak and groan disconcertingly. ', | |
nil | |
] } | |
; | |
+ RopeBridge; | |
+ RopeBridgeCanyon; | |
+ RopeBridgeMainBridge; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The bridge - part 2 | |
*/ | |
ropeBridge2: RopeBridgeRoom 'Middle of Rope Bridge' | |
'the middle of the rope bridge' 'rope bridge' | |
"This is just about halfway across the bridge. From here, the | |
ends of the bridge are so far away they're not readily visible, | |
making it almost feel like the bridge is floating in mid-air | |
over the canyon. " | |
south = ropeBridge1 | |
north = ropeBridge3 | |
atmosphereList: StopEventList { | |
[ | |
'The whole bridge abruptly falls about three feet, then stops | |
with a jerk. Xojo looks back and laughs nervously. ', | |
'The wind kicks up a little. The bridge sways and twists. ', | |
'A loud snapping noise comes from behind you somewhere. ', | |
'The ropes creak and groan. ', | |
nil | |
] } | |
; | |
+ RopeBridge; | |
+ RopeBridgeCanyon; | |
+ RopeBridgeMainBridge; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The bridge - part 3 - the fall | |
*/ | |
ropeBridge3: RopeBridgeRoom 'Hanging on a Rope Bridge' | |
'the dangling rope bridge' 'rope bridge' | |
"What was once a rope bridge is now hanging vertically alongside | |
the north cliff of the canyon. Fortunately, the construction of the | |
bridge seems somewhat usable as a ladder. It looks like it's only | |
about fifty feet to the top of the cliff above; the rest of the | |
bridge continues for some distance below. " | |
enteringRoom(traveler) | |
{ | |
if (traveler == me) | |
{ | |
/* uh-oh... */ | |
"You think you're starting to get the hang of this, and you move | |
forward with a little more confidence. The north end of the | |
bridge finally comes into sight---not much further now. | |
<.p>The bridge shudders with a sharp shock and twists to | |
the left. You stop and hold on tight. The vibration fades, | |
but the walkway is still at an odd angle, so you try to shift | |
your weight to right yourself. Another shudder, and the | |
bridge twists even more, then drops about six feet and jerks | |
to a stop. Your heart pounds and you hang on as tightly as | |
you can. | |
<.p>Xojo looks back. <q>Perhaps we should---</q> | |
<.p>The bridge gives way, and you're in free-fall. You hang | |
on to the rope out of reflex, but it's just falling with you. | |
Maybe not, though: the rope jerks tight and starts pulling | |
you toward the north face of the cliff. Suddenly you're | |
falling sideways rather than down, the north cliff approaching | |
fast. You brace yourself for impact just before you slam | |
into the cliff wall. | |
<.p>After a couple of bounces, you more or less come to a stop. | |
You take a look at yourself, and it doesn't look like you're | |
bleeding. Maybe you're so drenched with adrenaline that you | |
don't realize how badly you're hurt yet, but you don't seem to | |
have any serious injuries; you're undoubtedly a little bruised, | |
but no major body parts seem broken or missing. It looks like | |
you lost one of your shoes, though. "; | |
/* lose the shoe */ | |
myShoes.moveInto(nil); | |
myLeftShoe.makePresent(); | |
} | |
} | |
up: TravelMessage { -> canyonNorth | |
"It's a bit of work, but the rope lattice of the former walkway | |
actually makes a pretty good ladder. You make it to the top | |
of the cliff in almost no time, and Xojo helps you up over | |
the edge. " } | |
down: FakeConnector { "You do a little quick arithmetic: the | |
canyon is about a hundred meters across, as you recall, and | |
two hundred deep. If the whole bridge is still intact, that | |
means the former south end would still be about a hundred | |
meters above the floor of the canyon. Up seems like a much | |
better option. " } | |
roomBeforeAction() | |
{ | |
inherited(); | |
if (gActionIs(Stand) || gActionIs(StandOn)) | |
{ | |
"Standing isn't really an option right now. "; | |
exit; | |
} | |
} | |
; | |
+ RopeBridgeMainBridge; | |
+ Distant 'river/canyon' 'canyon' | |
"You'd rather not look down too much just now. " | |
tooDistantMsg(obj) | |
{ return 'You\'d rather not look down too much just now. '; } | |
; | |
+ Fixture 'north n canyon rock wall/cliff/walls/cliffs/rock' 'canyon wall' | |
"It's an almost vertical wall of rock. " | |
; | |
+ RopeBridge | |
desc = "The collapsed bridge is hanging vertically from above. " | |
dobjFor(Climb) remapTo(Up) | |
dobjFor(ClimbUp) asDobjFor(Climb) | |
dobjFor(ClimbDown) remapTo(Down) | |
dobjFor(Cross) { action() { "That's out of the question at this | |
point; climbing seems more likely. "; } } | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* North edge of canyon | |
*/ | |
canyonNorth: OutdoorRoom 'Edge of Canyon' 'the edge of the canyon' | |
"This is a rough area strewn with rocks and overgrown | |
with vegetation. To the south, the sheer cliff wall of | |
the canyon drops away almost vertically. A little path | |
leading northeast has been cut through the overgrowth. | |
<.p>The end of the rope bridge is anchored to a pair of | |
sturdy metal stakes driven into the rock. The bridge | |
itself hangs limply over the edge of the canyon. " | |
northeast = canyonPath | |
down: NoTravelMessage { "Nothing could persuade you to climb back | |
down onto the bridge. "; } | |
roomBeforeAction() | |
{ | |
if (gActionIn(Jump, JumpOffI)) | |
{ | |
"You'd rather keep your distance from the canyon. "; | |
exit; | |
} | |
} | |
; | |
+ canyonPath: TravelWithMessage, PathPassage ->courtyardPath | |
'little path' 'path' | |
"The path leads northeast, through the overgrowth. " | |
travelDesc = "You follow the path through the dense foliage. " | |
; | |
+ Decoration 'rock/rocks' 'rocks' | |
"Rocks of all sizes litter the area. " | |
isPlural = true | |
; | |
+ Decoration 'dense lush tropical | |
plant/plants/vegetation/overgrowth/jungle/foliage/overgrowth' | |
'vegetation' | |
"The dense, lush vegetation grows almost up to the edge of the | |
cliff. The only way through is the path leading northeast. " | |
isMassNoun = true | |
; | |
+ cnRopeBridge: StairwayDown 'rope bridge' 'rope bridge' | |
"The bridge hangs over the side of the canyon, disappearing | |
into the distance below. " | |
dobjFor(TravelVia) | |
{ | |
check() | |
{ | |
"Nothing could persuade you to set foot on that thing again. "; | |
exit; | |
} | |
} | |
dobjFor(Pull) | |
{ | |
verify() { } | |
action() { "The bridge is much too heavy to move. "; } | |
} | |
dobjFor(Cross) { verify() { illogical('It\'s more of a ladder | |
than a bridge at this point, so it\'s not something you | |
can cross any more. '); } } | |
dobjFor(Board) asDobjFor(TravelVia) | |
dobjFor(Enter) asDobjFor(TravelVia) | |
; | |
+ Fixture 'sturdy metal stake/stakes/pair' 'metal stake' | |
"The stakes are driven deep into the rock to make a sturdy anchor. " | |
dobjFor(Pull) | |
{ | |
verify() { } | |
action() { "The stakes are solidly embedded in the rock. "; } | |
} | |
; | |
+ Distant 'sheer cliff river/canyon/wall/cliff/walls/cliffs/edge' 'canyon' | |
"The view of the canyon isn't bad from here, but you don't want | |
to get too close to the edge. " | |
dobjFor(JumpOff) remapTo(Jump) | |
dobjFor(JumpOver) remapTo(Jump) | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* Plant courtyard | |
*/ | |
plantCourtyard: OutdoorRoom 'Courtyard' 'the courtyard' | |
"A sizable area of jungle has been cleared to form this courtyard. | |
The vast main administrative office building wraps around the north | |
and east sides of the area, and low wooden fences keep the jungle | |
at bay to the south and west. A narrow path leads southwest | |
into the jungle. A set of doors to the east leads into the building. | |
<.p>Several helicopters---you count five---are parked here, rotors | |
still spinning lazily. Dozens of people wearing black Mitachron-logo | |
polo shirts are busily dashing around, many carrying boxes or crates. " | |
vocabWords = 'courtyard' | |
in = adminDoorExt | |
east asExit(in) | |
southwest = courtyardPath | |
atmosphereList: ShuffledEventList { | |
['The Mitachron people are here in force, all right; all of | |
this activity must be to set up an instant demo. It\'s | |
a very good thing you let Xojo take you across that rope | |
bridge after all; if it had taken any longer to get here, | |
they might actually have been able to steal this deal away | |
from you. Luckily, it looks like you got here in time. ' | |
] | |
['One of the Mitachron people almost bumps into you with | |
something that looks like a bag of golf clubs, but you | |
manage to jump out of the way. ', | |
'A Mitachron woman with a clipboard zips past, saying | |
something into her headset about ice. ', | |
'Two Mitachron men shuffle by carrying an apparently very | |
heavy metal create that reminds you of a steamer trunk. ', | |
'The Mitachron employees busily carry things in and out | |
of the helicopters. ', | |
'Several Mitachron workers hustle by carrying some heavy | |
piece of equipment. '] | |
} | |
; | |
+ courtyardPath: PathPassage 'narrow path' 'path' | |
"The path leads southwest into the jungle. " | |
; | |
+ Distant 'lush tropical jungle/plant/plants/vegetation/overgrowth' 'jungle' | |
"The vegetation is lush, tropical, and mostly unfamiliar to you. " | |
; | |
+ Decoration 'low wood wooden fence/fences' 'wooden fence' | |
"The fences mark the boundaries of the courtyard to the south | |
and west. The jungle lies beyond. " | |
; | |
+ Decoration | |
'five black mitachron helicopters/choppers/copters' 'helicopters' | |
"They're all identical: each is all black, except for the yellow | |
Mitachron logo on the tail. " | |
isPlural = true | |
notImportantMsg = 'You don\'t want to get too close to the | |
helicopters; the Mitachron people might | |
not take kindly to an Omegatron employee | |
snooping around. ' | |
; | |
++ Decoration 'helicopter rotor/rotors' 'rotors' | |
"The rotors are still spinning lazily, as though the pilots were | |
keeping the choppers ready for a sudden departure. " | |
; | |
++ Decoration 'helicopter yellow mitachron tail/tails' 'helicopter tails' | |
"The tail of each helicopter is painted with the Mitachron logo. " | |
isPlural = true | |
; | |
+ Decoration 'yellow mitachron logo' 'mitachron logo' | |
"The Mitachron logo is a big yellow <q>M</q> in a heavy sans-serif | |
action-slant font, superimposed over a light yellow outline of a | |
globe. You've always thought it symbolizes Mitachron world | |
domination. " | |
; | |
+ Decoration | |
'mitachron-logo polo employee/employees/worker/workers/shirt/shirts/ | |
people/man/woman*men women' | |
'mitachron people' | |
"The Mitachron people are all walking fast and purposefully, | |
expressions of earnest resolve on their faces. They seem hurried | |
but not frantic, like they know exactly what they're doing but | |
they don't have any time to waste. Most are carrying boxes | |
or crates, and a few are carrying clipboards and directing the | |
others. " | |
isPlural = true | |
notImportantMsg = 'You can\'t seem to get anyone\'s attention. ' | |
; | |
++ Decoration 'box/crate/boxes/crates/clipboards/handsets/trunk/equipment' | |
'Mitachron stuff' | |
"The things they're carrying are of little interest to you, | |
except that all of this is probably for an instant demo, which | |
you'd like to preempt if possible. " | |
isMassNoun = true | |
; | |
+ Enterable 'vast main administrative administration office buildings' | |
'main administrative building' | |
"The building is huge: it runs hundreds of feet south to north, | |
then turns a corner and runs hundreds more feet east to west. | |
A set of doors to the east leads inside. " | |
connector = adminDoorExt | |
; | |
+ adminDoorExt: Door ->adminDoorInt | |
'main administrative office building door/doors/set' | |
'administration building doors' | |
"The doors lead into the building to the east. " | |
isPlural = true | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The administration building lobby | |
*/ | |
adminLobby: Room 'Lobby' 'the lobby' | |
"On past visits to the administration center, this large, open lobby | |
was quite austere and utilitarian, but it's been transformed: it's | |
festive, colorful, almost carnival-like. The room is crowded with | |
people talking and dancing. Balloons and confetti fill the air, a | |
live band plays from a stage, tables spread with food line the walls, | |
waiters weave through the crowd with big trays of drinks. | |
<.p>A set of doors leads outside to the west. " | |
vocabWords = 'lobby' | |
west = (gRevealed('talked-to-magnxi') ? noWest : adminDoorInt) | |
out asExit(west) | |
noWest: FakeConnector { "You try to make your way to the doors, but | |
you can't get through the crowd. "; } | |
/* | |
* reaching this room is a time-based plot point, so set a marker | |
* here | |
*/ | |
afterTravel(traveler, conn) | |
{ | |
inherited(traveler, conn); | |
plotMarker.eventReached(); | |
} | |
/* arrive here at about noon */ | |
plotMarker: ClockEvent { eventTime = [1, 12, 8] } | |
atmosphereList: ShuffledEventList { | |
['<q>Excuse me!</q> a waiter shouts. You make enough room | |
for him to get past, and he moves away through the crowd. ', | |
'The band finishes a song to a round of applause, then they | |
start another. ', | |
'A group of laughing people pushes past you. ', | |
'The band plays a slightly quieter passage briefly, then | |
the tempo picks up and they\'re even louder than before. ', | |
'The tempo of the music slows down a little, and things get | |
a little less noisy. ', | |
'The music changes to a slower dance song. ', | |
'The ad hoc dance area seems to expand to where you\'re standing, | |
forcing you to move out of the way a little. ' | |
] } | |
; | |
+ adminDoorInt: Door 'door/doors/set' 'doors' | |
"The doors lead outside to the west. " | |
isPlural = true | |
dobjFor(TravelVia) | |
{ | |
check() | |
{ | |
if (gRevealed('talked-to-magnxi')) | |
{ | |
"You try to leave, but there are too many people | |
blocking the way through the doors. "; | |
exit; | |
} | |
} | |
} | |
; | |
+ Decoration | |
'partying plant mitachron worker/workers/employees/crowd*people' 'crowd' | |
"It looks like they're mostly plant employees, but many Mitachron | |
people are here as well. " | |
/* use normal Thing Examine verification rules */ | |
dobjFor(Examine) { verify() { inherited Thing(); } } | |
; | |
+ Decoration 'big waiters/waitresses/tray/trays' 'waiters' | |
"The waiters are moving through the crowd carrying drinks. " | |
isPlural = true | |
; | |
+ Decoration 'drink/drinks/cocktail/cocktails/glass/glasses' 'drinks' | |
"The waiters are moving fast enough that you can't see exactly | |
what drinks they have, but it looks like a wide range of cocktails. " | |
isPlural = true | |
dobjFor(Take) | |
{ | |
verify() { } | |
action() { drinkScript.doScript(); } | |
} | |
dobjFor(Drink) asDobjFor(Take) | |
drinkScript: StopEventList { [ | |
'You\'re not much of a drinker normally, but right now you | |
could really use something to calm your nerves. You grab a | |
glass from a passing waiter\'s tray and down it in one gulp. | |
Practically the instant you\'re done, a waiter going the other | |
direction collects your glass. ', | |
'You grab another drink from a passing waiter and swallow it, | |
a little less frantically this time. A waiter collects the | |
glass when you\'re done. ', | |
'Better not overdo it; you still need to maintain a | |
businesslike demeanor. '] | |
} | |
notImportantMsg = 'The drinks aren\'t especially interesting | |
apart from their alcohol content. ' | |
dobjFor(GiveTo) | |
{ | |
preCond = [] | |
verify() { } | |
check() | |
{ | |
"No need; {the iobj/he} can help {itself} if | |
{it's} so inclined. "; | |
exit; | |
} | |
} | |
; | |
+ Decoration 'balloons/confetti/decorations' 'decorations' | |
"The decorations make the normally spartan lobby look festive. " | |
isPlural = true | |
; | |
+ Decoration 'food/table/tables' 'tables of food' | |
"Tables piled with food line the walls. " | |
notImportantMsg = 'You think you\'ll hold off on joining the party | |
until you\'ve had a chance to talk to the Colonel. ' | |
isPlural = true | |
; | |
+ Decoration 'live band/stage' 'band' | |
"They're playing big-band music. They seem pretty good. " | |
dobjFor(ListenTo) { verify() { logicalRank(50, 'x decoration'); } } | |
; | |
++ SimpleNoise 'big band big-band music/song' 'music' | |
"The live band is playing big-band-style music. " | |
; | |
/* | |
* once Frosst is mentioned, create an object for his un-presence, in | |
* case the player assumes he hasn't actually left yet | |
*/ | |
+ adminUnFrosst: PresentLater, Unthing | |
'slight pale junior (mitachron) frosst belker/executive/man*men' | |
'Frosst Belker' | |
isProperName = true | |
isHim = true | |
notHereMsg = 'You don\'t see Frosst Belker here; he left after | |
talking to the Colonel. ' | |
dobjFor(Follow) | |
{ | |
verify() { } | |
action() { "It's so crowded that you lost track of | |
Frosst Belker as soon as he walked away. "; } | |
} | |
; | |
/* ------------------------------------------------------------------------ */ | |
/* | |
* The colonel | |
*/ | |
magnxi: Person 'colonel grand high administrator magnxi/woman*women' | |
'Colonel Magnxi' | |
"Her official title is Grand High Administrator Magnxi, but she | |
insists on everyone using her military title, and she always wears | |
a dress uniform that looks like something out of the Napoleonic | |
era. You've never seen her wear this hat before, though; it pushes | |
the bizarreness of her outfit to a whole new level. " | |
isProperName = true | |
isHer = true | |
/* we know about the colonel from the start */ | |
isKnown = true | |
scoreMarker: Achievement { +1 "getting Colonel Magnxi's attention" } | |
beforeAction() | |
{ | |
/* do the normal work */ | |
inherited(); | |
/* special handling if throwing something at the colonel */ | |
if (gActionIs(ThrowAt) && (gIobj == self || gIobj.isIn(self))) | |
{ | |
"The Colonel might find that annoying, which is the last | |
thing you'd want right now. "; | |
exit; | |
} | |
} | |
; | |
+ InitiallyWorn | |
'dress uniform/sash/sashes/ribbons/medal/medals/epaulets/outfit' | |
'uniform' | |
"It's like a 19th century museum piece: sashes, ribbons, medals, | |
epaulets, the whole works. " | |
/* | |
* The English library tries hard to guess whether to use 'a' or | |
* 'an', but 'u' words are too unpredictable for the library to get | |
* them right every time, and this is one that it gets wrong. So, we | |
* need to override the aName to make it 'a uniform'. | |
*/ | |
aName = 'a uniform' | |
isListedInInventory = nil | |
; | |
+ InitiallyWorn 'military hat/insignia/brim' 'hat' | |
"It's positively huge; it's proportioned like a novelty chef hat, | |
ludicrously tall and ballooning outwards toward the top, but it's | |
dark blue, made of stiff material, and adorned with military | |
insignia. It's about two sizes too large for the Colonel's | |
head, and as a result it sits far too low on her head, almost covering | |
her eyes. " | |
isListedInInventory = nil | |
; | |
+ Decoration 'small group/people/companions' 'small group of people' | |
"The place is so crowded that you can't see who she's with. " | |
theDisambigName = 'Colonel Magnxi\'s companions' | |
; | |
+ InConversationState | |
/* | |
* this won't actually be used, since we end the scene as soon as we | |
* start a conversation with the colonel, but we need it to fit the | |
* standard structure | |
*/ | |
; | |
/* | |
* to allow entering a conversation, we need a topic to handle the | |
* response; but we don't actually need a response, since we handle | |
* everything just by starting the conversation | |
*/ | |
++ DefaultAnyTopic ""; | |
++ ConversationReadyState | |
isInitState = true | |
stateDesc = "She's chatting with a small group of people. " | |
specialDesc = "Colonel Magnxi is here, chatting with a group of people. " | |
tries = 0 | |
enterConversation(actor, entry) | |
{ | |
switch (++tries) | |
{ | |
case 1: | |
"<q>Colonel Magnxi!</q> you shout, trying to be heard over | |
the music, but the brass section picks just this moment to | |
get really loud. The Colonel doesn't seem to hear you. "; | |
break; | |
case 2: | |
"You push your way through the crowd and wave your arms, | |
shouting a little louder. <q>Colonel! Colonel Magnxi!</q> | |
She looks your way like she actually heard you this time, | |
but a waiter cuts in front of you. The waiter moves off, | |
but Colonel Magnxi is talking to someone else again now. "; | |
break; | |
case 3: | |
"You shout at the top of your lungs. <q>Colonel Magnxi!</q> | |
Just as you do, the music stops---your shout echoes | |
through the suddenly quiet room. Everyone turns and looks | |
at you. | |
<.p>The Colonel stares at you along with everyone else | |
for a moment, then smiles. <q>Oh, how lovely to see you | |
again,</q> the Colonel says in her perfect English accent. | |
She squints and wobbles a little, like she's had a bit to | |
drink. <q>Mister, um, Mister Muddling, isn't it? Well, do | |
join us. We were all just celebrating our new partnership | |
with the Mitachron Corporation. Isn't this a lovely party | |
Mitachron are throwing for us?</q> | |
<.p>New partnership? <q>But, but...</q> you stutter, not | |
believing what you're hearing. | |
<.p>The band starts a new song, a bit less deafening | |
than the last one. <q>And did you see my brilliant | |
new hat?</q> she asks, running her fingers around the | |
oversized brim. <q>It's a gift from Mr.\ Belker here. | |
Really, Frosst, it's too much.</q> | |
<.p>Oh, no. You didn't see him here until just now. The | |
slight, pale man standing next to Magnxi is Frosst Belker, | |
a junior executive at Mitachron. You've crossed paths with | |
him before, always on occasions just like this. | |
<.p><q>Ah, Mr.\ Mittling,</q> Belker says with that damned | |
smirk of his. He speaks with a faint accent you've never | |
been able to place, his vowels a bit elongated and nasal, | |
his consonants a little too crisply enunciated. | |
<q>Again we see there is no customer you can | |
court which I cannot take away.</q> He finishes his champagne | |
and hands the glass to a passing waiter. <q>Colonel, a pleasure | |
doing business, as always. I must be on my way, but do enjoy | |
the party.</q> He and the Colonel embrace like diplomats, | |
and then he turns to go, but he stops for a moment and | |
looks your way. <q>You too, of course, Mr.\ Mittling,</q> | |
he says with another smirk, then chuckles and walks off, | |
disappearing into the crowd. | |
<.p>The colonel goes back to mingling. | |
<.reveal talked-to-magnxi> "; | |
/* award some points for getting her attention */ | |
magnxi.scoreMarker.awardPointsOnce(); | |
/* move the departed frosst here */ | |
adminUnFrosst.makePresent(); | |
/* one last task for xojo */ | |
xojo.addToAgenda(xojoEmailAgenda.setDelay(1)); | |
/* all done */ | |
break; | |
case 4: | |
default: | |
"You try to get the Colonel's attention again, but she's | |
too busy talking to someone else. "; | |
break; | |
} | |
/* | |
* we can never actually enter a conversation, so stop the | |
* command here | |
*/ | |
exit; | |
} | |
; | |
adminEmail: Readable 'piece/paper/print-out/printout/email/e-mail' | |
'print-out' | |
"It's a print-out from an old-style line printer, printed on | |
continuous fan-fold paper with alternating stripes of white and | |
pale green. It's an e-mail message from your boss; he must be | |
back from Maui. It's in his distinctive all-lower-case style; | |
he does that to impress upon people that he's too important to | |
waste his valuable time pressing the Shift key. His spelling and | |
punctuation styles are similarly streamlined. | |
<.p><.blockquote> | |
<tt>doug- hope your done with the demo by now.....when you get back | |
i need you to go down to la to recrute a cal tech student named | |
brian stamer.\ rudyb saw an artical about him someware hes all | |
hot to hir ehim.\ do you no him....anyway rudyb wants you to | |
go cause you went to cal tech. | |
<.p>thx carl | |
<.p>btw- im out of the office next month....important conference | |
in fiji.\ im getting burned out from all this travel so im going to | |
take some time off when i get back.\ if you need anything just email. | |
<.p>bbtw- o-travel got you a grate!!!\ price on you're return | |
flight.\ they found a downgrade to economy-minus, it has a couple | |
xtra connectoins, im sure you wont mind.\ saves big $$$ in the bujit, | |
fiji kind of pushed it over, rudyb is all like your overspending | |
to much again so we all have to pitch in to costcut more. | |
<.p>have fun in la!!!!!!!!!!!!! | |
</tt> | |
<./blockquote> | |
<.p>Great. You're not even back from this miserable trip yet and | |
you already have another one scheduled. Oh, well. Might as well | |
arrange for the ox cart back to the airport, or whatever it is the | |
new, lower budget will allow... " | |
dobjFor(Examine) | |
{ | |
action() | |
{ | |
/* show the message */ | |
inherited(); | |
/* run the campus initialization */ | |
campusInit(); | |
/* end the turn here */ | |
exit; | |
} | |
} | |
; | |
This file has been truncated, but you can view the full file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2003, 2013 Michael J. Roberts | |
* | |
* Return to Ditch Day - Jay Santoshnimoorthy. Jay programs the | |
* calculator for us to solve the Hovarth puzzle, but it takes a bit of | |
* interacting with him to get him to cooperate. | |
*/ | |
#include <adv3.h> | |
#include <en_us.h> | |
#include "ditch3.h" | |
/* an unthing for jay, until he's present */ | |
unJay: Unthing 'jay santoshnimoorthy' 'Jay' @alley4main | |
isHim = true | |
isProperName = true | |
notHereMsg = 'You don\'t know what Jay looks like, so you wouldn\'t | |
know if he were here. ' | |
; | |
jay: PresentLater, Person 'jay santoshnimoorthy' 'Jay' @alley4main | |
"His height and build and long hair make him look like a | |
surfer dude. He's wearing a bright aloha shirt. " | |
/* jay is just part of the crowd, so don't mention him separately */ | |
specialDesc = "" | |
isHim = true | |
isProperName = true | |
/* | |
* enter the conversation state automatically - we use this when we | |
* trigger a conversation entry via some other conversational event, | |
* such as asking the other students where jay is | |
*/ | |
autoEnterConv() | |
{ | |
/* | |
* if our current state is a conversation-ready state, switch | |
* directly to the corresponding in-conversation state | |
*/ | |
if (curState.ofKind(ConversationReadyState)) | |
setCurState(curState.inConvState); | |
/* mark me as 'him' */ | |
gPlayerChar.setPronounObj(self); | |
/* make me the interlocutor */ | |
noteConversation(gPlayerChar); | |
} | |
; | |
+ InitiallyWorn 'bright vividly-colored aloha hawaiian shirt' 'aloha shirt' | |
"The vividly-colored shirt is adorned with a floral pattern. " | |
isListedInInventory = nil | |
; | |
+ InConversationState | |
stateDesc = "He's standing here talking to you. " | |
; | |
/* ask jay about the stack */ | |
++ AskTellShowTopic | |
[stackTopic, a4Materials, a4Envelopes, a4Map, turboTopic] | |
"<q>Which stack is this?</q> you ask. | |
<.p><q>It's the Turbo Power Animals stack,</q> he says. | |
<q>Go check out the sign.</q> He points down the hall | |
to the west. " | |
; | |
+++ AltTopic, StopEventList | |
['<q>Anything I can do to help with the stack?</q> you ask. | |
<.p>He looks over the papers on the table. <q>You know, we | |
don\'t have anyone working on Turbo Power Squirrel. We\'ve | |
figured out where he is---he\'s in the Guggenheim wind tunnel. | |
We need someone to get up there and bring him back.</q> | |
<.reveal squirrel-assigned> ', | |
'<q>How\'s the stack coming?</q> you ask. | |
<.p><q>We still need someone to go get Turbo Power Squirrel, | |
if you\'re interested,</q> he says. <q>We think he\'s up | |
in the Guggenheim wind tunnel.</q> '] | |
isActive = gRevealed('tpa-stack') | |
; | |
+++ AltTopic | |
"<q>How's the stack coming?</q> you ask. | |
<.p>He shrugs. <q>We'd be doing great if you could bring | |
back Turbo Power Squirrel for us.</q> " | |
isActive = gRevealed('squirrel-assigned') | |
; | |
+++ AltTopic | |
"You're a little hesitant to ask about the stack again; | |
you're afraid you might end up with another assignment. " | |
isConversational = nil | |
isActive = gRevealed('squirrel-returned') | |
; | |
/* ask about turbo power squirrel is about like asking about the stack */ | |
++ AskTellTopic @squirrel | |
"<q>Where was this squirrel I'm supposed to find?</q> you ask. | |
<.p><q>We're pretty sure he's in the Guggenheim wind tunnel,</q> Jay | |
says. <q>Up on the roof of Guggenheim.</q> " | |
isActive = gRevealed('squirrel-assigned') | |
; | |
+++ AltTopic | |
"<q>How's Turbo Power Squirrel doing?</q> you ask. | |
<.p><q>He's safe, thanks to you!</q> Jay says, smiling. " | |
isActive = gRevealed('squirrel-returned') | |
; | |
++ AskTellTopic @stamerStackTopic | |
"You ask Jay if he's seen Stamer's stack. He says he looked at | |
it and wasn't very interested---he calls it <q>another boring | |
black box stack.</q> " | |
; | |
/* once we assign the squirrel, guggenheim is a topic of interest */ | |
++ AskTellTopic [guggenheimTopic, windTunnelTopic] | |
"<q>How am I supposed to get up to the wind tunnel?</q> you ask. | |
<.p>He shrugs. <q>I was hoping you'd be able to figure that out.</q> " | |
isActive = gRevealed('squirrel-assigned') | |
; | |
+++ AltTopic | |
"<q>Have you been up in the wind tunnel before?</q> you ask. | |
<.p><q>Nope,</q> he says. " | |
isActive = (windTunnel.seen) | |
; | |
/* returning the squirrel */ | |
++ GiveShowTopic @squirrel | |
topicResponse() | |
{ | |
"You offer the squirrel action figure to Jay. <q>Hey! That's | |
great!</q> He takes the squirrel and holds it up for everyone | |
to see. <q>Turbo Power Squirrel is safe!</q> Everyone cheers. | |
Jay puts the figure on the table. | |
<.reveal squirrel-returned> "; | |
/* remove the squirrel from play */ | |
squirrel.moveInto(a4Table); | |
/* award some points for this */ | |
scoreMarker.awardPointsOnce(); | |
} | |
scoreMarker: Achievement { +5 "rescuing Turbo Power Squirrel" } | |
; | |
/* | |
* Ask Jay about the calculator. | |
* | |
* - if we've already programmed it, explain the instructions again | |
* | |
* - if we've shown it to him before, just say there's nothing more to | |
* say | |
* | |
* - otherwise, ask to show him the calculator | |
*/ | |
+++ AskTellTopic @calculator | |
"You describe the calculator to Jay and ask him if he knows | |
anything about it. | |
<.p><q>Sounds pretty generic,</q> he says. <q>Maybe if I | |
could take a look at it...</q> " | |
; | |
++++ AltTopic | |
"You ask Jay if there's anything else special about the | |
calculator. He just shrugs. " | |
isActive = gRevealed('calc-to-jay') | |
; | |
++++ AltTopic | |
"<q>How does that program you wrote work again?</q> you ask. | |
<.p>Jay points to the <q>+</q> key. <q>Just type the number, | |
then plus-plus-plus. Do that right after you get the quantum | |
machinery going, and the answer should come right up.</q> " | |
isActive = (calculator.isProgrammed) | |
; | |
/* | |
* Give Jay the calculator. There are several variations of this: | |
* | |
* - if we've already programmed the calculator, just re-explain how the | |
* programming works | |
* | |
* - if we've talked about hovarth numbers, AND we've shown him the | |
* article on macroscopic quantum computing, AND we've returned the | |
* squirrel, we're good to go | |
* | |
* - if we've talked about hovarth numbers AND we've shown him the QC | |
* article, but we haven't yet returned the squirrel, hold out for the | |
* squirrel | |
* | |
* - if we've only talked about hovarth numbers, explain that they're | |
* impossible to program (conventionally, anyway) | |
* | |
* - if we've only showed him the QRL article without talking about | |
* hovarth numbers, he's interested but has nothing to calculate | |
* | |
* - if we haven't done any of this, show off his prowess by playing a | |
* little tune | |
*/ | |
++ GiveShowTopic, StopEventList @calculator | |
[&response1, | |
'You offer Jay the calculator, but he doesn\'t take it. | |
<q>I don\'t want to show off,</q> he says. | |
<.convnode jay-programming> '] | |
response1() | |
{ | |
"<q>Have a look at this,</q> you say, handing Jay the calculator. | |
<.p>He turns it over a couple of times, turns it on, taps on | |
the keys for a bit. The calculator starts beeping a little | |
tune. One of the other students groans and playfully tries to | |
grab the calculator from Jay, but Jay dodges out of the way. | |
<.p><q>Don\'t encourage him,</q> the other guys says. <q>He | |
just can\'t help showing off his nerdly trick.</q> | |
<.p>Jay laughs. <q>Sorry,</q> he says. He turns off the | |
calculator and hands it back. | |
<.reveal calc-to-jay> | |
<.convnode jay-programming> "; | |
/* we mentioned that he turns off the calculator */ | |
calculator.makeOn(nil); | |
} | |
; | |
+++ AltTopic | |
"<q>Do you think you could program a Hovarth function on my | |
calculator?</q> you ask. | |
<.p><q>Dude, there's no point,</q> he says. <q>Unless you | |
have, oh, about ten to the fortieth years to wait for the | |
answer.</q> " | |
isActive = (gRevealed('hovarth-to-jay')) | |
; | |
+++ AltTopic | |
"You offer the calculator to Jay, but he waves you off. | |
<.p><q>I still haven't thought of the right problem,</q> he | |
says. <q>We need something interesting, something you can't | |
do on a normal computer.</q> " | |
isActive = (gRevealed('jay-ready-to-program')) | |
; | |
+++ AltTopic | |
topicResponse() | |
{ | |
"You offer Jay the calculator, but he doesn't take it. | |
<q>Tell you what,</q> he says. "; | |
if (gRevealed('squirrel-assigned')) | |
"<q>Remember Turbo Power Squirrel? Go bring him back | |
from the Guggenheim wind tunnel, and I'll help you out | |
with programming your calculator.</q> "; | |
else | |
"<q>We need someone to rescue Turbo Power Squirrel. We've | |
figured out that he's in the Guggenheim wind tunnel, but | |
no one's got any time to go get him. If you could go | |
rescue him and bring him back here, I'll help you out | |
with programming your calculator.</q> | |
<.reveal squirrel-assigned> "; | |
} | |
isActive = (gRevealed('jay-ready-to-program') | |
&& gRevealed('hovarth-to-jay')) | |
; | |
+++ AltTopic | |
topicResponse() | |
{ | |
/* | |
* if we've already talked about programming hovarth functions on | |
* the calculator, follow up on that thought; otherwise, | |
* introduce the idea here | |
*/ | |
if (gRevealed('jay-ready-to-program-hovarth')) | |
"You hand Jay the calculator. "; | |
else | |
"<q>Do you think you could program a quantum Hovarth | |
function?</q> you ask, handing Jay the calculator. | |
<.p><q>Dude, that's an excellent idea,</q> he says. "; | |
"He starts tapping furiously at the keypad with both | |
thumbs, almost too fast to see. <q>This is perfect,</q> | |
he says, still typing away. <q>The chipset in this model | |
has an unstable flip-flop on the key debouncer that we | |
can use as a true random source.</q> | |
<.p>He finally finishes, smiling broadly at his handiwork. | |
<q>There,</q> he says, handing the calculator back to you. | |
<q>It's the least I could do for the man who rescued | |
Turbo Power Squirrel.</q> He points to the <q>+</q> | |
key. <q>All you have to do is enter the number for | |
the Hovarth function, then press <q>plus</q> three times | |
in a row, really fast. So type the number, then type | |
plus-plus-plus. Make sure the quantum thingy is going | |
when you do. The result should come right up.</q> "; | |
/* the calculator is now programmed */ | |
calculator.isProgrammed = true; | |
/* leave it off, and clear the display */ | |
calculator.makeOn(nil); | |
} | |
isActive = (gRevealed('jay-ready-to-program') | |
&& gRevealed('hovarth-to-jay') | |
&& gRevealed('squirrel-returned')) | |
; | |
+++ AltTopic | |
"<q>How does that program you wrote work again?</q> you ask. | |
<.p>Jay points to the <q>+</q> key. <q>Just type the number, | |
then plus-plus-plus. Do that right after you get the quantum | |
machinery going, and the answer should come right up.</q> " | |
isActive = (calculator.isProgrammed) | |
; | |
++ GiveShowTopic @drdBook | |
topicResponse() | |
{ | |
/* just handle this with ASK JAY ABOUT HOVARTH NUMBERS */ | |
replaceAction(AskAbout, jay, hovarthTopic); | |
} | |
; | |
/* | |
* If we ask Jay about Hovarth numbers (or the DRD), mention that he | |
* knows about them, but leave it at that. If we're ready to program the | |
* calculator, take it as a suggestion. | |
*/ | |
++ AskTellShowTopic, StopEventList [hovarthTopic, drdTopic, drdBook] | |
['<q>Have you heard of Hovarth numbers?</q> you ask. | |
<.p><q>Yes, most definitely,</q> he says. <q>We had a phys | |
homework a few weeks ago that was all about Hovarth numbers. | |
Entirely heinous stuff.</q> | |
<.reveal hovarth-to-jay> | |
<.convnode jay-programming> ', | |
'<q>You said you know about Hovarth numbers?</q> you ask. | |
<.p><q>Yeah, from a phys class.</q> | |
<.convnode jay-programming> '] | |
; | |
+++ AltTopic | |
"<q>Have you heard of Hovarth numbers?</q> you ask. | |
<.p><q>Sure,</q> he says. <q>You know, that's a perfect problem | |
for a quantum computer. I could try programming it if you give | |
me a calculator.</q> | |
<.reveal hovarth-to-jay> | |
<.reveal jay-ready-to-program-hovarth> " | |
isActive = gRevealed('jay-ready-to-program') | |
; | |
+++ AltTopic | |
"<q>So you think you can program a Hovarth solver for me?</q> you ask. | |
<.p><q>I think so,</q> he says. <q>Give me a calculator and I'll | |
take a shot at it.</q> " | |
isActive = gRevealed('jay-ready-to-program-hovarth') | |
; | |
+++ AltTopic | |
"You ask Jay about the Hovarth calculation, and he explains | |
again how to use the calculator program he wrote for you: just | |
enter the number and press the <q>+</q> key three times in a row. " | |
isActive = (calculator.isProgrammed) | |
; | |
/* talk about the newspaper article */ | |
++ AskTellGiveShowTopic @newspaper | |
"<q>I saw the article about you in the <i>Tech</i>,</q> you say. | |
<q>Can you really program <i>any</i> calculator?</q> | |
<.p>Jay shrugs. <q>I don't know. I haven't found a | |
counter-example yet.</q> | |
<.convnode jay-programming> " | |
; | |
+++ AltTopic | |
"<q>That was a great article about you in the <i>Tech</i>,</q> | |
you say. | |
<.p>Jay smiles. <q>Thanks,</q> he says. " | |
isActive = gRevealed('jay-ready-to-program') | |
; | |
/* | |
* A conversation node for programming the calculator. We'll come here | |
* after we talk about the calculator, hovarth numbers, or the newspaper | |
* article - basically, anything that would lead us to talking about | |
* programming the calculator to do the hovarth calculation. We never | |
* come here if we've already talked about programming the calculator, | |
* since it's redundant with that previous |
View raw
(Sorry about that, but we can’t show files that are this big right now.)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment