commands finally dispatch
[mudsync.git] / mudsync / player.scm
index feb14f80a2375e9b67a4d6b4728ab558226c0c12..63af65e77a8f84071f5780cfd486f6a5fb183717 100644 (file)
   #:use-module (mudsync parser)
   #:use-module (8sync systems actors)
   #:use-module (8sync agenda)
+  #: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))
 
@@ -41,6 +44,9 @@
   (append player-actions
           gameobj-actions))
 
+(define player-dispatcher
+  (simple-dispatcher player-actions*))
+
 (define-class <player> (<gameobj>)
   (username #:init-keyword #:username
             #:accessor player-username)
@@ -54,7 +60,7 @@
   (message-handler
    #:init-value
    ;; @@: We're gonna need action inheritance real awful soon, huh?
-   (simple-dispatcher player-actions*)))
+   (wrap-apply player-dispatcher)))
 
 
 ;;; player message handlers
   (define winner
     (pk 'winner (find-command-winner command-candidates input-rest)))
 
-  (<- player (gameobj-gm player) 'write-home
-      #:text
-      (format #f "<~a>: ~s\n"
-              (player-username player)
-              input)))
+  (match winner
+    ((cmd-action winner-id message-args)
+     (apply send-message player (pk 'winner-id winner-id) (pk 'cmd-action cmd-action) (pk 'message-args message-args)))
+    (#f
+     (<- player (gameobj-gm player) 'write-home
+         #:text "Huh?\n"))))
 
 
 ;;; player methods
   (<- player (gameobj-gm player) 'write-home #:text message-text))
 
 
+;;; Command handling
+;;; ================
+
+;; @@: Hard to know whether this should be in player.scm or here...
+;; @@: This could be more efficient as a stream...!?
+(define (player-gather-command-handlers player verb)
+  (define player-loc
+    (let ((result (gameobj-loc player)))
+      (if result
+          result
+          (throw 'player-has-no-location
+                 "Player ~a has no location!  How'd that happen?\n"
+                 #:player-id (actor-id player)))))
 
+  ;; Ask the room for its commands
+  (define room-commands
+    ;; TODO: Map room id and sort
+    (message-ref
+     (<-wait player player-loc
+             'get-container-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)))
+
+  ;; @@: There's a race condition here if someone leaves the room
+  ;;   during this, heh...
+  ;;   I'm not sure it can be solved, but "lag" on the race can be
+  ;;   reduced maybe?
+
+  ;; 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
+    (map
+     (lambda (co-occupant)
+       (let ((result (<-wait player co-occupant 'get-commands
+                             #:verb verb)))
+         (list
+          (message-ref result 'commands)
+          (message-ref result 'goes-by)
+          co-occupant)))
+     co-occupants))
+
+  ;; Append our own command handlers
+  (define our-commands
+    (player-self-commands player))
+
+  ;; TODO: Append our inventory's relevant command handlers
+
+  ;; Now return a big ol sorted list of ((actor-id . command))
+  (append
+   (sort-commands-append-actor (pk 'room-commands room-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
+
+(define (sort-commands-append-actor commands actor-id goes-by)
+  (sort-commands-multi-actors
+   (map (lambda (command) (list command goes-by actor-id)) commands)))
+
+(define (sort-commands-multi-actors actors-and-commands)
+  (sort
+   actors-and-commands
+   (lambda (x y)
+     (> (command-priority (car (pk 'x x)))
+        (command-priority (car (pk 'y y)))))))
+
+
+(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
+  (define matcher-cache '())
+  (call/ec
+   (lambda (return)
+     (for-each
+      (match-lambda
+        ((command actor-goes-by actor-id)
+         (let* ((matcher (command-matcher command))
+                (matched (matcher line)))
+           (if (and matched
+                    ;; Great, it matched, but does it also pass
+                    ;; should-handle?
+                    (apply (command-should-handle command)
+                           actor-goes-by
+                           matched))  ; matched is kwargs if truthy
+               (return (list (command-action command)
+                             (pk 'earlier-actor-id actor-id) matched))
+               #f))))
+      sorted-candidates)
+     #f)))