add defeat logic, improve rendering, polishing

This commit is contained in:
Peter Tillemans 2024-05-24 17:07:17 +02:00
parent d0d2d2993b
commit 3a1dd6d028
14 changed files with 293 additions and 66 deletions

View file

@ -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

View file

@ -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

BIN
assets/images/defeat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View file

@ -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 -

View file

@ -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)

63
game/model/food.scm Normal file
View file

@ -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 <food>
(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))

View file

@ -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 <hero>
(%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))

View file

@ -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 <level>
@ -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)

37
game/render/food.scm Normal file
View file

@ -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)))

View file

@ -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))
)

View file

@ -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

View file

@ -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")

View file

@ -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")))