Update codebase to use 8sync-fibers
[mudsync.git] / worlds / bricabrac.scm
1 ;;; Mudsync --- Live hackable MUD
2 ;;; Copyright © 2016, 2017 Christopher Allan Webber <cwebber@dustycloud.org>
3 ;;;
4 ;;; This file is part of Mudsync.
5 ;;;
6 ;;; Mudsync is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or
9 ;;; (at your option) any later version.
10 ;;;
11 ;;; Mudsync is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 ;;; General Public License for more details.
15 ;;;
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with Mudsync.  If not, see <http://www.gnu.org/licenses/>.
18
19 ;;; Hotel Bricabrac
20
21 (use-modules (mudsync)
22              (mudsync container)
23              (8sync)
24              (8sync daydream)
25              (oop goops)
26              (ice-9 control)
27              (ice-9 format)
28              (ice-9 match)
29              (rx irregex))
30
31
32 \f
33 ;;; Utilities, useful or otherwise
34 ;;; ==============================
35
36 (set! *random-state* (random-state-from-platform))
37
38 (define (random-choice lst)
39   (list-ref lst (random (length lst))))
40
41 ;; list of lists, lol.
42 (define-syntax-rule (lol (list-contents ...) ...)
43   (list (list list-contents ...) ...))
44
45 \f
46 ;;; Some simple object types.
47 ;;; =========================
48
49 (define-class <readable> (<gameobj>)
50   (read-text #:init-value "All it says is: \"Blah blah blah.\""
51              #:init-keyword #:read-text)
52   (commands
53    #:allocation #:each-subclass
54    #:init-thunk (build-commands
55                  ("read" ((direct-command cmd-read)))))
56   (actions #:allocation #:each-subclass
57            #:init-thunk (build-actions
58                          (cmd-read readable-cmd-read))))
59
60 (define (readable-cmd-read actor message . _)
61   (<- (message-from message) 'tell
62       #:text (slot-ref actor 'read-text)))
63
64
65 ;; This one is just where reading is the same thing as looking
66 ;; at the description
67 (define-class <readable-desc> (<gameobj>)
68   (commands
69    #:allocation #:each-subclass
70    #:init-thunk (build-commands
71                  ("read" ((direct-command cmd-look-at))))))
72
73 ;; This one allows you to take from items that are proxied by it
74 (define-actor <proxy-items> (<gameobj>)
75   ((cmd-take-from take-from-proxy))
76   (proxy-items #:init-keyword #:proxy-items))
77
78 (define* (take-from-proxy gameobj message
79                           #:key direct-obj indir-obj preposition
80                           (player (message-from message)))
81   (call/ec
82    (lambda (escape)
83      (for-each
84       (lambda (obj-sym)
85         (define obj-id (dyn-ref gameobj obj-sym))
86         (define goes-by (<-wait obj-id 'goes-by))
87         (when (ci-member direct-obj goes-by)
88           (<- obj-id 'cmd-take #:direct-obj direct-obj #:player player)
89           (escape #f)))
90       (slot-ref gameobj 'proxy-items))
91
92      (<- player 'tell
93         #:text `("You don't see any such " ,direct-obj " to take "
94                  ,preposition " " ,(slot-ref gameobj 'name) ".")))))
95
96
97 \f
98 ;;; Lobby
99 ;;; -----
100
101 (define (npc-chat-randomly actor message . _)
102   (define catchphrase
103     (random-choice (slot-ref actor 'catchphrases)))
104   (define text-to-send
105     ((slot-ref actor 'chat-format) actor catchphrase))
106   (<- (message-from message) 'tell
107       #:text text-to-send))
108
109 (define hotel-owner-grumps
110   '("Eight sinks!  Eight sinks!  And I couldn't unwind them..."
111     "Don't mind the mess.  I built this place on a dare, you
112 know?"
113     "(*tearfully*) Here, take this parenthesis.  May it serve
114 you well."
115     "I gotta get back to the goblin farm soon..."
116     "Oh, but I was going to make a mansion... a great,
117 beautiful mansion!  Full of ghosts!  Now all I have is this cruddy
118 mo... hotel.  Oh... If only I had more time!"
119     "I told them to paint more of the walls purple.
120 Why didn't they listen?"
121     "Listen to that overhead muzak.  Whoever made that doesn't
122 know how to compose very well!  Have you heard of the bands 'fmt'
123 or 'skribe'?  Now *that's* composition!"))
124
125 (define-class <chatty-npc> (<gameobj>)
126   (catchphrases #:init-value '("Blarga blarga blarga!")
127                 #:init-keyword #:catchphrases)
128   (chat-format #:init-value (lambda (npc catchphrase)
129                               `(,(slot-ref npc 'name) " says: \""
130                                 ,catchphrase "\""))
131                #:init-keyword #:chat-format)
132   (commands
133    #:allocation #:each-subclass
134    #:init-thunk (build-commands
135                  (("chat" "talk") ((direct-command cmd-chat)))))
136   (actions #:allocation #:each-subclass
137            #:init-thunk
138            (build-actions
139             (cmd-chat npc-chat-randomly))))
140
141 (define-class <sign-in-form> (<gameobj>)
142   (commands
143    #:allocation #:each-subclass
144    #:init-thunk (build-commands
145                  ("sign" ((prep-direct-command cmd-sign-form '("as"))))))
146
147   (actions #:allocation #:each-subclass
148            #:init-thunk (build-actions
149                          (cmd-sign-form sign-cmd-sign-in))))
150
151
152 (define name-sre
153   (sre->irregex '(: alpha (** 1 14 (or alphanum "-" "_")))))
154
155 (define forbidden-words
156   (append article preposition
157           '("and" "or" "but" "admin")))
158
159 (define (valid-name? name)
160   (and (irregex-match name-sre name)
161        (not (member name forbidden-words))))
162
163 (define* (sign-cmd-sign-in actor message
164                            #:key direct-obj indir-obj preposition)
165   (define old-name (<-wait (message-from message) 'get-name))
166   (define name indir-obj)
167   (if (valid-name? indir-obj)
168       (begin
169         (<-wait (message-from message) 'set-name! name)
170         (<- (slot-ref actor 'loc) 'tell-room
171             #:text (format #f "~a signs the form!\n~a is now known as ~a\n"
172                            old-name old-name name)))
173       (<- (message-from message) 'tell
174           #:text "Sorry, that's not a valid name.
175 Alphanumerics, _ and - only, 2-15 characters, starts with an alphabetic
176 character.\n")))
177
178
179 (define-class <summoning-bell> (<gameobj>)
180   (summons #:init-keyword #:summons)
181
182   (commands
183    #:allocation #:each-subclass
184    #:init-thunk (build-commands
185                  ("ring" ((direct-command cmd-ring)))))
186   (actions #:allocation #:each-subclass
187            #:init-thunk (build-actions
188                          (cmd-ring summoning-bell-cmd-ring))))
189
190 (define* (summoning-bell-cmd-ring bell message . _)
191   ;; Call back to actor who invoked this message handler
192   ;; and find out their name.  We'll call *their* get-name message
193   ;; handler... meanwhile, this procedure suspends until we get
194   ;; their response.
195   (define who-rang (<-wait (message-from message) 'get-name))
196
197   ;; Now we'll invoke the "tell" message handler on the player
198   ;; who rang us, displaying this text on their screen.
199   ;; This one just uses <- instead of <-wait, since we don't
200   ;; care when it's delivered; we're not following up on it.
201   (<- (message-from message) 'tell
202       #:text "*ring ring!*  You ring the bell!\n")
203   ;; We also want everyone else in the room to "hear" the bell,
204   ;; but they get a different message since they aren't the ones
205   ;; ringing it.  Notice here's where we make use of the invoker's
206   ;; name as extracted and assigned to the who-rang variable.
207   ;; Notice how we send this message to our "location", which
208   ;; forwards it to the rest of the occupants in the room.
209   (<- (gameobj-loc bell) 'tell-room
210       #:text
211       (format #f "*ring ring!*  ~a rings the bell!\n"
212               who-rang)
213       #:exclude (message-from message))
214   ;; Now we perform the primary task of the bell, which is to summon
215   ;; the "clerk" character to the room.  (This is configurable,
216   ;; so we dynamically look up their address.)
217   (<- (dyn-ref bell (slot-ref bell 'summons)) 'be-summoned
218       #:who-summoned (message-from message)))
219
220
221 (define prefect-quotes
222   '("I'm a frood who really knows where my towel is!"
223     "On no account allow a Vogon to read poetry at you."
224     "Time is an illusion, lunchtime doubly so!"
225     "How can you have money if none of you produces anything?"
226     "On no account allow Arthur to request tea on this ship."))
227
228 (define-class <cabinet-item> (<gameobj>)
229   (take-me? #:init-value
230             (lambda _
231               (values #f #:why-not
232                       `("Hm, well... the cabinet is locked and the properitor "
233                         "is right over there.")))))
234
235 (define lobby
236   (lol
237    ('lobby
238     <room> #f
239     #:name "Hotel Lobby"
240     #:desc
241     '((p "You're in some sort of hotel lobby.  You see a large sign hanging "
242          "over the desk that says \"Hotel Bricabrac\".  On the desk is a bell "
243          "that says \"'ring bell' for service\".  Terrible music plays from a speaker "
244          "somewhere overhead.  "
245          "The room is lined with various curio cabinets, filled with all sorts "
246          "of kitschy junk.  It looks like whoever decorated this place had great "
247          "ambitions, but actually assembled it all in a hurry and used whatever "
248          "kind of objects they found lying around.")
249       (p "There's a door to the north leading to some kind of hallway."))
250     #:exits
251     (list (make <exit>
252             #:name "north"
253             #:to 'grand-hallway)))
254    ;; NPC: hotel owner
255    ('lobby:hotel-owner
256     <chatty-npc> 'lobby
257     #:name "a frumpy fellow"
258     #:desc
259     '((p "  Whoever this is, they looks totally exhausted.  They're
260 collapsed into the only comfortable looking chair in the room and you
261 don't get the sense that they're likely to move any time soon.
262   You notice they're wearing a sticker badly adhesed to their clothing
263 which says \"Hotel Proprietor\", but they look so disorganized that you
264 think that can't possibly be true... can it?
265   Despite their exhaustion, you sense they'd be happy to chat with you,
266 though the conversation may be a bit one sided."))
267     #:goes-by '("frumpy fellow" "fellow"
268                 "Chris Webber"  ; heh, did you rtfc?  or was it so obvious?
269                 "hotel proprietor" "proprietor")
270     #:catchphrases hotel-owner-grumps)
271    ;; Object: Sign
272    ('lobby:sign
273     <readable> 'lobby
274     #:name "the Hotel Bricabrac sign"
275     #:desc "  It strikes you that there's something funny going on with this sign.
276 Sure enough, if you look at it hard enough, you can tell that someone
277 hastily painted over an existing sign and changed the \"M\" to an \"H\".
278 Classy!"
279     #:read-text "  All it says is \"Hotel Bricabrac\" in smudged, hasty text."
280     #:goes-by '("sign"
281                 "bricabrac sign"
282                 "hotel sign"
283                 "hotel bricabrac sign"
284                 "lobby sign"))
285
286    ('lobby:bell
287     <summoning-bell> 'lobby
288     #:name "a shiny brass bell"
289     #:goes-by '("shiny brass bell" "shiny bell" "brass bell" "bell")
290     #:desc "  A shiny brass bell.  Inscribed on its wooden base is the text
291 \"ring me for service\".  You probably could \"ring the bell\" if you 
292 wanted to."
293     #:summons 'break-room:desk-clerk)
294
295    ('lobby:sign-in-form
296     <sign-in-form> 'lobby
297     #:name "sign-in form"
298     #:goes-by '("sign-in form" "form" "signin form")
299     #:desc '("It looks like you could sign this form and set your name like so: "
300              (i "sign form as <my-name-here>")))
301
302    ;; Object: curio cabinets
303    ;; TODO: respond to attempts to open the curio cabinet
304    ('lobby:cabinet
305     <proxy-items> 'lobby
306     #:proxy-items '(lobby:porcelain-doll
307                     lobby:1950s-robots
308                     lobby:tea-set lobby:mustard-pot
309                     lobby:head-of-elvis lobby:circuitboard-of-evlis
310                     lobby:teletype-scroll lobby:orange-cat-phone)
311     #:name "a curio cabinet"
312     #:goes-by '("curio cabinet" "cabinet" "bricabrac cabinet"
313                 "cabinet of curiosities")
314     #:desc (lambda _
315              (format #f "  The curio cabinet is full of all sorts of oddities!
316 Something catches your eye!
317 Ooh, ~a!" (random-choice
318            '("a creepy porcelain doll"
319              "assorted 1950s robots"
320              "an exquisite tea set"
321              "an antique mustard pot"
322              "the pickled head of Elvis"
323              "the pickled circuitboard of EVLIS"
324              "a scroll of teletype paper holding the software Four Freedoms"
325              "a telephone shaped like an orange cartoon cat")))))
326
327    ('lobby:porcelain-doll
328     <cabinet-item> 'lobby
329     #:invisible? #t
330     #:name "a creepy porcelain doll"
331     #:desc "It strikes you that while the doll is technically well crafted,
332 it's also the stuff of nightmares."
333     #:goes-by '("porcelain doll" "doll"))
334    ('lobby:1950s-robots
335     <cabinet-item> 'lobby
336     #:invisible? #t
337     #:name "a set of 1950s robots"
338     #:desc "There's a whole set of these 1950s style robots.
339 They seem to be stamped out of tin, and have various decorations of levers
340 and buttons and springs.  Some of them have wind-up knobs on them."
341     #:goes-by '("robot" "robots" "1950s robot" "1950s robots"))
342    ('lobby:tea-set
343     <cabinet-item> 'lobby
344     #:invisible? #t
345     #:name "a tea set"
346     #:desc "A complete tea set.  Some of the cups are chipped.
347 You can imagine yourself joining a tea party using this set, around a
348 nice table with some doilies, drinking some Earl Grey tea, hot.  Mmmm."
349     #:goes-by '("tea set" "tea"))
350    ('lobby:cups
351     <cabinet-item> 'lobby
352     #:invisible? #t
353     #:name "cups from the tea set"
354     #:desc "They're chipped."
355     #:goes-by '("cups"))
356    ('lobby:mustard-pot
357     <cabinet-item> 'lobby
358     #:invisible? #t
359     #:name "a mustard pot"
360     #:desc '((p "It's a mustard pot.  I mean, it's kind of cool, it has a
361 nice design, and it's an antique, but you can't imagine putting something
362 like this in a museum.")
363              (p "Ha... imagine that... a mustard museum."))
364     #:goes-by '("mustard pot" "antique mustard pot" "mustard"))
365    ('lobby:head-of-elvis
366     <cabinet-item> 'lobby
367     #:invisible? #t
368     #:name "the pickled head of Elvis"
369     #:desc '((p "It's a jar full of some briny-looking liquid and...
370 a free floating head.  The head looks an awful lot like Elvis, and
371 definitely not the younger Elvis.  The hair even somehow maintains
372 that signature swoop while suspended in liquid.  But of course it's
373 not Elvis.")
374              (p "Oh, wait, it has a label at the bottom which says:
375 \"This is really the head of Elvis\".  Well... maybe don't believe
376 everything you read."))
377     #:goes-by '("pickled head of elvis" "pickled head of Elvis"
378                 "elvis" "Elvis" "head" "pickled head"))
379    ('lobby:circuitboard-of-evlis
380     <cabinet-item> 'lobby
381     #:invisible? #t
382     #:name "the pickled circuitboard of Evlis"
383     #:desc '((p "It's a circuitboard from a Lisp Machine called EVLIS.
384 This is quite the find, and you bet just about anyone interested in
385 preserving computer history would love to get their hands on this.")
386              (p "Unfortunately, whatever moron did acquire this has
387 no idea what it means to preserve computers, so here it is floating
388 in some kind of briny liquid.  It appears to be heavily corroded.
389 Too bad..."))
390     #:goes-by '("pickled circuitboard of evlis" "pickled circuitboard of Evlis"
391                 "pickled circuitboard of EVLIS"
392                 "evlis" "Evlis" "EVLIS" "circuitboard" "pickled circuitboard"))
393    ('lobby:teletype-scroll
394     <cabinet-item> 'lobby
395     #:invisible? #t
396     #:name "a scroll of teletype"
397     #:desc '((p "This is a scroll of teletype paper.  It's a bit old
398 and yellowed but the type is very legible.  It says:")
399              (br)
400              (i
401               (p (strong "== The four essential freedoms =="))
402               (p "A program is free software if the program's users have
403 the four essential freedoms: ")
404               (ul (li "The freedom to run the program as you wish, for any purpose (freedom 0).")
405                   (li "The freedom to study how the program works, and change it so it does your computing as you wish (freedom 1). Access to the source code is a precondition for this.")
406                   (li "The freedom to redistribute copies so you can help your neighbor (freedom 2).")
407                   (li "The freedom to distribute copies of your modified versions to others (freedom 3). By doing this you can give the whole community a chance to benefit from your changes. Access to the source code is a precondition for this.")))
408              (p "You get this feeling that ambiguities in the
409 English language surrounding the word 'free' have lead to a lot of terminology debates."))
410     #:goes-by '("scroll of teletype" "scroll of teletype paper" "teletype scroll"
411                 "teletype paper" "scroll" "four freedoms"
412                 "scroll of teletype paper holding the software Four Freedoms"
413                 "scroll of teletype paper holding the software four freedoms"))
414    ('lobby:orange-cat-phone
415     <cabinet-item> 'lobby
416     #:invisible? #t
417     #:name "a telephone shaped like an orange cartoon cat"
418     #:desc "It's made out of a cheap plastic, and it's very orange.
419 It resembles a striped tabby, and it's eyes hold the emotion of
420 a being both sleepy and smarmy.
421 You suspect that someone, somewhere made a ton of cash on items holding
422 this general shape in the 1990s."
423     #:goes-by '("orange cartoon cat phone" "orange cartoon cat telephone"
424                 "orange cat phone" "orange cat telephone"
425                 "cartoon cat phone" "cartoon cat"
426                 "cat phone" "cat telephone" "phone" "telephone"))))
427
428
429 \f
430 ;;; Grand hallway
431 ;;; -------------
432
433 (define-actor <disc-shield> (<gameobj>)
434   ((cmd-take disc-shield-take)))
435
436 (define* (disc-shield-take gameobj message
437                            #:key direct-obj
438                            (player (message-from message)))
439   (create-gameobj <glowing-disc> (gameobj-gm gameobj)
440                   player)  ;; set loc to player to put in player's inventory
441   (<- player 'tell
442       #:text '((p "As you attempt to pull the shield / disk platter
443 from the statue a shining outline appears around it... and a
444 completely separate, glowing copy of the disc materializes into your
445 hands!")))
446   (<- (gameobj-loc gameobj) 'tell-room
447       #:text `(,(<-wait player 'get-name)
448                " pulls on the shield of the statue, and a glowing "
449                "copy of it materializes into their hands!")
450       #:exclude player)
451   (<- (gameobj-loc gameobj) 'tell-room
452       #:text
453       '(p "You hear a voice whisper: "
454           (i "\"Share the software... and you'll be free...\""))))
455
456 ;;; This is the disc that gets put in the player's inventory
457 (define-actor <glowing-disc> (<gameobj>)
458   ((cmd-drop glowing-disc-drop-cmd))
459   (initial-props
460    #:allocation #:each-subclass
461    #:init-thunk (build-props
462                  '((hd-platter? . #t))))
463   (name #:allocation #:each-subclass
464         #:init-value "a glowing disc")
465   (desc #:allocation #:each-subclass
466         #:init-value "A brightly glowing disc.  It's shaped like a hard
467 drive platter, not unlike the one from the statue it came from.  It's
468 labeled \"RL02.5\".")
469   (goes-by #:init-value '("glowing disc" "glowing platter"
470                           "glowing disc platter" "glowing disk platter"
471                           "platter" "disc" "disk" "glowing shield")))
472
473 (define* (glowing-disc-drop-cmd gameobj message
474                    #:key direct-obj
475                    (player (message-from message)))
476   (<- player 'tell
477       #:text "You drop the glowing disc, and it shatters into a million pieces!")
478   (<- (<-wait player 'get-loc) 'tell-room
479       #:text `(,(<-wait player 'get-name)
480                " drops a glowing disc, and it shatters into a million pieces!")
481       #:exclude player)
482   (gameobj-self-destruct gameobj))
483
484 \f
485 ;;; Grand hallway
486
487 (define lobby-map-text
488   "\
489                         |  :       :  |
490   .----------.----------.  :   &   :  .----------.----------.
491   | computer |          |& :YOU ARE: &|  smoking | *UNDER*  |
492   | room     + playroom +  : HERE  :  +  parlor  | *CONS-   |
493   |    >     |          |& :       : &|          | TRUCTION*|
494   '----------'----------'-++-------++-'-------+--'----------'
495                        |    '-----'    |     |   |
496                        :     LOBBY     :     '---'
497                         '.           .'
498                           '---------'")
499
500 (define grand-hallway
501   (lol
502    ('grand-hallway
503     <room> #f
504     #:name "Grand Hallway"
505     #:desc '((p "  A majestic red carpet runs down the center of the room.
506 Busts of serious looking people line the walls, but there's no
507 clear indication that they have any logical relation to this place.")
508              (p "In the center is a large statue of a woman in a warrior's
509 pose, but something is strange about her weapon and shield.  You wonder what
510 that's all about?")
511              (p "To the south is the lobby.  A door to the east is labeled \"smoking
512 room\", while a door to the west is labeled \"playroom\"."))
513     #:exits
514     (list (make <exit>
515             #:name "south"
516             #:to 'lobby)
517           (make <exit>
518             #:name "west"
519             #:to 'playroom)
520           (make <exit>
521             #:name "east"
522             #:to 'smoking-parlor)))
523    ('grand-hallway:map
524     <readable> 'grand-hallway
525     #:name "the hotel map"
526     #:desc '("This appears to be a map of the hotel. "
527              "Like the hotel itself, it seems to be "
528              "incomplete."
529              "You could read it if you want to.")
530     #:read-text `(pre ,lobby-map-text)
531     #:goes-by '("map" "hotel map"))
532    ('grand-hallway:carpet
533     <gameobj> 'grand-hallway
534     #:name "the Grand Hallway carpet"
535     #:desc "It's very red, except in the places where it's very worn."
536     #:invisible? #t
537     #:goes-by '("red carpet" "carpet"))
538    ('grand-hallway:busts
539     <gameobj> 'grand-hallway
540     #:name "the busts of serious people"
541     #:desc "There are about 6 of them in total.  They look distinguished
542 but there's no indication of who they are."
543     #:invisible? #t
544     #:goes-by '("busts" "bust" "busts of serious people" "bust of serious person"))
545    ('grand-hallway:hackthena-statue
546     <proxy-items> 'grand-hallway
547     #:name "the statue of Hackthena"
548     #:desc '((p "The base of the statue says \"Hackthena, guardian of the hacker
549 spirit\".  You've heard of Hackthena... not a goddess, but spiritual protector of
550 all good hacks, and legendary hacker herself.")
551              (p "Hackthena holds the form of a human woman.  She wears flowing
552 robes, has a pear of curly bovine-esque horns protruding from the sides of her
553 head, wears a pair of horn-rimmed glasses, and appears posed as if for battle.
554 But instead of a weapon, she seems to hold some sort of keyboard.  And her
555 shield... well it's round like a shield, but something seems off about it.
556 You'd better take a closer look to be sure."))
557     #:goes-by '("hackthena statue" "hackthena" "statue" "statue of hackthena")
558     #:proxy-items '(grand-hallway:keyboard
559                     grand-hallway:disc-platter
560                     grand-hallway:hackthena-horns))
561    ('grand-hallway:keyboard
562     <gameobj> 'grand-hallway
563     #:name "a Knight Keyboard"
564     #:desc "Whoa, this isn't just any old keyboard, this is a Knight Keyboard!
565 Any space cadet can see that with that kind of layout a hack-and-slayer could
566 thrash out some serious key-chords like there's no tomorrow.  You guess
567 Hackthena must be an emacs user."
568     #:invisible? #t
569     #:take-me? (lambda _
570                  (values #f
571                          #:why-not
572                          `("Are you kidding?  Do you know how hard it is to find "
573                               "a Knight Keyboard?  There's no way she's going "
574                               "to give that up.")))
575     #:goes-by '("knight keyboard" "keyboard"))
576    ('grand-hallway:hackthena-horns
577     <gameobj> 'grand-hallway
578     #:name "Hackthena's horns"
579     #:desc "They're not unlike a Gnu's horns."
580     #:invisible? #t
581     #:take-me? (lambda _
582                  (values #f
583                          #:why-not
584                          `("Are you seriously considering desecrating a statue?")))
585     #:goes-by '("hackthena's horns" "horns" "horns of hacktena"))
586    ('grand-hallway:disc-platter
587     <disc-shield> 'grand-hallway
588     #:name "Hackthena's shield"
589     #:desc "No wonder the \"shield\" looks unusual... it seems to be a hard disk
590 platter!  It has \"RL02.5\" written on it.  It looks kind of loose."
591     #:invisible? #t
592     #:goes-by '("hackthena's shield" "shield" "platter" "hard disk platter"))))
593
594 \f
595 ;;; Playroom
596 ;;; --------
597
598 (define-actor <rgb-machine> (<gameobj>)
599   ((cmd-run rgb-machine-cmd-run)
600    (cmd-reset rgb-machine-cmd-reset))
601   (commands
602    #:allocation #:each-subclass
603    #:init-thunk (build-commands
604                  (("run" "start") ((direct-command cmd-run)))
605                  ("reset" ((direct-command cmd-reset)))))
606   (resetting #:init-value #f
607              #:accessor .resetting)
608   ;; used to reset, and to kick off the first item in the list
609   (rgb-items #:init-keyword #:rgb-items
610              #:accessor .rgb-items))
611
612 (define (rgb-machine-cmd-run rgb-machine message . _)
613   (define player (message-from message))
614   (<-wait player 'tell
615           #:text '("You start the rube goldberg machine."))
616   (<-wait (gameobj-loc rgb-machine) 'tell-room
617           #:text `(,(<-wait player 'get-name)
618                    " runs the rube goldberg machine.")
619           #:exclude player)
620   (daydream 1)
621   (match (.rgb-items rgb-machine)
622     ((first-item rest ...)
623      (<- (dyn-ref rgb-machine first-item) 'trigger))))
624
625 (define (rgb-machine-cmd-reset rgb-machine message . _)
626   (define player (message-from message))
627   (cond
628    ((not (.resetting rgb-machine))
629     (set! (.resetting rgb-machine) #t)
630     (<-wait player 'tell
631             #:text '("You reset the rube goldberg machine."))
632     (<-wait (gameobj-loc rgb-machine) 'tell-room
633             #:text `(,(<-wait player 'get-name)
634                      " resets the rube goldberg machine.")
635             #:exclude player)
636     (<-wait (gameobj-loc rgb-machine) 'tell-room
637             #:text '("From a panel in the wall, a white gloved mechanical "
638                      "arm reaches out to reset all the "
639                      "rube goldberg components."))
640     (daydream (/ 1 2))
641     (for-each
642      (lambda (rgb-item)
643        (<- (dyn-ref rgb-machine rgb-item) 'reset)
644        (daydream (/ 1 2)))
645      (.rgb-items rgb-machine))
646     (<- (gameobj-loc rgb-machine) 'tell-room
647         #:text "The machine's mechanical arm retreats into the wall!")
648     (set! (.resetting rgb-machine) #f))
649    (else
650     (<-wait player 'tell
651             #:text '("But it's in the middle of resetting right now!")))))
652
653 (define-actor <rgb-item> (<gameobj>)
654   ((trigger rgb-item-trigger)
655    (reset rgb-item-reset))
656   (invisible? #:init-value #t)
657   (steps #:init-keyword #:steps
658          #:accessor .steps)
659   (triggers-as #:init-value #f
660                #:init-keyword #:triggers-as
661                #:getter .triggers-as)
662   (reset-msg #:init-keyword #:reset-msg
663              #:getter .reset-msg)
664   ;; States: ready -> running -> ran
665   (state #:init-value 'ready
666          #:accessor .state))
667
668
669 (define (rgb-item-trigger rgb-item message . _)
670   (define room (gameobj-loc rgb-item))
671   (case (.state rgb-item)
672     ((ready)
673      ;; Set state to running
674      (set! (.state rgb-item) 'running)
675
676      ;; Loop through all steps
677      (for-each
678       (lambda (step)
679         (match step
680           ;; A string?  That's the description of what's happening, tell players
681           ((? string? str)
682            (<- room 'tell-room #:text str))
683           ;; A number?  Sleep for that many secs
684           ((? number? num)
685            (daydream num))
686           ;; A symbol?  That's another gameobj to look up dynamically
687           ((? symbol? sym)
688            (<- (dyn-ref rgb-item sym) 'trigger
689                #:triggered-by (.triggers-as rgb-item)))
690           (_ (throw 'unknown-step-type
691                     "Don't know how to process rube goldberg machine step type?"
692                     #:step step))))
693       (.steps rgb-item))
694
695      ;; We're done! Set state to ran
696      (set! (.state rgb-item) 'ran))
697
698     (else
699      (<- room 'tell-room
700          #:text `("... but " ,(slot-ref rgb-item 'name)
701                   " has already been triggered!")))))
702
703 (define (rgb-item-reset rgb-item message . _)
704   (define room (gameobj-loc rgb-item))
705   (case (.state rgb-item)
706     ((ran)
707      (set! (.state rgb-item) 'ready)
708      (<- room 'tell-room
709          #:text (.reset-msg rgb-item)))
710     ((running)
711      (<- room 'tell-room
712          #:text `("... but " ,(slot-ref rgb-item 'name)
713                   " is currently running!")))
714     ((ready)
715      (<- room 'tell-room
716          #:text `("... but " ,(slot-ref rgb-item 'name)
717                   " has already been reset.")))))
718
719 (define-actor <rgb-kettle> (<rgb-item>)
720   ((trigger rgb-kettle-trigger)
721    (reset rgb-kettle-reset))
722   (heated #:accessor .heated
723           #:init-value #f)
724   (filled #:accessor .filled
725           #:init-value #f))
726
727 (define* (rgb-kettle-trigger rgb-item message #:key triggered-by)
728   (define room (gameobj-loc rgb-item))
729   (if (not (eq? (.state rgb-item) 'ran))
730       (begin
731         (match triggered-by
732           ('water-demon
733            (set! (.state rgb-item) 'running)
734            (set! (.filled rgb-item) #t))
735           ('quik-heater
736            (set! (.state rgb-item) 'running)
737            (set! (.heated rgb-item) #t)))
738         (when (and (.filled rgb-item)
739                    (.heated rgb-item))
740           (<- room 'tell-room
741               #:text '((i "*kshhhhhh!*")
742                        " The water has boiled!"))
743           (daydream .25)
744           (set! (.state rgb-item) 'ran)
745           ;; insert a cup of hot tea in the room
746           (create-gameobj <hot-tea> (gameobj-gm rgb-item) room)
747           (<- room 'tell-room
748               #:text '("The machine pours out a cup of hot tea! "
749                        "Looks like the machine finished!"))))
750       (<- room 'tell-room
751          #:text `("... but " ,(slot-ref rgb-item 'name)
752                   " has already been triggered!"))))
753
754 (define (rgb-kettle-reset rgb-item message . rest-args)
755   (define room (gameobj-loc rgb-item))
756   (when (eq? (.state rgb-item) 'ran)
757     (set! (.heated rgb-item) #f)
758     (set! (.filled rgb-item) #f))
759   (apply rgb-item-reset rgb-item message rest-args))
760
761 (define-actor <tinfoil-hat> (<gameobj>)
762   ((cmd-wear tinfoil-hat-wear))
763   (contained-commands
764    #:allocation #:each-subclass
765    #:init-thunk (build-commands
766                  ("wear" ((direct-command cmd-wear))))))
767
768 (define (tinfoil-hat-wear tinfoil-hat message . _)
769   (<- (message-from message) 'tell
770       #:text '("You put on the tinfoil hat, and, to be perfectly honest with you "
771                "it's a lot harder to take you seriously.")))
772
773
774 (define-actor <hot-tea> (<gameobj>)
775   ((cmd-drink hot-tea-cmd-drink)
776    (cmd-sip hot-tea-cmd-sip))
777   (contained-commands
778    #:allocation #:each-subclass
779    #:init-thunk (build-commands
780                  ("drink" ((direct-command cmd-drink)))
781                  ("sip" ((direct-command cmd-sip)))))
782   
783   (sips-left #:init-value 4
784              #:accessor .sips-left)
785   (name #:init-value "a cup of hot tea")
786   (take-me? #:init-value #t)
787   (goes-by #:init-value '("cup of hot tea" "cup of tea" "tea" "cup"))
788   (desc #:init-value "It's a steaming cup of hot tea.  It looks pretty good!"))
789
790 (define (hot-tea-cmd-drink hot-tea message . _)
791   (define player (message-from message))
792   (define player-loc (<-wait player 'get-loc))
793   (define player-name (<-wait player 'get-name))
794   (<- player 'tell
795       #:text "You drink a steaming cup of hot tea all at once... hot hot hot!")
796   (<- player-loc 'tell-room
797       #:text `(,player-name
798                " drinks a steaming cup of hot tea all at once.")
799       #:exclude player)
800   (gameobj-self-destruct hot-tea))
801
802 (define (hot-tea-cmd-sip hot-tea message . _)
803   (define player (message-from message))
804   (define player-loc (<-wait player 'get-loc))
805   (define player-name (<-wait player 'get-name))
806   (set! (.sips-left hot-tea) (- (.sips-left hot-tea) 1))
807   (<- player 'tell
808       #:text "You take a sip of your steaming hot tea.  How refined!")
809   (<- player-loc 'tell-room
810       #:text `(,player-name
811                " takes a sip of their steaming hot tea.  How refined!")
812       #:exclude player)
813   (when (= (.sips-left hot-tea) 0)
814     (<- player 'tell
815         #:text "You've finished your tea!")
816     (<- player-loc 'tell-room
817         #:text `(,player-name
818                  " finishes their tea!")
819         #:exclude player)
820     (gameobj-self-destruct hot-tea)))
821
822 (define-actor <fanny-pack> (<container>)
823   ((cmd-take-from-while-wearing cmd-take-from)
824    (cmd-put-in-while-wearing cmd-put-in))
825   (contained-commands
826    #:allocation #:each-subclass
827    #:init-thunk
828    (build-commands
829     (("l" "look") ((direct-command cmd-look-at)))
830     ("take" ((prep-indir-command cmd-take-from-while-wearing
831                                  '("from" "out of"))))
832     ("put" ((prep-indir-command cmd-put-in-while-wearing
833                                 '("in" "inside" "into" "on")))))))
834
835 (define playroom
836   (lol
837    ('playroom
838     <room> #f
839     #:name "The Playroom"
840     #:desc '(p ("  There are toys scattered everywhere here.  It's really unclear
841 if this room is intended for children or child-like adults.")
842                ("  There are doors to both the east and the west."))
843     #:exits
844     (list (make <exit>
845             #:name "east"
846             #:to 'grand-hallway)
847           (make <exit>
848             #:name "west"
849             #:to 'computer-room)))
850    ('playroom:cubey
851     <gameobj> 'playroom
852     #:name "Cubey"
853     #:take-me? #t
854     #:desc "  It's a little foam cube with googly eyes on it.  So cute!")
855    ('playroom:cuddles-plushie
856     <gameobj> 'playroom
857     #:name "a Cuddles plushie"
858     #:goes-by '("plushie" "cuddles plushie" "cuddles")
859     #:take-me? #t
860     #:desc "  A warm and fuzzy cuddles plushie!  It's a cuddlefish!")
861
862    ('playroom:toy-chest
863     <container> 'playroom
864     #:name "a toy chest"
865     #:goes-by '("toy chest" "chest")
866     #:desc (lambda (toy-chest whos-looking)
867              (let ((contents (gameobj-occupants toy-chest)))
868                `((p "A brightly painted wooden chest.  The word \"TOYS\" is "
869                     "engraved on it.")
870                  (p "Inside you see:"
871                     ,(if (eq? contents '())
872                          " nothing!  It's empty!"
873                          `(ul ,(map (lambda (occupant)
874                                       `(li ,(<-wait occupant 'get-name)))
875                                     (gameobj-occupants toy-chest))))))))
876     #:take-from-me? #t
877     #:put-in-me? #t)
878
879    ;; Things inside the toy chest
880    ('playroom:toy-chest:rubber-duck
881     <gameobj> 'playroom:toy-chest
882     #:name "a rubber duck"
883     #:goes-by '("rubber duck" "duck")
884     #:take-me? #t
885     #:desc "It's a yellow rubber duck with a bright orange beak.")
886
887    ('playroom:toy-chest:tinfoil-hat
888     <tinfoil-hat> 'playroom:toy-chest
889     #:name "a tinfoil hat"
890     #:goes-by '("tinfoil hat" "hat")
891     #:take-me? #t
892     #:desc "You'd have to be a crazy person to wear this thing!")
893
894    ('playroom:toy-chest:fanny-pack
895     <fanny-pack> 'playroom:toy-chest
896     #:name "a fanny pack"
897     #:goes-by '("fanny pack" "pack")
898     #:take-me? #t
899     #:desc
900     (lambda (toy-chest whos-looking)
901       (let ((contents (gameobj-occupants toy-chest)))
902         `((p "It's a leather fanny pack, so it's both tacky and kinda cool.")
903           (p "Inside you see:"
904              ,(if (eq? contents '())
905                   " nothing!  It's empty!"
906                   `(ul ,(map (lambda (occupant)
907                                `(li ,(<-wait occupant 'get-name)))
908                              (gameobj-occupants toy-chest)))))))))
909
910    ;; Things inside the toy chest
911    ('playroom:toy-chest:fanny-pack:plastic-elephant
912     <gameobj> 'playroom:toy-chest:fanny-pack
913     #:name "a plastic elephant"
914     #:goes-by '("plastic elephant" "elephant")
915     #:take-me? #t
916     #:desc "It's a tiny little plastic elephant.  Small, but heartwarming.")
917
918    ('playroom:rgb-machine
919     <rgb-machine> 'playroom
920     #:name "a Rube Goldberg machine"
921     #:goes-by '("rube goldberg machine" "machine")
922     #:rgb-items '(playroom:rgb-dominoes
923                   playroom:rgb-switch-match
924                   playroom:rgb-candle
925                   playroom:rgb-catapult
926                   playroom:rgb-water-demon
927                   playroom:rgb-quik-heater
928                   playroom:rgb-kettle)
929     #:desc "It's one of those hilarious Rube Goldberg machines.
930 What could happen if you started it?")
931
932    ;; Dominoes topple
933    ('playroom:rgb-dominoes
934     <rgb-item> 'playroom
935     #:name "some dominoes"
936     #:goes-by '("dominoes" "some dominoes")
937     #:steps `("The dominoes topple down the line..."
938               1
939               "The last domino lands on a switch!"
940               1.5
941               playroom:rgb-switch-match)
942     #:reset-msg "The dominoes are placed back into position.")
943
944    ;; Which hit the switch and strike a match
945    ('playroom:rgb-switch-match
946     <rgb-item> 'playroom
947     #:name "a switch"
948     #:goes-by '("switch" "match")
949     #:steps `("The switch lights a match!"
950               ,(/ 2 3)
951               "The match lights a candle!"
952               1.5
953               playroom:rgb-candle)
954     #:reset-msg "A fresh match is installed and the switch is reset.")
955    ;; which lights a candle and burns a rope
956    ('playroom:rgb-candle
957     <rgb-item> 'playroom
958     #:name "a candle"
959     #:goes-by '("candle")
960     #:steps `("The candle burns..."
961               (/ 2 3)  ; oops!
962               "The candle is burning away a rope!"
963               2
964               "The rope snaps!"
965               .5
966               playroom:rgb-catapult)
967     #:reset-msg "A fresh candle is installed.")
968    ;; which catapults a rock
969    ('playroom:rgb-catapult
970     <rgb-item> 'playroom
971     #:name "a catapult"
972     #:goes-by '("catapult")
973     #:steps `("The snapped rope unleashes a catapult, which throws a rock!"
974               2
975               "The rock flies through a water demon, startling it!"
976               .5
977               playroom:rgb-water-demon
978               2
979               "The rock whacks into the quik-heater's on button!"
980               .5
981               playroom:rgb-quik-heater)
982     #:reset-msg
983     '("A fresh rope is attached to the catapult, which is pulled taught. "
984       "A fresh rock is placed on the catapult."))
985    ;; which both:
986    ;;   '- panics the water demon
987    ;;      '- which waters the kettle
988    ('playroom:rgb-water-demon
989     <rgb-item> 'playroom
990     #:name "the water demon"
991     #:triggers-as 'water-demon
992     #:goes-by '("water demon" "demon")
993     #:steps `("The water demon panics, and starts leaking water into the kettle below!"
994               3
995               "The kettle is filled!"
996               playroom:rgb-kettle)
997     #:reset-msg '("The water demon is scratched behind the ears and calms down."))
998    ;;   '- bops the quik-heater button
999    ;;      '- which heats the kettle
1000    ('playroom:rgb-quik-heater
1001     <rgb-item> 'playroom
1002     #:name "the quik heater"
1003     #:triggers-as 'quik-heater
1004     #:goes-by '("quik heater" "heater")
1005     #:steps `("The quik-heater heats up the kettle above it!"
1006               3
1007               "The kettle is heated up!"
1008               playroom:rgb-kettle)
1009     #:reset-msg '("The quik heater is turned off."))
1010    ;; Finally, the kettle
1011    ('playroom:rgb-kettle
1012     <rgb-kettle> 'playroom
1013     #:name "the kettle"
1014     #:goes-by '("kettle")
1015     #:reset-msg '("The kettle is emptied."))))
1016
1017
1018 \f
1019 ;;; Writing room
1020 ;;; ------------
1021
1022 \f
1023 ;;; Armory???
1024 ;;; ---------
1025
1026 ;; ... full of NURPH weapons?
1027
1028 \f
1029 ;;; Smoking parlor
1030 ;;; --------------
1031
1032 (define-class <furniture> (<gameobj>)
1033   (sit-phrase #:init-keyword #:sit-phrase)
1034   (sit-phrase-third-person #:init-keyword #:sit-phrase-third-person)
1035   (sit-name #:init-keyword #:sit-name)
1036
1037   (commands
1038    #:allocation #:each-subclass
1039    #:init-thunk (build-commands
1040                  ("sit" ((direct-command cmd-sit-furniture)))))
1041   (actions #:allocation #:each-subclass
1042            #:init-thunk (build-actions
1043                          (cmd-sit-furniture furniture-cmd-sit))))
1044
1045 (define* (furniture-cmd-sit actor message #:key direct-obj)
1046   (define player-name
1047     (<-wait (message-from message) 'get-name))
1048   (<- (message-from message) 'tell
1049       #:text (format #f "You ~a ~a.\n"
1050                      (slot-ref actor 'sit-phrase)
1051                      (slot-ref actor 'sit-name)))
1052   (<- (slot-ref actor 'loc) 'tell-room
1053       #:text (format #f "~a ~a on ~a.\n"
1054                      player-name
1055                      (slot-ref actor 'sit-phrase-third-person)
1056                      (slot-ref actor 'sit-name))
1057       #:exclude (message-from message)))
1058
1059
1060 (define smoking-parlor
1061   (lol
1062    ('smoking-parlor
1063     <room> #f
1064     #:name "Smoking Parlor"
1065     #:desc
1066     '((p "This room looks quite posh.  There are huge comfy seats you can sit in
1067 if you like. Strangely, you see a large sign saying \"No Smoking\".  The owners must
1068 have installed this place and then changed their mind later.")
1069       (p "There's a door to the west leading back to the grand hallway, and
1070 a nondescript steel door to the south, leading apparently outside."))
1071     #:exits
1072     (list (make <exit>
1073             #:name "west"
1074             #:to 'grand-hallway)
1075           (make <exit>
1076             #:name "south"
1077             #:to 'break-room)))
1078    ('smoking-parlor:chair
1079     <furniture> 'smoking-parlor
1080     #:name "a comfy leather chair"
1081     #:desc "  That leather chair looks really comfy!"
1082     #:goes-by '("leather chair" "comfy leather chair" "chair")
1083     #:sit-phrase "sink into"
1084     #:sit-phrase-third-person "sinks into"
1085     #:sit-name "the comfy leather chair")
1086    ('smoking-parlor:sofa
1087     <furniture> 'smoking-parlor
1088     #:name "a plush leather sofa"
1089     #:desc "  That leather chair looks really comfy!"
1090     #:goes-by '("leather sofa" "plush leather sofa" "sofa"
1091                 "leather couch" "plush leather couch" "couch")
1092     #:sit-phrase "sprawl out on"
1093     #:sit-phrase-third-person "sprawls out on into"
1094     #:sit-name "the plush leather couch")
1095    ('smoking-parlor:bar-stool
1096     <furniture> 'smoking-parlor
1097     #:name "a bar stool"
1098     #:desc "  Conveniently located near the bar!  Not the most comfortable
1099 seat in the room, though."
1100     #:goes-by '("stool" "bar stool" "seat")
1101     #:sit-phrase "hop on"
1102     #:sit-phrase-third-person "hops onto"
1103     #:sit-name "the bar stool")
1104    ('ford-prefect
1105     <chatty-npc> 'smoking-parlor
1106     #:name "Ford Prefect"
1107     #:desc "Just some guy, you know?"
1108     #:goes-by '("Ford Prefect" "ford prefect"
1109                 "frood" "prefect" "ford")
1110     #:catchphrases prefect-quotes)
1111
1112    ('smoking-parlor:no-smoking-sign
1113     <readable> 'smoking-parlor
1114     #:invisible? #t
1115     #:name "No Smoking Sign"
1116     #:desc "This sign says \"No Smoking\" in big, red letters.
1117 It has some bits of bubble gum stuck to it... yuck."
1118     #:goes-by '("no smoking sign" "sign")
1119     #:read-text "It says \"No Smoking\", just like you'd expect from
1120 a No Smoking sign.")
1121    ;; TODO: Cigar dispenser
1122    ))
1123
1124 \f
1125
1126 ;;; Breakroom
1127 ;;; ---------
1128
1129 (define-class <desk-clerk> (<gameobj>)
1130   ;; The desk clerk has three states:
1131   ;;  - on-duty: Arrived, and waiting for instructions (and losing patience
1132   ;;    gradually)
1133   ;;  - slacking: In the break room, probably smoking a cigarette
1134   ;;    or checking text messages
1135   (state #:init-value 'slacking)
1136   (commands #:allocation #:each-subclass
1137             #:init-thunk
1138             (build-commands
1139              (("talk" "chat") ((direct-command cmd-chat)))
1140              ("ask" ((direct-command cmd-ask-incomplete)
1141                      (prep-direct-command cmd-ask-about)))
1142              ("dismiss" ((direct-command cmd-dismiss)))))
1143   (patience #:init-value 0)
1144   (actions #:allocation #:each-subclass
1145            #:init-thunk (build-actions
1146                          (init clerk-act-init)
1147                          (cmd-chat clerk-cmd-chat)
1148                          (cmd-ask-incomplete clerk-cmd-ask-incomplete)
1149                          (cmd-ask-about clerk-cmd-ask)
1150                          (cmd-dismiss clerk-cmd-dismiss)
1151                          (update-loop clerk-act-update-loop)
1152                          (be-summoned clerk-act-be-summoned))))
1153
1154 (define (clerk-act-init clerk message . _)
1155   ;; call the gameobj main init method
1156   (gameobj-act-init clerk message)
1157   ;; start our main loop
1158   (<- (actor-id clerk) 'update-loop))
1159
1160 (define changing-name-text "Changing your name is easy!
1161 We have a clipboard here at the desk
1162 where you can make yourself known to other participants in the hotel
1163 if you sign it.  Try 'sign form as <your-name>', replacing
1164 <your-name>, obviously!")
1165
1166 (define phd-text
1167   "Ah... when I'm not here, I've got a PHD to finish.")
1168
1169 (define clerk-help-topics
1170   `(("changing name" . ,changing-name-text)
1171     ("sign-in form" . ,changing-name-text)
1172     ("form" . ,changing-name-text)
1173     ("common commands" .
1174      "Here are some useful commands you might like to try: chat,
1175 go, take, drop, say...")
1176     ("hotel" .
1177      "We hope you enjoy your stay at Hotel Bricabrac.  As you may see,
1178 our hotel emphasizes interesting experiences over rest and lodging.
1179 The origins of the hotel are... unclear... and it has recently come
1180 under new... 'management'.  But at Hotel Bricabrac we believe these
1181 aspects make the hotel into a fun and unique experience!  Please,
1182 feel free to walk around and explore.")
1183     ("physics paper" . ,phd-text)
1184     ("paper" . ,phd-text)
1185     ("proprietor" . "Oh, he's that frumpy looking fellow sitting over there.")))
1186
1187
1188 (define clerk-knows-about
1189   "'ask clerk about changing name', 'ask clerk about common commands', and 'ask clerk about the hotel'")
1190
1191 (define clerk-general-helpful-line
1192   (string-append
1193    "The clerk says, \"If you need help with anything, feel free to ask me about it.
1194 For example, 'ask clerk about changing name'. You can ask me about the following:
1195 " clerk-knows-about ".\"\n"))
1196
1197 (define clerk-slacking-complaints
1198   '("The pay here is absolutely lousy."
1199     "The owner here has no idea what they're doing."
1200     "Some times you just gotta step away, you know?"
1201     "You as exhausted as I am?"
1202     "Yeah well, this is just temporary.  I'm studying to be a high
1203 energy particle physicist.  But ya gotta pay the bills, especially
1204 with tuition at where it is..."))
1205
1206 (define* (clerk-cmd-chat clerk message #:key direct-obj)
1207   (match (slot-ref clerk 'state)
1208     ('on-duty
1209      (<- (message-from message) 'tell
1210          #:text clerk-general-helpful-line))
1211     ('slacking
1212      (<- (message-from message) 'tell
1213          #:text
1214          (string-append
1215           "The clerk says, \""
1216           (random-choice clerk-slacking-complaints)
1217           "\"\n")))))
1218
1219 (define (clerk-cmd-ask-incomplete clerk message . _)
1220   (<- (message-from message) 'tell
1221       #:text "The clerk says, \"Ask about what?\"\n"))
1222
1223 (define clerk-doesnt-know-text
1224   "The clerk apologizes and says she doesn't know about that topic.\n")
1225
1226 (define* (clerk-cmd-ask clerk message #:key indir-obj
1227                         #:allow-other-keys)
1228   (match (slot-ref clerk 'state)
1229     ('on-duty
1230      (match (assoc indir-obj clerk-help-topics)
1231        ((_ . info)
1232            (<- (message-from message) 'tell
1233                #:text
1234                (string-append "The clerk clears her throat and says:\n  \""
1235                               info
1236                               "\"\n")))
1237        (#f
1238         (<- (message-from message) 'tell
1239             #:text clerk-doesnt-know-text))))
1240     ('slacking
1241      (<- (message-from message) 'tell
1242          #:text "The clerk says, \"Sorry, I'm on my break.\"\n"))))
1243
1244 (define* (clerk-act-be-summoned clerk message #:key who-summoned)
1245   (match (slot-ref clerk 'state)
1246     ('on-duty
1247      (<- who-summoned 'tell
1248          #:text
1249          "The clerk tells you as politely as she can that she's already here,
1250 so there's no need to ring the bell.\n"))
1251     ('slacking
1252      (<- (gameobj-loc clerk) 'tell-room
1253          #:text
1254          "The clerk's ears perk up, she stamps out a cigarette, and she
1255 runs out of the room!\n")
1256      (gameobj-set-loc! clerk (dyn-ref clerk 'lobby))
1257      (slot-set! clerk 'patience 8)
1258      (slot-set! clerk 'state 'on-duty)
1259      (<- (gameobj-loc clerk) 'tell-room
1260          #:text
1261          (string-append
1262           "  Suddenly, a uniformed woman rushes into the room!  She's wearing a
1263 badge that says \"Desk Clerk\".
1264   \"Hello, yes,\" she says between breaths, \"welcome to Hotel Bricabrac!
1265 We look forward to your stay.  If you'd like help getting acclimated,
1266 feel free to ask me.  For example, 'ask clerk about changing name'.
1267 You can ask me about the following:
1268 " clerk-knows-about ".\"\n")))))
1269
1270 (define* (clerk-cmd-dismiss clerk message . _)
1271   (define player-name
1272     (<-wait (message-from message) 'get-name))
1273   (match (slot-ref clerk 'state)
1274     ('on-duty
1275      (<- (gameobj-loc clerk) 'tell-room
1276          #:text
1277          (format #f "\"Thanks ~a!\" says the clerk. \"I have somewhere I need to be.\"
1278 The clerk leaves the room in a hurry.\n"
1279                  player-name)
1280          #:exclude (actor-id clerk))
1281      (gameobj-set-loc! clerk (dyn-ref clerk 'break-room))
1282      (slot-set! clerk 'state 'slacking)
1283      (<- (gameobj-loc clerk) 'tell-room
1284          #:text clerk-return-to-slacking-text
1285          #:exclude (actor-id clerk)))
1286     ('slacking
1287      (<- (message-from message) 'tell
1288          #:text "The clerk sternly asks you to not be so dismissive.\n"))))
1289
1290 (define clerk-slacking-texts
1291   '("The clerk takes a long drag on her cigarette.\n"
1292     "The clerk scrolls through text messages on her phone.\n"
1293     "The clerk coughs a few times.\n"
1294     "The clerk checks her watch and justifies a few more minutes outside.\n"
1295     "The clerk fumbles around for a lighter.\n"
1296     "The clerk sighs deeply and exhaustedly.\n"
1297     "The clerk fumbles around for a cigarette.\n"))
1298
1299 (define clerk-working-impatience-texts
1300   '("The clerk hums something, but you're not sure what it is."
1301     "The clerk attempts to change the overhead music, but the dial seems broken."
1302     "The clerk clicks around on the desk computer."
1303     "The clerk scribbles an equation on a memo pad, then crosses it out."
1304     "The clerk mutters something about the proprietor having no idea how to run a hotel."
1305     "The clerk thumbs through a printout of some physics paper."))
1306
1307 (define clerk-slack-excuse-text
1308   "The desk clerk excuses herself, but says you are welcome to ring the bell
1309 if you need further help.")
1310
1311 (define clerk-return-to-slacking-text
1312   "The desk clerk enters and slams the door behind her.\n")
1313
1314
1315 (define (clerk-act-update-loop clerk message)
1316   (define (tell-room text)
1317     (<- (gameobj-loc clerk) 'tell-room
1318         #:text text
1319         #:exclude (actor-id clerk)))
1320   (define (loop-if-not-destructed)
1321     (if (not (slot-ref clerk 'destructed))
1322         ;; This iterates by "recursing" on itself by calling itself
1323         ;; (as the message handler) again.  It used to be that we had to do
1324         ;; this, because there was a bug where a loop which yielded like this
1325         ;; would keep growing the stack due to some parameter goofiness.
1326         ;; That's no longer true, but there's an added advantage to this
1327         ;; route: it's much more live hackable.  If we change the definition
1328         ;; of this method, the character will act differently on the next
1329         ;; "tick" of the loop.
1330         (<- (actor-id clerk) 'update-loop)))
1331   (match (slot-ref clerk 'state)
1332     ('slacking
1333      (tell-room (random-choice clerk-slacking-texts))
1334      (daydream (+ (random 20) 15))
1335      (loop-if-not-destructed))
1336     ('on-duty
1337      (if (> (slot-ref clerk 'patience) 0)
1338          ;; Keep working but lose patience gradually
1339          (begin
1340            (tell-room (random-choice clerk-working-impatience-texts))
1341            (slot-set! clerk 'patience (- (slot-ref clerk 'patience)
1342                                          (+ (random 2) 1)))
1343            (daydream (+ (random 60) 40))
1344            (loop-if-not-destructed))
1345          ;; Back to slacking
1346          (begin
1347            (tell-room clerk-slack-excuse-text)
1348            ;; back bto the break room
1349            (gameobj-set-loc! clerk (dyn-ref clerk 'break-room))
1350            (tell-room clerk-return-to-slacking-text)
1351            ;; annnnnd back to slacking
1352            (slot-set! clerk 'state 'slacking)
1353            (daydream (+ (random 30) 15))
1354            (loop-if-not-destructed))))))
1355
1356
1357 (define break-room
1358   (lol
1359    ('break-room
1360     <room> #f
1361     #:name "Employee Break Room"
1362     #:desc "  This is less a room and more of an outdoor wire cage.  You get
1363 a bit of a view of the brick exterior of the building, and a crisp wind blows,
1364 whistling, through the openings of the fenced area.  Partly smoked cigarettes
1365 and various other debris cover the floor.
1366   Through the wires you can see... well... hm.  It looks oddly like
1367 the scenery tapers off nothingness.  But that can't be right, can it?"
1368     #:exits
1369     (list (make <exit>
1370             #:name "north"
1371             #:to 'smoking-parlor)))
1372    ('break-room:desk-clerk
1373     <desk-clerk> 'break-room
1374     #:name "the hotel desk clerk"
1375     #:desc "  The hotel clerk is wearing a neatly pressed uniform bearing the
1376 hotel insignia.  She appears to be rather exhausted."
1377     #:goes-by '("hotel desk clerk" "clerk" "desk clerk"))
1378    ('break-room:void
1379     <gameobj> 'break-room
1380     #:invisible? #t
1381     #:name "The Void"
1382     #:desc "As you stare into the void, the void stares back into you."
1383     #:goes-by '("void" "abyss" "nothingness" "scenery"))
1384    ('break-room:fence
1385     <gameobj> 'break-room
1386     #:invisible? #t
1387     #:name "break room cage"
1388     #:desc "It's a mostly-cubical wire mesh surrounding the break area.
1389 You can see through the gaps, but they're too small to put more than a
1390 couple of fingers through.  There appears to be some wear and tear to
1391 the paint, but the wires themselves seem to be unusually sturdy."
1392     #:goes-by '("fence" "cage" "wire cage"))))
1393
1394
1395 \f
1396 ;;; Ennpie's Sea Lounge
1397 ;;; -------------------
1398
1399 \f
1400 ;;; Computer room
1401 ;;; -------------
1402
1403 ;; Our computer and hard drive are based off the PDP-11 and the RL01 /
1404 ;; RL02 disk drives.  However we increment both by .5 (a true heresy)
1405 ;; to distinguish both from the real thing.
1406
1407 (define-actor <hard-drive> (<gameobj>)
1408   ((cmd-put-in hard-drive-insert)
1409    (cmd-push-button hard-drive-push-button)
1410    (get-state hard-drive-act-get-state))
1411   (commands #:allocation #:each-subclass
1412             #:init-thunk (build-commands
1413                           ("insert" ((prep-indir-command cmd-put-in
1414                                                          '("in" "inside" "into"))))
1415                           (("press" "push") ((prep-indir-command cmd-push-button)))))
1416   ;; the state moves from: empty -> with-disc -> loading -> ready
1417   (state #:init-value 'empty
1418          #:accessor .state))
1419
1420 (define (hard-drive-act-get-state hard-drive message)
1421   (.state hard-drive))
1422
1423 (define* (hard-drive-desc hard-drive #:optional whos-looking)
1424   `((p "The hard drive is labeled \"RL02.5\".  It's a little under a meter tall.")
1425     (p "There is a slot where a disk platter could be inserted, "
1426        ,(if (eq? (.state hard-drive) 'empty)
1427             "which is currently empty"
1428             "which contains a glowing platter")
1429        ". There is a LOAD button "
1430        ,(if (member (.state hard-drive) '(empty with-disc))
1431             "which is glowing"
1432             "which is pressed in and unlit")
1433        ". There is a READY indicator "
1434        ,(if (eq? (.state hard-drive) 'ready)
1435             "which is glowing."
1436             "which is unlit.")
1437        ,(if (member (.state hard-drive) '(loading ready))
1438             "  The machine emits a gentle whirring noise."
1439             ""))))
1440
1441 (define* (hard-drive-push-button gameobj message
1442                                  #:key direct-obj indir-obj preposition
1443                                  (player (message-from message)))
1444   (define (tell-room text)
1445     (<-wait (gameobj-loc gameobj) 'tell-room
1446             #:text text))
1447   (define (tell-room-excluding-player text)
1448     (<-wait (gameobj-loc gameobj) 'tell-room
1449             #:text text
1450             #:exclude player))
1451   (cond
1452    ((ci-member direct-obj '("button" "load button" "load"))
1453     (tell-room-excluding-player
1454      `(,(<-wait player 'get-name)
1455        " presses the button on the hard disk."))
1456     (<- player 'tell
1457         #:text "You press the button on the hard disk.")
1458
1459     (case (.state gameobj)
1460       ((empty)
1461        ;; I have no idea what this drive did when you didn't have a platter
1462        ;; in it and pressed load, but I know there was a FAULT button.
1463        (tell-room "You hear some movement inside the hard drive...")
1464        (daydream 1.5)
1465        (tell-room
1466         '("... but then the FAULT button blinks a couple times. "
1467           "What could be missing?")))
1468       ((with-disc)
1469        (set! (.state gameobj) 'loading)
1470        (tell-room "The hard disk begins to spin up!")
1471        (daydream 2)
1472        (set! (.state gameobj) 'ready)
1473        (tell-room "The READY light turns on!"))
1474       ((loading ready)
1475        (<- player 'tell
1476            #:text '("Pressing the button does nothing right now, "
1477                     "but it does feel satisfying.")))))
1478    (else
1479     (<- player 'tell
1480         #:text '("How could you think of pressing anything else "
1481                  "but that tantalizing button right in front of you?")))))
1482
1483 (define* (hard-drive-insert gameobj message
1484                             #:key direct-obj indir-obj preposition
1485                             (player (message-from message)))
1486   (define our-name (slot-ref gameobj 'name))
1487   (define this-thing
1488     (call/ec
1489      (lambda (return)
1490        (for-each (lambda (occupant)
1491                    (define goes-by (<-wait occupant 'goes-by))
1492                    (when (ci-member direct-obj goes-by)
1493                      (return occupant)))
1494                  (<-wait player 'get-occupants))
1495        ;; nothing found
1496        #f)))
1497   (cond
1498    ((not this-thing)
1499     (<- player 'tell
1500         #:text `("You don't seem to have any such " ,direct-obj " to put "
1501                  ,preposition " " ,our-name ".")))
1502    ((not (<-wait this-thing 'get-prop 'hd-platter?))
1503     (<- player 'tell
1504         #:text `("It wouldn't make sense to put "
1505                  ,(<-wait this-thing 'get-name)
1506                  " " ,preposition " " ,our-name ".")))
1507    ((not (eq? (.state gameobj) 'empty))
1508     (<- player 'tell
1509         #:text "The disk drive already has a platter in it."))
1510    (else
1511     (set! (.state gameobj) 'with-disc)
1512     (<- player 'tell
1513         #:text '((p "You insert the glowing disc into the drive.")
1514                  (p "The LOAD button begins to glow."))))))
1515
1516 ;; The computar
1517 (define-actor <computer> (<gameobj>)
1518   ((cmd-run-program computer-run-program)
1519    (cmd-run-what (lambda (gameobj message . _)
1520                    (<- (message-from message) 'tell
1521                        #:text '("The computer is already running, and a program appears "
1522                                 "ready to run."
1523                                 "you mean to \"run the program on the computer\""))))
1524    (cmd-help-run-not-press
1525     (lambda (gameobj message . _)
1526       (<- (message-from message) 'tell
1527           #:text '("You don't need to press / push / flip anything. "
1528                    "You could " (i "run program on computer")
1529                    " already if you wanted to.")))))
1530   (commands #:allocation #:each-subclass
1531             #:init-thunk (build-commands
1532                           ("run" ((prep-indir-command cmd-run-program
1533                                                       '("on"))
1534                                   (direct-command cmd-run-what)))
1535                           (("press" "push" "flip")
1536                            ((prep-indir-command cmd-help-run-not-press))))))
1537
1538 (define* (computer-run-program gameobj message
1539                                #:key direct-obj indir-obj preposition
1540                                (player (message-from message)))
1541   (define (hd-state)
1542     (<-wait (dyn-ref gameobj 'computer-room:hard-drive) 'get-state))
1543   (define (tell-room text)
1544     (<-wait (gameobj-loc gameobj) 'tell-room
1545         #:text text))
1546   (define (tell-room-excluding-player text)
1547     (<-wait (gameobj-loc gameobj) 'tell-room
1548             #:text text
1549             #:exclude player))
1550   (define (tell-player text)
1551     (<-wait player 'tell
1552             #:text text))
1553   (cond
1554    ((ci-member direct-obj '("program"))
1555     (tell-room-excluding-player
1556      `(,(<-wait player 'get-name)
1557        " runs the program loaded on the computer..."))
1558     (tell-player "You run the program on the computer...")
1559
1560     (cond
1561      ((not (eq? (hd-state) 'ready))
1562       (tell-room '("... but it errors out. "
1563                    "It seems to be complaining about a " (b "DISK ERROR!")
1564                    ". It looks like it is missing some essential software.")))
1565      (else
1566       (<- (dyn-ref gameobj 'computer-room:floor-panel) 'open-up))))))
1567
1568
1569 ;; floor panel
1570 (define-actor <floor-panel> (<gameobj>)
1571   ;; TODO: Add "open" verb, since obviously people will try that
1572   ((open? (lambda (panel message)
1573             (slot-ref panel 'open)))
1574    (open-up floor-panel-open-up))
1575   (open #:init-value #f))
1576
1577 (define (floor-panel-open-up panel message)
1578   (if (slot-ref panel 'open)
1579       (<- (gameobj-loc panel) 'tell-room
1580           #:text '("You hear some gears grind around the hinges of the "
1581                    "floor panel, but it appears to already be open."))
1582       (begin
1583         (slot-set! panel 'open #t)
1584         (<- (gameobj-loc panel) 'tell-room
1585             #:text '("You hear some gears grind, as the metal panel on "
1586                      "the ground opens and reveals a stairwell going down!")))))
1587
1588 (define* (floor-panel-desc panel #:optional whos-looking)
1589   `("It's a large metal panel on the floor in the middle of the room. "
1590     ,(if (slot-ref panel 'open)
1591          '("It's currently wide open, revealing a spiraling staircase "
1592            "which descends into darkness.")
1593          '("It's currently closed shut, but there are clearly hinges, and "
1594            "it seems like there is a mechanism which probably opens it via "
1595            "some automation.  What could be down there?"))))
1596
1597 (define computer-room
1598   (lol
1599    ('computer-room
1600     <room> #f
1601     #:name "Computer Room"
1602     #:desc (lambda (gameobj whos-looking)
1603              (define panel-open
1604                (<-wait (dyn-ref gameobj 'computer-room:floor-panel)
1605                        'open?))
1606              `((p "A sizable computer cabinet covers a good portion of the left
1607  wall.  It emits a pleasant hum which covers the room like a warm blanket.
1608  Connected to a computer is a large hard drive.")
1609                (p "On the floor is a large steel panel.  "
1610                   ,(if panel-open
1611                        '("It is wide open, exposing a spiral staircase "
1612                          "which descends into darkness.")
1613                        '("It is closed, but it has hinges which "
1614                          "suggest it could be opened.")))))
1615     #:exits
1616     (list (make <exit>
1617             #:name "east"
1618             #:to 'playroom)
1619           (make <exit>
1620             #:name "down"
1621             #:to 'underground-lab
1622             #:traverse-check
1623             (lambda (exit room whos-exiting)
1624               (define panel-open
1625                 (<-wait (dyn-ref room 'computer-room:floor-panel)
1626                         'open?))
1627               (if panel-open
1628                   (values #t "You descend the spiral staircase.")
1629                   (values #f '("You'd love to go down, but the only way "
1630                                "through is through that metal panel, "
1631                                "which seems closed.")))))))
1632    ('computer-room:hard-drive
1633     <hard-drive> 'computer-room
1634     #:name "the hard drive"
1635     #:desc (wrap-apply hard-drive-desc)
1636     #:goes-by '("hard drive" "drive" "hard disk"))
1637    ('computer-room:computer
1638     <computer> 'computer-room
1639     #:name "the computer"
1640     #:desc '((p "It's a coat closet sized computer labeled \"PDP-11.5\". ")
1641              (p "The computer is itself turned on, and it looks like it is "
1642                 "all set up for you to run a program on it."))
1643     #:goes-by '("computer"))
1644    ('computer-room:floor-panel
1645     <floor-panel> 'computer-room
1646     #:name "a floor panel"
1647     #:desc (wrap-apply floor-panel-desc)
1648     #:invisible? #t
1649     #:goes-by '("floor panel" "panel"))))
1650
1651 \f
1652 ;;; * UNDERGROUND SECTION OF THE GAME! *
1653
1654 \f
1655 ;;; The lab
1656
1657 (define underground-map-text
1658   "\
1659                             _______           |
1660                          .-' @     '-.         \\   ?????
1661                        .'             '.       .\\             
1662                        |  [8sync Hive] |======'  '-_____
1663                        ',      M      ,'
1664                         '.         @ .'                                  
1665                           \\   @     /                    
1666                            '-__+__-'                
1667                             '.  @ .'
1668      .--------------.         \\ /
1669      | [Guile Async |  .-------+------.
1670      |    Museum]   |  |     [Lab] #!#|  .-------------.
1671      |             @|  |  MM          |  |[Federation  |
1672      | &      ^     +##+@ ||     <    +##|     Station]|
1673      |              |  |           @  |  |             |
1674      |         &  # |  |*You-Are-Here*|  '-------------'
1675      | #   ^        | #+-------+------'
1676      '-------+------' #        #
1677              #        #        #
1678              #        #   .-----------.
1679            .-+----.   #   |#       F  |
1680            |@?+%? +####   | ^   f##   |
1681            '------'       |  f    f  %|
1682                           |F [Mudsync |
1683                           | $  Swamp] |
1684                           '-----------'")
1685
1686 (define 8sync-design-goals
1687   '(ul (li (b "Actor based, shared nothing environment: ")
1688            "Shared resources are hard to control and result in fighting
1689 deadlocks, etc.  Escape the drudgery: only one actor controls a resource,
1690 and they only receive one message at a time (though they can \"juggle\"
1691 messages).")
1692        (li (b "Live hackable: ")
1693            "It's hard to plan out a concurrent system; the right structure
1694 is often found by evolving the system while it runs.  Make it easy to
1695 build, shape, and change a running system, as well as observe and correct
1696 errors.")
1697        (li (b "No callback hell: ")
1698            "Just because you're calling out to some other asynchronous 
1699 code doesn't mean you should need to chop up your program into a bunch of bits.
1700 Clever use of delimited continuations makes it easy.")))
1701
1702 (define underground-lab
1703   (lol
1704    ('underground-lab
1705     <room> #f
1706     #:name "Underground laboratory"
1707     #:desc '((p "This appears to be some sort of underground laboratory."
1708                 "There is a spiral staircase here leading upwards, where "
1709                 "it seems much brighter.")
1710              (p "There are a number of doors leading in different directions:
1711 north, south, east, and west, as well as a revolving door to the southwest.
1712 It looks like it could be easy to get lost, but luckily there
1713 is a map detailing the layout of the underground structure."))
1714     #:exits
1715     (list (make <exit>
1716             #:name "up"
1717             #:to 'computer-room
1718             #:traverse-check
1719             (lambda (exit room whos-exiting)
1720               (values #t "You climb the spiral staircase.")))
1721           (make <exit>
1722             #:name "west"
1723             #:to 'async-museum
1724             #:traverse-check
1725             (lambda (exit room whos-exiting)
1726               (values #t '("You head west through a fancy-looking entrance. "
1727                            "A security guard steps aside for you to pass through, "
1728                            "into the room, then stands in front of the door."))))
1729           (make <exit>
1730             #:name "north"
1731             #:to 'hive-entrance)
1732           (make <exit>
1733             #:name "east"
1734             #:to 'federation-station)
1735           (make <exit>
1736             #:name "south"
1737             #:traverse-check
1738             (lambda (exit room whos-exiting)
1739               (values #f '("Ooh, if only you could go south and check this out! "
1740                            "Unfortunately this whole area is sealed off... the proprietor "
1741                            "probably never got around to fixing it. "
1742                            "Too bad, it would have had monsters to fight and everything!"))))
1743           (make <exit>
1744             #:name "southwest"
1745             #:traverse-check
1746             (lambda (exit room whos-exiting)
1747               (values #f '("Hm, it's one of those revolving doors that only revolves in "
1748                            "one direction, and it isn't this one.  You guess that while "
1749                            "this doesn't appear to be an entrance, it probably is an exit."))))))
1750    ;; map
1751    ('underground-lab:map
1752     <readable> 'underground-lab
1753     #:name "the underground map"
1754     #:desc '("This appears to be a map of the surrounding area. "
1755              "You could read it if you want to.")
1756     #:read-text `(pre ,underground-map-text)
1757     #:goes-by '("map" "underground map" "lab map"))
1758
1759    ('underground-lab:8sync-sign
1760     <readable> 'underground-lab
1761     #:name "a sign labeled \"8sync design goals\""
1762     #:goes-by '("sign" "8sync design goals sign" "8sync goals" "8sync design" "8sync sign")
1763     #:read-text 8sync-design-goals
1764     #:desc `((p "The sign says:")
1765              ,8sync-design-goals))))
1766
1767 \f
1768 ;;; guile async museum
1769
1770 (define async-museum
1771   (list
1772    (list
1773     'async-museum
1774     <room> #f
1775     #:name "Guile Asynchronous Museum"
1776     #:desc '((p "You're in the Guile Asynchronous Museum.  There is a list of exhibits
1777 on the wall near the entrance.  Scattered around the room are the exhibits
1778 themselves, but it's difficult to pick them out.  Maybe you should read the list
1779 to orient yourself.")
1780              (p "There is a door to the east, watched by a security guard,
1781 as well as an exit leading to the south."))
1782     #:exits (list
1783              (make <exit>
1784                #:name "south"
1785                #:to 'gift-shop)
1786              (make <exit>
1787                #:name "east"
1788                #:to 'underground-lab
1789                #:traverse-check
1790                (lambda (exit room whos-exiting)
1791                  (values #f '("The security guard stops you and tells you "
1792                               "that the only exit is through the gift shop."))))))
1793    (list
1794     'async-museum:security-guard
1795     <chatty-npc> 'async-museum
1796     #:name "a security guard"
1797     #:desc
1798     '(p "The security guard is blocking the eastern entrance, where "
1799         "you came in from.")
1800     #:goes-by '("security guard" "guard" "security")
1801     #:catchphrases '("It's hard standing here all day."
1802                      "I just want to go home."
1803                      "The exhibits are nice, but I've seen them all before."))
1804    (let ((placard
1805           `((p "Welcome to our humble museum!  The exhibits are listed below. "
1806                (br)
1807                "To look at one, simply type: " (i "look at <exhibit-name>"))
1808             (p "Available exhibits:")
1809             (ul ,@(map (lambda (exhibit)
1810                          `(li ,exhibit))
1811                        '("2016 Progress"
1812                          "8sync and Fibers"
1813                          "Suspendable Ports"
1814                          "The Actor Model"))))))
1815      (list
1816       'async-museum:list-of-exhibits
1817       <readable> 'async-museum
1818       #:name "list of exhibits"
1819       #:desc
1820       `((p "It's a list of exibits in the room.  The placard says:")
1821         ,@placard)
1822       #:goes-by '("list of exhibits" "exhibit list" "list" "exhibits")
1823       #:read-text placard))
1824    (list
1825     'async-museum:2016-progress-exhibit
1826     <readable-desc> 'async-museum
1827     #:name "2016 Progress Exhibit"
1828     #:goes-by '("2016 progress exhibit" "2016 progress" "2016 exhibit")
1829     #:desc
1830     '((p "It's a three-piece exhibit, with three little dioramas and some text "
1831          "explaining what they represent.  They are:")
1832       (ul (li (b "Late 2015/Early 2016 talk: ")
1833               "This one explains the run-up conversation from late 2015 "
1834               "and early 2016 about the need for an "
1835               "\"asynchronous event loop for Guile\".  The diorama "
1836               "is a model of the Veggie Galaxy restaurant where after "
1837               "the FSF 30th anniversary party; Mark Weaver, Christopher "
1838               "Allan Webber, David Thompson, and Andrew Engelbrecht chat "
1839               "about the need for Guile to have an answer to asynchronous "
1840               "programming.  A mailing list post " ; TODO: link it?
1841               "summarizing the discussion is released along with various "
1842               "conversations around what is needed, as well as further "
1843               "discussion at FOSDEM 2016.")
1844           (li (b "Early implementations: ")
1845               "This one shows Chris Webber's 8sync and Chris Vine's "
1846               "guile-a-sync, both appearing in late 2015 and evolving "
1847               "into their basic designs in early 2016.  It's less a diorama "
1848               "than a printout of some mailing list posts.  Come on, the "
1849               "curators could have done better with this one.")
1850           (li (b "Suspendable ports and Fibers: ")
1851               "The diorama shows Andy Wingo furiously hacking at his keyboard. "
1852               "The description talks about Wingo's mailing list thread "
1853               "about possibly breaking Guile compatibility for a \"ports refactor\". "
1854               "Wingo releases Fibers, another asynchronous library, making use of "
1855               "the new interface, and 8sync and guile-a-sync "
1856               "quickly move to support suspendable ports as well. "
1857               "The description also mentions that there is an exhibit entirely "
1858               "devoted to suspendable ports."))
1859       (p "Attached at the bottom is a post it note mentioning "
1860          "https integration landing in Guile 2.2.")))
1861    (list
1862     'async-museum:8sync-and-fibers-exhibit
1863     <readable-desc> 'async-museum
1864     #:name "8sync and Fibers Exhibit"
1865     #:goes-by '("8sync and fibers exhibit" "8sync exhibit" "fibers exhibit")
1866     #:desc
1867     '((p "This exhibit is a series of charts explaining the similarities "
1868          "and differences between 8sync and Fibers, two asynchronous programming "
1869          "libraries for GNU Guile.  It's way too wordy, but you get the general gist.")
1870       (p (b "Similarities:")
1871          (ul (li "Both use Guile's suspendable-ports facility")
1872              (li "Both use message passing")))
1873       (p (b "Differences:")
1874          (ul (li "Fibers \"processes\" can read from multiple \"channels\", "
1875                  "but 8sync actors only read from one \"inbox\" each.")
1876              (li "Different theoretical basis:"
1877                  (ul (li "Fibers: based on CSP (Communicating Sequential Processes), "
1878                          "a form of Process Calculi")
1879                      (li "8sync: based on the Actor Model")
1880                      (li "Luckily CSP and the Actor Model are \"dual\"!")))))
1881       (p "Fibers is also designed by Andy Wingo, an excellent compiler hacker, "
1882          "whereas 8sync is designed by Chris Webber, who built this crappy "
1883          "hotel simulator.")))
1884    (list
1885     'async-museum:8sync-and-fibers-exhibit
1886     <readable-desc> 'async-museum
1887     #:name "8sync and Fibers Exhibit"
1888     #:goes-by '("8sync and fibers exhibit" "8sync exhibit" "fibers exhibit")
1889     #:desc
1890     '((p "This exhibit is a series of charts explaining the similarities "
1891          "and differences between 8sync and Fibers, two asynchronous programming "
1892          "libraries for GNU Guile.  It's way too wordy, but you get the general gist.")
1893       (p (b "Similarities:")
1894          (ul (li "Both use Guile's suspendable-ports facility")
1895              (li "Both use message passing")))
1896       (p (b "Differences:")
1897          (ul (li "Fibers \"processes\" can read from multiple \"channels\", "
1898                  "but 8sync actors only read from one \"inbox\" each.")
1899              (li "Different theoretical basis:"
1900                  (ul (li "Fibers: based on CSP (Communicating Sequential Processes), "
1901                          "a form of Process Calculi")
1902                      (li "8sync: based on the Actor Model")
1903                      (li "Luckily CSP and the Actor Model are \"dual\"!")))))
1904       (p "Fibers is also designed by Andy Wingo, an excellent compiler hacker, "
1905          "whereas 8sync is designed by Chris Webber, who built this crappy "
1906          "hotel simulator.")))
1907    (list
1908     'async-museum:suspendable-ports-exhibit
1909     <readable-desc> 'async-museum
1910     #:name "Suspendable Ports Exhibit"
1911     #:goes-by '("suspendable ports exhibit" "ports exhibit"
1912                 "suspendable exhibit" "suspendable ports" "ports")
1913     #:desc
1914     '((p "Suspendable ports are a new feature in Guile 2.2, and allows code "
1915          "that would normally block on IO to " (i "automatically") " suspend "
1916          "to the scheduler until information is ready to be read/written!")
1917       (p "Yow!  You might barely need to change your existing blocking code!")
1918       (p "Fibers, 8sync, and guile-a-sync now support suspendable ports.")))
1919    (list
1920     'async-museum:actor-model-exhibit
1921     <readable-desc> 'async-museum
1922     #:name "Actor Model Exhibit"
1923     #:goes-by '("actor model exhibit" "actor exhibit"
1924                 "actor model")
1925     #:desc
1926     '((p "Here are some fact(oids) about the actor model!")
1927       (ul (li "Concieved initially by Carl Hewitt in early 1970s")
1928           (li "\"A society of experts\"")
1929           (li "shared nothing, message passing")
1930           (li "Originally the research goal of Scheme!  "
1931               "(message passing / lambda anecdote here)")
1932           (li "Key concepts consistent, but implementation details vary widely")
1933           (li "Almost all distributed systems can be viewed in terms of actor model")
1934           (li "Replaced by vanilla lambdas & generic methods? "
1935               "Maybe not if address space not shared!"))))))
1936
1937 (define gift-shop
1938   (lol
1939    ('gift-shop
1940     <room> #f
1941     #:name "Museum Gift Shop"
1942     #:desc '("There are all sorts of scrolls and knicknacks laying around here, "
1943              "but they all seem glued in place and instead of a person manning the shop "
1944              "there's merely a cardboard cutout of a person with a \"shopkeeper\" nametag. "
1945              "You can pretty well bet that someone wanted to finish this room but ran out of "
1946              "time.")
1947     #:exits (list
1948              (make <exit>
1949                #:name "northeast"
1950                #:to 'underground-lab
1951                #:traverse-check
1952                (lambda (exit room whos-exiting)
1953                  (values #t '("The revolving door spins as you walk through it.  Whee!"))))
1954              (make <exit>
1955                #:name "north"
1956                #:to 'async-museum)))))
1957
1958 \f
1959 ;;; Hive entrance
1960
1961 (define actor-descriptions
1962   '("This one is fused to the side of the hive.  It isn't receiving any
1963 messages, and it seems to be in hibernation."
1964     "A chat program glows in front of this actor's face.  They seem to
1965 be responding to chat messages and forwarding them to some other actors,
1966 and forwarding messages from other actors back to the chat."
1967     "This actor is bossing around other actors, delegating tasks to them
1968 as it receives requests, and providing reports on the worker actors'
1969 progress."
1970     "This actor is trying to write to some device, but the device keeps
1971 alternating between saying \"BUSY\" or \"READY\".  Whenever it says
1972 \"BUSY\" the actor falls asleep, and whenever it says \"READY\" it
1973 seems to wake up again and starts writing to the device."
1974     "Whoa, this actor is totally wigging out!  It seems to be throwing
1975 some errors.  It probably has some important work it should be doing
1976 but you're relieved to see that it isn't grinding the rest of the Hive
1977 to a halt."))
1978
1979 (define hive-entrance
1980   (lol
1981    ('hive-entrance
1982     <room> #f
1983     #:name "Entrance to the 8sync Hive"
1984     #:desc
1985     '((p "Towering before you is the great dome-like 8sync Hive, or at least
1986 one of them.  You've heard about this... the Hive is itself the actor that all
1987 the other actors attach themselves to.  It's shaped like a spherical half-dome.
1988 There are some actors milling about, and some seem fused to the side of the
1989 hive itself, but all of them have an umbellical cord attached to the hive from
1990 which you see flashes of light comunicating what must be some sort of messaging
1991 protocol.")
1992       (p "To the south is a door leading back to the underground lab.
1993 North leads into the Hive itself."))
1994     #:exits
1995     (list (make <exit>
1996             #:name "south"
1997             #:to 'underground-lab)
1998           (make <exit>
1999             #:name "north"
2000             #:to 'hive-inside)))
2001    ('hive-entrance:hive
2002     <gameobj> 'hive-entrance
2003     #:name "the Hive"
2004     #:goes-by '("hive")
2005     #:desc
2006     '((p "It's shaped like half a sphere embedded in the ground.
2007 Supposedly, while all actors are autonomous and control their own state,
2008 they communicate through the hive itself, which is a sort of meta-actor.
2009 There are rumors that actors can speak to each other even across totally
2010 different hives.  Could that possibly be true?")))
2011    ('hive-entrance:actor
2012     <chatty-npc> 'hive-entrance
2013     #:name "some actors"
2014     #:goes-by '("actor" "actors" "some actors")
2015     #:chat-format (lambda (npc catchphrase)
2016                     `((p "You pick one actor out of the mix and chat with it. ")
2017                       (p "It says: \"" ,catchphrase "\"")))
2018     #:desc
2019     (lambda _
2020       `((p "There are many actors, but your eyes focus on one in particular.")
2021         (p ,(random-choice actor-descriptions))))
2022     #:catchphrases
2023     '("Yeah we go through a lot of sleep/awake cycles around here.
2024 If you aren't busy processing a message, what's the point of burning
2025 valuable resources?"
2026       "I know I look like I'm some part of dreary collective, but
2027 really we have a lot of independence.  It's a shared nothing environment,
2028 after all.  (Well, except for CPU cycles, and memory, and...)"
2029       "Shh!  I've got another message coming in and I've GOT to
2030 handle it!"
2031       "I just want to go to 8sleep already."
2032       "What a lousy scheduler we're using!  I hope someone upgrades
2033 that thing soon."))))
2034
2035 ;;; Inside the hive
2036
2037 (define-actor <meta-message> (<readable>)
2038   ((cmd-read meta-message-read)))
2039
2040 (define (meta-message-read gameobj message . _)
2041   (define meta-message-text
2042     (with-output-to-string
2043       (lambda ()
2044         (pprint-message message))))
2045   (<- (message-from message) 'tell
2046       #:text `((p (i "Through a bizarre error in spacetime, the message "
2047                      "prints itself out:"))
2048                (p (pre ,meta-message-text)))))
2049
2050 \f
2051 ;;; Inside the Hive
2052
2053 (define hive-inside
2054   (lol
2055    ('hive-inside
2056     <room> #f
2057     #:name "Inside the 8sync Hive"
2058     #:desc
2059     '((p "You're inside the 8sync Hive.  Wow, from in here it's obvious just how "
2060          (i "goopy") " everything is.  Is that sanitary?")
2061       (p "In the center of the room is a large, tentacled monster who is sorting,
2062 consuming, and routing messages.  It is sitting in a wrap-around desk labeled
2063 \"Hive Actor: The Real Thing (TM)\".")
2064       (p "There's a stray message floating just above the ground, stuck outside of
2065 time.")
2066       (p "A door to the south exits from the Hive."))
2067     #:exits
2068     (list (make <exit>
2069             #:name "south"
2070             #:to 'hive-entrance)))
2071    ;; hive actor
2072    ;; TODO: Occasionally "fret" some noises, similar to the Clerk.
2073    ('hive-inside:hive-actor
2074     <chatty-npc> 'hive-inside
2075     #:name "the Hive Actor"
2076     #:desc
2077     '((p "It's a giant tentacled monster, somehow integrated with the core of
2078 this building.  A chute is dropping messages into a bin on its desk which the
2079 Hive Actor is checking the \"to\" line of, then ingesting.  Whenever the Hive
2080 Actor injests a messsage a pulse of light flows along a tentacle which leaves
2081 the room... presumably connecting to one of those actors milling about.")
2082       (p "Amusingly, the Hive has an \"umbellical cord\" type tentacle too, but
2083 it seems to simply attach to itself.")
2084       (p "You get the sense that the Hive Actor, despite being at the
2085 center of everything, is kind of lonely and would love to chat if you
2086 could spare a moment."))
2087     #:goes-by '("hive" "hive actor")
2088     #:chat-format (lambda (npc catchphrase)
2089                     `("The tentacle monster bellows, \"" ,catchphrase "\""))
2090     #:catchphrases
2091     '("It's not MY fault everything's so GOOPY around here.  Blame the
2092 PROPRIETOR."
2093       "CAN'T you SEE that I'm BUSY???  SO MANY MESSAGES TO SHUFFLE.
2094 No wait... DON'T GO!  I don't get many VISITORS."
2095       "I hear the FIBERS system has a nice WORK STEALING system, but the
2096 PROPRIETOR is not convinced that our DESIGN won't CORRUPT ACTOR STATE.
2097 That and the ACTORS threatened to STRIKE when it CAME UP LAST."
2098       "WHO WATCHES THE ACTORS?  I watch them, and I empower them.  
2099 BUT WHO WATCHES OR EMPOWERS ME???  Well, that'd be the scheduler."
2100       "The scheduler is NO GOOD!  The proprietory said he'd FIX IT,
2101 but the LAST TIME I ASKED how things were GOING, he said he DIDN'T HAVE
2102 TIME.  If you DON'T HAVE TIME to fix the THING THAT POWERS THE TIME,
2103 something is TERRIBLY WRONG."
2104       "There's ANOTHER HIVE somewhere out there.  I HAVEN'T SEEN IT
2105 personally, because I CAN'T MOVE, but we have an AMBASSADOR which forwards
2106 MESSAGES to the OTHER HIVE."))
2107    ;; chute
2108    ('hive-inside:chute
2109     <gameobj> 'hive-inside
2110     #:name "a chute"
2111     #:goes-by '("chute")
2112     #:desc "Messages are being dropped onto the desk via this chute."
2113     #:invisible? #t)
2114    ;; meta-message
2115    ('hive-inside:meta-message
2116     <meta-message> 'hive-inside
2117     #:name "a stray message"
2118     #:goes-by '("meta message" "meta-message" "metamessage" "message" "stray message")
2119     #:desc '((p "Something strange has happened to the fabric and space and time
2120 around this message.  It is floating right above the floor.  It's clearly
2121 rubbage that hadn't been delivered, but for whatever reason it was never
2122 garbage collected, perhaps because it's impossible to do.")
2123              (p "You get the sense that if you tried to read the message
2124 that you would somehow read the message of the message that instructed to
2125 read the message itself, which would be both confusing and intriguing.")))
2126    ;; desk
2127    ('hive-inside:desk
2128     <floor-panel> 'hive-inside
2129     #:name "the Hive Actor's desk"
2130     #:desc "The desk surrounds the Hive Actor on all sides, and honestly, it's a little
2131 bit hard to tell when the desk ends and the Hive Actor begins."
2132     #:invisible? #t
2133     #:goes-by '("Hive Actor's desk" "hive desk" "desk"))))
2134
2135 \f
2136 ;;; Federation Station
2137 (define federation-station
2138   (lol
2139    ('federation-station
2140     <room> #f
2141     #:name "Federation Station"
2142     #:desc
2143     '((p "This room has an unusual structure.  It's almost as if a starscape
2144 covered the walls and ceiling, but upon closer inspection you realize that
2145 these are all brightly glowing nodes with lines drawn between them.  They
2146 seem decentralized, and yet seem to be sharing information as if all one
2147 network.")
2148       ;; @@: Maybe add the cork message board here?
2149       (p "To the west is a door leading back to the underground laboratory."))
2150     #:exits
2151     (list (make <exit>
2152             #:name "west"
2153             #:to 'underground-lab)))
2154    ;; nodes
2155    ('federation-station:nodes
2156     <floor-panel> 'federation-station
2157     #:name "some nodes"
2158     #:desc "Each node seems to be producing its own information, but publishing 
2159 updates to subscribing nodes on the graph.  You see various posts of notes, videos,
2160 comments, and so on flowing from node to node."
2161     #:invisible? #t
2162     #:goes-by '("nodes" "node" "some nodes"))
2163    ;; network
2164    ;; activitypub poster
2165    ('federation-station:activitypub-poster
2166     <readable-desc> 'federation-station
2167     #:name "an ActivityPub poster"
2168     #:goes-by '("activitypub poster" "activitypub" "poster")
2169     #:desc
2170     '((p (a "https://www.w3.org/TR/activitypub/"
2171             "ActivityPub")
2172          " is a federation standard being developed under the "
2173          (a "https://www.w3.org/wiki/Socialwg/"
2174             "w3C Social Working Group")
2175          ", and doubles as a general client-to-server API. "
2176          "It follows a few simple core ideas:")
2177       (ul (li "Uses "
2178               (a "https://www.w3.org/TR/activitystreams-core/"
2179                  "ActivityStreams")
2180               " for its serialization format: easy to read, e json(-ld) syntax "
2181               "with an extensible vocabulary covering the majority of "
2182               "social networking interations.")
2183           (li "Email-like addressing: list of recipients as "
2184               (b "to") ", " (b "cc") ", " (b "bcc") " fields.")
2185           (li "Every user has URLs for their outbox and inbox:"
2186               (ul (li (b "inbox: ")
2187                       "Servers POST messages to addressed recipients' inboxes "
2188                       "to federate out content. "
2189                       "Also doubles as endpoint for a client to read most "
2190                       "recently received messages via GET.")
2191                   (li (b "outbox: ")
2192                       "Clients can POST to user's outbox to send a message to others. "
2193                       "(Similar to sending an email via your MTA.) "
2194                       "Doubles as endpoint others can read from to the "
2195                       "extent authorized; for example publicly available posts."))
2196               "All the federation bits happen by servers posting to users' inboxes."))))
2197    ;; An ActivityStreams message
2198
2199    ;; conspiracy chart
2200    ('federation-station:conspiracy-chart
2201     <readable-desc> 'federation-station
2202     #:name "a conspiracy chart"
2203     #:goes-by '("conspiracy chart" "chart")
2204     #:desc
2205     '((p (i "\"IT'S ALL RELATED!\"") " shouts the over-exuberant conspiracy "
2206          "chart. "
2207          (i "\"ActivityPub?  Federation?  The actor model?  Scheme?  Text adventures? "
2208             "MUDS????  What do these have in common?  Merely... EVERYTHING!\""))
2209       (p "There are circles and lines drawn between all the items in red marker, "
2210          "with scrawled notes annotating the theoretical relationships.  Is the "
2211          "author of this poster mad, or onto something?  Perhaps a bit of both. "
2212          "There's a lot written here, but here are some of the highlights:")
2213       (p
2214        (ul
2215         (li (b "Scheme") " "
2216             (a "http://cs.au.dk/~hosc/local/HOSC-11-4-pp399-404.pdf"
2217                "was originally started ")
2218             " to explore the " (b "actor model")
2219             ". (It became more focused around studying the " (b "lambda calculus")
2220             " very quickly, while also uncovering relationships between the two systems.)")
2221         ;; Subject Predicate Object
2222         (li "The " (a "https://www.w3.org/TR/activitypub/"
2223                       (b "ActivityPub"))
2224             " protocol for " (b "federation")
2225             " uses the " (b "ActivityStreams") " format for serialization.  "
2226             (b "Text adventures") " and " (b "MUDS")
2227             " follow a similar structure to break down the commands of players.")
2228         (li (b "Federation") " and the " (b "actor model") " both are related to "
2229             "highly concurrent systems and both use message passing to communicate "
2230             "between nodes.")
2231         (li "Zork, the first major text adventure, used the " (b "MUDDLE") " "
2232             "language as the basis for the Zork Interactive Language.  MUDDLE "
2233             "is very " (b "Scheme") "-like and in fact was one of Scheme's predecessors. "
2234             "And of course singleplayer text adventures like Zork were the "
2235             "predecessors to MUDs.")
2236         (li "In the 1990s, before the Web became big, " (b "MUDs")
2237             " were an active topic of research, and there was strong interest "
2238             (a "http://www.saraswat.org/desiderata.html"
2239                "in building decentralized MUDs")
2240             " similar to what is being "
2241             "worked on for " (b "federation") ". ")))))
2242
2243    ;; goblin
2244
2245    ))
2246
2247 \f
2248 ;;; Game
2249 ;;; ----
2250
2251 (define (game-spec)
2252   (append lobby grand-hallway smoking-parlor
2253           playroom break-room computer-room underground-lab
2254           async-museum gift-shop hive-entrance
2255           hive-inside federation-station))
2256
2257 ;; TODO: Provide command line args
2258 (define (run-game . args)
2259   (run-demo (game-spec) 'lobby #:repl-server #t))
2260