;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Character Plans ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; These are building blocks for plans... Although they produce a ;;; function that looks like a plan, the return value of the building ;;; blocks are not useful for the game engine. For instance, a building ;;; block could return #f, which essentially kills a characters plan. ;;; Character plans best use these building blocks for simplification. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Movement plans ;;; Make an animate go toward a final destination (define (go-toward animate dest) (transfer animate (location animate) (find-path (location animate) dest))) ;;; Make a plan that moves towards different places, in order. Returns ;;; the place that the character is currently moving towards. (define (plan:round-robin . places) ;; check to see that everything is a place (for-each (lambda (x) (unless (instance-of? x ) (error 'plan:round-robin "expects arguments to be places"))) places) ;; create a circle to rotate among (append! places places) ;; return a plan (lambda (anim) (when (eq? (location anim) (head places)) (set! places (tail places))) (go-toward anim (head places)) (head places))) ;;; Make a plan that moves towards a random place, then choose ;;; a different place at random and move towards it. Returns the place ;;; that the character is currently moving towards. (define (plan:random-visit . places) ;; check to see that everything is a place (for-each (lambda (x) (unless (instance-of? x ) (error 'plan:round-robin "expects arguments to be places"))) places) ;; if at current dest, switch our destination, otherwise keep going (let ((dest (random-elt locs))) (lambda (anim) (when (eq? (location anim) dest) (set! dest (random-elt locs))) (go-toward animate dest) dest))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Speaking plans ;;; Make a plan that shows the animate saying something. Returns #f ;;; if the character didn't say anything and #t if the character ;;; did say something. (define (plan:speak-random . utterances) ;; check to see that the utterances are all strings (for-each (lambda (x) (unless (string? x) (error 'plan:speak-random "expects arguments to be strings"))) utterances) ;; return a plan (lambda (anim) (let ((say-it (zero? (random 10)))) (when say-it (say anim (random-elt utterances))) say-it))) ;;; Make a plan that shows the animate saying something when the animate ;;; is in a certain location. The arguments are a list of associations ;;; of the form (cons ). Returns #t when the character ;;; did say something, #f otherwise. (define (plan:speak-place wait . utterances) ;; check to see that the utterances is an associative list ;; of places and strings (for-each (lambda (x) (unless (and (instance-of? (head x) ) (string? (tail x))) (error 'plan:speak-place "expects arguments to be associations"))) utterances) (lambda (anim) (let ((current (assq (location anim) utterances))) (when current (say anim (tail current))) (and current #t)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Action plans ;;; Make a plan that shows the animate doing something at random. ;;; Returns #t when the animate did something, #f otherwise (define (plan:action-random . actions) ;; check to see that the utterances are all strings (for-each (lambda (x) (unless (string? x) (error 'plan:speak-random "expects arguments to be strings"))) actions) ;; return a plan (lambda (anim) (let ((do-it (zero? (random 10)))) (when do-it (place-message (location anim) anim #f (random-elt actions))) do-it))) ;;; Make a plan that shows the animate doing something when the animate ;;; is in a certain location. The arguments are a list of associations ;;; of the form (cons ). The string can hold one ;;; occurence of ~a to be replaced with the name of the animate. For ;;; example "~a picks up a book." could become "Brandon picks up a ;;; book." Returns #t when something happened, #f otherwise. (define (plan:action-place . actions) ;; check to see that the utterances is an associative list ;; of places and strings (for-each (lambda (x) (unless (and (instance-of? (head x) ) (string? (tail x))) (error 'plan:action-place "expects arguments to be associations"))) actions) (lambda (anim) (let ((current (assq (location anim) actions))) (when current (place-message (location anim) anim #f (format (tail current) (name anim)))) (and current #t)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Examples ;;; The following are examples of how to use the above plans as building ;;; blocks for more interesting plans. It's always a good idea to let ;;; some of these building blocks outside the main plan. That way you ;;; prevent repeating computation. ;; (define (plan:get-donut) ;; (let ((motion (plan:round-robin loc:csuglab loc:vending)) ;; (donut (plan:action-place ;; (cons loc:vending ;; (echos "~a is getting a donut from the" ;; "vending machine"))))) ;; (lambda (anim) ;; (donut anim) ;; (motion anim) ;; 30))) ;; (define (plan:get-coke) ;; (let ((motion (plan:random-visit loc:lounge loc:vending ;; loc:upson320)) ;; (coke (plan:speak-place ;; (cons loc:vending ;; "Ahh, caffeine... isn't coke great?")))) ;; (lambda (anim) ;; (coke anim) ;; (motion anim) ;; 60)))