forked from felixp7/jaiffa
-
Notifications
You must be signed in to change notification settings - Fork 3
/
catch-that-cat.js
400 lines (379 loc) · 15.4 KB
/
catch-that-cat.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
// Demo of Jiffo (formerly for Jaiffa).
// 2010-05-18 Felix Pleșoianu <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
s = story("Catch that cat");
s.author = "Felix Pleşoianu";
s.date = new Date(2010, 4, 18);
s.tagline = "An interactive tech demo";
s.blurb = "Ugh. Monday morning. You're still not recovered after the party"
+ " two days ago, and you have an SMS from your wife: 'Will stay"
+ " with mom for one more day. You take the cat to the vet.' Meh.";
s.about = "Catch that cat is the official tech demo for the Jaiffa library.";
s.credits = "Alpha testers: a hovering egg, a Sitka deer and a batty blue bat."
+ "\nBeta testers: Abbey Spracklin and Nightwrath.";
s.player.description =
"You look at your hairy arms, dirty T-shirt and worn-out jeans,"
+ " and wiggle your toes in the cheap Chinese sandals. Sigh.";
current(room(
"Living room",
"This is your one-room apartment in eastern Bucharest."));
tv = thing("TV set", "Unfortunately, it's broken. Again.");
thing("couch").$use = thing("couch").$sit_on =
"You rest on the couch for a few moments. Aah, comfy!";
thing("coffee table");
thing("remote control").is("portable");
bottle = thing("beer bottle", "It's empty and sad.").is("portable");
bottle.$drink = bottle.$use =
"You turn the bottle upside-down. It's really empty!";
mitzi = actor("Mitzi", "She's your black and white cat, young and energetic.")
.altname("cat", "kitty", "kitten");
mitzi.$catch = mitzi.$take = [
"Mitzi deftly escapes your grasp.",
"Mitzi runs between your feet.",
"You catch Mitzi, but she wiggles until you drop her."];
mitzi.$wrestle = mitzi.$squeeze = mitzi.$brush =
"You'd have to hold her first.";
mitzi.$eat = "Ew! Besides, your wife would kill you.";
mitzi.$lick = "She's the cat, not you. Don't mix things up.";
mitzi.$call = "Yeah, good luck with that.";
mitzi.$pet = mitzi.$fluff = mitzi.$touch =
"Mitzi purrs and pushes into your hand.";
mitzi.$poke = "Mitzi nips you, and shows off her claws for good measure.";
mitzi.heremsg = [
"Mitzi scampers about.",
"Mitzi pounces a dust bunny.",
"Mitzi chases her own tail.",
"Mitzi races across the floor.",
"Mitzi starts licking herself furiously."];
mitzi.act = function() {
if (this.location == room("Basement")) {
if (this.location.dark) {
this.heremsg =
"A pair of shining eyes stare at you"
+ " from the darkness.";
} else {
this.heremsg =
"Mitzi peers at you from behind"
+ " some water pipes.";
}
} else if (!this.location) {
var loc = thing("cat carrier").location;
if (loc == s.player || loc == s.player.location) {
say("Mitzi shuffles inside her carrier.");
}
} else if (s.player.location != this.location) {
this.move(s.player.location);
say("Mitzi scampers after you.");
if (this.location == room("Ground floor")) {
this.move(room("Basement"));
this.heremsg = undefined;
this.$pet = this.$take =
"You can't quite reach her right now.";
say("Mitzi squeezes through a hole in the door"
+ " and into the basement.");
} else if (thing("broom").location == s.player
|| thing("broom").location == this.location) {
say("Mitzi gives the broom a wide berth.");
}
}
}
exit("North to balcony").altname("n").to(room("Balcony"));
exit("Northwest to kitchen").altname("nw").to(room("Kitchen"));
exit("West to hallway").altname("w", "out").to(room("Hallway"));
tv.$turn_on = tv.$switch_on = tv.$use = tv.description;
tv.$turn_off = tv.$switch_off = "It's already very much off.";
tv.$watch = function() {
say("You look at the blank TV screen for a while.");
say("It's actually better than most channels.");
};
tv.$fix = tv.$repair =
"You tried that with the old TV. That's why you have a new one."
+ "\nPlus, you'd need the right tools.";
thing("remote control").$use =
"Click! Click! The batteries must be dry. No wait, TV's broken.";
bottle.$fill = function(actor) {
if (actor.location == room("Bathroom")) {
say("The faucet gives off a gargling sound, but no water.");
} else if (actor.location == room("Kitchen")) {
say("The faucet gives off a gargling sound, but no water.");
} else {
say("There's no source of water here.");
say("Or beer, for that matter.");
}
};
bottle.$break = "On second thought, you need to return it intact.";
bottle.$open = bottle.$close = "Too late, alas!";
current(room("Balcony")
.desc("From the 11th floor you have a wonderful urban vista."
+ " Endless rows of apartment buildings everywhere you look."));
broom = thing("broom", "An ordinary broom, and Mitzi's nemesis.")
.is("portable");
exit("South to living room").altname("s", "out").to(room("Living room"));
room("Balcony").$jump_off = room("Balcony").$jump_from =
"Nooooo! Don't jump! Life is beautiful! And you have a game to win!";
broom.$use = "Aww! But you just swept the floors yesterday.";
broom.$wave = broom.$shake = broom.$show = function (actor) {
if (mitzi.location != actor.location) {
say("Who are you trying to impress?");
} else if (mitzi.location.dark) {
say("Mitzi ignores the broom, believing you can't see her.");
} else if (thing("cat carrier").location == actor) {
mitzi.move(null);
say('Mitzi waits until you put down her carrier, and climbs in.');
} else if (thing("cat carrier").location == actor.location) {
mitzi.move(null);
say("Mitzi climbs into the cat carrier timidly.");
} else {
say("Mitzi stops for a moment and looks for her carrier.");
}
}
k = current(room("Kitchen",
"Stacks of dirty dishes raise from every possible surface."));
crate = thing("beer crate",
"A 12-slot beer crate with 11 empty bottles in it.")
.is("portable");
crate.after$examine = "The last slot is broken and can't hold a bottle.";
crate.$empty = "Better not, you'll need to return it and the bottles.";
exit("Out to living room").altname("se", "o").to(room("Living room"));
k.$clean = "The kitchen is such a mess, you wouldn't know where to start.";
current(room("Hallway"));
exit("North to bathroom").altname("n").to(room("Bathroom"));
exit("East to living room").altname("e").to(room("Living room"));
exit("West to the outside").altname("w")
.to(room("11th floor"))
.via(door("outside door"));
door("outside door").is("locked").key = thing("keychain");
key = thing("keychain").is("portable").move(null); // Remove from game for now.
key.altname("key", "keys", "chain").$use = "Try unlocking a door.";
thing("cellphone").altname("mobile", "phone").move(null)
.$use = "You try to call the vet and reschedule the appointment,"
+ " but no dice. They're booked solid.";
s.$call_vet = thing("cellphone").$use;
s.$call_wife = "Sure, so she can chew you out.";
current(room("Bathroom",
"A dim lightbulb, peeling paint, water on the floor. Home sweet home."));
laundry = thing("Dirty laundry").altname("clothes").is("portable")
.desc("These are the clothes you've been wearing all weekend.");
laundry.after$examine = laundry.after$take = laundry.after$drop =
"Something rattles in the pile of laundry.";
laundry.$wear = "Ew! They smell!";
laundry.$burn = "You fumble for a lighter. Oh wait, you don't smoke.";
laundry.$search = laundry.$shake = laundry.$poke = laundry.$open =
function (actor) {
if (!thing("keychain").location) {
thing("keychain").move(actor);
thing("cellphone").move(actor);
say("Aha! There's your keys and your cellphone. Pocketed.");
laundry.after$examine = laundry.after$take =
laundry.after$drop = undefined;
} else {
say("There's nothing else in there.");
}
};
exit("South to hallway").altname("s", "out", "o").to(room("Hallway"));
current(room("11th floor"));
exit("East to your apartment").altname("e")
.to(room("Hallway"))
.via(door("outside door"));
exit("Down the stairs").altname("d").to(room("Ground floor"))
.travelmsg = "You trot down the 11 flights of stairs. Sigh.";
lift = thing("elevator");
lift.$call = lift.$use = function (actor) {
say("You push the elevator's call button repeatedly.");
say("After a while, nothing seems to happen. How typical.");
}
lift.react_before = function (actor, action, target) {
if (action != "$go")
return;
else if (target == exit("Down the stairs"))
this.move(room("Ground floor"));
else if (target == exit("Up the stairs"))
this.move(room("11th floor"));
}
current(room("Ground floor"));
light = thing("light switch", "It's hanging by a wire.");
exit("Up the stairs").altname("u").to(room("11th floor"))
.travelmsg = "You trot up the 11 flights of stairs. Sigh.";
exit("Down to basement").altname("d")
.to(room("Basement"))
.via(door("basement door"));
exit("Out of the building").altname("outside", "o", "exit", "leave")
.to(room("Outside your apartment building"));
door("basement door").is("locked").key = thing("keychain");
light.$fix = light.$repair = function (actor) {
if (this.working) {
say("Already fixed.");
} else if (actor.has(thing("toolkit"))) {
this.working = true;
this.desc("A freshly repaired,"
+ " but otherwise ordinary light switch.");
say("You set to work, grumbling and mumbling.");
say("There, fixed. Not for the first time, either.");
} else {
say("You need tools for that.");
}
}
light.$flip = light.$use = function (actor) {
if (this.working) {
room("Basement").dark = !room("Basement").dark;
say("You flip the switch.");
} else {
say("The light switch is in no condition to be flipped.");
}
}
current(room("Basement")).is("dark");
room("Basement").desc(
"A vast room, silent and damp, subdivided by flimsy plywood walls."
+ " Ridiculously, big padlocks adorn stall doors a kid could break.");
exit("Up to ground floor").altname("u", "out", "o")
.to(room("Ground floor"))
.via(door("basement door"));
current(room("Outside your apartment building").altname("street").desc(
"A narrow backstreet meanders among 12-story apartment buildings."));
thing("trees").altname("tree");
thing("grass");
thing("lamp posts").altname("post", "streetlight");
car = thing("your car", "A second-hand Dacia 1310, almost an antique now.")
.altname("tire", "trunk", "window");
car.after$examine = "It's been sitting in the sun, so now it's hot and..."
+ " Oh no, you have a flat tire.";
car.$fix = car.$change = car.$repair = function (actor) {
if (this.working) {
say("You've already changed the tire.");
} else if (actor.has(toolkit)) {
this.working = true;
this.after$examine =
"It's been sitting in the sun, so now it's hot.";
say("You set to work changing the tire, sweating heavily.");
say("It seems to take much longer than it actually does.");
} else {
"You need tools for that.";
}
}
car.$drive = car.$get_in = car.$enter = car.$use = function (actor) {
if (!this.working) {
say("Your car isn't going anywhere with a flat tire.");
} else if (mitzi.location) {
say("No point in going to the vet without Mitzi.");
} else {
say("You load Mitzi's carrier in the car, and drive off to see"
+ " the vet. About halfway there, you run out of gas.");
s.the_end("You have won!");
}
}
car.$open = car.$close = car.$lock = car.$unlock = function (actor) {
if (actor.has(key)) {
say("You play with the remote and double-check the doors."
+ " Everything seems fine.");
} else {
say("You don't have the keys.");
}
}
car.$search = function (actor) {
if (thing("cat carrier").location == null) {
thing("cat carrier").move(actor);
say("You search the trunk and come up with"
+ " Mitzi's cat carrier. Ah, there it was!");
} else {
say("You look through the trunk, but find nothing useful.");
}
}
exit("Building entrance").altname("enter", "in")
.to(room("Ground floor"));
exit("Corner store").altname("south", "s")
.to(room("Corner store")).travelmsg = "You cross the street.";
thing("cat carrier").is("portable").move(null);
thing("cat carrier").after$examine = function(actor) {
if (!mitzi.location) say("Mitzi peers back at you from inside.");
}
current(room("Corner store").desc(
"Actually a converted ground floor apartment, but it does the job."));
thing("shelves");
thing("counter");
thing("cash register");
gro = thing("groceries");
gro.$take = gro.$buy = "You don't need anything right now.";
neighbor = actor("Mr. Petrescu",
"Your portly, balding, middle-aged neighbor and store owner.")
.altname("neighbor", "man");
neighbor.$take =
"Mr. Petrescu would not appreciate that. Besides, he's heavy.";
neighbor.$talk_to = "You chat amiably with Mr. Petrescu about nothing.";
neighbor.heremsg = [
"Mr. Petrescu idly rearranges some bottles on the shelves.",
"Mr. Petrescu updates a few price labels.",
"Mr. Petrescu taps his foot lightly and hums a little song.",
"Mr. Petrescu fumbles under the counter.",
"Mr. Petrescu fiddles with the cash register."];
neighbor.react_after = function (actor, action, target) {
if (action == "$examine" && target == thing("toolkit")) {
say('Mr. Petrescu beams. "Like my new toolkit, neighbor?"');
} else if (action == "$go" && target == exit("Corner shop")) {
say('Mr. Petrescu says,'
+ ' "Hello, neighbor. Can I get you anything?"');
}
}
toolkit = thing("toolkit",
"A big portable toolkit, of the kind they sell for car repairs.")
.altname("tools", "kit").is("portable");
toolkit.$borrow = toolkit.$get; // 'borrow' redirects properly no matter what.
toolkit.$request = toolkit.$get;
toolkit.$buy = '"Sorry, that\'s not for sale," says Mr. Petrescu.';
toolkit.$use = "You probably want to fix something.";
exit("Out of the shop").altname("outside", "o", "exit", "leave")
.to(room("Outside your apartment building"));
bottle.after$drop = function (actor) {
if (this.location == room("Corner shop")) {
this.move(null);
say('You put the bottle on the counter.'
+ ' "I believe you wanted this back?"');
say('Mr. Petrescu takes it in the back.'
+ '"Yes, indeed. Thank you, neighbor."');
}
}
thing("beer crate").after$drop = function (actor) {
if (this.location == room("Corner shop")) {
this.move(null);
say('You carefully set the crate down by the counter.'
+ ' "I believe you wanted this back?"');
say('Mr. Petrescu takes it in the back.'
+ '"Yes, indeed. Thank you, neighbor."');
}
}
toolkit.before$take = function (actor) {
if (this.borrowed) return false; // Don't bother playing the dialogue
// once the toolkit has been moved.
if (!this.asked_about) {
this.asked_about = true;
say('"Say, neighbor," you ask, "can I borrow your tools?"');
}
if (bottle.location == null && crate.location == null) {
this.borrowed = true;
say('Mr. Petrescu gestures towards the toolkit.'
+ '"Help yourself. Just take care of it, will you?"');
return false;
} else {
say('"Not so fast," says Mr. Petrescu,'
+ ' "Where is my beer crate?'
+ ' I need to return all the empty bottles."');
return true;
}
}
s.$drive = "What do you want to drive?";