aboutsummaryrefslogtreecommitdiff
path: root/tests/physics-test.scm
diff options
context:
space:
mode:
authorGene Pasquet <dev@etenil.net>2026-04-17 16:30:34 +0100
committerGene Pasquet <dev@etenil.net>2026-04-17 16:30:34 +0100
commit8251c85a4a588504d38a2fad05e4b0fe1cdccb9d (patch)
treec3fcedb7331caf798f2355c7549b35aa3aaf6ac8 /tests/physics-test.scm
parent5de3b9cf122542f2a0c1c906c8ce8add20e5c8c6 (diff)
Convert entities to alists
Diffstat (limited to 'tests/physics-test.scm')
-rw-r--r--tests/physics-test.scm247
1 files changed, 126 insertions, 121 deletions
diff --git a/tests/physics-test.scm b/tests/physics-test.scm
index 88ddcce..4ab4b17 100644
--- a/tests/physics-test.scm
+++ b/tests/physics-test.scm
@@ -41,6 +41,11 @@
(include "entity.scm")
(import downstroke-entity)
+(import (only (list-utils alist) plist->alist))
+
+;; Test helper: build an alist entity from plist-style keyword args.
+(define (entity . kws) (plist->alist kws))
+
;; Load world module first
(include "world.scm")
(import downstroke-world)
@@ -74,82 +79,82 @@
;; Integration helper: simulate one frame of physics
(define (tick e tm held?)
(let* ((e (apply-input-to-entity e held?))
- (e (apply-gravity e #f 0))
- (e (apply-velocity-x e #f 0))
- (e (resolve-tile-collisions-x e (test-scene tilemap: tm) 0))
- (e (apply-velocity-y e #f 0))
- (e (resolve-tile-collisions-y e (test-scene tilemap: tm) 0))
- (e (detect-on-solid e (test-scene tilemap: tm) 0)))
+ (e (apply-gravity #f e 0))
+ (e (apply-velocity-x #f e 0))
+ (e (resolve-tile-collisions-x (test-scene tilemap: tm) e 0))
+ (e (apply-velocity-y #f e 0))
+ (e (resolve-tile-collisions-y (test-scene tilemap: tm) e 0))
+ (e (detect-on-solid (test-scene tilemap: tm) e 0)))
e))
;; Test: apply-gravity
(test-group "apply-gravity"
(test-group "gravity? true, vy starts at 0"
- (let* ((e '(#:type rock #:x 0 #:y 0 #:vx 0 #:vy 0 #:gravity? #t))
- (result (apply-gravity e #f 0)))
+ (let* ((e (entity #:type 'rock #:x 0 #:y 0 #:vx 0 #:vy 0 #:gravity? #t))
+ (result (apply-gravity #f e 0)))
(test-equal "vy increased by gravity" *gravity* (entity-ref result #:vy))
(test-equal "x unchanged" 0 (entity-ref result #:x))
(test-equal "y unchanged" 0 (entity-ref result #:y))
(test-equal "vx unchanged" 0 (entity-ref result #:vx))))
(test-group "gravity? true, vy already has value"
- (let* ((e '(#:type rock #:x 0 #:y 0 #:vx 0 #:vy 3 #:gravity? #t))
- (result (apply-gravity e #f 0)))
+ (let* ((e (entity #:type 'rock #:x 0 #:y 0 #:vx 0 #:vy 3 #:gravity? #t))
+ (result (apply-gravity #f e 0)))
(test-equal "vy increased by gravity" 4 (entity-ref result #:vy))))
(test-group "gravity? false"
- (let* ((e '(#:type static #:x 0 #:y 0 #:vx 0 #:vy 0 #:gravity? #f))
- (result (apply-gravity e #f 0)))
+ (let* ((e (entity #:type 'static #:x 0 #:y 0 #:vx 0 #:vy 0 #:gravity? #f))
+ (result (apply-gravity #f e 0)))
(test-equal "vy unchanged" 0 (entity-ref result #:vy))))
(test-group "no gravity? field at all"
- (let* ((e '(#:type static #:x 5 #:y 5))
- (result (apply-gravity e #f 0)))
+ (let* ((e (entity #:type 'static #:x 5 #:y 5))
+ (result (apply-gravity #f e 0)))
(test-equal "entity unchanged" e result))))
(test-group "apply-velocity-x"
(test-group "basic horizontal movement"
- (let* ((e '(#:type rock #:x 10 #:y 20 #:vx 5 #:vy -2))
- (result (apply-velocity-x e #f 0)))
+ (let* ((e (entity #:type 'rock #:x 10 #:y 20 #:vx 5 #:vy -2))
+ (result (apply-velocity-x #f e 0)))
(test-equal "x moved by vx" 15 (entity-ref result #:x))
(test-equal "y unchanged" 20 (entity-ref result #:y))
(test-equal "vy unchanged" -2 (entity-ref result #:vy))))
(test-group "zero vx"
- (let* ((e '(#:type rock #:x 10 #:y 20 #:vx 0 #:vy 3))
- (result (apply-velocity-x e #f 0)))
+ (let* ((e (entity #:type 'rock #:x 10 #:y 20 #:vx 0 #:vy 3))
+ (result (apply-velocity-x #f e 0)))
(test-equal "x unchanged" 10 (entity-ref result #:x))
(test-equal "y unchanged" 20 (entity-ref result #:y)))))
(test-group "apply-velocity-y"
(test-group "basic vertical movement"
- (let* ((e '(#:type rock #:x 10 #:y 20 #:vx 3 #:vy -5))
- (result (apply-velocity-y e #f 0)))
+ (let* ((e (entity #:type 'rock #:x 10 #:y 20 #:vx 3 #:vy -5))
+ (result (apply-velocity-y #f e 0)))
(test-equal "x unchanged" 10 (entity-ref result #:x))
(test-equal "y moved by vy" 15 (entity-ref result #:y))
(test-equal "vx unchanged" 3 (entity-ref result #:vx))))
(test-group "zero vy"
- (let* ((e '(#:type rock #:x 10 #:y 20 #:vx 3 #:vy 0))
- (result (apply-velocity-y e #f 0)))
+ (let* ((e (entity #:type 'rock #:x 10 #:y 20 #:vx 3 #:vy 0))
+ (result (apply-velocity-y #f e 0)))
(test-equal "x unchanged" 10 (entity-ref result #:x))
(test-equal "y unchanged" 20 (entity-ref result #:y)))))
(test-group "apply-velocity"
(test-group "basic movement"
- (let* ((e '(#:type rock #:x 10 #:y 20 #:vx 3 #:vy -2))
+ (let* ((e (entity #:type 'rock #:x 10 #:y 20 #:vx 3 #:vy -2))
(result (apply-velocity e)))
(test-equal "x moved by vx" 13 (entity-ref result #:x))
(test-equal "y moved by vy" 18 (entity-ref result #:y))))
(test-group "zero velocity"
- (let* ((e '(#:type rock #:x 10 #:y 20 #:vx 0 #:vy 0))
+ (let* ((e (entity #:type 'rock #:x 10 #:y 20 #:vx 0 #:vy 0))
(result (apply-velocity e)))
(test-equal "x unchanged" 10 (entity-ref result #:x))
(test-equal "y unchanged" 20 (entity-ref result #:y))))
(test-group "no velocity fields (defaults to 0)"
- (let* ((e '(#:type static #:x 5 #:y 5))
+ (let* ((e (entity #:type 'static #:x 5 #:y 5))
(result (apply-velocity e)))
(test-equal "x unchanged" 5 (entity-ref result #:x))
(test-equal "y unchanged" 5 (entity-ref result #:y)))))
@@ -201,100 +206,100 @@
(test-group "resolve-tile-collisions-x"
(test-group "no collision: entity unchanged"
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (0 0 0))))
- (e '(#:type player #:x 0 #:y 0 #:width 16 #:height 16 #:vx 2 #:vy 0)))
- (let ((result (resolve-tile-collisions-x e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16 #:vx 2 #:vy 0)))
+ (let ((result (resolve-tile-collisions-x (test-scene tilemap: tm) e 0)))
(test-equal "x unchanged" 0 (entity-ref result #:x))
(test-equal "vx unchanged" 2 (entity-ref result #:vx)))))
(test-group "zero vx: skipped entirely"
(let* ((tm (make-test-tilemap '((0 1 0) (0 0 0) (0 0 0))))
- (e '(#:type player #:x 0 #:y 0 #:width 16 #:height 16 #:vx 0 #:vy 0)))
- (test-equal "entity eq? when vx=0" e (resolve-tile-collisions-x e (test-scene tilemap: tm) 0))))
+ (e (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16 #:vx 0 #:vy 0)))
+ (test-equal "entity eq? when vx=0" e (resolve-tile-collisions-x (test-scene tilemap: tm) e 0))))
(test-group "collision moving right: push left"
;; solid at col=1 (x=16..31); entity at x=20 overlaps it, vx>0
(let* ((tm (make-test-tilemap '((0 0 0) (0 1 0) (0 0 0))))
- (e '(#:type player #:x 20 #:y 16 #:width 16 #:height 16 #:vx 5 #:vy 0)))
- (let ((result (resolve-tile-collisions-x e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 20 #:y 16 #:width 16 #:height 16 #:vx 5 #:vy 0)))
+ (let ((result (resolve-tile-collisions-x (test-scene tilemap: tm) e 0)))
(test-equal "pushed left of solid tile" 0 (entity-ref result #:x))
(test-equal "vx zeroed" 0 (entity-ref result #:vx)))))
(test-group "collision moving left: push right"
;; solid at col=1 (x=16..31); entity at x=16 overlaps it, vx<0
(let* ((tm (make-test-tilemap '((0 0 0) (0 1 0) (0 0 0))))
- (e '(#:type player #:x 16 #:y 16 #:width 16 #:height 16 #:vx -5 #:vy 0)))
- (let ((result (resolve-tile-collisions-x e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 16 #:y 16 #:width 16 #:height 16 #:vx -5 #:vy 0)))
+ (let ((result (resolve-tile-collisions-x (test-scene tilemap: tm) e 0)))
(test-equal "pushed right of solid tile" 32 (entity-ref result #:x))
(test-equal "vx zeroed" 0 (entity-ref result #:vx)))))
(test-group "floating-point x position"
;; solid at col=1; entity at x=20.5 (float), vx>0
(let* ((tm (make-test-tilemap '((0 0 0) (0 1 0) (0 0 0))))
- (e '(#:type player #:x 20.5 #:y 16 #:width 16 #:height 16 #:vx 2 #:vy 0)))
- (let ((result (resolve-tile-collisions-x e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 20.5 #:y 16 #:width 16 #:height 16 #:vx 2 #:vy 0)))
+ (let ((result (resolve-tile-collisions-x (test-scene tilemap: tm) e 0)))
(test-equal "pushed left of solid tile" 0 (entity-ref result #:x))
(test-equal "vx zeroed" 0 (entity-ref result #:vx)))))
(test-group "entity spanning two columns: both checked"
;; wall at col=3; 20px-wide entity at x=28 spans cols 1 and 2, no collision
(let* ((tm (make-test-tilemap '((0 0 0 1) (0 0 0 1) (0 0 0 1))))
- (e '(#:type player #:x 28 #:y 0 #:width 20 #:height 16 #:vx 3 #:vy 0)))
- (let ((result (resolve-tile-collisions-x e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 28 #:y 0 #:width 20 #:height 16 #:vx 3 #:vy 0)))
+ (let ((result (resolve-tile-collisions-x (test-scene tilemap: tm) e 0)))
(test-equal "no collision yet" 28 (entity-ref result #:x))))
;; entity moved to x=34 now spans cols 2 and 3 (solid), pushed left
(let* ((tm (make-test-tilemap '((0 0 0 1) (0 0 0 1) (0 0 0 1))))
- (e '(#:type player #:x 34 #:y 0 #:width 20 #:height 16 #:vx 3 #:vy 0)))
- (let ((result (resolve-tile-collisions-x e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 34 #:y 0 #:width 20 #:height 16 #:vx 3 #:vy 0)))
+ (let ((result (resolve-tile-collisions-x (test-scene tilemap: tm) e 0)))
(test-equal "pushed left of wall" 28 (entity-ref result #:x))
(test-equal "vx zeroed" 0 (entity-ref result #:vx))))))
(test-group "resolve-tile-collisions-y"
(test-group "no collision: entity unchanged"
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (0 0 0))))
- (e '(#:type player #:x 0 #:y 0 #:width 16 #:height 16 #:vx 0 #:vy 2)))
- (let ((result (resolve-tile-collisions-y e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16 #:vx 0 #:vy 2)))
+ (let ((result (resolve-tile-collisions-y (test-scene tilemap: tm) e 0)))
(test-equal "y unchanged" 0 (entity-ref result #:y))
(test-equal "vy unchanged" 2 (entity-ref result #:vy)))))
(test-group "zero vy: skipped entirely"
(let* ((tm (make-test-tilemap '((1 0 0) (0 0 0) (0 0 0))))
- (e '(#:type player #:x 0 #:y 0 #:width 16 #:height 16 #:vx 0 #:vy 0)))
- (test-equal "entity eq? when vy=0" e (resolve-tile-collisions-y e (test-scene tilemap: tm) 0))))
+ (e (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16 #:vx 0 #:vy 0)))
+ (test-equal "entity eq? when vy=0" e (resolve-tile-collisions-y (test-scene tilemap: tm) e 0))))
(test-group "collision moving down: push up"
;; solid at row=1 (y=16..31); entity at y=20 overlaps it, vy>0
(let* ((tm (make-test-tilemap '((0 0 0) (1 0 0) (0 0 0))))
- (e '(#:type player #:x 0 #:y 20 #:width 16 #:height 16 #:vx 0 #:vy 5)))
- (let ((result (resolve-tile-collisions-y e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 20 #:width 16 #:height 16 #:vx 0 #:vy 5)))
+ (let ((result (resolve-tile-collisions-y (test-scene tilemap: tm) e 0)))
(test-equal "pushed above solid tile" 0 (entity-ref result #:y))
(test-equal "vy zeroed" 0 (entity-ref result #:vy)))))
(test-group "collision moving up: push down"
;; solid at row=1 (y=16..31); entity at y=16 overlaps it from below, vy<0
(let* ((tm (make-test-tilemap '((0 0 0) (0 1 0) (0 0 0))))
- (e '(#:type player #:x 16 #:y 16 #:width 16 #:height 16 #:vx 0 #:vy -5)))
- (let ((result (resolve-tile-collisions-y e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 16 #:y 16 #:width 16 #:height 16 #:vx 0 #:vy -5)))
+ (let ((result (resolve-tile-collisions-y (test-scene tilemap: tm) e 0)))
(test-equal "pushed below solid tile" 32 (entity-ref result #:y))
(test-equal "vy zeroed" 0 (entity-ref result #:vy)))))
(test-group "floating-point y position"
;; solid at row=1; entity at y=20.5 (float), vy>0
(let* ((tm (make-test-tilemap '((0 0 0) (1 0 0) (0 0 0))))
- (e '(#:type player #:x 0 #:y 20.5 #:width 16 #:height 16 #:vx 0 #:vy 3)))
- (let ((result (resolve-tile-collisions-y e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 20.5 #:width 16 #:height 16 #:vx 0 #:vy 3)))
+ (let ((result (resolve-tile-collisions-y (test-scene tilemap: tm) e 0)))
(test-equal "pushed above solid tile" 0 (entity-ref result #:y))
(test-equal "vy zeroed" 0 (entity-ref result #:vy)))))
(test-group "entity spanning two rows: both checked"
;; floor at row=3; 20px-tall entity at y=28 spans rows 1 and 2, no collision
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (0 0 0) (1 1 1))))
- (e '(#:type player #:x 0 #:y 28 #:width 16 #:height 20 #:vx 0 #:vy 3)))
- (let ((result (resolve-tile-collisions-y e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 28 #:width 16 #:height 20 #:vx 0 #:vy 3)))
+ (let ((result (resolve-tile-collisions-y (test-scene tilemap: tm) e 0)))
(test-equal "no collision yet" 28 (entity-ref result #:y))))
;; entity at y=34 now spans rows 2 and 3 (solid), pushed up
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (0 0 0) (1 1 1))))
- (e '(#:type player #:x 0 #:y 34 #:width 16 #:height 20 #:vx 0 #:vy 3)))
- (let ((result (resolve-tile-collisions-y e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 34 #:width 16 #:height 20 #:vx 0 #:vy 3)))
+ (let ((result (resolve-tile-collisions-y (test-scene tilemap: tm) e 0)))
(test-equal "pushed above floor" 28 (entity-ref result #:y))
(test-equal "vy zeroed" 0 (entity-ref result #:vy))))))
@@ -304,8 +309,8 @@
;; After apply-velocity-y the entity lands at y=34 (overlapping both rows 2 and 3).
;; Correct: snap to top of row 2 → y=16. Bug was: fold overwrote row 2 snap with row 3 snap → y=32 (inside row 2).
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (1 0 0) (1 0 0) (0 0 0))))
- (e '(#:type player #:x 0 #:y 34 #:width 16 #:height 16 #:vx 0 #:vy 20)))
- (let ((result (resolve-tile-collisions-y e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 34 #:width 16 #:height 16 #:vx 0 #:vy 20)))
+ (let ((result (resolve-tile-collisions-y (test-scene tilemap: tm) e 0)))
(test-equal "snapped to first solid row" 16 (entity-ref result #:y))
(test-equal "vy zeroed" 0 (entity-ref result #:vy)))))
@@ -315,8 +320,8 @@
;; 3x4 tilemap: air on rows 0-2, solid floor on row 3
;; Player starts at y=0, 16px tall; floor is at y=48
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (0 0 0) (1 1 1))))
- (e0 '(#:type player #:x 0 #:y 0 #:width 16 #:height 16
- #:vx 0 #:vy 0 #:gravity? #t #:input-map ())))
+ (e0 (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16
+ #:vx 0 #:vy 0 #:gravity? #t #:input-map '())))
(let loop ((e e0) (n 10))
(if (= n 0)
(begin
@@ -328,8 +333,8 @@
;; Player already on floor, should stay there
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (1 1 1))))
;; Floor at row 2 (y=32); player at y=16, height=16: bottom at y=32
- (e0 '(#:type player #:x 0 #:y 16 #:width 16 #:height 16
- #:vx 0 #:vy 0 #:gravity? #t #:input-map ())))
+ (e0 (entity #:type 'player #:x 0 #:y 16 #:width 16 #:height 16
+ #:vx 0 #:vy 0 #:gravity? #t #:input-map '())))
(let loop ((e e0) (n 10))
(if (= n 0)
(test-assert "player stays on floor" (<= (entity-ref e #:y) 16))
@@ -350,7 +355,7 @@
(test-group "resolve-entity-collisions"
(define (make-solid x y w h)
- (list #:type 'block #:x x #:y y #:width w #:height h #:solid? #t))
+ (entity #:type 'block #:x x #:y y #:width w #:height h #:solid? #t))
(test-group "no overlap: entities unchanged"
(let* ((a (make-solid 0 0 16 16))
@@ -383,9 +388,9 @@
(test-group "immovable: landing uses vertical separation when horizontal overlap is shallower"
;; Without the landing rule, ovx < ovy would pick horizontal separation and shove the
;; mover sideways off a narrow platform. Box center remains above shelf center → snap on top.
- (let* ((shelf (list #:type 'static #:x 100 #:y 200 #:width 16 #:height 16
- #:solid? #t #:immovable? #t))
- (box (list #:type 'box #:x 92 #:y 196 #:width 16 #:height 16
+ (let* ((shelf (entity #:type 'static #:x 100 #:y 200 #:width 16 #:height 16
+ #:solid? #t #:immovable? #t))
+ (box (entity #:type 'box #:x 92 #:y 196 #:width 16 #:height 16
#:solid? #t #:immovable? #f #:vx 0 #:vy 0))
(result (resolve-entity-collisions (list shelf box)))
(box2 (list-ref result 1)))
@@ -394,7 +399,7 @@
(test-group "non-solid entity ignored"
(let* ((a (make-solid 0 0 16 16))
- (b (list #:type 'goal #:x 5 #:y 5 #:width 16 #:height 16))
+ (b (entity #:type 'goal #:x 5 #:y 5 #:width 16 #:height 16))
(result (resolve-entity-collisions (list a b))))
(test-equal "a x unchanged" 0 (entity-ref (list-ref result 0) #:x 0))
(test-equal "b x unchanged" 5 (entity-ref (list-ref result 1) #:x 0)))))
@@ -406,72 +411,72 @@
;; tilewidth=tileheight=16
;; Entity standing: y=16, h=16 → bottom at y=32, probe at y=33 → row=2 → solid
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (1 1 1))))
- (e (list #:type 'player #:x 0 #:y 16 #:width 16 #:height 16
- #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f))
- (result (detect-on-solid e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 16 #:width 16 #:height 16
+ #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f))
+ (result (detect-on-solid (test-scene tilemap: tm) e 0)))
(test-assert "on-ground? is #t" (entity-ref result #:on-ground? #f))))
(test-group "entity in mid-air"
;; Entity in mid-air: y=0, h=16 → bottom at 16, probe at 17 → row=1 → empty
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (1 1 1))))
- (e (list #:type 'player #:x 0 #:y 0 #:width 16 #:height 16
- #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #t))
- (result (detect-on-solid e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16
+ #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #t))
+ (result (detect-on-solid (test-scene tilemap: tm) e 0)))
(test-assert "on-ground? is #f" (not (entity-ref result #:on-ground? #f)))))
(test-group "entity probe spans two tiles, left is solid"
;; Entity at x=0, w=16: left foot at col 0; probe below
;; Row with solid at col 0, empty at col 1: should be on-ground
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (1 0 0))))
- (e (list #:type 'player #:x 0 #:y 16 #:width 16 #:height 16
- #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f))
- (result (detect-on-solid e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 0 #:y 16 #:width 16 #:height 16
+ #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f))
+ (result (detect-on-solid (test-scene tilemap: tm) e 0)))
(test-assert "on-ground? is #t (left foot on solid)" (entity-ref result #:on-ground? #f))))
(test-group "entity probe spans two tiles, right is solid"
;; Entity at x=8, w=16: left foot at col 0, right foot at col 1; probe below
;; Row with empty at col 0, solid at col 1: should be on-ground (right foot on solid)
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (0 1 0))))
- (e (list #:type 'player #:x 8 #:y 16 #:width 16 #:height 16
- #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f))
- (result (detect-on-solid e (test-scene tilemap: tm) 0)))
+ (e (entity #:type 'player #:x 8 #:y 16 #:width 16 #:height 16
+ #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f))
+ (result (detect-on-solid (test-scene tilemap: tm) e 0)))
(test-assert "on-ground? is #t (right foot on solid)" (entity-ref result #:on-ground? #f))))
(test-group "standing on solid entity (no tile): moving platform / crate"
;; All-air tilemap; wide platform top at y=32; player feet (bottom) at y=32
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (0 0 0))))
- (platform (list #:type 'platform #:x 0 #:y 32 #:width 64 #:height 16
- #:solid? #t #:vx 0 #:vy 0 #:gravity? #f))
- (player (list #:type 'player #:x 8 #:y 16 #:width 16 #:height 16
- #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f))
+ (platform (entity #:type 'platform #:x 0 #:y 32 #:width 64 #:height 16
+ #:solid? #t #:vx 0 #:vy 0 #:gravity? #f))
+ (player (entity #:type 'player #:x 8 #:y 16 #:width 16 #:height 16
+ #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f))
(ents (list platform player))
- (result (detect-on-solid player (test-scene tilemap: tm entities: ents) 0)))
+ (result (detect-on-solid (test-scene tilemap: tm entities: ents) player 0)))
(test-assert "on-ground? from entity top" (entity-ref result #:on-ground? #f))))
(test-group "scene with empty entity list: no entity below"
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (0 0 0))))
- (platform (list #:type 'platform #:x 0 #:y 32 #:width 64 #:height 16 #:solid? #t))
- (player (list #:type 'player #:x 8 #:y 16 #:width 16 #:height 16
- #:gravity? #t #:on-ground? #f))
- (result (detect-on-solid player (test-scene tilemap: tm) 0)))
+ (platform (entity #:type 'platform #:x 0 #:y 32 #:width 64 #:height 16 #:solid? #t))
+ (player (entity #:type 'player #:x 8 #:y 16 #:width 16 #:height 16
+ #:gravity? #t #:on-ground? #f))
+ (result (detect-on-solid (test-scene tilemap: tm) player 0)))
(test-assert "empty entity list → not on ground" (not (entity-ref result #:on-ground? #f))))))
(test-group "apply-acceleration"
(test-group "gravity? #t, ay set: consumed into vy and cleared"
- (let* ((e '(#:type player #:x 0 #:y 0 #:vy 3 #:ay 5 #:gravity? #t))
- (result (apply-acceleration e #f 0)))
+ (let* ((e (entity #:type 'player #:x 0 #:y 0 #:vy 3 #:ay 5 #:gravity? #t))
+ (result (apply-acceleration #f e 0)))
(test-equal "vy += ay" 8 (entity-ref result #:vy 0))
(test-equal "ay cleared" 0 (entity-ref result #:ay 0))))
(test-group "gravity? #t, ay is 0: vy unchanged"
- (let* ((e '(#:type player #:x 0 #:y 0 #:vy 3 #:ay 0 #:gravity? #t))
- (result (apply-acceleration e #f 0)))
+ (let* ((e (entity #:type 'player #:x 0 #:y 0 #:vy 3 #:ay 0 #:gravity? #t))
+ (result (apply-acceleration #f e 0)))
(test-equal "vy unchanged" 3 (entity-ref result #:vy 0))
(test-equal "ay still 0" 0 (entity-ref result #:ay 0))))
(test-group "gravity? #f: entity unchanged"
- (let* ((e '(#:type player #:x 0 #:y 0 #:vy 3 #:ay 5 #:gravity? #f))
- (result (apply-acceleration e #f 0)))
+ (let* ((e (entity #:type 'player #:x 0 #:y 0 #:vy 3 #:ay 5 #:gravity? #f))
+ (result (apply-acceleration #f e 0)))
(test-equal "entity unchanged" e result))))
(test-group "pixel->tile"
@@ -485,14 +490,14 @@
(test-group "entity-tile-cells"
(test-group "entity aligned to one tile"
(let* ((tm (make-test-tilemap '((0 0) (0 0))))
- (e '(#:type player #:x 0 #:y 0 #:width 16 #:height 16))
+ (e (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16))
(cells (entity-tile-cells e tm)))
(test-equal "one cell" 1 (length cells))
(test-equal "cell is (0 . 0)" '(0 . 0) (car cells))))
(test-group "entity spanning 2 cols and 2 rows"
(let* ((tm (make-test-tilemap '((0 0 0) (0 0 0) (0 0 0))))
- (e '(#:type player #:x 8 #:y 8 #:width 16 #:height 16))
+ (e (entity #:type 'player #:x 8 #:y 8 #:width 16 #:height 16))
(cells (entity-tile-cells e tm)))
(test-equal "four cells" 4 (length cells)))))
@@ -531,43 +536,43 @@
(test-group "push-entity"
(test-group "push right (sign=1): x += overlap/2, vx=1"
- (let* ((e '(#:type player #:x 10 #:y 0 #:vx 0 #:vy 0))
+ (let* ((e (entity #:type 'player #:x 10 #:y 0 #:vx 0 #:vy 0))
(result (push-entity e #:x #:vx 10 6 1)))
(test-equal "x = 10 + 3" 13 (entity-ref result #:x 0))
(test-equal "vx = 1" 1 (entity-ref result #:vx 0))))
(test-group "push left (sign=-1): x -= overlap/2, vx=-1"
- (let* ((e '(#:type player #:x 10 #:y 0 #:vx 0 #:vy 0))
+ (let* ((e (entity #:type 'player #:x 10 #:y 0 #:vx 0 #:vy 0))
(result (push-entity e #:x #:vx 10 6 -1)))
(test-equal "x = 10 - 3" 7 (entity-ref result #:x 0))
(test-equal "vx = -1" -1 (entity-ref result #:vx 0)))))
(test-group "entity-center-on-axis"
- (let ((e '(#:type player #:x 10 #:y 20 #:width 16 #:height 24)))
+ (let ((e (entity #:type 'player #:x 10 #:y 20 #:width 16 #:height 24)))
(test-equal "center-x = 10 + 8 = 18" 18 (entity-center-on-axis e #:x))
(test-equal "center-y = 20 + 12 = 32" 32 (entity-center-on-axis e #:y))))
(test-group "aabb-overlap-on-axis"
(test-group "x overlap: a at x=0 w=16, b at x=10 w=16 → overlap=6"
;; half-sum of widths = 16, center dist = |18 - 8| = 10, overlap = 16 - 10 = 6
- (let ((a '(#:type player #:x 0 #:y 0 #:width 16 #:height 16))
- (b '(#:type player #:x 10 #:y 0 #:width 16 #:height 16)))
+ (let ((a (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16))
+ (b (entity #:type 'player #:x 10 #:y 0 #:width 16 #:height 16)))
(test-equal "x overlap = 6" 6 (aabb-overlap-on-axis #:x a b))))
(test-group "y overlap: a at y=0 h=16, b at y=10 h=16 → overlap=6"
- (let ((a '(#:type player #:x 0 #:y 0 #:width 16 #:height 16))
- (b '(#:type player #:x 0 #:y 10 #:width 16 #:height 16)))
+ (let ((a (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16))
+ (b (entity #:type 'player #:x 0 #:y 10 #:width 16 #:height 16)))
(test-equal "y overlap = 6" 6 (aabb-overlap-on-axis #:y a b))))
(test-group "no overlap: negative value"
- (let ((a '(#:type player #:x 0 #:y 0 #:width 16 #:height 16))
- (b '(#:type player #:x 100 #:y 0 #:width 16 #:height 16)))
+ (let ((a (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16))
+ (b (entity #:type 'player #:x 100 #:y 0 #:width 16 #:height 16)))
(test-assert "x overlap is negative" (< (aabb-overlap-on-axis #:x a b) 0)))))
(test-group "push-along-axis"
(test-group "x axis: a left of b, pushed apart"
- (let* ((a '(#:type player #:x 0 #:y 0 #:width 16 #:height 16))
- (b '(#:type player #:x 10 #:y 0 #:width 16 #:height 16))
+ (let* ((a (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16))
+ (b (entity #:type 'player #:x 10 #:y 0 #:width 16 #:height 16))
(result (push-along-axis #:x a b 6))
(ra (car result))
(rb (cdr result)))
@@ -577,8 +582,8 @@
(test-equal "b vx = 1" 1 (entity-ref rb #:vx 0))))
(test-group "y axis: a above b, pushed apart"
- (let* ((a '(#:type player #:x 0 #:y 0 #:width 16 #:height 16))
- (b '(#:type player #:x 0 #:y 10 #:width 16 #:height 16))
+ (let* ((a (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16))
+ (b (entity #:type 'player #:x 0 #:y 10 #:width 16 #:height 16))
(result (push-along-axis #:y a b 6))
(ra (car result))
(rb (cdr result)))
@@ -588,42 +593,42 @@
(test-group "push-apart"
(test-group "x overlap smaller: pushes on x axis"
;; a at (0,0), b at (10,0), both 16x16: ovx=6, ovy=16 → push on x
- (let* ((a '(#:type player #:x 0 #:y 0 #:width 16 #:height 16))
- (b '(#:type player #:x 10 #:y 0 #:width 16 #:height 16))
+ (let* ((a (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16))
+ (b (entity #:type 'player #:x 10 #:y 0 #:width 16 #:height 16))
(result (push-apart a b)))
(test-equal "a pushed left" -3 (entity-ref (car result) #:x 0))
(test-equal "b pushed right" 13 (entity-ref (cdr result) #:x 0))))
(test-group "y overlap smaller: pushes on y axis"
;; a at (0,0), b at (0,10), both 16x16: ovx=16, ovy=6 → push on y
- (let* ((a '(#:type player #:x 0 #:y 0 #:width 16 #:height 16))
- (b '(#:type player #:x 0 #:y 10 #:width 16 #:height 16))
+ (let* ((a (entity #:type 'player #:x 0 #:y 0 #:width 16 #:height 16))
+ (b (entity #:type 'player #:x 0 #:y 10 #:width 16 #:height 16))
(result (push-apart a b)))
(test-equal "a pushed up" -3 (entity-ref (car result) #:y 0))
(test-equal "b pushed down" 13 (entity-ref (cdr result) #:y 0)))))
(test-group "skip-pipelines"
(test-group "apply-gravity"
- (let* ((e '(#:type t #:vy 0 #:gravity? #t #:skip-pipelines (gravity)))
- (r (apply-gravity e #f 0)))
+ (let* ((e (entity #:type 't #:vy 0 #:gravity? #t #:skip-pipelines '(gravity)))
+ (r (apply-gravity #f e 0)))
(test-equal "skipped: vy unchanged" 0 (entity-ref r #:vy))))
(test-group "apply-velocity-x"
- (let* ((e '(#:type t #:x 10 #:vx 5 #:skip-pipelines (velocity-x)))
- (r (apply-velocity-x e #f 0)))
+ (let* ((e (entity #:type 't #:x 10 #:vx 5 #:skip-pipelines '(velocity-x)))
+ (r (apply-velocity-x #f e 0)))
(test-equal "skipped: x unchanged" 10 (entity-ref r #:x))))
(test-group "resolve-pair with entity-collisions skip"
- (define (make-solid x y) (list #:type 'block #:x x #:y y #:width 16 #:height 16 #:solid? #t))
- (let* ((a (list #:type 'ghost #:x 0 #:y 0 #:width 16 #:height 16 #:solid? #t
- #:skip-pipelines '(entity-collisions)))
+ (define (make-solid x y) (entity #:type 'block #:x x #:y y #:width 16 #:height 16 #:solid? #t))
+ (let* ((a (entity #:type 'ghost #:x 0 #:y 0 #:width 16 #:height 16 #:solid? #t
+ #:skip-pipelines '(entity-collisions)))
(b (make-solid 10 0)))
(test-assert "no resolution" (not (resolve-pair a b))))))
(test-group "resolve-pair"
- (define (make-solid x y) (list #:type 'block #:x x #:y y #:width 16 #:height 16 #:solid? #t))
+ (define (make-solid x y) (entity #:type 'block #:x x #:y y #:width 16 #:height 16 #:solid? #t))
(test-group "one entity not solid: returns #f"
(let ((a (make-solid 0 0))
- (b '(#:type ghost #:x 5 #:y 5 #:width 16 #:height 16)))
+ (b (entity #:type 'ghost #:x 5 #:y 5 #:width 16 #:height 16)))
(test-assert "returns #f" (not (resolve-pair a b)))))
(test-group "no overlap: returns #f"
@@ -641,9 +646,9 @@
(test-group "immovable"
(define (make-static x y)
- (list #:type 'wall #:x x #:y y #:width 16 #:height 16 #:solid? #t #:immovable? #t))
+ (entity #:type 'wall #:x x #:y y #:width 16 #:height 16 #:solid? #t #:immovable? #t))
(define (make-box x y)
- (list #:type 'box #:x x #:y y #:width 16 #:height 16 #:solid? #t))
+ (entity #:type 'box #:x x #:y y #:width 16 #:height 16 #:solid? #t))
(test-group "both immovable and overlapping: #f"
(let* ((a (make-static 0 0))
(b (make-static 8 0)))