X-Git-Url: https://jxself.org/git/?p=mudsync.git;a=blobdiff_plain;f=mudsync%2Fcommand.scm;fp=mudsync%2Fcommand.scm;h=28092fcf43cf0f7b1939ba0910a411c6c3ae1ba5;hp=0000000000000000000000000000000000000000;hb=136ce3b725c83af0b7e8be632e943de6b07b65c5;hpb=c3e788ae7726f7e52a62ffeb30254bfa0f0da7cb diff --git a/mudsync/command.scm b/mudsync/command.scm new file mode 100644 index 0000000..28092fc --- /dev/null +++ b/mudsync/command.scm @@ -0,0 +1,191 @@ +(define-module (mudsync command) + #:use-module (mudsync parser) + #:use-module (mudsync gameobj) + #:use-module (8sync systems actors) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) + #:use-module (ice-9 control) + #:use-module (ice-9 match) + + #:export (direct-command + indir-command + empty-command + direct-greedy-command + greedy-command + player-gather-command-handlers + find-command-winner)) + +;;; Commands +;;; ======== + +(define %low-priority 0) +(define %default-priority 1) +(define %high-priority 2) + +(define-record-type + (make-command verbs matcher should-handle action priority) + command? + (verbs command-verbs) + (matcher command-matcher) + (should-handle command-should-handle) + (action command-action) + (priority command-priority)) + + +(define (direct-command verbs action) + (make-command verbs + cmatch-direct-obj + ;; @@: Should we allow fancier matching than this? + ;; Let the actor itself pass along this whole method? + (lambda* (goes-by #:key direct-obj) + (member direct-obj goes-by)) + action + %default-priority)) + +(define* (indir-command verbs action #:optional prepositions) + (make-command verbs + cmatch-indir-obj + ;; @@: Should we allow fancier matching than this? + ;; Let the actor itself pass along this whole method? + (lambda* (goes-by #:key direct-obj indir-obj preposition) + (if prepositions + (and + (member indir-obj goes-by) + (member preposition prepositions)) + (member indir-obj goes-by))) + action + %high-priority)) + +(define (empty-command verbs action) + (make-command verbs + cmatch-empty + (const #t) + action + %low-priority)) + +(define (greedy-command verbs action) + (make-command verbs + cmatch-greedy + (const #t) + action + %low-priority)) + +(define (direct-greedy-command verbs action) + "greedy commands but which match the direct object" + (make-command verbs + cmatch-direct-obj-greedy + (lambda* (goes-by #:key direct-obj rest) + (member direct-obj goes-by)) + action + %low-priority)) + +;; @@: We should probably ONLY allow these to go to users! +(define* (custom-command verbs matcher should-handle action + #:optional (priority %default-priority)) + "Full-grained customizable command." + (make-command verbs matcher should-handle action priority)) + + +;;; 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 + ((@@ (mudsync player) 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 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 actor-id)) commands))) + +(define (sort-commands-multi-actors actors-and-commands) + (sort + actors-and-commands + (lambda (x y) + (> (command-priority (cdr x)) + (command-priority (cdr y)))))) + + +(define-record-type + (make-command-winner action actor-id) + command-winner? + (action command-winner-action) + (actor-id command-winner-action-id)) + + +(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 + (define matcher-cache '()) + (find + (match-lambda + ((command actor-id actor-goes-by) + + (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 + actor-id + #f)))) + sorted-candidates))