diff --git a/Makefile.am b/Makefile.am index 7690930..e7bbbe3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,11 +25,13 @@ godir=$(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache SOURCES = \ game/util/assets.scm \ game/util/pipe.scm \ + game/model/food.scm \ game/model/hero.scm \ game/model/key.scm \ game/model/level.scm \ game/model/other.scm \ game/model/runner.scm \ + game/render/food.scm \ game/render/hero.scm \ game/render/key.scm \ game/render/level.scm \ @@ -41,6 +43,7 @@ asset_files = \ assets/images/lr_penguin2.png \ assets/images/glamshot.png \ assets/images/boxart.png \ + assets/images/defeat.jpg \ assets/images/victory.jpg \ assets/levels/level-001.map \ assets/levels/level-002.map diff --git a/README.org b/README.org index f43b8e5..bdd0c7f 100644 --- a/README.org +++ b/README.org @@ -36,7 +36,7 @@ let the _others_ fall in so you can pass over their head. So now, go out, collect the keys and maintain a healthy diet! -* Game Plan [16/21] +* Game Plan [19/22] - [X] start project organization - [X] select some assets to start with @@ -56,8 +56,9 @@ So now, go out, collect the keys and maintain a healthy diet! - [X] make other avoid each other - [X] ask AI for some boxart - [X] Detect success reaching the goal and progress to next level - - [ ] add foods and bloat indicator - - [ ] scale hero waist related to bloat level + - [X] Detect defeat by discouragement + - [X] add foods and bloat indicator + - [X] scale hero waist related to bloat level - [X] create more levels - [ ] dig potholes - [ ] make others steal keys diff --git a/assets/images/defeat.jpg b/assets/images/defeat.jpg new file mode 100644 index 0000000..0963646 Binary files /dev/null and b/assets/images/defeat.jpg differ diff --git a/assets/images/simples_pimples.png b/assets/images/simples_pimples.png index 3c625fc..0f3dd2d 100644 Binary files a/assets/images/simples_pimples.png and b/assets/images/simples_pimples.png differ diff --git a/assets/levels/level-001.map b/assets/levels/level-001.map index e0e5dab..eebd5f2 100644 --- a/assets/levels/level-001.map +++ b/assets/levels/level-001.map @@ -5,7 +5,7 @@ WWWWWGWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW W H W W O H O W -W H K K K H W +W H K F K f K FFFF H W W O BBBBBBBBBBBBBBBBBBBBBBBBBBHB O W - W H W W H W @@ -21,7 +21,7 @@ W H W W H W W H W W H W -W H K H W - +W HFKF H W - W P BHBBBBBBB W W H W W H W @@ -29,6 +29,6 @@ W H W W H W - W H W W H W -W K H W +W K fFFFFH W WBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBW WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW - \ No newline at end of file diff --git a/game/main.scm b/game/main.scm index ab20d47..a8cbd48 100644 --- a/game/main.scm +++ b/game/main.scm @@ -4,10 +4,12 @@ #:use-module (game model hero) #:use-module (game model other) #:use-module (game model key) + #:use-module (game model food) #:use-module (game render level) #:use-module (game render hero) #:use-module (game render other) #:use-module (game render key) + #:use-module (game render food) #:use-module (game util assets) #:use-module (chickadee) #:use-module (chickadee math rect) @@ -27,8 +29,10 @@ (define hero #f) (define others #f) (define keys #f) +(define foods #f) +(define calories 0.0) (define inputs '()) -(define has-won? #f) +(define state 'running) (define (load) (assets-load) @@ -44,17 +48,19 @@ (set! hero (hero-load level)) (set! others (other-load-others level)) (set! keys (keys-load level)) - - ) + (set! foods (food-load level))) (define (update dt) (poll-coop-repl-server repl) - (set! hero (hero-update hero level inputs keys dt)) + (set! hero (hero-update hero level inputs keys (food-total-calories-eaten foods) dt)) (set! others (map (lambda (other) (other-update other level hero others dt)) others)) (set! keys (keys-update keys hero)) + (set! foods (food-update foods hero)) + (if (any (lambda (other) (hero-collides? hero (other-position other))) others) + (set! state 'lost)) (if (level-same-cell? (hero-position hero) (level-find-goal level)) (if (null? levels) - (set! has-won? #t) + (set! status 'won) (load-level))) ) @@ -62,12 +68,14 @@ (render-level-draw level) (for-each render-other others) (render-keys keys (level-find-goal level)) + (render-foods foods) (render-hero hero)) (define (draw _alpha) - (if has-won? - (render-victory) - (render-level))) + (case state + ((won) (render-victory)) + ((lost) (render-defeat)) + (else (render-level)))) (define (set-add set item) (if (member item set) diff --git a/game/model/food.scm b/game/model/food.scm new file mode 100644 index 0000000..ca45284 --- /dev/null +++ b/game/model/food.scm @@ -0,0 +1,63 @@ +(define-module (game model food) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) + #:use-module (srfi srfi-9 gnu) + #:use-module (game model level) + #:use-module (game model hero) + #:use-module (chickadee math vector) + #:export (make-food + food-position + food-healthy? + food-eaten? + food-load + food-update + food-total-calories-eaten + )) + +(define-immutable-record-type + (make-food position healthy eaten) + food? + (position food-position) + (healthy food-healthy?) + (eaten food-eaten?) + ) + +(define (food-collides? food position) + (< (vec2-magnitude (vec2- (food-position food) position)) (/ level-cell-size 4))) + + +(define (food-eat food) + (make-food (food-position food) (food-healthy? food) #t)) + +(define good-calories 60) +(define bad-calories 180) + +(define (food-calories food) + (if (food-healthy? food) good-calories bad-calories)) + +(define (make-good-food position) + (make-food position #t #f)) + +(define (make-bad-food position) + (make-food position #f #f)) + +(define (food-load level) + (append + (map make-good-food (level-find-good-foods level)) + (map make-bad-food (level-find-bad-foods level)))) + + +(define (check-if-eaten food hero) + (if (food-collides? food (hero-position hero)) + (food-eat food) + food)) + +(define (food-total-calories-eaten foods) + (fold + 0 + (map food-calories + (filter food-eaten? foods)))) + +(define (food-update foods hero) + (map + (lambda (food) (check-if-eaten food hero)) + foods)) diff --git a/game/model/hero.scm b/game/model/hero.scm index 3dda191..a51b77f 100644 --- a/game/model/hero.scm +++ b/game/model/hero.scm @@ -11,28 +11,32 @@ #:use-module (game model runner) #:export (hero-load hero-state - hero-bloat + hero-exercise hero-position hero-x hero-y hero-update + hero-bloat + hero-collides? )) (define-immutable-record-type - (%make-hero runner bloat) + (%make-hero runner exercise eaten) hero? (runner hero-runner hero-with-runner) - (bloat hero-bloat hero-with-bloat) ;; 0.0 to 2.0, how much the hero is inflated + (exercise hero-exercise hero-with-exercise) ;; calories-burned + (eaten hero-eaten hero-with-eaten) ;; calories-eaten ) (define default-state 'fall) -(define default-bloat 1.0) +(define default-exercise 0) +(define default-eaten 0) (define (hero-load level) "Create a hero at the position in the level map" (let ((runner (runner-load (level-find-hero level)))) - (%make-hero runner default-bloat))) + (%make-hero runner default-exercise default-eaten))) (define (hero-x hero) "return the x coordinate as an integer" @@ -51,35 +55,70 @@ "return the current position of the hero" (runner-position (hero-runner hero))) +(define (hero-bloat hero) + "return the current bloat of the hero clamped to 0.5 and 2.0" + (let* ((calories (- (hero-eaten hero) (hero-exercise hero))) + (raw-bloat (+ 1.0 (/ calories 2000.0))) + ) + (cond + ((< calories -1000) 0.5) + ((> calories 1000) 2.0) + (else raw-bloat) + ))) + (define (collides? a b) - "return true if the hero collides with the given position" + "return true if the position collides with the given position" (< (vec2-magnitude (vec2- a b)) level-cell-size)) -(define (blocked-by-door? level keys position) +(define (blocked-by-door? level keys position bloat) "return true if the hero is blocked by a door" (let ((door-position (level-find-goal level))) (and (collides? position door-position) - (not (null? keys))))) + (not + (and + (< 0.5 bloat 1.5) ;; healthy + (null? keys)))))) ;; all keys collected -(define (hero-update hero level inputs keys dt) - (let ((new-runner (runner-update (hero-runner hero) level inputs dt))) - (if (blocked-by-door? level keys (runner-position new-runner)) - hero - (hero-with-runner hero new-runner) - ))) +(define climbing-exercise 0.5) +(define moving-exercise 0.2) +(define (calculate-exercise hero new-position) + (let ((movement (vec2- new-position (hero-position hero)))) + (+ + (if (> (vec2-y movement) 0) climbing-exercise 0) + (if (not (zero? (vec2-x movement))) moving-exercise 0)))) + + +(define (hero-update hero level inputs keys calories-eaten dt) + (let* ((new-runner (runner-update (hero-runner hero) level inputs dt)) + (new-runner (if (blocked-by-door? level keys + (runner-position new-runner) + (hero-bloat hero)) + (hero-runner hero) + new-runner + )) + (exercise (calculate-exercise hero (runner-position new-runner)))) + (-> hero + (hero-with-runner new-runner) + (hero-with-exercise (+ (hero-exercise hero) exercise)) + (hero-with-eaten calories-eaten)))) + +(define (hero-collides? hero position) + "return true if the hero collides with the given position" + (collides? (hero-position hero) position)) ;; Tests (test-begin "hero-model") + (let* ((level (level-parse "WWWWW\nW...W\nW.P.W\nW..HW\nWWWWW\n")) (hero (hero-load level)) (default-position (level-find-hero level))) (test-assert (hero? hero)) - (test-equal default-bloat (hero-bloat hero)) + (test-equal default-exercise (hero-exercise hero)) - (test-equal 1.5 (hero-bloat (hero-with-bloat hero 1.5))) - (test-equal default-bloat (hero-bloat hero))) + (test-equal 1.5 (hero-exercise (hero-with-exercise hero 1.5))) + (test-equal default-exercise (hero-exercise hero))) (let* ((level (level-parse "WWGWW\nW.H.W\nW.P.W\nWWWWW\n")) (hero (hero-load level)) diff --git a/game/model/level.scm b/game/model/level.scm index 1187b6c..0914819 100644 --- a/game/model/level.scm +++ b/game/model/level.scm @@ -21,8 +21,11 @@ level-find-goal level-find-others level-find-keys + level-find-good-foods + level-find-bad-foods level-tile-blocked? level-same-cell? + coord->cell )) (define-record-type @@ -44,6 +47,8 @@ ((#\O) 'other) ((#\G) 'goal) ((#\K) 'key) + ((#\F) 'bad-food) + ((#\f) 'good-food) (else 'empty))) (define (content->lines content) @@ -125,6 +130,11 @@ (define (level-find-keys level) (level-find-tile level 'key)) +(define (level-find-good-foods level) + (level-find-tile level 'good-food)) + +(define (level-find-bad-foods level) + (level-find-tile level 'bad-food)) (define (level-same-cell? a b) (and (= (coord->cell (vec2-x a)) (coord->cell (vec2-x b))) @@ -155,6 +165,8 @@ (test-equal (parse-tile #\O) 'other) (test-equal (parse-tile #\G) 'goal) (test-equal (parse-tile #\space) 'empty) +(test-equal (parse-tile #\F) 'bad-food) +(test-equal (parse-tile #\f) 'good-food) (test-equal (level-width (level-parse-file "assets/levels/level-001.map")) 40) (test-equal (level-height (level-parse-file "assets/levels/level-001.map")) 30) diff --git a/game/render/food.scm b/game/render/food.scm new file mode 100644 index 0000000..b4dec46 --- /dev/null +++ b/game/render/food.scm @@ -0,0 +1,37 @@ +(define-module (game render food) + #:use-module (game model food) + #:use-module (game model level) + #:use-module (game util assets) + #:use-module (chickadee math vector) + #:use-module (chickadee graphics sprite) + #:use-module (chickadee graphics texture) + #:use-module (chickadee graphics text) + #:export (render-foods) + ) + + +(define healthy-food-offset 3226) +(define healthy-food-cycle 15) +(define bad-food-offset 3026) +(define bad-food-cycle 8) + +(define (food-index food cycle) + (let ((x (coord->cell (vec2-x (food-position food)))) + (y (coord->cell (vec2-y (food-position food))))) + (pk "food remainder : " (remainder (+ (* x 37) (* 73 y)) cycle)))) + +(define (render-food food) + (let ((index (food-index food (if (food-healthy? food) healthy-food-cycle bad-food-cycle))) + (food-offset (if (food-healthy? food) healthy-food-offset bad-food-offset))) + (when (not (food-eaten? food)) + (draw-sprite + (texture-atlas-ref tile-atlas (+ food-offset index)) + (food-position food))))) + + +(define (render-foods foods) + (for-each render-food foods) + (draw-text + (format #f "Calories Eaten: ~a" (food-total-calories-eaten foods)) + (vec2 16 4))) + diff --git a/game/render/hero.scm b/game/render/hero.scm index 51589a7..17d7898 100644 --- a/game/render/hero.scm +++ b/game/render/hero.scm @@ -5,6 +5,7 @@ #:use-module (chickadee math vector) #:use-module (chickadee graphics sprite) #:use-module (chickadee graphics texture) + #:use-module (chickadee graphics text) #:export (render-hero render-hero-load)) @@ -35,24 +36,60 @@ (hero-index (+ hero-climbing-offset animation-frame))) (texture-atlas-ref hero-atlas hero-index))) +(define red-tile 3501) +(define green-tile 3502) +(define good-marker-tile 1227) +(define bad-marker-tile 1226) + +(define (render-bloat-indicator bloat) + (let ((red (texture-atlas-ref tile-atlas red-tile)) + (green (texture-atlas-ref tile-atlas green-tile)) + (good-marker (texture-atlas-ref tile-atlas good-marker-tile)) + (bad-marker (texture-atlas-ref tile-atlas bad-marker-tile)) + ) + (draw-sprite red (vec2 0 128)) + (draw-sprite red (vec2 0 144)) + (draw-sprite red (vec2 0 160)) + (draw-sprite green (vec2 0 176)) + (draw-sprite green (vec2 0 192)) + (draw-sprite green (vec2 0 208)) + (draw-sprite green (vec2 0 224)) + (draw-sprite green (vec2 0 240)) + (draw-sprite green (vec2 0 256)) + (draw-sprite red (vec2 0 272)) + (draw-sprite red (vec2 0 288)) + (draw-sprite red (vec2 0 304)) + (draw-sprite + (if (< 0.5 bloat 1.5) good-marker bad-marker) + (vec2 0 (+ 120 (* 96 bloat)))) + + )) + (define (render-hero hero) - (case (hero-state hero) - ((stationary) (draw-sprite - (hero-sprite-stationary hero) - (vec2+ (hero-position hero) (vec2 -16.0 1.0)))) - ((fall) (draw-sprite - (hero-sprite-falling hero) - (vec2+ (hero-position hero) (vec2 -16.0 1.0)))) - ((climb) (draw-sprite - (hero-sprite-climbing hero) - (vec2+ (hero-position hero) (vec2 -16.0 1.0)))) - ((go-left) (draw-sprite - (hero-sprite-walking hero) - (vec2+ (hero-position hero) (vec2 16.0 1.0)) - #:scale (vec2 -1.0 1.0) - )) - ((go-right) (draw-sprite - (hero-sprite-walking hero) - (vec2+ (hero-position hero) (vec2 -16.0 1.0)))) - ) + (let ((position (vec2+ (hero-position hero) + (if (equal? 'go-left (hero-state hero)) + (vec2* (vec2 16.0 1.0) (hero-bloat hero)) + (vec2* (vec2 -16.0 1.0) (hero-bloat hero))))) + (scale (if (equal? 'go-left (hero-state hero)) + (vec2 (- (hero-bloat hero)) 1.0) + (vec2 (hero-bloat hero) 1.0))) + (sprite (case (hero-state hero) + ((stationary) (hero-sprite-stationary hero)) + ((fall) (hero-sprite-falling hero)) + ((climb) (hero-sprite-climbing hero)) + ((go-left) (hero-sprite-walking hero)) + ((go-right) (hero-sprite-walking hero)) + )) + ) + (draw-sprite sprite position #:scale scale)) + + (draw-text + (format #f "Calories Exercised: ~a" (floor (hero-exercise hero))) + (vec2 216 4)) + (draw-text + (format #f "Bloat: ~a" (hero-bloat hero)) + (vec2 516 4)) + + (render-bloat-indicator (hero-bloat hero)) + ) diff --git a/game/render/key.scm b/game/render/key.scm index c9abe52..2b54f29 100644 --- a/game/render/key.scm +++ b/game/render/key.scm @@ -1,6 +1,7 @@ (define-module (game render key) #:use-module (game util assets) #:use-module (game model key) + #:use-module (game render level) #:use-module (chickadee math vector) #:use-module (chickadee graphics sprite) #:use-module (chickadee graphics texture) @@ -8,7 +9,7 @@ (define key-index 1726) (define closed-goal-index 3404) -(define open-goal-index 3350) +(define open-goal-index ladder-index) (define (render-key key) (draw-sprite diff --git a/game/render/level.scm b/game/render/level.scm index 115746a..71ec9c4 100644 --- a/game/render/level.scm +++ b/game/render/level.scm @@ -11,6 +11,10 @@ render-level-draw render-level-set! render-victory + render-defeat + empty-index + brick-index + ladder-index )) @@ -18,22 +22,29 @@ ;; load the tile texture and split it into a tile atlas (define (render-level-load) - (set! sprite-batch (make-sprite-batch tile-texture #:capacity 1200)) + (set! sprite-batch (make-sprite-batch tile-texture #:capacity 2400)) ) ;; map tile symbols to tile indices in the tile atlas + +(define empty-index 2500) +(define brick-index 3750) +(define ladder-index 3352) + (define (level-tile-index tile) (case tile - ((empty) 3800) - ((brick) 3750) + ((empty) empty-index) + ((brick) brick-index) ((wall) 3709) - ((ladder) 3350) - ((goal) 3800) ;; render empty tile for goal location - ((hero) 3800) ;; render empty tile for hero start location - ((other) 3800) ;; render empty tile for enemy start location - ((key) 3800) + ((ladder) ladder-index) + + ((goal) empty-index) ;; render empty tile for goal location + ((hero) empty-index) ;; render empty tile for hero start location + ((other) empty-index) ;; render empty tile for enemy start location + ((good-food bad-food) empty-index) + ((key) empty-index) ;; render visual reminder of unknown tile (else 3326))) @@ -49,26 +60,42 @@ ((position (vec2 (* 16 column) (* 16 row))) (tile (level-tile-at level position)) (texture-region (texture-atlas-ref tile-atlas (level-tile-index tile)))) + (if (equal? tile 'ladder) + ;; add background behind ladder + (if (or (equal? (level-tile-at level (vec2+ position (vec2 16 0))) 'brick) + (equal? (level-tile-at level (vec2- position (vec2 -16 0))) 'brick) + + ) + (sprite-batch-add! sprite-batch position + #:texture-region (texture-atlas-ref tile-atlas brick-index)) + (sprite-batch-add! sprite-batch position + #:texture-region (texture-atlas-ref tile-atlas empty-index)))) + (sprite-batch-add! sprite-batch position #:texture-region texture-region))) + (iota (level-width level) 0))) (iota (level-height level) 0))) + ;; render the level tiles -(define (render-level-draw level) - (draw-sprite-batch sprite-batch)) + (define (render-level-draw level) + (draw-sprite-batch sprite-batch)) (define (render-victory) (draw-sprite (load-image (assets-file-name "assets/images/victory.jpg")) (vec2 0 0))) +(define (render-defeat) + (draw-sprite (load-image (assets-file-name "assets/images/defeat.jpg")) (vec2 0 0))) + (test-begin "tile translation") -(test-equal (level-tile-index 'empty) 3800) +(test-equal (level-tile-index 'empty) empty-index) (test-equal (level-tile-index 'brick) 3750) (test-equal (level-tile-index 'wall) 3709) -(test-equal (level-tile-index 'ladder) 3350) -(test-equal (level-tile-index 'goal) 3404) +(test-equal (level-tile-index 'ladder) ladder-index) +(test-equal (level-tile-index 'goal) empty-index) (test-equal (level-tile-index 'unknown) 3326) (test-end "tile translation") diff --git a/game/util/assets.scm b/game/util/assets.scm index c2f6a2c..a4aaf82 100644 --- a/game/util/assets.scm +++ b/game/util/assets.scm @@ -31,8 +31,7 @@ (lambda (filename) (string-suffix? ".map" filename )))))) (define (assets-load) - (set! tile-texture (load-image (assets-file-name "assets/images/simples_pimples.png") - #:transparent-color black)) + (set! tile-texture (load-image (assets-file-name "assets/images/simples_pimples.png"))) (set! tile-atlas (split-texture tile-texture 16 16)) (set! hero-texture (load-image (assets-file-name "assets/images/lr_penguin2.png")))