X-Git-Url: https://jxself.org/git/?p=mudsync.git;a=blobdiff_plain;f=mudsync%2Froom.scm;h=ba4af4c335e05abd81fd489450b6317c9caceffc;hp=2e1b99fcdf1d8f94e4712e36736db572e42bb7e3;hb=HEAD;hpb=4ea75c3db75e83c8133e2833d0ad7820aacc30b7 diff --git a/mudsync/room.scm b/mudsync/room.scm index 2e1b99f..ed4eafe 100644 --- a/mudsync/room.scm +++ b/mudsync/room.scm @@ -1,5 +1,5 @@ ;;; Mudsync --- Live hackable MUD -;;; Copyright © 2016 Christopher Allan Webber +;;; Copyright © 2016 Christine Lemmer-Webber ;;; ;;; This file is part of Mudsync. ;;; @@ -19,25 +19,21 @@ (define-module (mudsync room) #:use-module (mudsync command) #:use-module (mudsync gameobj) - #:use-module (8sync systems actors) + #:use-module (mudsync utils) + #:use-module (8sync actors) #:use-module (8sync agenda) #:use-module (oop goops) #:use-module (srfi srfi-1) - #:export ( - room-actions - room-actions* - - )) + #:use-module (ice-9 control) + #:export ( )) ;;; Exits ;;; ===== (define-class () - ;; Used for wiring - (to-symbol #:init-keyword #:to-symbol) - ;; The actual address we use - (to-address #:init-keyword #:address) + (to #:init-keyword #:to + #:init-value #f) ;; Name of the room (@@: Should this be names?) (name #:getter exit-name #:init-keyword #:name) @@ -52,39 +48,22 @@ (traverse-check #:init-value (const #t) #:init-keyword #:traverse-check)) -(define* (exit-can-traverse? exit actor - #:optional (target-actor (actor-id actor))) - ((slot-ref exit 'traverse-check) exit actor target-actor)) +;; @@: Should we make whos-exiting optional? Would there ever be any +;; reason? +(define* (exit-can-traverse? exit room whos-exiting) + ((slot-ref exit 'traverse-check) exit room whos-exiting)) -(define* (exit-is-visible? exit actor - #:optional (target-actor (actor-id actor))) - ((slot-ref exit 'traverse-check) exit actor target-actor)) +(define* (exit-is-visible? exit room whos-exiting) + ((slot-ref exit 'visible-check) exit room whos-exiting)) ;;; Rooms ;;; ===== -(define %room-contain-commands - (list - (loose-direct-command "look" 'cmd-look-at) - (empty-command "look" 'cmd-look-room) - (empty-command "go" 'cmd-go-where) - (loose-direct-command "go" 'cmd-go))) - -(define room-actions - (build-actions - ;; desc == description - (wire-exits! (wrap-apply room-wire-exits!)) - (cmd-go (wrap-apply room-cmd-go)) - (cmd-go-where (wrap-apply room-cmd-go-where)) - (cmd-look-room (wrap-apply room-cmd-look-room)))) - -(define room-actions* - (append room-actions gameobj-actions)) - -(define room-action-dispatch - (simple-dispatcher room-actions*)) +(define (exit-shorthand name) + (lambda (room message) + (room-cmd-go room message #:direct-obj name))) ;; TODO: Subclass from container? (define-class () @@ -93,48 +72,247 @@ #:init-keyword #:exits #:getter room-exits) - (container-commands - #:init-value (wrap %room-contain-commands)) - - (message-handler + (container-dom-commands #:allocation #:each-subclass - ;; @@: Can remove this indirection once things settle - #:init-value (wrap-apply room-action-dispatch))) + #:init-thunk + (build-commands + (("l" "look") ((empty-command cmd-look-room))) + ("go" ((empty-command cmd-go-where) + (loose-direct-command cmd-go))) + (("say" "\"" "'") ((greedy-command cmd-say))) + (("emote" "/me") ((greedy-command cmd-emote))) + ;; movement aliases + (("n" "north") ((empty-command go-north))) + (("ne" "northeast") ((empty-command go-northeast))) + (("e" "east") ((empty-command go-east))) + (("se" "southeast") ((empty-command go-southeast))) + (("s" "south") ((empty-command go-south))) + (("sw" "southwest") ((empty-command go-southwest))) + (("w" "west") ((empty-command go-west))) + (("nw" "northwest") ((empty-command go-northwest))) + (("u" "up") ((empty-command go-up))) + (("d" "down") ((empty-command go-down))))) + (container-sub-commands + #:allocation #:each-subclass + #:init-thunk + (build-commands + (("l" "look") ((loose-direct-command cmd-look-at-from-room))))) -(define (room-wire-exits! room message) - "Actually hook up the rooms' exit addresses to the rooms they -claim to point to." - (for-each - (lambda (exit) - (define new-exit - (message-ref - (<-wait room (gameobj-gm room) 'lookup-room - #:symbol (slot-ref exit 'to-symbol)) - 'room-id)) + (actions #:allocation #:each-subclass + #:init-thunk + (build-actions + (cmd-go room-cmd-go) + (cmd-go-where room-cmd-go-where) + (announce-entrance room-announce-entrance) + (look-room room-look-room) + (tell-room room-act-tell-room) + ;; in this case the command is the same version as the normal + ;; look-room version + (cmd-look-room room-look-room) + (cmd-look-at-from-room room-look-dont-see-it) + (cmd-say room-cmd-say) + (cmd-emote room-cmd-emote) + ;; movement aliases + (go-north (exit-shorthand "north")) + (go-northeast (exit-shorthand "northeast")) + (go-east (exit-shorthand "east")) + (go-southeast (exit-shorthand "southeast")) + (go-south (exit-shorthand "south")) + (go-southwest (exit-shorthand "southwest")) + (go-west (exit-shorthand "west")) + (go-northwest (exit-shorthand "northwest")) + (go-up (exit-shorthand "up")) + (go-down (exit-shorthand "down"))))) - (slot-set! exit 'to-address new-exit)) +(define common-exit-aliases + '(("n" . "north") + ("ne" . "northeast") + ("e" . "east") + ("se" . "southeast") + ("s" . "south") + ("sw" . "southwest") + ("w" . "west") + ("nw" . "northwest") + ("u" . "up") + ("d" . "down"))) - (room-exits room))) +(define (dealias-exit-name exit-name) + (or (assoc-ref common-exit-aliases exit-name) + exit-name)) -(define-mhandler (room-cmd-go room message direct-obj) +(define* (room-cmd-go room message #:key direct-obj) (define exit (find (lambda (exit) - (equal? (exit-name exit) direct-obj)) + (equal? (exit-name exit) (dealias-exit-name direct-obj))) (room-exits room))) + (define to-address (if exit + ;; Get the exit, but resolve it dynamically + ;; in case it's a special + (dyn-ref room (slot-ref exit 'to)) + #f)) + (define player (message-from message)) + (define player-name + (mbody-val (<-wait player 'get-name))) (cond (exit - (<-wait room (message-from message) 'set-loc! - #:loc (slot-ref exit 'to-address)) - (<- room (message-from message) 'look-room)) + (call-with-values (lambda () + (exit-can-traverse? exit room player)) + (lambda* (can-traverse? #:optional player-flavortext + room-flavortext) + (cond + ;; The exit itself objects to moving + ((not can-traverse?) + (<- player 'tell + #:text (or player-flavortext + `("You try to go " ,direct-obj " but something " + "seems to block you."))) + (when room-flavortext + (room-tell-room room room-flavortext + #:exclude player))) + ;; to-address points nowhere, or exit not set. + ((not exit) + (<- player 'tell + #:text '((i "Yikes!") " Something weird is going on. " + "It seems like this exit leads nowhere, " + "in a programming bug kind of way. " + "Maybe tell an administrator?"))) + ;; looks like we can go, so let's go! + (else + ;; Set the player's new location + (<-wait player 'set-loc! + #:loc to-address) + (when player-flavortext + (<-wait player 'tell + #:text player-flavortext)) + ;; Tell everyone else the person walked away + (room-tell-room + room (or room-flavortext + (format #f "~a wanders ~a.\n" + player-name direct-obj))) + (<- to-address 'announce-entrance + #:who-entered player) + ;; Have the new room update the player to the new location + (<- to-address 'look-room + #:to-id player)))))) (else - (<- room (message-from message) 'tell + (<- player 'tell #:text "You don't see any way to go there.\n")))) -(define-mhandler (room-cmd-go-where room message) - (<- room (message-from message) 'tell +(define (room-cmd-go-where room message) + (<- (message-from message) 'tell #:text "Go where?\n")) -(define-mhandler (room-cmd-look-room room message) - (<- room (message-from message) 'look-room)) +;;; look commands + +(define (room-player-looks-around room player-id) + "Handle looking around the room" + ;; Get the room text + (define room-text + `((strong "=> " ,(slot-ref room 'name) " <=") + (p ,(gameobj-desc room)))) + + ;; Get a list of other things the player would see in the room + (define occupant-names-all + (map + (lambda (occupant) + (call-with-message (<-wait occupant 'visible-name + #:whos-looking player-id) + (lambda* (_ #:key text) + text))) + (remove + (lambda (x) (equal? x player-id)) + (hash-map->list (lambda (x _) x) + (slot-ref room 'occupants))))) + + ;; Strip out the #f responses (these aren't listed because they lack a name + ;; or they aren't "obviously visible" to the player) + (define occupant-names-filtered + (filter identity occupant-names-all)) + + (define occupant-names-string + (if (eq? occupant-names-filtered '()) + #f + (format #f "You see here: ~a.\n" + (string-join occupant-names-filtered + ", ")))) + + (define final-text + (if occupant-names-string + `(,@room-text + (p (em ,occupant-names-string))) + room-text)) + + (<- player-id 'tell + #:text final-text)) + + +(define* (room-look-room room message + ;; Either send it to the #:to-id of the message, + ;; or to the sender of the message + #:key (to-id (message-from message))) + "Command: Player asks to look around the room" + (room-player-looks-around room to-id)) + +(define (room-find-thing-called room called-this) + "Find something called CALLED-THIS in the room, if any." + (call/ec + (lambda (return) + (for-each + (lambda (occupant) + (define goes-by (mbody-val (<-wait occupant 'goes-by))) + (if (ci-member called-this goes-by) + (return occupant))) + (hash-map->list (lambda (key val) key) + (slot-ref room 'occupants))) + #f))) + +(define* (room-look-dont-see-it room message #:key direct-obj) + "In general, if we get to this point, we didn't find something to look at." + (<- (message-from message) 'tell + #:text "You don't see that here, so you can't look at it.\n")) + + +(define* (room-tell-room room text #:key exclude wait) + (define who-to-tell (gameobj-occupants room #:exclude exclude)) + (for-each + (lambda (tell-me) + ;; @@: Does anything really care? + (define deliver-method + (if wait + <-wait + <-)) + (deliver-method tell-me 'tell + #:text text)) + who-to-tell)) + +(define* (room-act-tell-room room message #:key text exclude wait) + "Tell the room some messages." + (room-tell-room room text + #:exclude exclude + #:wait wait)) + +(define* (room-cmd-say room message #:key phrase) + "Command: Say something to room participants." + (define player-name + (mbody-val (<-wait (message-from message) 'get-name))) + (define message-to-send + `((b "<" ,player-name ">") " " ,phrase)) + (room-tell-room room message-to-send)) + +(define* (room-cmd-emote room message #:key phrase) + "Command: Say something to room participants." + (define player-name + (mbody-val (<-wait (message-from message) 'get-name))) + (define message-to-send + `((b "* " ,player-name) " " ,phrase)) + (room-tell-room room message-to-send)) + +(define* (room-announce-entrance room message #:key who-entered) + (define player-name + (mbody-val (<-wait who-entered 'get-name))) + (define message-to-send + (format #f "~a enters the room.\n" player-name)) + (room-tell-room room message-to-send + #:exclude who-entered))