summaryrefslogtreecommitdiff
path: root/bonnie-bee/game.scm
diff options
context:
space:
mode:
Diffstat (limited to 'bonnie-bee/game.scm')
-rw-r--r--bonnie-bee/game.scm791
1 files changed, 447 insertions, 344 deletions
diff --git a/bonnie-bee/game.scm b/bonnie-bee/game.scm
index 18b177b..40d5cb6 100644
--- a/bonnie-bee/game.scm
+++ b/bonnie-bee/game.scm
@@ -20,12 +20,20 @@
#:use-module (chickadee math vector)
#:use-module (chickadee scripting)
#:use-module (oop goops)
- #:use-module (starling asset)
- #:use-module (starling kernel)
- #:use-module (starling node)
- #:use-module (starling node-2d)
- #:use-module (starling scene)
- #:export (<game>))
+ #:use-module (catbird)
+ #:use-module (catbird asset)
+ #:use-module (catbird kernel)
+ #:use-module (catbird observer)
+ #:use-module (catbird mode)
+ #:use-module (catbird node)
+ #:use-module (catbird node-2d)
+ #:use-module (catbird scene)
+ #:export (make-game-scene))
+
+
+;;;
+;;; Shadow label
+;;;
(define %text-color (rgb #xfee761))
@@ -37,7 +45,7 @@
(make <label>
#:name 'shadow
#:position (vec2 0.0 1.0)
- #:font (font label)
+ #:font (slot-ref label 'font)
#:text (text label)
#:color (color label)))
(set! (color label) black))
@@ -46,79 +54,83 @@
(next-method)
(case slot-name
((text)
- (set! (text (& label shadow)) new))) )
+ (set! (text (& label shadow)) new))))
-(define %game-bounds (make-rect 0.0 0.0 %game-width %game-height))
+
+;;;
+;;; Heads-up display
+;;;
-(define-class <game> (<scene-2d>)
- (state #:accessor state #:init-value 'play)
- (quadtree #:getter quadtree #:init-form (make-quadtree %game-bounds))
- (scroll-speed #:getter scroll-speed #:init-value 0.0)
+(define-class <hud> (<node-2d>)
(last-lives #:accessor last-lives #:init-value 0)
(last-pollen #:accessor last-pollen #:init-value 0)
- (last-score #:accessor last-score #:init-value 0)
- (music-source #:getter music-source #:init-form (make-source #:loop? #t)))
-
-(define-method (change-scroll-speed (game <game>) speed)
- (slot-set! game 'scroll-speed speed))
-
-(define-method (spawn (game <game>) (actor <actor>))
- (set! (quadtree actor) (quadtree game))
- (quadtree-insert! (quadtree game) (world-hitbox actor) actor)
- (attach-to game actor))
-
-(define-method (player (game <game>))
- (& game player))
-
-(define-method (bullets (game <game>))
- (& game bullets))
-
-(define-method (particles (game <game>))
- (& game particles))
+ (last-score #:accessor last-score #:init-value 0))
-(define-method (on-boot (game <game>))
- (set! *random-state* (random-state-from-platform))
- (set-cameras! game)
- (attach-to game
- (make <background>
- #:name 'background
- #:texture background-image)
- (make <particles>
- #:name 'particles
- #:particles (make-particles 2048
- #:texture (asset-ref particle-image)
- #:end-color (make-color 1.0 1.0 1.0 0.0)))
- (make <bullets>
- #:name 'bullets
- #:rank 4
- #:quadtree (quadtree game))
+(define-method (on-boot (hud <hud>))
+ (attach-to hud
(make <shadow-label>
- #:name 'hud-lives
- #:rank 5
+ #:name 'lives
#:position (vec2 2.0 2.0)
#:font monogram-font
#:text "lives 0"
#:color %text-color)
(make <shadow-label>
- #:name 'hud-pollen
- #:rank 5
+ #:name 'pollen
#:position (vec2 (/ %game-width 2.0) 2.0)
#:font monogram-font
#:text "pollen 0"
#:align 'center
#:color %text-color)
(make <shadow-label>
- #:name 'hud-score
- #:rank 5
+ #:name 'score
#:position (vec2 (- %game-width 2.0) 2.0)
#:font monogram-font
#:text "score 0"
#:align 'right
#:color %text-color)))
-(define-method (play-music (game <game>) music-asset)
- (set-source-audio! (music-source game) (asset-ref music-asset))
- (source-play (music-source game)))
+(define-method (update (hud <hud>) dt)
+ (let ((p (& (parent hud) player)))
+ (when (not (= (last-lives hud) (lives p)))
+ (set! (text (& hud lives))
+ (string-append "lives " (number->string (lives p)))))
+ (when (not (= (last-pollen hud) (pollen p)))
+ (let ((n (pollen p)))
+ (set! (text (& hud pollen))
+ (string-append "pollen "
+ (if (= n %max-pollen) "MAX" (number->string n))))))
+ (when (not (= (last-score hud) (score p)))
+ (set! (text (& hud score))
+ (string-append "score " (number->string (score p)))))
+ (set! (last-lives hud) (lives p))
+ (set! (last-pollen hud) (pollen p))
+ (set! (last-score hud) (score p))))
+
+
+;;;
+;;; Game scene
+;;;
+
+(define %game-bounds (make-rect 0.0 0.0 %game-width %game-height))
+(define %default-player-position (vec2 (/ %game-width 2.0) 20.0))
+
+(define-class <game-scene> (<scene>)
+ (quadtree #:getter quadtree #:init-form (make-quadtree %game-bounds))
+ (scroll-speed #:getter scroll-speed #:init-value 0.0)
+ (music-source #:getter music-source #:init-form (make-source #:loop? #t))
+ (intro-script #:accessor intro-script #:init-value #f))
+
+(define-method (change-scroll-speed (scene <game-scene>) speed)
+ (slot-set! scene 'scroll-speed speed))
+
+(define-method (spawn (scene <game-scene>) (actor <actor>))
+ (set! (quadtree actor) (quadtree scene))
+ (quadtree-insert! (quadtree scene) (world-hitbox actor) actor)
+ (attach-to scene actor))
+
+(define-method (play-music (scene <game-scene>) music-asset)
+ (set-source-audio! (music-source scene) (artifact music-asset))
+ (source-play (music-source scene)))
(define* (make-turret p #:key (interval 0.5) (speed 1.0))
(let ((turret (make <turret>
@@ -130,11 +142,11 @@
(run-script turret
(sleep (steps 1))
(forever
- (let ((theta (+ (angle-to turret (player (parent turret)))
+ (let ((theta (+ (angle-to turret (& (parent turret) player))
(* (- (* (random:uniform) 2.0) 1.0) 0.2)))
(speed 1.5))
- (audio-play (asset-ref enemy-shoot-sound))
- (add-bullet (bullets (parent turret))
+ (audio-play (artifact enemy-shoot-sound))
+ (add-bullet (& (parent turret) bullets)
medium-enemy-bullet
(position turret)
(vec2 (* (cos theta) speed)
@@ -168,11 +180,14 @@
#:health 20
#:points 1000))
-(define-method (change-state (game <game>) new-state)
- (set! (state game) new-state))
+(define-method (skip-intro (scene <game-scene>))
+ (teleport (& scene player)
+ (vec2-x %default-player-position)
+ (vec2-y %default-player-position))
+ (cancel-script (intro-script scene)))
-(define-method (run-level (game <game>))
- (define (tutorial message key)
+(define-method (run-level (scene <game-scene>))
+ (define (tutorial message continue?)
(let ((l (make <shadow-label>
#:name 'tutorial
#:rank 5
@@ -181,160 +196,196 @@
#:text message
#:align 'center
#:color %text-color)))
- (attach-to game l)
- (if key
- (wait-until (key-pressed? key))
+ (attach-to scene l)
+ (if continue?
+ (begin
+ (wait-until (continue?))
+ (sleep 1.0))
(sleep 2.0))
(detach l)))
- (run-script game
- (fade-in game 1.0))
- (run-script game
- (unless (getenv "SKIP_INTRO")
- ;; Intro
- (let ((popcorn (make-popcorn (vec2 (/ %game-width 2.0) (+ %game-height 16.0))))
- (bullet-speed 5.0)
- (flower-1 (make-flower (vec2 194.0 210.0)))
- (flower-2 (make-flower (vec2 50.0 206.0)))
- (flower-3 (make-flower (vec2 280.0 201.0)))
- (flower-4 (make-flower (vec2 121.0 197.0)))
- (flower-5 (make-flower (vec2 58.0 134.0)))
- (flower-6 (make-flower (vec2 145.0 135.0)))
- (flower-7 (make-flower (vec2 220.0 151.0)))
- (flower-8 (make-flower (vec2 290.0 129.0))))
- (define (shoot-at flower)
- (add-bullet (bullets game)
- medium-enemy-bullet
- (position popcorn)
- (vec2* (direction-to popcorn flower) bullet-speed)))
- (change-state game 'intro)
- (spawn game flower-1)
- (spawn game flower-2)
- (spawn game flower-3)
- (spawn game flower-4)
- (spawn game flower-5)
- (spawn game flower-6)
- (spawn game flower-7)
- (spawn game flower-8)
- (spawn game (make-flower (vec2 194.0 95.0)))
- (spawn game (make-flower (vec2 102.0 82.0)))
- (spawn game (make-flower (vec2 247.0 45.0)))
- (spawn game (make-flower (vec2 159.0 40.0)))
- (spawn game (make-flower (vec2 73.0 28.0)))
- (teleport (player game) 159.0 40.0)
- (play-music game intro-music)
- (sleep 1.0)
- (audio-play (asset-ref pollen-release-sound))
- (sleep 0.6)
- (move-to (player game) 247.0 45.0 1.0)
- (sleep 0.2)
- (audio-play (asset-ref pollen-release-sound))
- (sleep 0.6)
- (move-to (player game) 102.0 76.0 1.0)
- (sleep 0.2)
- (audio-play (asset-ref pollen-release-sound))
- (sleep 2.0)
- (run-script game
- (tween 1.0 1.0 0.0
- (lambda (volume)
- (set-source-volume! (music-source game) volume))))
- (source-stop (music-source game))
- (spawn game popcorn)
- (change-velocity popcorn 0.0 -1.0)
- (tween 2.4 -1.0 0.0
- (lambda (dy)
- (change-velocity popcorn 0.0 dy)))
- (shoot-at flower-1)
- (sleep 0.3)
- (shoot-at flower-2)
- (sleep 0.3)
- (shoot-at flower-3)
- (sleep 0.3)
- (shoot-at flower-4)
- (sleep 0.3)
- (shoot-at flower-8)
- (sleep 0.3)
- (shoot-at flower-7)
- (sleep 0.3)
- (shoot-at flower-6)
- (sleep 0.3)
- (shoot-at flower-5)
- (sleep 1.0)
- (audio-play (asset-ref hehehe-sound))
- (sleep 1.0)
- (tween 0.5 0.0 1.5
- (lambda (dy)
- (change-velocity popcorn 0.0 dy)))
- (sleep 1.0)))
+ (run-script scene
+ (fade-in scene 1.0))
+ (run-script scene
+ (set! (scoring-enabled? (& scene player)) #f)
+ (set! (intro-script scene)
+ (run-script scene
+ ;; Intro
+ (let ((popcorn (make-popcorn (vec2 (/ %game-width 2.0) (+ %game-height 16.0))))
+ (bullet-speed 5.0)
+ (flower-1 (make-flower (vec2 194.0 210.0)))
+ (flower-2 (make-flower (vec2 50.0 206.0)))
+ (flower-3 (make-flower (vec2 280.0 201.0)))
+ (flower-4 (make-flower (vec2 121.0 197.0)))
+ (flower-5 (make-flower (vec2 58.0 134.0)))
+ (flower-6 (make-flower (vec2 145.0 135.0)))
+ (flower-7 (make-flower (vec2 220.0 151.0)))
+ (flower-8 (make-flower (vec2 290.0 129.0))))
+ (define (shoot-at flower)
+ (add-bullet (& scene bullets)
+ medium-enemy-bullet
+ (position popcorn)
+ (vec2* (direction-to popcorn flower) bullet-speed)))
+ (spawn scene flower-1)
+ (spawn scene flower-2)
+ (spawn scene flower-3)
+ (spawn scene flower-4)
+ (spawn scene flower-5)
+ (spawn scene flower-6)
+ (spawn scene flower-7)
+ (spawn scene flower-8)
+ (spawn scene (make-flower (vec2 194.0 95.0)))
+ (spawn scene (make-flower (vec2 102.0 82.0)))
+ (spawn scene (make-flower (vec2 247.0 45.0)))
+ (spawn scene (make-flower (vec2 159.0 40.0)))
+ (spawn scene (make-flower (vec2 73.0 28.0)))
+ (teleport (& scene player) 159.0 40.0)
+ (play-music scene intro-music)
+ (sleep 1.0)
+ (audio-play (artifact pollen-release-sound))
+ (sleep 0.6)
+ (move-to (& scene player) 247.0 45.0 1.0)
+ (sleep 0.2)
+ (audio-play (artifact pollen-release-sound))
+ (sleep 0.6)
+ (move-to (& scene player) 102.0 76.0 1.0)
+ (sleep 0.2)
+ (audio-play (artifact pollen-release-sound))
+ (sleep 2.0)
+ (run-script scene
+ (tween 1.0 1.0 0.0
+ (lambda (volume)
+ (set-source-volume! (music-source scene) volume))))
+ (source-stop (music-source scene))
+ (spawn scene popcorn)
+ (change-velocity popcorn 0.0 -1.0)
+ (tween 2.4 -1.0 0.0
+ (lambda (dy)
+ (change-velocity popcorn 0.0 dy)))
+ (shoot-at flower-1)
+ (sleep 0.3)
+ (shoot-at flower-2)
+ (sleep 0.3)
+ (shoot-at flower-3)
+ (sleep 0.3)
+ (shoot-at flower-4)
+ (sleep 0.3)
+ (shoot-at flower-8)
+ (sleep 0.3)
+ (shoot-at flower-7)
+ (sleep 0.3)
+ (shoot-at flower-6)
+ (sleep 0.3)
+ (shoot-at flower-5)
+ (sleep 1.0)
+ (audio-play (artifact hehehe-sound))
+ (sleep 1.0)
+ (tween 0.5 0.0 1.5
+ (lambda (dy)
+ (change-velocity popcorn 0.0 dy)))
+ (sleep 1.0)
+ (enable-pollen!)
+ (replace-major-mode scene (make <play-mode>))
+ (play-music scene main-music)
+ (set-source-volume! (music-source scene) 1.0)
+ ;; Tutorial
+ (tutorial "move with ARROW keys or controller DPAD"
+ (lambda ()
+ (moving? (& scene player))))
+ (tutorial "press Z key or A button to shoot"
+ (lambda ()
+ (shoot? (& scene player))))
+ (sleep 2.0)
+ (tutorial "shoot FLOWERS and collect POLLEN for special power" #f)
+ (tutorial "press X key or B button to unleash stored power"
+ (lambda ()
+ (let ((p (& scene player)))
+ (or (bombing? p)
+ ;; Ensure bomb can be used to avoid a soft lock.
+ (begin
+ (unless (= (pollen p) %max-pollen)
+ (set! (pollen p) %max-pollen))
+ #f)))))
+ (tutorial "godspeed you, little bee!" #f))))
+ (join (intro-script scene))
+ (set! (intro-script scene) #f)
+ ;; The intro and tutorial sequence set up a bunch of state, but if
+ ;; the player skips it, we need to set it up here. Each of the
+ ;; following operations may be no-ops, depending on how far into
+ ;; the intro the player was before they skipped it.
(enable-pollen!)
- (change-state game 'play)
- (play-music game main-music)
- (set-source-volume! (music-source game) 1.0)
- (unless (getenv "SKIP_TUTORIAL")
- ;; Tutorial
- (tutorial "move with ARROW keys" #f)
- (tutorial "press Z to shoot" 'z)
- (sleep 2.0)
- (tutorial "shoot FLOWERS and collect POLLEN for special power" #f)
- (tutorial "press X to unleash stored power" 'x)
- (tutorial "godspeed you, little bee!" #f))
- (change-scroll-speed game 15.0)
+ (unless (is-a? (major-mode scene) <play-mode>)
+ (replace-major-mode scene (make <play-mode>)))
+ (play-music scene main-music)
+ (set-source-volume! (music-source scene) 1.0)
+ (set! (pollen (& scene player)) 0)
+ (and=> (& scene tutorial) detach)
+ (clear-bullets (& scene bullets))
+ (for-each-child (lambda (child)
+ (when (and (is-a? child <actor>)
+ (not (is-a? child <player>)))
+ (detach child)))
+ scene)
+ ;; Start the game
+ (set! (scoring-enabled? (& scene player)) #t)
+ (change-scroll-speed scene 15.0)
+ (sleep 1.0)
;; Wave 1
(define* (popcorn-line n spawn-point velocity delay #:optional script)
(let loop ((i 0))
(when (< i n)
(let ((popcorn (make-popcorn (vec2-copy spawn-point) velocity)))
- (spawn game popcorn)
+ (spawn scene popcorn)
(when script
(run-script popcorn (script popcorn))))
(sleep delay)
(loop (+ i 1)))))
(define (spawn-flower x)
- (spawn game (make-flower (vec2 x (+ %game-height 32.0)))))
+ (spawn scene (make-flower (vec2 x (+ %game-height 32.0)))))
(unless (getenv "SKIP_WAVE1")
- (spawn game (make-flower (vec2 (- %game-width 64.0) (+ %game-height 32.0))))
+ (spawn scene (make-flower (vec2 (- %game-width 64.0) (+ %game-height 32.0))))
(popcorn-line 5 (vec2 64.0 (+ %game-height 16.0))
(vec2 0.0 -2.0) 0.2)
(sleep 0.5)
(popcorn-line 5 (vec2 (- %game-width 64.0) (+ %game-height 16.0))
(vec2 0.0 -2.0) 0.2)
(sleep 0.5)
- (spawn game (make-flower (vec2 64.0 (+ %game-height 32.0))))
+ (spawn scene (make-flower (vec2 64.0 (+ %game-height 32.0))))
(popcorn-line 5 (vec2 (/ %game-width 2.0) (+ %game-height 16.0))
(vec2 0.0 -2.0) 0.2)
(sleep 1.0)
- (run-script game
+ (run-script scene
(popcorn-line 10 (vec2 -16.0 220.0)
(vec2 3.0 -0.5) 0.2))
(popcorn-line 10 (vec2 (+ %game-width 16.0) 200.0)
(vec2 -3.0 -0.5) 0.2)
- (spawn game (make-flower (vec2 (/ %game-width 2.0) (+ %game-height 32.0))))
+ (spawn scene (make-flower (vec2 (/ %game-width 2.0) (+ %game-height 32.0))))
(sleep 1.0)
- (run-script game
+ (run-script scene
(popcorn-line 30 (vec2 -16.0 220.0)
(vec2 2.0 -2.0) 0.2))
- (run-script game
+ (run-script scene
(popcorn-line 30 (vec2 (+ %game-width 16.0) 220.0)
(vec2 -2.0 -2.0) 0.2))
- (spawn game (make-flower (vec2 128.0 (+ %game-height 32.0))))
+ (spawn scene (make-flower (vec2 128.0 (+ %game-height 32.0))))
(sleep 2.0)
- (run-script game
+ (run-script scene
(popcorn-line 20 (vec2 -16.0 20.0)
(vec2 2.0 2.0) 0.2))
- (spawn game (make-flower (vec2 256.0 (+ %game-height 32.0))))
+ (spawn scene (make-flower (vec2 256.0 (+ %game-height 32.0))))
(popcorn-line 20 (vec2 (+ %game-width 16.0) 20.0)
(vec2 -2.0 2.0) 0.2)
(sleep 3.0))
;; Wave 2
(define (spawn-turret x)
- (spawn game (make-turret (vec2 x (+ %game-height 32.0)))))
+ (spawn scene (make-turret (vec2 x (+ %game-height 32.0)))))
(define (shoot-down enemy)
(sleep 0.2)
(repeat 10
- (add-bullet (bullets game)
- medium-enemy-bullet
- (position enemy)
- (vec2 0.0 -2.0))
- (sleep 0.1)))
+ (add-bullet (& scene bullets)
+ medium-enemy-bullet
+ (position enemy)
+ (vec2 0.0 -2.0))
+ (sleep 0.1)))
(unless (getenv "SKIP_WAVE2")
(popcorn-line 10 (vec2 (+ %game-width 16.0) 200.0)
(vec2 -3.0 -0.5) 0.2)
@@ -409,7 +460,7 @@
(popcorn-line 10 (vec2 (+ %game-width 16.0) 200.0)
(vec2 -3.0 -0.5) 0.2)
(sleep 1.0)
- (run-script game
+ (run-script scene
(spawn-turret (* %game-width 0.25))
(sleep 2.0)
(spawn-turret (* %game-width 0.75))
@@ -425,14 +476,14 @@
(- (random:uniform) 0.5)))
(+ %game-height 16.0))
(vec2 0.0 -2.0))))
- (spawn game popcorn)
+ (spawn scene popcorn)
(run-script popcorn
(sleep 0.5)
(tween 0.5 -2.0 0.0
(lambda (dy)
(change-velocity popcorn 0.0 dy)))
(let ((d (vec2-normalize
- (vec2- (vec2+ (position (player game)) (vec2 0.0 16.0))
+ (vec2- (vec2+ (position (& scene player)) (vec2 0.0 16.0))
(position popcorn)))))
(tween 0.5 0.0 4.0
(lambda (speed)
@@ -442,7 +493,7 @@
;; Wave 3
(define (spawn-moth p)
(let ((moth (make-moth p)))
- (spawn game moth)
+ (spawn scene moth)
(run-script moth
(sleep 0.2)
(tween 1.0 -2.0 0.0
@@ -456,11 +507,11 @@
(n 9)
(arc-step (/ (- arc-end arc-start) n))
(speed 1.9))
- (audio-play (asset-ref enemy-shoot-sound))
+ (audio-play (artifact enemy-shoot-sound))
(let arc-loop ((j 0))
(when (< j n)
(let ((theta (+ arc-start (* j arc-step))))
- (add-bullet (bullets game)
+ (add-bullet (& scene bullets)
medium-enemy-bullet
(position moth)
(vec2 (* (cos theta) speed)
@@ -507,11 +558,11 @@
(sleep 7.0))
;; Boss
(unless (getenv "SKIP_BOSS")
- (run-script game
+ (run-script scene
(repeat 8
- (audio-play (asset-ref alarm-sound))
+ (audio-play (artifact alarm-sound))
(sleep 1.0)))
- (run-script game
+ (run-script scene
(let ((warning (make <shadow-label>
#:name 'warning
#:rank 9
@@ -520,11 +571,11 @@
#:text "WARNING!! BIG OL' BAD THING AHEAD!"
#:align 'center
#:color %text-color)))
- (attach-to game warning)
+ (attach-to scene warning)
(blink warning 15 0.25)
(detach warning)))
(sleep 7.0)
- (spawn game (make <boss>
+ (spawn scene (make <boss>
#:name 'boss
#:rank 2
#:position (vec2 (/ %game-width 2.0)
@@ -535,49 +586,65 @@
(sleep 4.5)
(tween 1.0 15.0 0.0
(lambda (speed)
- (change-scroll-speed game speed)))
- (wait-until (dead? (& game boss)))
- (clear-bullets (bullets game)))
+ (change-scroll-speed scene speed)))
+ (wait-until (dead? (& scene boss)))
+ (clear-bullets (& scene bullets)))
;; Victory!
- (set! (invincible? (player game)) #t)
- (add-to-score (player game) (* (lives (player game)) 50000))
- (game-complete game)))
+ (set! (invincible? (& scene player)) #t)
+ (add-to-score (& scene player) (* (lives (& scene player)) 50000))
+ (game-complete scene)))
-(define-method (reset-game (game <game>))
- (stop-scripts game)
+(define-method (reset-game (scene <game-scene>))
+ (stop-scripts scene)
(disable-pollen!)
- (change-scroll-speed game 0.0)
- (clear-bullets (bullets game))
- (quadtree-clear! (quadtree game))
- (show (& game hud-lives))
- (show (& game hud-pollen))
+ (change-scroll-speed scene 0.0)
+ (clear-bullets (& scene bullets))
+ (quadtree-clear! (quadtree scene))
+ (show (& scene hud lives))
+ (show (& scene hud pollen))
(for-each-child (lambda (child)
(when (or (is-a? child <actor>)
(memq (name child)
'(game-over game-complete tutorial warning)))
(detach child)))
- game)
- (spawn game
+ scene)
+ (spawn scene
(make <player>
#:name 'player
#:rank 3
- #:position (vec2 (/ %game-width 2.0) 20.0)
+ #:position (vec2-copy %default-player-position)
#:hitbox (make-rect -0.5 -0.5 1.0 1.0)))
- (run-level game))
+ (run-level scene))
-(define-method (on-enter (game <game>))
- (reset-game game))
+(define-method (on-boot (scene <game-scene>))
+ (attach-to scene
+ (make <background>
+ #:name 'background
+ #:texture background-image)
+ (make <particles>
+ #:name 'particles
+ #:particles (make-particles 2048
+ #:texture (artifact particle-image)
+ #:end-color (make-color 1.0 1.0 1.0 0.0)))
+ (make <bullets>
+ #:name 'bullets
+ #:rank 4
+ #:quadtree (quadtree scene))
+ (make <hud>
+ #:name 'hud
+ #:rank 5)))
-(define-method (game-complete (game <game>))
- (hide (& game hud-pollen))
+(define-method (game-complete (scene <game-scene>))
+ (set! (scoring-enabled? (& scene player)) #f)
+ (hide (& scene hud pollen))
(tween 1.0 1.0 0.0
(lambda (volume)
- (set-source-volume! (music-source game) volume)))
- (source-stop (music-source game))
- (play-music game intro-music)
- (set-source-volume! (music-source game) 1.0)
- (change-state game 'pre-game-complete)
- (let ((p (player game)))
+ (set-source-volume! (music-source scene) volume)))
+ (source-stop (music-source scene))
+ (play-music scene intro-music)
+ (set-source-volume! (music-source scene) 1.0)
+ (replace-major-mode scene (make <nothing-mode>))
+ (let ((p (& scene player)))
(stop-scripts p)
(set! (shoot? p) #f)
(set! (move-left? p) #f)
@@ -585,20 +652,20 @@
(set! (move-down? p) #f)
(set! (move-up? p) #f))
(sleep 1.0)
- (audio-play (asset-ref pickup-sound))
- (spawn game (make-flower (vec2 (/ %game-width 4) 160.0)))
+ (audio-play (artifact pickup-sound))
+ (spawn scene (make-flower (vec2 (/ %game-width 4) 160.0)))
(sleep 1.0)
- (audio-play (asset-ref pickup-sound))
- (spawn game (make-flower (vec2 (/ %game-width 2) 160.0)))
+ (audio-play (artifact pickup-sound))
+ (spawn scene (make-flower (vec2 (/ %game-width 2) 160.0)))
(sleep 1.0)
- (audio-play (asset-ref pickup-sound))
- (spawn game (make-flower (vec2 (- %game-width (/ %game-width 4)) 160.0)))
+ (audio-play (artifact pickup-sound))
+ (spawn scene (make-flower (vec2 (- %game-width (/ %game-width 4)) 160.0)))
(sleep 1.0)
- (change-state game 'game-complete)
+ (replace-major-mode scene (make <game-complete-mode>))
(let ((group (make <node-2d>
#:name 'game-complete
#:rank 5)))
- (attach-to game group)
+ (attach-to scene group)
(attach-to group
(make <shadow-label>
#:position (vec2 (/ %game-width 2.0) (/ %game-height 2.0))
@@ -609,22 +676,23 @@
(make <shadow-label>
#:position (vec2 (/ %game-width 2.0) (- (/ %game-height 2.0) 20.0))
#:font monogram-font
- #:text "press ENTER to play again"
+ #:text "press ENTER key or START button to play again"
#:align 'center
#:color %text-color)))
(forever
- (add-bullet (bullets game)
+ (add-bullet (& scene bullets)
pollen-pickup
(vec2 (random %game-width) (+ %game-height 0.0))
(vec2 0.0 (- (+ (random:uniform) 0.5))))
(sleep 0.1)))
-(define-method (game-over (game <game>))
- (hide (& game hud-lives))
- (hide (& game hud-pollen))
- (change-state game 'game-over)
- (stop-scripts game)
- (let ((p (player game)))
+(define-method (game-over (scene <game-scene>))
+ (set! (scoring-enabled? (& scene player)) #f)
+ (hide (& scene hud lives))
+ (hide (& scene hud pollen))
+ (replace-major-mode scene (make <game-over-mode>))
+ (stop-scripts scene)
+ (let ((p (& scene player)))
(stop-scripts p)
(hide p)
(set! (shoot? p) #f)
@@ -632,14 +700,14 @@
(set! (move-right? p) #f)
(set! (move-down? p) #f)
(set! (move-up? p) #f))
- (run-script game
- (tween 1.0 (scroll-speed game) 0.0
+ (run-script scene
+ (tween 1.0 (scroll-speed scene) 0.0
(lambda (speed)
- (change-scroll-speed game speed))))
+ (change-scroll-speed scene speed))))
(let ((group (make <node-2d>
#:name 'game-over
#:rank 5)))
- (attach-to game group)
+ (attach-to scene group)
(attach-to group
(make <shadow-label>
#:position (vec2 (/ %game-width 2.0) (/ %game-height 2.0))
@@ -650,21 +718,21 @@
(make <shadow-label>
#:position (vec2 (/ %game-width 2.0) (- (/ %game-height 2.0) 20.0))
#:font monogram-font
- #:text "press ENTER to try again"
+ #:text "press ENTER key or START button to try again"
#:align 'center
#:color %text-color))))
-(define-method (toggle-pause (game <game>))
- (if (paused? game)
+(define-method (toggle-pause (scene <game-scene>))
+ (if (paused? scene)
(begin
- (detach (& game pause-overlay))
- (source-play (music-source game))
- (resume game))
+ (detach (& scene pause-overlay))
+ (source-play (music-source scene))
+ (resume scene))
(let ((overlay (make <node-2d>
#:name 'pause-overlay
#:rank 99)))
- (pause game)
- (source-pause (music-source game))
+ (pause scene)
+ (source-pause (music-source scene))
(attach-to overlay
(make <sprite>
#:texture darkness-image
@@ -676,110 +744,145 @@
#:align 'center
#:vertical-align 'center
#:color %text-color))
- (attach-to game overlay))))
+ (attach-to scene overlay))))
-(define-method (play-again (game <game>))
- (change-state game 'play-again)
- (run-script game
+(define-method (play-again (scene <game-scene>))
+ (replace-major-mode scene (make <nothing-mode>))
+ (run-script scene
(tween 0.5 1.0 0.0
(lambda (volume)
- (set-source-volume! (music-source game) volume)))
- (source-stop (music-source game))
- (set-source-volume! (music-source game) 1.0))
- (run-script game
- (fade-out game 1.0)
- (reset-game game)))
-
-(define-method (close-game (game <game>))
- (pop-scene (current-kernel)))
-
-(define-method (on-quit (game <game>))
- (close-game game))
-
-(define-method (on-key-press (game <game>) key modifiers repeat?)
- (case (state game)
- ((play)
- (case key
- ((left)
- (set! (move-left? (player game)) #t))
- ((right)
- (set! (move-right? (player game)) #t))
- ((down)
- (set! (move-down? (player game)) #t))
- ((up)
- (set! (move-up? (player game)) #t))
- ((z)
- (set! (shoot? (player game)) #t))
- ((x)
- (bomb (player game)))
- ((escape)
- (close-game game))
- ((return)
- (toggle-pause game))))
- ((intro)
- (case key
- ((escape)
- (close-game game))
- ((return)
- (toggle-pause game))))
- ((game-over game-complete)
- (case key
- ((escape)
- (close-game game))
- ((return)
- (play-again game))))))
-
-(define-method (on-key-release (game <game>) key modifiers)
- (case (state game)
- ((play)
- (case key
- ((left)
- (set! (move-left? (player game)) #f))
- ((right)
- (set! (move-right? (player game)) #f))
- ((down)
- (set! (move-down? (player game)) #f))
- ((up)
- (set! (move-up? (player game)) #f))
- ((z)
- (set! (shoot? (player game)) #f))))))
-
-(define-method (update (game <game>) dt)
+ (set-source-volume! (music-source scene) volume)))
+ (source-stop (music-source scene))
+ (set-source-volume! (music-source scene) 1.0))
+ (run-script scene
+ (fade-out scene 1.0)
+ (replace-major-mode scene (make <intro-mode>))))
+
+(define-method (clean-up-actors (scene <game-scene>))
+ (let ((p (& scene player)))
+ ;; This probably shouldn't be here, but it is.
+ (shoot-maybe p (& scene bullets))
+ ;; Remove dead actors and actors that have gone too far off
+ ;; screen.
+ (for-each-child (lambda (child)
+ (cond
+ ((dead? child)
+ (on-death child)
+ (quadtree-delete! (quadtree scene)
+ (world-hitbox child)
+ child)
+ (add-to-score (& scene player) (points child))
+ (detach child))
+ ((out-of-bounds? child)
+ (quadtree-delete! (quadtree scene)
+ (world-hitbox child)
+ child)
+ (detach child))))
+ scene)
+ (when (= (lives p) 0) ; bummer
+ (game-over scene))))
+
+(define-method (update (scene <game-scene>) dt)
(next-method)
- (let ((bg (& game background)))
- (set! (scroll-y bg) (+ (scroll-y bg) (* (scroll-speed game) dt))))
- (case (state game)
- ((intro play)
- (let ((p (player game)))
- (when (not (= (last-lives game) (lives p)))
- (set! (text (& game hud-lives))
- (string-append "lives " (number->string (lives p))))
- (when (= (lives p) 0)
- (game-over game)))
- (when (not (= (last-pollen game) (pollen p)))
- (let ((n (pollen p)))
- (set! (text (& game hud-pollen))
- (string-append "pollen "
- (if (= n %max-pollen) "MAX" (number->string n))))))
- (when (not (= (last-score game) (score p)))
- (set! (text (& game hud-score))
- (string-append "score " (number->string (score p)))))
- (set! (last-lives game) (lives p))
- (set! (last-pollen game) (pollen p))
- (set! (last-score game) (score p))
- (shoot-maybe p (bullets game))
- (for-each-child (lambda (child)
- (cond
- ((dead? child)
- (on-death child)
- (quadtree-delete! (quadtree game)
- (world-hitbox child)
- child)
- (add-to-score (player game) (points child))
- (detach child))
- ((out-of-bounds? child)
- (quadtree-delete! (quadtree game)
- (world-hitbox child)
- child)
- (detach child))))
- game)))))
+ (let ((bg (& scene background)))
+ (set! (scroll-y bg) (+ (scroll-y bg) (* (scroll-speed scene) dt)))))
+
+
+;;;
+;;; Modes
+;;;
+
+(define-method (close-game (mode <major-mode>))
+ (exit-catbird))
+
+(define-method (pause-game (mode <major-mode>))
+ (toggle-pause (parent mode)))
+
+(define-method (skip-intro (mode <major-mode>))
+ (skip-intro (parent mode)))
+
+(define-class <intro-mode> (<major-mode>))
+
+(define-method (on-enter (mode <intro-mode>))
+ (reset-game (parent mode)))
+
+(define-method (update (mode <intro-mode>) dt)
+ (clean-up-actors (parent mode)))
+
+(bind-input <intro-mode> (key-press 'escape) close-game)
+(bind-input <intro-mode> (key-press 'return) skip-intro)
+(bind-input <intro-mode> (controller-press '0 'back) close-game)
+(bind-input <intro-mode> (controller-press '0 'start) skip-intro)
+
+(define-class <play-mode> (<major-mode>))
+
+(define-method (update (mode <play-mode>) dt)
+ (clean-up-actors (parent mode)))
+
+(define (move-command accessor value)
+ (lambda (mode)
+ (set! (accessor (& (parent mode) player)) value)))
+
+(define-method (player-shoot (mode <play-mode>))
+ (set! (shoot? (& (parent mode) player)) #t))
+
+(define-method (player-stop-shooting (mode <play-mode>))
+ (set! (shoot? (& (parent mode) player)) #f))
+
+(define-method (player-bomb (mode <play-mode>))
+ (bomb (& (parent mode) player)))
+
+(define-method (skip-intro-or-pause-game (mode <play-mode>))
+ (let ((scene (parent mode)))
+ (if (intro-script scene)
+ (skip-intro scene)
+ (toggle-pause scene))))
+
+(bind-input <play-mode> (key-press 'left) (move-command move-left? #t))
+(bind-input <play-mode> (key-press 'right) (move-command move-right? #t))
+(bind-input <play-mode> (key-press 'down) (move-command move-down? #t))
+(bind-input <play-mode> (key-press 'up) (move-command move-up? #t))
+(bind-input <play-mode> (key-release 'left) (move-command move-left? #f))
+(bind-input <play-mode> (key-release 'right) (move-command move-right? #f))
+(bind-input <play-mode> (key-release 'down) (move-command move-down? #f))
+(bind-input <play-mode> (key-release 'up) (move-command move-up? #f))
+(bind-input <play-mode> (key-press 'z) player-shoot)
+(bind-input <play-mode> (key-release 'z) player-stop-shooting)
+(bind-input <play-mode> (key-press 'x) player-bomb)
+(bind-input <play-mode> (key-press 'escape) close-game)
+(bind-input <play-mode> (key-press 'return) skip-intro-or-pause-game)
+(bind-input <play-mode> (controller-press 0 'dpad-left) (move-command move-left? #t))
+(bind-input <play-mode> (controller-press 0 'dpad-right) (move-command move-right? #t))
+(bind-input <play-mode> (controller-press 0 'dpad-down) (move-command move-down? #t))
+(bind-input <play-mode> (controller-press 0 'dpad-up) (move-command move-up? #t))
+(bind-input <play-mode> (controller-release 0 'dpad-left) (move-command move-left? #f))
+(bind-input <play-mode> (controller-release 0 'dpad-right) (move-command move-right? #f))
+(bind-input <play-mode> (controller-release 0 'dpad-down) (move-command move-down? #f))
+(bind-input <play-mode> (controller-release 0 'dpad-up) (move-command move-up? #f))
+(bind-input <play-mode> (controller-press 0 'a) player-shoot)
+(bind-input <play-mode> (controller-release 0 'a) player-stop-shooting)
+(bind-input <play-mode> (controller-press 0 'b) player-bomb)
+(bind-input <play-mode> (controller-press 0 'back) close-game)
+(bind-input <play-mode> (controller-press 0 'start) skip-intro-or-pause-game)
+
+(define-class <game-over-mode> (<major-mode>))
+
+(define-method (play-game-again (mode <major-mode>))
+ (play-again (parent mode)))
+
+(bind-input <game-over-mode> (key-press 'escape) close-game)
+(bind-input <game-over-mode> (key-press 'return) play-game-again)
+(bind-input <game-over-mode> (controller-press 0 'back) close-game)
+(bind-input <game-over-mode> (controller-press 0 'start) play-game-again)
+
+(define-class <game-complete-mode> (<major-mode>))
+
+(bind-input <game-complete-mode> (key-press 'escape) close-game)
+(bind-input <game-complete-mode> (key-press 'return) play-game-again)
+(bind-input <game-complete-mode> (controller-press 0 'back) close-game)
+(bind-input <game-complete-mode> (controller-press 0 'start) play-game-again)
+
+(define (make-game-scene)
+ (let ((scene (make <game-scene>)))
+ (replace-major-mode scene (make <intro-mode>))
+ scene))