Skip to content

Instantly share code, notes, and snippets.

@toriningen
Created October 26, 2016 11:38
Show Gist options
  • Save toriningen/86667f6e2517b1afead86408b95fe360 to your computer and use it in GitHub Desktop.
Save toriningen/86667f6e2517b1afead86408b95fe360 to your computer and use it in GitHub Desktop.
Return to Ditch Day (TADS source)
/*
* 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 &copy;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 &copy;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&gt;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'
;
/*
* 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)
;
/*
* 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 &amp; Progress</i> magazine
number XLVI-3, and a chapter in a textbook, <i>Introductory
Quantum Physics</i> by Bl&ouml;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)
;
/*
* 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&mdash;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&amp;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&amp;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&amp;G, is
the department that maintains the campus's infrastructure. This
modest single-story structure is where the B&amp;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 &amp; 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>&nbsp;<<cardDept
>>&nbsp;&nbsp;&nbsp;</u></font>
\nJob: <font face='tads-typewriter'><u>&nbsp;<<cardJob
>>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</u></font>
\nLocation: <font face='tads-typewriter'><u>&nbsp;<<cardLoc
>>&nbsp;&nbsp;&nbsp;</u></font>
\nDesc.: <font face='tads-typewriter'><u>&nbsp;<<cardDesc
>>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</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&auml;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&auml;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&mdash;Page, Lloyd, and Ruddock&mdash;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&mdash;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. "
;
/*
* 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> "
;
/*
* 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&acirc;ch&eacute; 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&cent; Per Game - Quarters Only
\n\tFor Amusement Only
\n\t<i>Manufactured by ARITA GAMES</i>
\n\t<font size=-1>&copy;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&uuml;gel,
Super-Genius</i>.</q> "
readDesc = "The notebook is filled with the same sort of flowery
cursive as the cover.
<.p><i>
&ldquo;The Laboratory Notebook and Philosophical Manifesto
of Dr.\ Klaus W.D. von Gefl&uuml;gel, Super-Genius
<.p>&ldquo;I, Klaus von Gefl&uuml;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 &lsquo;fail.&rsquo; 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>&ldquo;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.&rdquo;
</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&uuml;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>&nbsp;FORTRAN&nbsp;</s>
\n<tab to=lang><s>&nbsp;Pascal&nbsp;</s>
\n<tab to=lang><s>&nbsp;C&nbsp;</s>
\n<tab to=lang><s>&nbsp;C++&nbsp;</s>
\n<tab to=lang><s>&nbsp;Java&nbsp;</s>
\n<tab to=lang>&nbsp;QUBITS&nbsp;
</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&ocirc;pital and generated a small but pathological
function which left surds all over the place, and drove
Polly to deviation...</q> "
;
/*
* 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 */
# 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
/*
* 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. "; }
}
;
/*
* 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&mdash;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> "
;
/*
* 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
;
/*
* 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&eacute;sum&eacute;,</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&eacute;sum&eacute;,</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&eacute;sum&eacute;?</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&eacute;sum&eacute; 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&mdash;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&mdash;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.
/*
* 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.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

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