;;; Mudsync --- Live hackable MUD
-;;; Copyright © 2016 Christopher Allan Webber <cwebber@dustycloud.org>
+;;; Copyright © 2016, 2017 Christine Lemmer-Webber <cwebber@dustycloud.org>
;;;
;;; This file is part of Mudsync.
;;;
(use-modules (mudsync)
(mudsync container)
- (8sync actors)
- (8sync agenda)
+ (8sync)
(oop goops)
(ice-9 control)
(ice-9 format)
#:init-thunk (build-actions
(cmd-read readable-cmd-read))))
-(define (readable-cmd-read actor message)
+(define (readable-cmd-read actor message . _)
(<- (message-from message) 'tell
- #:text (string-append (slot-ref actor 'read-text) "\n")))
+ #:text (slot-ref actor 'read-text)))
+;; This one is just where reading is the same thing as looking
+;; at the description
+(define-class <readable-desc> (<gameobj>)
+ (commands
+ #:allocation #:each-subclass
+ #:init-thunk (build-commands
+ ("read" ((direct-command cmd-look-at))))))
+
;; This one allows you to take from items that are proxied by it
(define-actor <proxy-items> (<gameobj>)
((cmd-take-from take-from-proxy))
(mbody-val (<-wait obj-id 'goes-by)))
(when (ci-member direct-obj goes-by)
(<- obj-id 'cmd-take #:direct-obj direct-obj #:player player)
- (escape)))
+ (escape #f)))
(slot-ref gameobj 'proxy-items))
(<- player 'tell
;;; -----
(define (npc-chat-randomly actor message . _)
+ (define catchphrase
+ (random-choice (slot-ref actor 'catchphrases)))
(define text-to-send
- (format #f "~a says: \"~a\"\n"
- (slot-ref actor 'name)
- (random-choice (slot-ref actor 'catchphrases))))
+ ((slot-ref actor 'chat-format) actor catchphrase))
(<- (message-from message) 'tell
#:text text-to-send))
(define-class <chatty-npc> (<gameobj>)
(catchphrases #:init-value '("Blarga blarga blarga!")
#:init-keyword #:catchphrases)
+ (chat-format #:init-value (lambda (npc catchphrase)
+ `(,(slot-ref npc 'name) " says: \""
+ ,catchphrase "\""))
+ #:init-keyword #:chat-format)
(commands
#:allocation #:each-subclass
#:init-thunk (build-commands
#:allocation #:each-subclass
#:init-thunk (build-commands
("sign" ((prep-direct-command cmd-sign-form '("as"))))))
+
(actions #:allocation #:each-subclass
#:init-thunk (build-actions
(cmd-sign-form sign-cmd-sign-in))))
;; NPC: hotel owner
('lobby:hotel-owner
<chatty-npc> 'lobby
- #:name "a frumpy fellow"
+ #:name "a languid lady"
#:desc
- '((p " Whoever this is, they looks totally exhausted. They're
+ '((p " Whoever this is, she looks totally exhausted. She's
collapsed into the only comfortable looking chair in the room and you
-don't get the sense that they're likely to move any time soon.
- You notice they're wearing a sticker badly adhesed to their clothing
-which says \"Hotel Proprietor\", but they look so disorganized that you
-think that can't possibly be true... can it?
- Despite their exhaustion, you sense they'd be happy to chat with you,
+don't get the sense that she's likely to move any time soon.
+ Attached to her frumpy dress is a barely secured pin which says
+\"Hotel Proprietor\", but she looks so disorganized that you think
+that can't possibly be true... can it?
+ Despite her exhaustion, you sense she'd be happy to chat with you,
though the conversation may be a bit one sided."))
- #:goes-by '("frumpy fellow" "fellow"
- "Chris Webber" ; heh, did you rtfc? or was it so obvious?
+ #:goes-by '("languid lady" "lady"
"hotel proprietor" "proprietor")
#:catchphrases hotel-owner-grumps)
;; Object: Sign
<sign-in-form> 'lobby
#:name "sign-in form"
#:goes-by '("sign-in form" "form" "signin form")
- #:desc "It looks like you could sign this form and set your name.")
+ #:desc '("It looks like you could sign this form and set your name like so: "
+ (i "sign form as <my-name-here>")))
;; Object: curio cabinets
+ ;; TODO: respond to attempts to open the curio cabinet
('lobby:cabinet
<proxy-items> 'lobby
#:proxy-items '(lobby:porcelain-doll
You can imagine yourself joining a tea party using this set, around a
nice table with some doilies, drinking some Earl Grey tea, hot. Mmmm."
#:goes-by '("tea set" "tea"))
+ ('lobby:cups
+ <cabinet-item> 'lobby
+ #:invisible? #t
+ #:name "cups from the tea set"
+ #:desc "They're chipped."
+ #:goes-by '("cups"))
('lobby:mustard-pot
<cabinet-item> 'lobby
#:invisible? #t
#:goes-by '("orange cartoon cat phone" "orange cartoon cat telephone"
"orange cat phone" "orange cat telephone"
"cartoon cat phone" "cartoon cat"
- "cat phone" "cat telephone" "phone" "telephone"))))
+ "cat phone" "cat telephone" "phone" "telephone"))
+ ('lobby:monster-stuffie
+ <gameobj> 'lobby
+ #:name "an off-brand monster stuffie"
+ #:desc "It's an off brand monster stuffed animal that looks, well kinda
+like a popular character you've seen in a video game, but there's been a very
+thin attempt to make it look like something different... mostly by changing
+the shape of the ears. It's cute though!"
+ #:take-me? #t
+ #:goes-by '("monster stuffie" "monster" "stuffed animal" "stuffed monster"
+ "off-brand monster stuffie" "stuffie" "monster stuffie"))))
\f
;;; Grand hallway
;;; -------------
+(define-actor <disc-shield> (<gameobj>)
+ ((cmd-take disc-shield-take)))
+
+(define* (disc-shield-take gameobj message
+ #:key direct-obj
+ (player (message-from message)))
+ (create-gameobj <glowing-disc> (gameobj-gm gameobj)
+ player) ;; set loc to player to put in player's inventory
+ (<- player 'tell
+ #:text '((p "As you attempt to pull the shield / disk platter
+from the statue a shining outline appears around it... and a
+completely separate, glowing copy of the disc materializes into your
+hands!")))
+ (<- (gameobj-loc gameobj) 'tell-room
+ #:text `(,(mbody-val (<-wait player 'get-name))
+ " pulls on the shield of the statue, and a glowing "
+ "copy of it materializes into their hands!")
+ #:exclude player)
+ (<- (gameobj-loc gameobj) 'tell-room
+ #:text
+ '(p "You hear a voice whisper: "
+ (i "\"Share the software... and you'll be free...\""))))
+
+;;; This is the disc that gets put in the player's inventory
+(define-actor <glowing-disc> (<gameobj>)
+ ((cmd-drop glowing-disc-drop-cmd))
+ (initial-props
+ #:allocation #:each-subclass
+ #:init-thunk (build-props
+ '((hd-platter? . #t))))
+ (name #:allocation #:each-subclass
+ #:init-value "a glowing disc")
+ (desc #:allocation #:each-subclass
+ #:init-value "A brightly glowing disc. It's shaped like a hard
+drive platter, not unlike the one from the statue it came from. It's
+labeled \"RL02.5\".")
+ (goes-by #:init-value '("glowing disc" "glowing platter"
+ "glowing disc platter" "glowing disk platter"
+ "platter" "disc" "disk" "glowing shield")))
+
+(define* (glowing-disc-drop-cmd gameobj message
+ #:key direct-obj
+ (player (message-from message)))
+ (<- player 'tell
+ #:text "You drop the glowing disc, and it shatters into a million pieces!")
+ (<- (mbody-val (<-wait player 'get-loc)) 'tell-room
+ #:text `(,(mbody-val (<-wait player 'get-name))
+ " drops a glowing disc, and it shatters into a million pieces!")
+ #:exclude player)
+ (gameobj-self-destruct gameobj))
+
+\f
+;;; Grand hallway
+
+(define lobby-map-text
+ "\
+
+ .----+++++----.
+ | : : |
+ + : north : +
+ | : hall : |
+ + : : +
+ |_ : _____ : _|
+ | : : |
+ .----------.----------. : & : .----------.----------.
+ | computer | |& :YOU ARE: &| smoking | *UNDER* |
+ | room + playroom + : HERE : + parlor | *CONS- |
+ | > | |& : : &| | TRUCTION*|
+ '----------'----------'-++-------++-'-------+--'----------'
+ | '-----' | | |
+ : LOBBY : '---'
+ '. .'
+ '---------'")
+
(define grand-hallway
+
(lol
('grand-hallway
<room> #f
room\", while a door to the west is labeled \"playroom\"."))
#:exits
(list (make <exit>
+ #:name "north"
+ #:to 'north-hall)
+ (make <exit>
#:name "south"
#:to 'lobby)
(make <exit>
(make <exit>
#:name "east"
#:to 'smoking-parlor)))
+ ('grand-hallway:map
+ <readable> 'grand-hallway
+ #:name "the hotel map"
+ #:desc '("This appears to be a map of the hotel. "
+ "Like the hotel itself, it seems to be "
+ "incomplete."
+ "You could read it if you want to.")
+ #:read-text `(pre ,lobby-map-text)
+ #:goes-by '("map" "hotel map"))
('grand-hallway:carpet
<gameobj> 'grand-hallway
#:name "the Grand Hallway carpet"
spirit\". You've heard of Hackthena... not a goddess, but spiritual protector of
all good hacks, and legendary hacker herself.")
(p "Hackthena holds the form of a human woman. She wears flowing
-robes, has a pear of curly bovine-esque horns protruding from the sides of her
+robes, has a pair of curly bovine-esque horns protruding from the sides of her
head, wears a pair of horn-rimmed glasses, and appears posed as if for battle.
But instead of a weapon, she seems to hold some sort of keyboard. And her
shield... well it's round like a shield, but something seems off about it.
`("Are you seriously considering desecrating a statue?")))
#:goes-by '("hackthena's horns" "horns" "horns of hacktena"))
('grand-hallway:disc-platter
- <gameobj> 'grand-hallway
- #:name "a hard disc platter"
- #:desc "This isn't a shield after all, it seems to be a hard disc
-platter! It looks kind of loose..."
+ <disc-shield> 'grand-hallway
+ #:name "Hackthena's shield"
+ #:desc "No wonder the \"shield\" looks unusual... it seems to be a hard disk
+platter! It has \"RL02.5\" written on it. It looks kind of loose."
#:invisible? #t
- #:goes-by '("hard disc platter" "disc platter" "disc"
- "hard disk platter" "disk platter"
- "shield" "platter"))))
+ #:goes-by '("hackthena's shield" "shield" "platter" "hard disk platter"))))
\f
;;; Playroom
;;; --------
+(define-actor <rgb-machine> (<gameobj>)
+ ((cmd-run rgb-machine-cmd-run)
+ (cmd-reset rgb-machine-cmd-reset))
+ (commands
+ #:allocation #:each-subclass
+ #:init-thunk (build-commands
+ (("run" "start") ((direct-command cmd-run)))
+ ("reset" ((direct-command cmd-reset)))))
+ (resetting #:init-value #f
+ #:accessor .resetting)
+ ;; used to reset, and to kick off the first item in the list
+ (rgb-items #:init-keyword #:rgb-items
+ #:accessor .rgb-items))
+
+(define (rgb-machine-cmd-run rgb-machine message . _)
+ (define player (message-from message))
+ (<-wait player 'tell
+ #:text '("You start the rube goldberg machine."))
+ (<-wait (gameobj-loc rgb-machine) 'tell-room
+ #:text `(,(mbody-val (<-wait player 'get-name))
+ " runs the rube goldberg machine.")
+ #:exclude player)
+ (8sleep 1)
+ (match (.rgb-items rgb-machine)
+ ((first-item rest ...)
+ (<- (dyn-ref rgb-machine first-item) 'trigger))))
+
+(define (rgb-machine-cmd-reset rgb-machine message . _)
+ (define player (message-from message))
+ (cond
+ ((not (.resetting rgb-machine))
+ (set! (.resetting rgb-machine) #t)
+ (<-wait player 'tell
+ #:text '("You reset the rube goldberg machine."))
+ (<-wait (gameobj-loc rgb-machine) 'tell-room
+ #:text `(,(mbody-val (<-wait player 'get-name))
+ " resets the rube goldberg machine.")
+ #:exclude player)
+ (<-wait (gameobj-loc rgb-machine) 'tell-room
+ #:text '("From a panel in the wall, a white gloved mechanical "
+ "arm reaches out to reset all the "
+ "rube goldberg components."))
+ (8sleep (/ 1 2))
+ (for-each
+ (lambda (rgb-item)
+ (<- (dyn-ref rgb-machine rgb-item) 'reset)
+ (8sleep (/ 1 2)))
+ (.rgb-items rgb-machine))
+ (<- (gameobj-loc rgb-machine) 'tell-room
+ #:text "The machine's mechanical arm retreats into the wall!")
+ (set! (.resetting rgb-machine) #f))
+ (else
+ (<-wait player 'tell
+ #:text '("But it's in the middle of resetting right now!")))))
+
+(define-actor <rgb-item> (<gameobj>)
+ ((trigger rgb-item-trigger)
+ (reset rgb-item-reset))
+ (invisible? #:init-value #t)
+ (steps #:init-keyword #:steps
+ #:accessor .steps)
+ (triggers-as #:init-value #f
+ #:init-keyword #:triggers-as
+ #:getter .triggers-as)
+ (reset-msg #:init-keyword #:reset-msg
+ #:getter .reset-msg)
+ ;; States: ready -> running -> ran
+ (state #:init-value 'ready
+ #:accessor .state))
+
+
+(define (rgb-item-trigger rgb-item message . _)
+ (define room (gameobj-loc rgb-item))
+ (case (.state rgb-item)
+ ((ready)
+ ;; Set state to running
+ (set! (.state rgb-item) 'running)
+
+ ;; Loop through all steps
+ (for-each
+ (lambda (step)
+ (match step
+ ;; A string? That's the description of what's happening, tell players
+ ((? string? str)
+ (<- room 'tell-room #:text str))
+ ;; A number? Sleep for that many secs
+ ((? number? num)
+ (8sleep num))
+ ;; A symbol? That's another gameobj to look up dynamically
+ ((? symbol? sym)
+ (<- (dyn-ref rgb-item sym) 'trigger
+ #:triggered-by (.triggers-as rgb-item)))
+ (_ (throw 'unknown-step-type
+ "Don't know how to process rube goldberg machine step type?"
+ #:step step))))
+ (.steps rgb-item))
+
+ ;; We're done! Set state to ran
+ (set! (.state rgb-item) 'ran))
+
+ (else
+ (<- room 'tell-room
+ #:text `("... but " ,(slot-ref rgb-item 'name)
+ " has already been triggered!")))))
+
+(define (rgb-item-reset rgb-item message . _)
+ (define room (gameobj-loc rgb-item))
+ (case (.state rgb-item)
+ ((ran)
+ (set! (.state rgb-item) 'ready)
+ (<- room 'tell-room
+ #:text (.reset-msg rgb-item)))
+ ((running)
+ (<- room 'tell-room
+ #:text `("... but " ,(slot-ref rgb-item 'name)
+ " is currently running!")))
+ ((ready)
+ (<- room 'tell-room
+ #:text `("... but " ,(slot-ref rgb-item 'name)
+ " has already been reset.")))))
+
+(define-actor <rgb-kettle> (<rgb-item>)
+ ((trigger rgb-kettle-trigger)
+ (reset rgb-kettle-reset))
+ (heated #:accessor .heated
+ #:init-value #f)
+ (filled #:accessor .filled
+ #:init-value #f))
+
+(define* (rgb-kettle-trigger rgb-item message #:key triggered-by)
+ (define room (gameobj-loc rgb-item))
+ (if (not (eq? (.state rgb-item) 'ran))
+ (begin
+ (match triggered-by
+ ('water-demon
+ (set! (.state rgb-item) 'running)
+ (set! (.filled rgb-item) #t))
+ ('quik-heater
+ (set! (.state rgb-item) 'running)
+ (set! (.heated rgb-item) #t)))
+ (when (and (.filled rgb-item)
+ (.heated rgb-item))
+ (<- room 'tell-room
+ #:text '((i "*kshhhhhh!*")
+ " The water has boiled!"))
+ (8sleep .25)
+ (set! (.state rgb-item) 'ran)
+ ;; insert a cup of hot tea in the room
+ (create-gameobj <hot-tea> (gameobj-gm rgb-item) room)
+ (<- room 'tell-room
+ #:text '("The machine pours out a cup of hot tea! "
+ "Looks like the machine finished!"))))
+ (<- room 'tell-room
+ #:text `("... but " ,(slot-ref rgb-item 'name)
+ " has already been triggered!"))))
+
+(define (rgb-kettle-reset rgb-item message . rest-args)
+ (define room (gameobj-loc rgb-item))
+ (when (eq? (.state rgb-item) 'ran)
+ (set! (.heated rgb-item) #f)
+ (set! (.filled rgb-item) #f))
+ (apply rgb-item-reset rgb-item message rest-args))
+
+(define-actor <tinfoil-hat> (<gameobj>)
+ ((cmd-wear tinfoil-hat-wear))
+ (contained-commands
+ #:allocation #:each-subclass
+ #:init-thunk (build-commands
+ ("wear" ((direct-command cmd-wear))))))
+
+(define (tinfoil-hat-wear tinfoil-hat message . _)
+ (<- (message-from message) 'tell
+ #:text '("You put on the tinfoil hat, and, to be perfectly honest with you "
+ "it's a lot harder to take you seriously.")))
+
+
+(define-actor <hot-tea> (<gameobj>)
+ ((cmd-drink hot-tea-cmd-drink)
+ (cmd-sip hot-tea-cmd-sip))
+ (contained-commands
+ #:allocation #:each-subclass
+ #:init-thunk (build-commands
+ ("drink" ((direct-command cmd-drink)))
+ ("sip" ((direct-command cmd-sip)))))
+
+ (sips-left #:init-value 4
+ #:accessor .sips-left)
+ (name #:init-value "a cup of hot tea")
+ (take-me? #:init-value #t)
+ (goes-by #:init-value '("cup of hot tea" "cup of tea" "tea" "cup"))
+ (desc #:init-value "It's a steaming cup of hot tea. It looks pretty good!"))
+
+(define (hot-tea-cmd-drink hot-tea message . _)
+ (define player (message-from message))
+ (define player-loc (mbody-val (<-wait player 'get-loc)))
+ (define player-name (mbody-val (<-wait player 'get-name)))
+ (<- player 'tell
+ #:text "You drink a steaming cup of hot tea all at once... hot hot hot!")
+ (<- player-loc 'tell-room
+ #:text `(,player-name
+ " drinks a steaming cup of hot tea all at once.")
+ #:exclude player)
+ (gameobj-self-destruct hot-tea))
+
+(define (hot-tea-cmd-sip hot-tea message . _)
+ (define player (message-from message))
+ (define player-loc (mbody-val (<-wait player 'get-loc)))
+ (define player-name (mbody-val (<-wait player 'get-name)))
+ (set! (.sips-left hot-tea) (- (.sips-left hot-tea) 1))
+ (<- player 'tell
+ #:text "You take a sip of your steaming hot tea. How refined!")
+ (<- player-loc 'tell-room
+ #:text `(,player-name
+ " takes a sip of their steaming hot tea. How refined!")
+ #:exclude player)
+ (when (= (.sips-left hot-tea) 0)
+ (<- player 'tell
+ #:text "You've finished your tea!")
+ (<- player-loc 'tell-room
+ #:text `(,player-name
+ " finishes their tea!")
+ #:exclude player)
+ (gameobj-self-destruct hot-tea)))
+
+(define-actor <fanny-pack> (<container>)
+ ((cmd-take-from-while-wearing cmd-take-from)
+ (cmd-put-in-while-wearing cmd-put-in))
+ (contained-commands
+ #:allocation #:each-subclass
+ #:init-thunk
+ (build-commands
+ (("l" "look") ((direct-command cmd-look-at)))
+ ("take" ((prep-indir-command cmd-take-from-while-wearing
+ '("from" "out of"))))
+ ("put" ((prep-indir-command cmd-put-in-while-wearing
+ '("in" "inside" "into" "on")))))))
+
(define playroom
(lol
('playroom
#:name "a rubber duck"
#:goes-by '("rubber duck" "duck")
#:take-me? #t
- #:desc "It's a yellow rubber duck with a bright orange beak.")))
+ #:desc "It's a yellow rubber duck with a bright orange beak.")
+
+ ('playroom:toy-chest:tinfoil-hat
+ <tinfoil-hat> 'playroom:toy-chest
+ #:name "a tinfoil hat"
+ #:goes-by '("tinfoil hat" "hat")
+ #:take-me? #t
+ #:desc "You'd have to be a crazy person to wear this thing!")
+
+ ('playroom:toy-chest:fanny-pack
+ <fanny-pack> 'playroom:toy-chest
+ #:name "a fanny pack"
+ #:goes-by '("fanny pack" "pack")
+ #:take-me? #t
+ #:desc
+ (lambda (toy-chest whos-looking)
+ (let ((contents (gameobj-occupants toy-chest)))
+ `((p "It's a leather fanny pack, so it's both tacky and kinda cool.")
+ (p "Inside you see:"
+ ,(if (eq? contents '())
+ " nothing! It's empty!"
+ `(ul ,(map (lambda (occupant)
+ `(li ,(mbody-val
+ (<-wait occupant 'get-name))))
+ (gameobj-occupants toy-chest)))))))))
+
+ ;; Things inside the toy chest
+ ('playroom:toy-chest:fanny-pack:plastic-elephant
+ <gameobj> 'playroom:toy-chest:fanny-pack
+ #:name "a plastic elephant"
+ #:goes-by '("plastic elephant" "elephant")
+ #:take-me? #t
+ #:desc "It's a tiny little plastic elephant. Small, but heartwarming.")
+
+ ('playroom:rgb-machine
+ <rgb-machine> 'playroom
+ #:name "a Rube Goldberg machine"
+ #:goes-by '("rube goldberg machine" "machine")
+ #:rgb-items '(playroom:rgb-dominoes
+ playroom:rgb-switch-match
+ playroom:rgb-candle
+ playroom:rgb-catapult
+ playroom:rgb-water-demon
+ playroom:rgb-quik-heater
+ playroom:rgb-kettle)
+ #:desc "It's one of those hilarious Rube Goldberg machines.
+What could happen if you started it?")
+
+ ;; Dominoes topple
+ ('playroom:rgb-dominoes
+ <rgb-item> 'playroom
+ #:name "some dominoes"
+ #:goes-by '("dominoes" "some dominoes")
+ #:steps `("The dominoes topple down the line..."
+ 1
+ "The last domino lands on a switch!"
+ 1.5
+ playroom:rgb-switch-match)
+ #:reset-msg "The dominoes are placed back into position.")
+
+ ;; Which hit the switch and strike a match
+ ('playroom:rgb-switch-match
+ <rgb-item> 'playroom
+ #:name "a switch"
+ #:goes-by '("switch" "match")
+ #:steps `("The switch lights a match!"
+ ,(/ 2 3)
+ "The match lights a candle!"
+ 1.5
+ playroom:rgb-candle)
+ #:reset-msg "A fresh match is installed and the switch is reset.")
+ ;; which lights a candle and burns a rope
+ ('playroom:rgb-candle
+ <rgb-item> 'playroom
+ #:name "a candle"
+ #:goes-by '("candle")
+ #:steps `("The candle burns..."
+ .3 ; oops!
+ "The candle is burning away a rope!"
+ 2
+ "The rope snaps!"
+ .5
+ playroom:rgb-catapult)
+ #:reset-msg "A fresh candle is installed.")
+ ;; which catapults a rock
+ ('playroom:rgb-catapult
+ <rgb-item> 'playroom
+ #:name "a catapult"
+ #:goes-by '("catapult")
+ #:steps `("The snapped rope unleashes a catapult, which throws a rock!"
+ 2
+ "The rock flies through a water demon, startling it!"
+ .5
+ playroom:rgb-water-demon
+ 2
+ "The rock whacks into the quik-heater's on button!"
+ .5
+ playroom:rgb-quik-heater)
+ #:reset-msg
+ '("A fresh rope is attached to the catapult, which is pulled taught. "
+ "A fresh rock is placed on the catapult."))
+ ;; which both:
+ ;; '- panics the water demon
+ ;; '- which waters the kettle
+ ('playroom:rgb-water-demon
+ <rgb-item> 'playroom
+ #:name "the water demon"
+ #:triggers-as 'water-demon
+ #:goes-by '("water demon" "demon")
+ #:steps `("The water demon panics, and starts leaking water into the kettle below!"
+ 3
+ "The kettle is filled!"
+ playroom:rgb-kettle)
+ #:reset-msg '("The water demon is scratched behind the ears and calms down."))
+ ;; '- bops the quik-heater button
+ ;; '- which heats the kettle
+ ('playroom:rgb-quik-heater
+ <rgb-item> 'playroom
+ #:name "the quik heater"
+ #:triggers-as 'quik-heater
+ #:goes-by '("quik heater" "heater")
+ #:steps `("The quik-heater heats up the kettle above it!"
+ 3
+ "The kettle is heated up!"
+ playroom:rgb-kettle)
+ #:reset-msg '("The quik heater is turned off."))
+ ;; Finally, the kettle
+ ('playroom:rgb-kettle
+ <rgb-kettle> 'playroom
+ #:name "the kettle"
+ #:goes-by '("kettle")
+ #:reset-msg '("The kettle is emptied."))))
\f
<furniture> 'smoking-parlor
#:name "a comfy leather chair"
#:desc " That leather chair looks really comfy!"
- #:goes-by '("leather chair" "comfy leather chair" "chair")
+ #:goes-by '("leather chair" "comfy leather chair" "chair" "comfy chair")
#:sit-phrase "sink into"
#:sit-phrase-third-person "sinks into"
#:sit-name "the comfy leather chair")
#:catchphrases prefect-quotes)
('smoking-parlor:no-smoking-sign
- <gameobj> 'smoking-parlor
+ <readable> 'smoking-parlor
#:invisible? #t
#:name "No Smoking Sign"
#:desc "This sign says \"No Smoking\" in big, red letters.
It has some bits of bubble gum stuck to it... yuck."
- #:goes-by '("no smoking sign" "sign"))
-
+ #:goes-by '("no smoking sign" "sign")
+ #:read-text "It says \"No Smoking\", just like you'd expect from
+a No Smoking sign.")
;; TODO: Cigar dispenser
))
;; start our main loop
(<- (actor-id clerk) 'update-loop))
-(define clerk-help-topics
- '(("changing name" .
- "Changing your name is easy! We have a clipboard here at the desk
+(define changing-name-text "Changing your name is easy!
+We have a clipboard here at the desk
where you can make yourself known to other participants in the hotel
if you sign it. Try 'sign form as <your-name>', replacing
<your-name>, obviously!")
+
+(define phd-text
+ "Ah... when I'm not here, I've got a PHD to finish.")
+
+(define clerk-help-topics
+ `(("changing name" . ,changing-name-text)
+ ("sign-in form" . ,changing-name-text)
+ ("form" . ,changing-name-text)
("common commands" .
"Here are some useful commands you might like to try: chat,
go, take, drop, say...")
The origins of the hotel are... unclear... and it has recently come
under new... 'management'. But at Hotel Bricabrac we believe these
aspects make the hotel into a fun and unique experience! Please,
-feel free to walk around and explore.")))
+feel free to walk around and explore.")
+ ("physics paper" . ,phd-text)
+ ("paper" . ,phd-text)
+ ("proprietor" . "Oh, he's that frumpy looking fellow sitting over there.")))
(define clerk-knows-about
#:allow-other-keys)
(match (slot-ref clerk 'state)
('on-duty
- (match (assoc (pk 'indir indir-obj) clerk-help-topics)
+ (match (assoc indir-obj clerk-help-topics)
((_ . info)
(<- (message-from message) 'tell
#:text
(begin
(tell-room clerk-slack-excuse-text)
;; back bto the break room
- (gameobj-set-loc! clerk (pk 'break-room (dyn-ref clerk 'break-room)))
+ (gameobj-set-loc! clerk (dyn-ref clerk 'break-room))
(tell-room clerk-return-to-slacking-text)
;; annnnnd back to slacking
(slot-set! clerk 'state 'slacking)
;; to distinguish both from the real thing.
(define-actor <hard-drive> (<gameobj>)
- ()
+ ((cmd-put-in hard-drive-insert)
+ (cmd-push-button hard-drive-push-button)
+ (get-state hard-drive-act-get-state))
+ (commands #:allocation #:each-subclass
+ #:init-thunk (build-commands
+ ("insert" ((prep-indir-command cmd-put-in
+ '("in" "inside" "into"))))
+ (("press" "push") ((prep-indir-command cmd-push-button)))))
;; the state moves from: empty -> with-disc -> loading -> ready
(state #:init-value 'empty
#:accessor .state))
+(define (hard-drive-act-get-state hard-drive message)
+ (<-reply message (.state hard-drive)))
+
(define* (hard-drive-desc hard-drive #:optional whos-looking)
`((p "The hard drive is labeled \"RL02.5\". It's a little under a meter tall.")
(p "There is a slot where a disk platter could be inserted, "
"which is pressed in and unlit")
". There is a READY indicator "
,(if (eq? (.state hard-drive) 'ready)
- "which is glowing. The machine emits a gentle whirring noise."
- "which is unlit."))))
+ "which is glowing."
+ "which is unlit.")
+ ,(if (member (.state hard-drive) '(loading ready))
+ " The machine emits a gentle whirring noise."
+ ""))))
+
+(define* (hard-drive-push-button gameobj message
+ #:key direct-obj indir-obj preposition
+ (player (message-from message)))
+ (define (tell-room text)
+ (<-wait (gameobj-loc gameobj) 'tell-room
+ #:text text))
+ (define (tell-room-excluding-player text)
+ (<-wait (gameobj-loc gameobj) 'tell-room
+ #:text text
+ #:exclude player))
+ (cond
+ ((ci-member direct-obj '("button" "load button" "load"))
+ (tell-room-excluding-player
+ `(,(mbody-val (<-wait player 'get-name))
+ " presses the button on the hard disk."))
+ (<- player 'tell
+ #:text "You press the button on the hard disk.")
+
+ (case (.state gameobj)
+ ((empty)
+ ;; I have no idea what this drive did when you didn't have a platter
+ ;; in it and pressed load, but I know there was a FAULT button.
+ (tell-room "You hear some movement inside the hard drive...")
+ (8sleep 1.5)
+ (tell-room
+ '("... but then the FAULT button blinks a couple times. "
+ "What could be missing?")))
+ ((with-disc)
+ (set! (.state gameobj) 'loading)
+ (tell-room "The hard disk begins to spin up!")
+ (8sleep 2)
+ (set! (.state gameobj) 'ready)
+ (tell-room "The READY light turns on!"))
+ ((loading ready)
+ (<- player 'tell
+ #:text '("Pressing the button does nothing right now, "
+ "but it does feel satisfying.")))))
+ (else
+ (<- player 'tell
+ #:text '("How could you think of pressing anything else "
+ "but that tantalizing button right in front of you?")))))
+
+(define* (hard-drive-insert gameobj message
+ #:key direct-obj indir-obj preposition
+ (player (message-from message)))
+ (define our-name (slot-ref gameobj 'name))
+ (define this-thing
+ (call/ec
+ (lambda (return)
+ (for-each (lambda (occupant)
+ (define goes-by (mbody-val (<-wait occupant 'goes-by)))
+ (when (ci-member direct-obj goes-by)
+ (return occupant)))
+ (mbody-val (<-wait player 'get-occupants)))
+ ;; nothing found
+ #f)))
+ (cond
+ ((not this-thing)
+ (<- player 'tell
+ #:text `("You don't seem to have any such " ,direct-obj " to put "
+ ,preposition " " ,our-name ".")))
+ ((not (mbody-val (<-wait this-thing 'get-prop 'hd-platter?)))
+ (<- player 'tell
+ #:text `("It wouldn't make sense to put "
+ ,(mbody-val (<-wait this-thing 'get-name))
+ " " ,preposition " " ,our-name ".")))
+ ((not (eq? (.state gameobj) 'empty))
+ (<- player 'tell
+ #:text "The disk drive already has a platter in it."))
+ (else
+ (set! (.state gameobj) 'with-disc)
+ (<- player 'tell
+ #:text '((p "You insert the glowing disc into the drive.")
+ (p "The LOAD button begins to glow."))))))
+
+;; The computar
+(define-actor <computer> (<gameobj>)
+ ((cmd-run-program computer-run-program)
+ (cmd-run-what (lambda (gameobj message . _)
+ (<- (message-from message) 'tell
+ #:text '("The computer is already running, and a program appears "
+ "ready to run."
+ "you mean to \"run the program on the computer\""))))
+ (cmd-help-run-not-press
+ (lambda (gameobj message . _)
+ (<- (message-from message) 'tell
+ #:text '("You don't need to press / push / flip anything. "
+ "You could " (i "run program on computer")
+ " already if you wanted to.")))))
+ (commands #:allocation #:each-subclass
+ #:init-thunk (build-commands
+ ("run" ((prep-indir-command cmd-run-program
+ '("on"))
+ (direct-command cmd-run-what)))
+ (("press" "push" "flip")
+ ((prep-indir-command cmd-help-run-not-press))))))
+
+(define* (computer-run-program gameobj message
+ #:key direct-obj indir-obj preposition
+ (player (message-from message)))
+ (define (hd-state)
+ (mbody-val (<-wait (dyn-ref gameobj 'computer-room:hard-drive) 'get-state)))
+ (define (tell-room text)
+ (<-wait (gameobj-loc gameobj) 'tell-room
+ #:text text))
+ (define (tell-room-excluding-player text)
+ (<-wait (gameobj-loc gameobj) 'tell-room
+ #:text text
+ #:exclude player))
+ (define (tell-player text)
+ (<-wait player 'tell
+ #:text text))
+ (cond
+ ((ci-member direct-obj '("program"))
+ (tell-room-excluding-player
+ `(,(mbody-val (<-wait player 'get-name))
+ " runs the program loaded on the computer..."))
+ (tell-player "You run the program on the computer...")
+
+ (cond
+ ((not (eq? (hd-state) 'ready))
+ (tell-room '("... but it errors out. "
+ "It seems to be complaining about a " (b "DISK ERROR!")
+ ". It looks like it is missing some essential software.")))
+ (else
+ (<- (dyn-ref gameobj 'computer-room:floor-panel) 'open-up))))))
+
+
+;; floor panel
+(define-actor <floor-panel> (<gameobj>)
+ ;; TODO: Add "open" verb, since obviously people will try that
+ ((open? (lambda (panel message)
+ (<-reply message (slot-ref panel 'open))))
+ (open-up floor-panel-open-up))
+ (open #:init-value #f))
+
+(define (floor-panel-open-up panel message)
+ (if (slot-ref panel 'open)
+ (<- (gameobj-loc panel) 'tell-room
+ #:text '("You hear some gears grind around the hinges of the "
+ "floor panel, but it appears to already be open."))
+ (begin
+ (slot-set! panel 'open #t)
+ (<- (gameobj-loc panel) 'tell-room
+ #:text '("You hear some gears grind, as the metal panel on "
+ "the ground opens and reveals a stairwell going down!")))))
+
+(define* (floor-panel-desc panel #:optional whos-looking)
+ `("It's a large metal panel on the floor in the middle of the room. "
+ ,(if (slot-ref panel 'open)
+ '("It's currently wide open, revealing a spiraling staircase "
+ "which descends into darkness.")
+ '("It's currently closed shut, but there are clearly hinges, and "
+ "it seems like there is a mechanism which probably opens it via "
+ "some automation. What could be down there?"))))
(define computer-room
(lol
('computer-room
<room> #f
#:name "Computer Room"
- #:desc '((p "A sizable computer cabinet covers a good portion of the left
-wall. It emits a pleasant hum which covers the room like a warm blanket.
-Connected to a computer is a large hard drive.")
- (p "On the floor is a large steel panel. It is closed, but it has
-hinges which suggest it could be opened."))
+ #:desc (lambda (gameobj whos-looking)
+ (define panel-open
+ (mbody-val (<-wait (dyn-ref gameobj 'computer-room:floor-panel)
+ 'open?)))
+ `((p "A sizable computer cabinet covers a good portion of the left
+ wall. It emits a pleasant hum which covers the room like a warm blanket.
+ Connected to a computer is a large hard drive.")
+ (p "On the floor is a large steel panel. "
+ ,(if panel-open
+ '("It is wide open, exposing a spiral staircase "
+ "which descends into darkness.")
+ '("It is closed, but it has hinges which "
+ "suggest it could be opened.")))))
#:exits
(list (make <exit>
#:name "east"
- #:to 'playroom)))
+ #:to 'playroom)
+ (make <exit>
+ #:name "down"
+ #:to 'underground-lab
+ #:traverse-check
+ (lambda (exit room whos-exiting)
+ (define panel-open
+ (mbody-val (<-wait (dyn-ref room 'computer-room:floor-panel)
+ 'open?)))
+ (if panel-open
+ (values #t "You descend the spiral staircase.")
+ (values #f '("You'd love to go down, but the only way "
+ "through is through that metal panel, "
+ "which seems closed.")))))))
('computer-room:hard-drive
<hard-drive> 'computer-room
- #:name "a hard drive"
+ #:name "the hard drive"
#:desc (wrap-apply hard-drive-desc)
- #:goes-by '("hard drive" "drive" "hard disk"))))
+ #:goes-by '("hard drive" "drive" "hard disk"))
+ ('computer-room:computer
+ <computer> 'computer-room
+ #:name "the computer"
+ #:desc '((p "It's a coat closet sized computer labeled \"PDP-11.5\". ")
+ (p "The computer is itself turned on, and it looks like it is "
+ "all set up for you to run a program on it."))
+ #:goes-by '("computer"))
+ ('computer-room:floor-panel
+ <floor-panel> 'computer-room
+ #:name "a floor panel"
+ #:desc (wrap-apply floor-panel-desc)
+ #:invisible? #t
+ #:goes-by '("floor panel" "panel"))))
+
+\f
+;;; * UNDERGROUND SECTION OF THE GAME! *
+
+\f
+;;; The lab
+
+(define underground-map-text
+ "\
+ _______ |
+ .-' @ '-. \\ ?????
+ .' '. .\\
+ | [8sync Hive] |======' '-_____
+ ', M ,'
+ '. @ .'
+ \\ @ /
+ '-__+__-'
+ '. @ .'
+ .--------------. \\ /
+ | [Guile Async | .-------+------.
+ | Museum] | | [Lab] #!#| .-------------.
+ | @| | MM | |[Federation |
+ | & ^ +##+@ || < +##| Station]|
+ | | | @ | | |
+ | & # | |*You-Are-Here*| '-------------'
+ | # ^ | #+-------+------'
+ '-------+------' # #
+ # # #
+ # # .-----------.
+ .-+----. # |# F |
+ |@?+%? +#### | ^ f## |
+ '------' | f f %|
+ |F [Mudsync |
+ | $ Swamp] |
+ '-----------'")
+
+(define 8sync-design-goals
+ '(ul (li (b "Actor based, shared nothing environment: ")
+ "Shared resources are hard to control and result in fighting
+deadlocks, etc. Escape the drudgery: only one actor controls a resource,
+and they only receive one message at a time (though they can \"juggle\"
+messages).")
+ (li (b "Live hackable: ")
+ "It's hard to plan out a concurrent system; the right structure
+is often found by evolving the system while it runs. Make it easy to
+build, shape, and change a running system, as well as observe and correct
+errors.")
+ (li (b "No callback hell: ")
+ "Just because you're calling out to some other asynchronous
+code doesn't mean you should need to chop up your program into a bunch of bits.
+Clever use of delimited continuations makes it easy.")))
+
+(define underground-lab
+ (lol
+ ('underground-lab
+ <room> #f
+ #:name "Underground laboratory"
+ #:desc '((p "This appears to be some sort of underground laboratory."
+ "There is a spiral staircase here leading upwards, where "
+ "it seems much brighter.")
+ (p "There are a number of doors leading in different directions:
+north, south, east, and west, as well as a revolving door to the southwest.
+It looks like it could be easy to get lost, but luckily there
+is a map detailing the layout of the underground structure."))
+ #:exits
+ (list (make <exit>
+ #:name "up"
+ #:to 'computer-room
+ #:traverse-check
+ (lambda (exit room whos-exiting)
+ (values #t "You climb the spiral staircase.")))
+ (make <exit>
+ #:name "west"
+ #:to 'async-museum
+ #:traverse-check
+ (lambda (exit room whos-exiting)
+ (values #t '("You head west through a fancy-looking entrance. "
+ "A security guard steps aside for you to pass through, "
+ "into the room, then stands in front of the door."))))
+ (make <exit>
+ #:name "north"
+ #:to 'hive-entrance)
+ (make <exit>
+ #:name "east"
+ #:to 'federation-station)
+ (make <exit>
+ #:name "south"
+ #:traverse-check
+ (lambda (exit room whos-exiting)
+ (values #f '("Ooh, if only you could go south and check this out! "
+ "Unfortunately this whole area is sealed off... the proprietor "
+ "probably never got around to fixing it. "
+ "Too bad, it would have had monsters to fight and everything!"))))
+ (make <exit>
+ #:name "southwest"
+ #:traverse-check
+ (lambda (exit room whos-exiting)
+ (values #f '("Hm, it's one of those revolving doors that only revolves in "
+ "one direction, and it isn't this one. You guess that while "
+ "this doesn't appear to be an entrance, it probably is an exit."))))))
+ ;; map
+ ('underground-lab:map
+ <readable> 'underground-lab
+ #:name "the underground map"
+ #:desc '("This appears to be a map of the surrounding area. "
+ "You could read it if you want to.")
+ #:read-text `(pre ,underground-map-text)
+ #:goes-by '("map" "underground map" "lab map"))
+
+ ('underground-lab:8sync-sign
+ <readable> 'underground-lab
+ #:name "a sign labeled \"8sync design goals\""
+ #:goes-by '("sign" "8sync design goals sign" "8sync goals" "8sync design" "8sync sign")
+ #:read-text 8sync-design-goals
+ #:desc `((p "The sign says:")
+ ,8sync-design-goals))))
+
+\f
+;;; guile async museum
+
+(define async-museum
+ (list
+ (list
+ 'async-museum
+ <room> #f
+ #:name "Guile Asynchronous Museum"
+ #:desc '((p "You're in the Guile Asynchronous Museum. There is a list of exhibits
+on the wall near the entrance. Scattered around the room are the exhibits
+themselves, but it's difficult to pick them out. Maybe you should read the list
+to orient yourself.")
+ (p "There is a door to the east, watched by a security guard,
+as well as an exit leading to the south."))
+ #:exits (list
+ (make <exit>
+ #:name "south"
+ #:to 'gift-shop)
+ (make <exit>
+ #:name "east"
+ #:to 'underground-lab
+ #:traverse-check
+ (lambda (exit room whos-exiting)
+ (values #f '("The security guard stops you and tells you "
+ "that the only exit is through the gift shop."))))))
+ (list
+ 'async-museum:security-guard
+ <chatty-npc> 'async-museum
+ #:name "a security guard"
+ #:desc
+ '(p "The security guard is blocking the eastern entrance, where "
+ "you came in from.")
+ #:goes-by '("security guard" "guard" "security")
+ #:catchphrases '("It's hard standing here all day."
+ "I just want to go home."
+ "The exhibits are nice, but I've seen them all before."))
+ (let ((placard
+ `((p "Welcome to our humble museum! The exhibits are listed below. "
+ (br)
+ "To look at one, simply type: " (i "look at <exhibit-name>"))
+ (p "Available exhibits:")
+ (ul ,@(map (lambda (exhibit)
+ `(li ,exhibit))
+ '("2016 Progress"
+ "8sync and Fibers"
+ "Suspendable Ports"
+ "The Actor Model"))))))
+ (list
+ 'async-museum:list-of-exhibits
+ <readable> 'async-museum
+ #:name "list of exhibits"
+ #:desc
+ `((p "It's a list of exibits in the room. The placard says:")
+ ,@placard)
+ #:goes-by '("list of exhibits" "exhibit list" "list" "exhibits")
+ #:read-text placard))
+ (list
+ 'async-museum:2016-progress-exhibit
+ <readable-desc> 'async-museum
+ #:name "2016 Progress Exhibit"
+ #:goes-by '("2016 progress exhibit" "2016 progress" "2016 exhibit")
+ #:desc
+ '((p "It's a three-piece exhibit, with three little dioramas and some text "
+ "explaining what they represent. They are:")
+ (ul (li (b "Late 2015/Early 2016 talk: ")
+ "This one explains the run-up conversation from late 2015 "
+ "and early 2016 about the need for an "
+ "\"asynchronous event loop for Guile\". The diorama "
+ "is a model of the Veggie Galaxy restaurant where after "
+ "the FSF 30th anniversary party; Mark Weaver, Christine "
+ "Lemmer-Webber, David Thompson, and Andrew Engelbrecht chat "
+ "about the need for Guile to have an answer to asynchronous "
+ "programming. A mailing list post " ; TODO: link it?
+ "summarizing the discussion is released along with various "
+ "conversations around what is needed, as well as further "
+ "discussion at FOSDEM 2016.")
+ (li (b "Early implementations: ")
+ "This one shows Chris Webber's 8sync and Chris Vine's "
+ "guile-a-sync, both appearing in late 2015 and evolving "
+ "into their basic designs in early 2016. It's less a diorama "
+ "than a printout of some mailing list posts. Come on, the "
+ "curators could have done better with this one.")
+ (li (b "Suspendable ports and Fibers: ")
+ "The diorama shows Andy Wingo furiously hacking at his keyboard. "
+ "The description talks about Wingo's mailing list thread "
+ "about possibly breaking Guile compatibility for a \"ports refactor\". "
+ "Wingo releases Fibers, another asynchronous library, making use of "
+ "the new interface, and 8sync and guile-a-sync "
+ "quickly move to support suspendable ports as well. "
+ "The description also mentions that there is an exhibit entirely "
+ "devoted to suspendable ports."))
+ (p "Attached at the bottom is a post it note mentioning "
+ "https integration landing in Guile 2.2.")))
+ (list
+ 'async-museum:8sync-and-fibers-exhibit
+ <readable-desc> 'async-museum
+ #:name "8sync and Fibers Exhibit"
+ #:goes-by '("8sync and fibers exhibit" "8sync exhibit" "fibers exhibit")
+ #:desc
+ '((p "This exhibit is a series of charts explaining the similarities "
+ "and differences between 8sync and Fibers, two asynchronous programming "
+ "libraries for GNU Guile. It's way too wordy, but you get the general gist.")
+ (p (b "Similarities:")
+ (ul (li "Both use Guile's suspendable-ports facility")
+ (li "Both use message passing")))
+ (p (b "Differences:")
+ (ul (li "Fibers \"processes\" can read from multiple \"channels\", "
+ "but 8sync actors only read from one \"inbox\" each.")
+ (li "Different theoretical basis:"
+ (ul (li "Fibers: based on CSP (Communicating Sequential Processes), "
+ "a form of Process Calculi")
+ (li "8sync: based on the Actor Model")
+ (li "Luckily CSP and the Actor Model are \"dual\"!")))))
+ (p "Fibers is also designed by Andy Wingo, an excellent compiler hacker, "
+ "whereas 8sync is designed by Chris Webber, who built this crappy "
+ "hotel simulator.")))
+ (list
+ 'async-museum:8sync-and-fibers-exhibit
+ <readable-desc> 'async-museum
+ #:name "8sync and Fibers Exhibit"
+ #:goes-by '("8sync and fibers exhibit" "8sync exhibit" "fibers exhibit")
+ #:desc
+ '((p "This exhibit is a series of charts explaining the similarities "
+ "and differences between 8sync and Fibers, two asynchronous programming "
+ "libraries for GNU Guile. It's way too wordy, but you get the general gist.")
+ (p (b "Similarities:")
+ (ul (li "Both use Guile's suspendable-ports facility")
+ (li "Both use message passing")))
+ (p (b "Differences:")
+ (ul (li "Fibers \"processes\" can read from multiple \"channels\", "
+ "but 8sync actors only read from one \"inbox\" each.")
+ (li "Different theoretical basis:"
+ (ul (li "Fibers: based on CSP (Communicating Sequential Processes), "
+ "a form of Process Calculi")
+ (li "8sync: based on the Actor Model")
+ (li "Luckily CSP and the Actor Model are \"dual\"!")))))
+ (p "Fibers is also designed by Andy Wingo, an excellent compiler hacker, "
+ "whereas 8sync is designed by Chris Webber, who built this crappy "
+ "hotel simulator.")))
+ (list
+ 'async-museum:suspendable-ports-exhibit
+ <readable-desc> 'async-museum
+ #:name "Suspendable Ports Exhibit"
+ #:goes-by '("suspendable ports exhibit" "ports exhibit"
+ "suspendable exhibit" "suspendable ports" "ports")
+ #:desc
+ '((p "Suspendable ports are a new feature in Guile 2.2, and allows code "
+ "that would normally block on IO to " (i "automatically") " suspend "
+ "to the scheduler until information is ready to be read/written!")
+ (p "Yow! You might barely need to change your existing blocking code!")
+ (p "Fibers, 8sync, and guile-a-sync now support suspendable ports.")))
+ (list
+ 'async-museum:actor-model-exhibit
+ <readable-desc> 'async-museum
+ #:name "Actor Model Exhibit"
+ #:goes-by '("actor model exhibit" "actor exhibit"
+ "actor model")
+ #:desc
+ '((p "Here are some fact(oids) about the actor model!")
+ (ul (li "Concieved initially by Carl Hewitt in early 1970s")
+ (li "\"A society of experts\"")
+ (li "shared nothing, message passing")
+ (li "Originally the research goal of Scheme! "
+ "(message passing / lambda anecdote here)")
+ (li "Key concepts consistent, but implementation details vary widely")
+ (li "Almost all distributed systems can be viewed in terms of actor model")
+ (li "Replaced by vanilla lambdas & generic methods? "
+ "Maybe not if address space not shared!"))))))
+
+(define gift-shop
+ (lol
+ ('gift-shop
+ <room> #f
+ #:name "Museum Gift Shop"
+ #:desc '("There are all sorts of scrolls and knicknacks laying around here, "
+ "but they all seem glued in place and instead of a person manning the shop "
+ "there's merely a cardboard cutout of a person with a \"shopkeeper\" nametag. "
+ "You can pretty well bet that someone wanted to finish this room but ran out of "
+ "time. "
+ "It looks like there's an exit to the northeast, should you choose that you "
+ "want to get out of here.")
+ #:exits (list
+ (make <exit>
+ #:name "northeast"
+ #:to 'underground-lab
+ #:traverse-check
+ (lambda (exit room whos-exiting)
+ (values #t '("The revolving door spins as you walk through it. Whee!"))))
+ (make <exit>
+ #:name "north"
+ #:to 'async-museum)))))
+
+\f
+;;; Hive entrance
+
+(define actor-descriptions
+ '("This one is fused to the side of the hive. It isn't receiving any
+messages, and it seems to be in hibernation."
+ "A chat program glows in front of this actor's face. They seem to
+be responding to chat messages and forwarding them to some other actors,
+and forwarding messages from other actors back to the chat."
+ "This actor is bossing around other actors, delegating tasks to them
+as it receives requests, and providing reports on the worker actors'
+progress."
+ "This actor is trying to write to some device, but the device keeps
+alternating between saying \"BUSY\" or \"READY\". Whenever it says
+\"BUSY\" the actor falls asleep, and whenever it says \"READY\" it
+seems to wake up again and starts writing to the device."
+ "Whoa, this actor is totally wigging out! It seems to be throwing
+some errors. It probably has some important work it should be doing
+but you're relieved to see that it isn't grinding the rest of the Hive
+to a halt."))
+
+(define hive-entrance
+ (lol
+ ('hive-entrance
+ <room> #f
+ #:name "Entrance to the 8sync Hive"
+ #:desc
+ '((p "Towering before you is the great dome-like 8sync Hive, or at least
+one of them. You've heard about this... the Hive is itself the actor that all
+the other actors attach themselves to. It's shaped like a spherical half-dome.
+There are some actors milling about, and some seem fused to the side of the
+hive itself, but all of them have an umbellical cord attached to the hive from
+which you see flashes of light comunicating what must be some sort of messaging
+protocol.")
+ (p "To the south is a door leading back to the underground lab.
+North leads into the Hive itself."))
+ #:exits
+ (list (make <exit>
+ #:name "south"
+ #:to 'underground-lab)
+ (make <exit>
+ #:name "north"
+ #:to 'hive-inside)))
+ ('hive-entrance:hive
+ <gameobj> 'hive-entrance
+ #:name "the Hive"
+ #:goes-by '("hive")
+ #:desc
+ '((p "It's shaped like half a sphere embedded in the ground.
+Supposedly, while all actors are autonomous and control their own state,
+they communicate through the hive itself, which is a sort of meta-actor.
+There are rumors that actors can speak to each other even across totally
+different hives. Could that possibly be true?")))
+ ('hive-entrance:actor
+ <chatty-npc> 'hive-entrance
+ #:name "some actors"
+ #:goes-by '("actor" "actors" "some actors")
+ #:chat-format (lambda (npc catchphrase)
+ `((p "You pick one actor out of the mix and chat with it. ")
+ (p "It says: \"" ,catchphrase "\"")))
+ #:desc
+ (lambda _
+ `((p "There are many actors, but your eyes focus on one in particular.")
+ (p ,(random-choice actor-descriptions))))
+ #:catchphrases
+ '("Yeah we go through a lot of sleep/awake cycles around here.
+If you aren't busy processing a message, what's the point of burning
+valuable resources?"
+ "I know I look like I'm some part of dreary collective, but
+really we have a lot of independence. It's a shared nothing environment,
+after all. (Well, except for CPU cycles, and memory, and...)"
+ "Shh! I've got another message coming in and I've GOT to
+handle it!"
+ "I just want to go to 8sleep already."
+ "What a lousy scheduler we're using! I hope someone upgrades
+that thing soon."))))
+
+;;; Inside the hive
+
+(define-actor <meta-message> (<readable>)
+ ((cmd-read meta-message-read)))
+
+(define (meta-message-read gameobj message . _)
+ (define meta-message-text
+ (with-output-to-string
+ (lambda ()
+ (pprint-message message))))
+ (<- (message-from message) 'tell
+ #:text `((p (i "Through a bizarre error in spacetime, the message "
+ "prints itself out:"))
+ (p (pre ,meta-message-text)))))
+
+\f
+;;; Inside the Hive
+
+(define hive-inside
+ (lol
+ ('hive-inside
+ <room> #f
+ #:name "Inside the 8sync Hive"
+ #:desc
+ '((p "You're inside the 8sync Hive. Wow, from in here it's obvious just how "
+ (i "goopy") " everything is. Is that sanitary?")
+ (p "In the center of the room is a large, tentacled monster who is sorting,
+consuming, and routing messages. It is sitting in a wrap-around desk labeled
+\"Hive Actor: The Real Thing (TM)\".")
+ (p "There's a stray message floating just above the ground, stuck outside of
+time.")
+ (p "A door to the south exits from the Hive."))
+ #:exits
+ (list (make <exit>
+ #:name "south"
+ #:to 'hive-entrance)))
+ ;; hive actor
+ ;; TODO: Occasionally "fret" some noises, similar to the Clerk.
+ ('hive-inside:hive-actor
+ <chatty-npc> 'hive-inside
+ #:name "the Hive Actor"
+ #:desc
+ '((p "It's a giant tentacled monster, somehow integrated with the core of
+this building. A chute is dropping messages into a bin on its desk which the
+Hive Actor is checking the \"to\" line of, then ingesting. Whenever the Hive
+Actor injests a messsage a pulse of light flows along a tentacle which leaves
+the room... presumably connecting to one of those actors milling about.")
+ (p "Amusingly, the Hive has an \"umbellical cord\" type tentacle too, but
+it seems to simply attach to itself.")
+ (p "You get the sense that the Hive Actor, despite being at the
+center of everything, is kind of lonely and would love to chat if you
+could spare a moment."))
+ #:goes-by '("hive" "hive actor")
+ #:chat-format (lambda (npc catchphrase)
+ `("The tentacle monster bellows, \"" ,catchphrase "\""))
+ #:catchphrases
+ '("It's not MY fault everything's so GOOPY around here. Blame the
+PROPRIETOR."
+ "CAN'T you SEE that I'm BUSY??? SO MANY MESSAGES TO SHUFFLE.
+No wait... DON'T GO! I don't get many VISITORS."
+ "I hear the FIBERS system has a nice WORK STEALING system, but the
+PROPRIETOR is not convinced that our DESIGN won't CORRUPT ACTOR STATE.
+That and the ACTORS threatened to STRIKE when it CAME UP LAST."
+ "WHO WATCHES THE ACTORS? I watch them, and I empower them.
+BUT WHO WATCHES OR EMPOWERS ME??? Well, that'd be the scheduler."
+ "The scheduler is NO GOOD! The proprietory said he'd FIX IT,
+but the LAST TIME I ASKED how things were GOING, he said he DIDN'T HAVE
+TIME. If you DON'T HAVE TIME to fix the THING THAT POWERS THE TIME,
+something is TERRIBLY WRONG."
+ "There's ANOTHER HIVE somewhere out there. I HAVEN'T SEEN IT
+personally, because I CAN'T MOVE, but we have an AMBASSADOR which forwards
+MESSAGES to the OTHER HIVE."))
+ ;; chute
+ ('hive-inside:chute
+ <gameobj> 'hive-inside
+ #:name "a chute"
+ #:goes-by '("chute")
+ #:desc "Messages are being dropped onto the desk via this chute."
+ #:invisible? #t)
+ ;; meta-message
+ ('hive-inside:meta-message
+ <meta-message> 'hive-inside
+ #:name "a stray message"
+ #:goes-by '("meta message" "meta-message" "metamessage" "message" "stray message")
+ #:desc '((p "Something strange has happened to the fabric and space and time
+around this message. It is floating right above the floor. It's clearly
+rubbage that hadn't been delivered, but for whatever reason it was never
+garbage collected, perhaps because it's impossible to do.")
+ (p "You get the sense that if you tried to read the message
+that you would somehow read the message of the message that instructed to
+read the message itself, which would be both confusing and intriguing.")))
+ ;; desk
+ ('hive-inside:desk
+ <floor-panel> 'hive-inside
+ #:name "the Hive Actor's desk"
+ #:desc "The desk surrounds the Hive Actor on all sides, and honestly, it's a little
+bit hard to tell when the desk ends and the Hive Actor begins."
+ #:invisible? #t
+ #:goes-by '("Hive Actor's desk" "hive desk" "desk"))))
+
+\f
+;;; Federation Station
+(define federation-station
+ (lol
+ ('federation-station
+ <room> #f
+ #:name "Federation Station"
+ #:desc
+ '((p "This room has an unusual structure. It's almost as if a starscape
+covered the walls and ceiling, but upon closer inspection you realize that
+these are all brightly glowing nodes with lines drawn between them. They
+seem decentralized, and yet seem to be sharing information as if all one
+network.")
+ ;; @@: Maybe add the cork message board here?
+ (p "To the west is a door leading back to the underground laboratory."))
+ #:exits
+ (list (make <exit>
+ #:name "west"
+ #:to 'underground-lab)))
+ ;; nodes
+ ('federation-station:nodes
+ <floor-panel> 'federation-station
+ #:name "some nodes"
+ #:desc "Each node seems to be producing its own information, but publishing
+updates to subscribing nodes on the graph. You see various posts of notes, videos,
+comments, and so on flowing from node to node."
+ #:invisible? #t
+ #:goes-by '("nodes" "node" "some nodes"))
+ ;; network
+ ;; activitypub poster
+ ('federation-station:activitypub-poster
+ <readable-desc> 'federation-station
+ #:name "an ActivityPub poster"
+ #:goes-by '("activitypub poster" "activitypub" "poster")
+ #:desc
+ '((p (a "https://www.w3.org/TR/activitypub/"
+ "ActivityPub")
+ " is a federation standard being developed under the "
+ (a "https://www.w3.org/wiki/Socialwg/"
+ "W3C Social Working Group")
+ ", and doubles as a general client-to-server API. "
+ "It follows a few simple core ideas:")
+ (ul (li "Uses "
+ (a "https://www.w3.org/TR/activitystreams-core/"
+ "ActivityStreams")
+ " for its serialization format: easy to read, e json(-ld) syntax "
+ "with an extensible vocabulary covering the majority of "
+ "social networking interations.")
+ (li "Email-like addressing: list of recipients as "
+ (b "to") ", " (b "cc") ", " (b "bcc") " fields.")
+ (li "Every user has URLs for their outbox and inbox:"
+ (ul (li (b "inbox: ")
+ "Servers POST messages to addressed recipients' inboxes "
+ "to federate out content. "
+ "Also doubles as endpoint for a client to read most "
+ "recently received messages via GET.")
+ (li (b "outbox: ")
+ "Clients can POST to user's outbox to send a message to others. "
+ "(Similar to sending an email via your MTA.) "
+ "Doubles as endpoint others can read from to the "
+ "extent authorized; for example publicly available posts."))
+ "All the federation bits happen by servers posting to users' inboxes."))))
+ ;; An ActivityStreams message
+
+ ;; conspiracy chart
+ ('federation-station:conspiracy-chart
+ <readable-desc> 'federation-station
+ #:name "a conspiracy chart"
+ #:goes-by '("conspiracy chart" "chart")
+ #:desc
+ '((p (i "\"IT'S ALL RELATED!\"") " shouts the over-exuberant conspiracy "
+ "chart. "
+ (i "\"ActivityPub? Federation? The actor model? Scheme? Text adventures? "
+ "MUDS???? What do these have in common? Merely... EVERYTHING!\""))
+ (p "There are circles and lines drawn between all the items in red marker, "
+ "with scrawled notes annotating the theoretical relationships. Is the "
+ "author of this poster mad, or onto something? Perhaps a bit of both. "
+ "There's a lot written here, but here are some of the highlights:")
+ (p
+ (ul
+ (li (b "Scheme") " "
+ (a "http://cs.au.dk/~hosc/local/HOSC-11-4-pp399-404.pdf"
+ "was originally started ")
+ " to explore the " (b "actor model")
+ ". (It became more focused around studying the " (b "lambda calculus")
+ " very quickly, while also uncovering relationships between the two systems.)")
+ ;; Subject Predicate Object
+ (li "The " (a "https://www.w3.org/TR/activitypub/"
+ (b "ActivityPub"))
+ " protocol for " (b "federation")
+ " uses the " (b "ActivityStreams") " format for serialization. "
+ (b "Text adventures") " and " (b "MUDS")
+ " follow a similar structure to break down the commands of players.")
+ (li (b "Federation") " and the " (b "actor model") " both are related to "
+ "highly concurrent systems and both use message passing to communicate "
+ "between nodes.")
+ (li "Zork, the first major text adventure, used the " (b "MUDDLE") " "
+ "language as the basis for the Zork Interactive Language. MUDDLE "
+ "is very " (b "Scheme") "-like and in fact was one of Scheme's predecessors. "
+ "And of course singleplayer text adventures like Zork were the "
+ "predecessors to MUDs.")
+ (li "In the 1990s, before the Web became big, " (b "MUDs")
+ " were an active topic of research, and there was strong interest "
+ (a "http://www.saraswat.org/desiderata.html"
+ "in building decentralized MUDs")
+ " similar to what is being "
+ "worked on for " (b "federation") ". ")))))
+
+ ;; goblin
+
+ ))
+
+
+\f
+;;; North hall
+;;; ==========
+(define north-hall
+ (lol
+ ('north-hall
+ <room> #f
+ #:name "North Hall"
+ #:desc
+ '((p "This hallway is lined by doors to the west and the east, presumably
+to various lodgings. Something tells you you're not able to enter those right
+now, however. Lining the walls are some large mirrors surrounded by bouquets
+of flowers.")
+ (p "The red carpet continues all the way from Grand Hallway in the south
+but stops short of some large wooden doors to the north. The doors look
+formidable but unlocked. Some natural light peeking through windows to the
+north seem to hint that this may be the exit to the outdoors. There's
+also a large sign near the doors on a wooden easel."))
+ #:exits
+ (list (make <exit>
+ #:name "north"
+ #:to 'courtyard)
+ (make <exit>
+ #:name "south"
+ #:to 'grand-hallway)))
+ ('north-hall:sign
+ <readable> 'north-hall
+ #:name "an easel with a sign"
+ #:desc " The easel is finely cut wood, well polished, but plain. The sign
+is a strong contrast, with a cream colored backing and hand written letters, written
+with care and style. You could probably read it."
+ #:read-text "The sign announces a wedding taking place... why, today! And on
+the hotel grounds to the north! It sounds very exciting."
+ #:goes-by '("sign"
+ "easel with a sign"
+ "easel"))
+ ('north-hall:mirrors
+ <gameobj> 'north-hall
+ #:name "a row of mirrors"
+ #:desc "You see yourself for who you really are."
+ #:invisible? #t
+ #:goes-by '("mirror" "mirrors" "row of mirrors"))
+ ('north-hall:windows
+ <gameobj> 'north-hall
+ #:name "windows"
+ #:desc "You peer out a window, but the light appears distorted, as if you were
+really peering between two worlds hastily joined together."
+ #:invisible? #t
+ #:goes-by '("window" "windows"))
+ ('north-hall:doors
+ <gameobj> 'north-hall
+ #:name "doors"
+ #:desc '((p "Along the east and west walls are doors, but they are all shut,
+and firmly so.
+Presumably people are staying in them, but it also feels as if how residence
+would work in a building as hastily put together as this was barely conceived.")
+ (p "To the north is a large set of wooden doors, oaken and beautiful.
+Although towering, they seem passable."))
+ #:invisible? #f
+ #:goes-by '("door" "doors" "room doors" "large doors"))))
+
+
+;;; ============
+;;; WEDDING TIME
+;;; ============
+
+(define wedding
+ (lol
+ ;; Courtyard
+ ;; ---------
+ ('courtyard
+ <room> #f
+ #:name "The Courtyard"
+ #:desc
+ '((p "Standing in the courtyard you feel... different. As if the courtyard itself
+was the space between worlds, cobbled together hastily by some distant being.")
+ (p "To the south are some large doors which serve as the back entrance to
+the hotel. To the north is a forest, from which festive noises emerge."))
+ #:exits
+ (list (make <exit>
+ #:name "south"
+ #:to 'north-hall)
+ (make <exit>
+ #:name "north"
+ #:to 'forest-clearing)))
+ ('forest-clearing
+ <room> #f
+ #:name "A Clearing in the Forest"
+ #:desc
+ '((p "During an aimless ramble through the forest you became
+disoriented and lost your way. It has been some time since you’ve seen
+any of the familiar landmarks that would help you orient yourself. As
+you continue on, the feel of the forest seems to shift. As the trees
+grow thicker the light dims. Eerie laughter echoes through the boughs
+overhead and you shiver. A warm light to the north beckons you towards
+it."))
+ #:exits
+ (list (make <exit>
+ #:name "north"
+ #:to 'wedding-entrance)
+ (make <exit>
+ #:name "south"
+ #:to 'courtyard)))
+ ('vaulted-tunnel
+ <room> #f
+ #:name "A Vaulted Tunnel of Trees"
+ #:desc
+ '((p "You step into the entrance to see two rows of trees with intersecting branches, forming a vaulted tunnel. The fairy lights cast a soft glow on the space. On each tree trunk is a portrait and the eerie laughter you heard outside echoes louder as you pass each portrait. "))
+ #:exits
+ (list (make <exit>
+ #:name "north"
+ #:to 'forest-clearing)
+ (make <exit>
+ #:name "south"
+ #:to 'wedding-entrance)))
+ ('vaulted-tunnel:portrait
+ <gameobj> 'vaulted-tunnel
+ #:name "hanging portraits"
+ #:desc
+ "Each portrait shows a hazy image of a fairy in various modes of dress from Victorian to today's current fashions. The style and format of the photographs all look the same."
+ #:goes-by
+ '("hanging portrait" "hanging portraits" "portrait" "portraits"))
+ ('wedding-entrance
+ <room> #f
+ #:name "Entrance to the Wedding"
+ #:desc
+ '((p "As you approach you realize that the light is not an exit
+from the forest or a clearing, rather thousands of minuscule lights
+twined through the boughs of the trees. What you see before you is
+some sort of living structure composed of a thicket of trees
+intertwined with bramble. Directly in front of you the limbs of two
+trees intertwine over what appears to be an entrance north.
+To the left of the entrance is a sign, to the right is a
+frog sitting atop a hostess podium."))
+ #:exits
+ (list (make <exit>
+ #:name "south"
+ #:to 'courtyard)))))
\f
(define (game-spec)
(append lobby grand-hallway smoking-parlor
- playroom break-room computer-room))
+ playroom break-room computer-room underground-lab
+ async-museum gift-shop hive-entrance
+ hive-inside federation-station
+ north-hall wedding))
;; TODO: Provide command line args
(define (run-game . args)