;;; Mudsync --- Live hackable MUD
-;;; Copyright © 2016 Christopher Allan Webber <cwebber@dustycloud.org>
+;;; Copyright © 2016 Christine Lemmer-Webber <cwebber@dustycloud.org>
;;;
;;; This file is part of Mudsync.
;;;
#:use-module (mudsync gameobj)
#:use-module (mudsync game-master)
#:use-module (mudsync parser)
- #:use-module (8sync systems actors)
+ #:use-module (8sync actors)
#:use-module (8sync agenda)
+ #:use-module (8sync rmeta-slot)
#:use-module (ice-9 control)
#:use-module (ice-9 format)
#:use-module (ice-9 match)
#:use-module (oop goops)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-9)
- #:export (<player>
- player-self-commands))
+ #:export (<player>))
;;; Players
;;; =======
-(define player-actions
- (build-actions
- (init (wrap-apply player-init))
- (handle-input (wrap-apply player-handle-input))
- (tell (wrap-apply player-tell))
- (disconnect-self-destruct (wrap-apply player-disconnect-self-destruct))))
-
-(define player-actions*
- (append player-actions
- gameobj-actions))
-
-(define player-dispatcher
- (simple-dispatcher player-actions*))
-
(define-class <player> (<gameobj>)
(username #:init-keyword #:username
#:getter player-username)
- (self-commands
- #:init-value '()
- #:getter player-self-commands)
+ (self-commands #:allocation #:each-subclass
+ #:init-thunk
+ (build-commands
+ (("inventory" "inv" "i") ((empty-command cmd-inventory)))
+ ("help" ((empty-command cmd-help)))))
- (message-handler
- #:init-value
- ;; @@: We're gonna need action inheritance real awful soon, huh?
- (wrap-apply player-dispatcher)))
+ (actions #:allocation #:each-subclass
+ #:init-thunk
+ (build-actions
+ (init player-init)
+ (handle-input player-handle-input)
+ (tell player-tell)
+ (disconnect-self-destruct player-disconnect-self-destruct)
+ (cmd-inventory player-cmd-inventory)
+ (cmd-help player-cmd-help))))
;;; player message handlers
-(define-mhandler (player-init player message)
+(define (player-init player message)
;; Look around the room we're in
- (<- player (gameobj-loc player) 'look-room))
+ (<- (gameobj-loc player) 'look-room))
-(define-mhandler (player-handle-input player message input)
+(define* (player-handle-input player message #:key input)
(define split-input (split-verb-and-rest input))
(define input-verb (car split-input))
(define input-rest (cdr split-input))
(match winner
((cmd-action winner-id message-args)
- (apply send-message player winner-id cmd-action message-args))
+ (apply <- winner-id cmd-action message-args))
(#f
- (<- player (gameobj-gm player) 'write-home
- #:text "Huh?\n"))))
+ (<- (gameobj-gm player) 'write-home
+ #:text "Sorry, I didn't understand that? (type \"help\" for common commands)\n"))))
-(define-mhandler (player-tell player message text)
- (<- player (gameobj-gm player) 'write-home
+(define* (player-tell player message #:key text)
+ (<- (gameobj-gm player) 'write-home
#:text text))
-(define-mhandler (player-disconnect-self-destruct player message)
+(define (player-disconnect-self-destruct player message)
"Action routine for being told to disconnect and self destruct."
(define loc (gameobj-loc player))
(when loc
- (<- player loc 'tell-room
+ (<- loc 'tell-room
#:exclude (actor-id player)
#:text (format #f "~a disappears in a puff of entropy!\n"
(slot-ref player 'name))))
(gameobj-self-destruct player))
+(define (player-cmd-inventory player message)
+ "Display the inventory for the player"
+ (define inv-names
+ (map
+ (lambda (inv-item)
+ (mbody-val (<-wait inv-item 'get-name)))
+ (gameobj-occupants player)))
+ (define text-to-show
+ (if (eq? inv-names '())
+ "You aren't carrying anything.\n"
+ `((p "You are carrying:")
+ (ul ,(map (lambda (item-name)
+ `(li ,item-name))
+ inv-names)))))
+ (<- (actor-id player) 'tell #:text text-to-show))
+
+(define (player-cmd-help player message)
+ (<- (actor-id player) 'tell
+ #:text '((strong "** Mudsync Help **")(br)
+ (p "You're playing Mudsync, a multiplayer text-adventure. "
+ "Type different commands to interact with your surroundings "
+ "and other players.")
+ (p "Some common commands:"
+ (ul (li (strong "say <message>") " -- "
+ "Chat with other players in the same room. "
+ "(Also aliased to the " (b "\"") " character.)")
+ (li (strong "look") " -- "
+ "Look around the room you're in.")
+ (li (strong "look [at] <object>") " -- "
+ "Examine a particular object.")
+ (li (strong "go <exit>") " -- "
+ "Move to another room in <exit> direction.")))
+ (p "Different objects can be interacted with in different ways. "
+ "For example, if there's a bell in the same room as you, "
+ "you might try typing " (em "ring bell")
+ " and see what happens."))))
+
;;; Command handling
;;; ================
#:player-id (actor-id player)))))
;; Ask the room for its commands
- (define room-commands
+ (define room-dom-commands
+ ;; TODO: Map room id and sort
+ (mbody-receive (_ #:key commands)
+ (<-wait player-loc 'get-container-dom-commands
+ #:verb verb)
+ commands))
+
+ (define room-sub-commands
;; TODO: Map room id and sort
- (message-ref
- (<-wait player player-loc
- 'get-container-commands
- #:verb verb)
- 'commands))
+ (mbody-receive (_ #:key commands)
+ (<-wait player-loc 'get-container-sub-commands
+ #:verb verb)
+ commands))
;; All the co-occupants of the room (not including ourself)
(define co-occupants
(remove
(lambda (x) (equal? x (actor-id player)))
- (message-ref
- (<-wait player player-loc 'get-occupants)
- 'occupants)))
+ (mbody-val (<-wait player-loc 'get-occupants))))
;; @@: There's a race condition here if someone leaves the room
;; during this, heh...
;; Get all the co-occupants' commands
(define co-occupant-commands
- ;; TODO: Switch this to a fold. Ignore a result if it
- ;; returns false for in the command response
(fold
(lambda (co-occupant prev)
- (let* ((result (<-wait player co-occupant 'get-commands
- #:verb verb))
- (commands (message-ref result 'commands))
- (goes-by (message-ref result 'goes-by)))
+ (mbody-receive (_ #:key commands goes-by)
+ (<-wait co-occupant 'get-commands
+ #:verb verb)
(append
(map (lambda (command)
(list command goes-by co-occupant))
;; Append our own command handlers
(define our-commands
- (player-self-commands player))
+ (class-rmeta-ref (class-of player) 'self-commands verb
+ #:dflt '()))
- ;; TODO: Append our inventory's relevant command handlers
+ ;; Append our inventory's relevant command handlers
+ (define inv-items
+ (gameobj-occupants player))
+ (define inv-item-commands
+ (fold
+ (lambda (inv-item prev)
+ (mbody-receive (_ #:key commands goes-by)
+ (<-wait inv-item 'get-contained-commands
+ #:verb verb)
+ (append
+ (map (lambda (command)
+ (list command goes-by inv-item))
+ commands)
+ prev)))
+ '()
+ inv-items))
;; Now return a big ol sorted list of ((actor-id . command))
(append
- (sort-commands-append-actor room-commands
+ (sort-commands-append-actor room-dom-commands
player-loc '()) ; room doesn't go by anything
(sort-commands-multi-actors co-occupant-commands)
(sort-commands-append-actor our-commands
- (actor-id player) '()))) ; nor does player
+ (actor-id player) '()) ; nor does player
+ (sort-commands-multi-actors inv-item-commands)
+ (sort-commands-append-actor room-sub-commands
+ player-loc '())))
(define (sort-commands-append-actor commands actor-id goes-by)
(sort-commands-multi-actors
(define (find-command-winner sorted-candidates line)
"Find a command winner from a sorted list of candidates"
;; A cache of results from matchers we've already seen
- ;; TODO: fill this in
+ ;; TODO: fill in this cache. This is a *critical* optimization!
(define matcher-cache '())
(call/ec
(lambda (return)