(define-module (bonnie-bee player) #:use-module (bonnie-bee actor) #:use-module (bonnie-bee assets) #:use-module (bonnie-bee bullet) #:use-module (bonnie-bee common) #:use-module (bonnie-bee flower) #:use-module (chickadee audio) #:use-module (chickadee game-loop) #:use-module (chickadee graphics particles) #:use-module (chickadee math) #:use-module (chickadee math vector) #:use-module (chickadee scripting) #:use-module (oop goops) #:use-module (catbird asset) #:use-module (catbird node) #:use-module (catbird node-2d) #:export (%max-pollen move-left? move-right? move-down? move-up? moving? shoot? invincible? bombing? scoring-enabled? speed lives pollen score shoot-maybe bomb add-to-score)) (define %max-pollen 50) (define-class () (move-left? #:accessor move-left? #:init-value #f #:watch? #t) (move-right? #:accessor move-right? #:init-value #f #:watch? #t) (move-down? #:accessor move-down? #:init-value #f) (move-up? #:accessor move-up? #:init-value #f) (shoot? #:accessor shoot? #:init-value #f #:watch? #t) (last-shot #:accessor last-shot #:init-value 0) (shot-interval #:getter shot-interval #:init-form (steps 2)) (speed #:accessor speed #:init-value 1.8) (lives #:accessor lives #:init-value 3) (invincible? #:accessor invincible? #:init-value #f) (pollen #:accessor pollen #:init-value 0) (score #:accessor score #:init-value 0) (scoring-enabled? #:accessor scoring-enabled? #:init-value #f) (bombing? #:accessor bombing? #:init-value #f)) (define-method (moving? (player )) (or (move-left? player) (move-right? player) (move-down? player) (move-up? player))) (define-method (on-boot (player )) (attach-to player (make #:name 'sprite #:atlas bee-atlas #:origin (vec2 16.0 16.0) #:animations `((default . ,(make #:frames #(0 1) #:frame-duration 0.05)) (move-left . ,(make #:frames #(2 3) #:frame-duration 0.05)) (move-right . ,(make #:frames #(4 5) #:frame-duration 0.05)) (idle . ,(make #:frames #(0 1) #:frame-duration 0.75)))))) (define-method (update-animation (player )) (change-animation (& player sprite) (cond ((and (move-left? player) (not (move-right? player))) 'move-left) ((and (not (move-left? player)) (move-right? player)) 'move-right) (else 'default)))) (define-method (on-change (player ) slot-name old new) (case slot-name ((shoot?) (when (and new (not (and old new))) (set! (last-shot player) 0))) ((move-left? move-right?) (unless (eq? old new) (update-animation player))))) (define-method (after-move (player )) (let ((p (position player))) (cond ((< (vec2-x p) 8.0) (set-vec2-x! p 8.0)) ((> (vec2-x p) 312.0) (set-vec2-x! p 312.0))) (cond ((< (vec2-y p) 8.0) (set-vec2-y! p 8.0)) ((> (vec2-y p) 232.0) (set-vec2-y! p 232.0))))) (define-method (update (player ) dt) (let ((v (velocity player))) (set-vec2! v (+ (if (move-left? player) -1.0 0.0) (if (move-right? player) 1.0 0.0)) (+ (if (move-down? player) -1.0 0.0) (if (move-up? player) 1.0 0.0))) (vec2-normalize! v) (vec2-mult! v (speed player))) (next-method)) (define-method (lose-life (player )) (unless (invincible? player) (set! (lives player) (max (- (lives player) 1) 0)) (set! (pollen player) 0) (set! (invincible? player) #t) (audio-play (artifact player-death-sound)) (add-particle-emitter (particles (& (parent player) particles)) (make-particle-emitter (world-hitbox player) 10 5)) (run-script player (blink player 20 (steps 5)) (set! (invincible? player) #f)))) (define-method (on-collide (player ) (bullet )) (cond ((eq? (type bullet) pollen-pickup) (audio-play (artifact pickup-sound) #:volume 0.3) (kill-bullet bullet) (add-to-score player 5) (set! (pollen player) (min (+ (pollen player) 1) %max-pollen)) #t) ((or (eq? (type bullet) small-enemy-bullet) (eq? (type bullet) medium-enemy-bullet) (eq? (type bullet) large-enemy-bullet)) (kill-bullet bullet) (lose-life player) #t) (else #f))) (define-method (on-collide (player ) (thing )) (lose-life player)) (define-method (on-collide (thing ) (player )) (lose-life player)) (define-method (on-collide (player ) (flower )) #f) (define-method (on-collide (flower ) (player )) #f) (define-method (shoot-maybe (player ) bullets) (with-agenda (agenda player) (when (and (shoot? player) (>= (- (agenda-time) (last-shot player)) (shot-interval player))) (let ((p (position player))) (set! (last-shot player) (agenda-time)) (audio-play (artifact player-shoot-sound) #:volume 0.2) (add-bullet bullets player-primary-bullet (vec2 (vec2-x p) (+ (vec2-y p) 14.0)) (vec2 0.0 6.0)))))) (define-method (bomb (player )) (unless (or (bombing? player) (< (pollen player) 10)) (run-script player (let* ((times (floor (/ (pollen player) 10))) (num-bullets 64) (theta-step (/ tau num-bullets)) (radius 16.0) (speed 5.0) (p (position player)) (bullets (& (parent player) bullets))) (add-to-score player (expt 5 times)) (set! (bombing? player) #t) (set! (pollen player) 0) (set! (invincible? player) #t) (let loop ((i 0)) (when (< i times) (audio-play (artifact player-bomb-sound)) (let shot-loop ((j 0)) (when (< j num-bullets) (let ((theta (* j theta-step))) (add-bullet bullets player-bomb-bullet (vec2/polar p radius theta) (vec2 (* (cos theta) speed) (* (sin theta) speed)))) (shot-loop (+ j 1)))) (sleep (* (current-timestep) 3)) (loop (+ i 1)))) (sleep 1.0) (set! (invincible? player) #f) (set! (bombing? player) #f))))) (define-method (add-to-score (player ) points) (when (scoring-enabled? player) (set! (score player) (+ (score player) points))))