#lang r7rs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Ghost ADT ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; A ghost has a position, direction, movement mode, and type identity. ;; The targeting AI is handled by the level — this ADT only stores state. ;; Each ghost instance represents one of the four ghosts (Blinky, Inky, ;; Pinky, Clyde). Contains NO graphics code. (define-library (pacman-project adt ghost) (import (scheme base) (pacman-project adt position)) (export make-ghost) (begin ;; make-ghost :: symbol, number, number, number, number, number -> ghost ;; Creates a ghost of the given type at start-row/col with a scatter ;; corner target and a house exit delay (0 = starts outside). (define (make-ghost ghost-type start-row start-col scatter-row scatter-col exit-delay) (let ((position (make-position start-row start-col)) (direction 'left) (mode (if (= exit-delay 0) 'scatter 'in-house)) (scatter-target (make-position scatter-row scatter-col)) (house-timer exit-delay) (movement-timer 0) (reverse-queued? #f) (prev-row start-row) (prev-col start-col)) ;; direction! :: symbol -> / (define (direction! new-dir) (set! direction new-dir)) ;; mode! :: symbol -> / ;; Sets the ghost mode. When switching between chase/scatter, ;; the ghost must reverse direction (queued for next move). (define (mode! new-mode) (when (and (not (eq? mode new-mode)) (not (eq? mode 'in-house)) (not (eq? new-mode 'in-house))) (set! reverse-queued? #t)) (set! mode new-mode)) ;; move! :: number, number -> / ;; Saves previous position, then moves by delta. (define (move! delta-row delta-col) (set! prev-row (position 'row)) (set! prev-col (position 'col)) ((position 'row!) (+ (position 'row) delta-row)) ((position 'col!) (+ (position 'col) delta-col))) ;; sync-prev! :: -> / ;; Sets previous position to current. Call after teleportation ;; or ghost house exit to prevent long-range interpolation. (define (sync-prev!) (set! prev-row (position 'row)) (set! prev-col (position 'col))) ;; consume-reverse! :: -> boolean ;; Returns #t and clears the flag if a reverse was queued. (define (consume-reverse!) (if reverse-queued? (begin (set! reverse-queued? #f) #t) #f)) ;; update-house-timer! :: number -> / ;; Decreases house timer. When it reaches 0, ghost exits. (define (update-house-timer! delta-time) (when (eq? mode 'in-house) (set! house-timer (- house-timer delta-time)) (when (<= house-timer 0) (set! mode 'scatter)))) ;; reset-movement-timer! :: -> / (define (reset-movement-timer!) (set! movement-timer 0)) ;; set-movement-timer! :: number -> / (define (set-movement-timer! val) (set! movement-timer val)) ;; advance-movement-timer! :: number -> number ;; Returns updated movement timer value. (define (advance-movement-timer! delta-time) (set! movement-timer (+ movement-timer delta-time)) movement-timer) ;; dispatch-ghost :: symbol -> any (define (dispatch-ghost msg) (cond ((eq? msg 'position) position) ((eq? msg 'direction) direction) ((eq? msg 'direction!) direction!) ((eq? msg 'type) ghost-type) ((eq? msg 'mode) mode) ((eq? msg 'mode!) mode!) ((eq? msg 'move!) move!) ((eq? msg 'prev-row) prev-row) ((eq? msg 'prev-col) prev-col) ((eq? msg 'sync-prev!) sync-prev!) ((eq? msg 'scatter-target) scatter-target) ((eq? msg 'in-house?) (eq? mode 'in-house)) ((eq? msg 'consume-reverse!) consume-reverse!) ((eq? msg 'update-house-timer!) update-house-timer!) ((eq? msg 'movement-timer) movement-timer) ((eq? msg 'advance-movement-timer!) advance-movement-timer!) ((eq? msg 'reset-movement-timer!) reset-movement-timer!) ((eq? msg 'set-movement-timer!) set-movement-timer!) (else (error "Ghost ADT -- Unknown message:" msg)))) dispatch-ghost))))