(import scheme (chicken base) (chicken random) (only srfi-1 iota take) (only (list-utils alist) plist->alist) (prefix sdl2 "sdl2:") (prefix sdl2-ttf "ttf:") (prefix sdl2-image "img:") downstroke-engine downstroke-world downstroke-tilemap downstroke-physics downstroke-assets downstroke-entity downstroke-scene-loader downstroke-tween downstroke-prefabs) ;; ── Constants ──────────────────────────────────────────────────────────────── (define +game-width+ 600) (define +game-height+ 400) (define +demo-bot-cycle-ms+ 2600.0) (define +demo-bot-half-cycle-ms+ (/ +demo-bot-cycle-ms+ 2.0)) (define +demo-bot-jump-interval-ms+ 720.0) ;; ── Mutable demo state ────────────────────────────────────────────────────── (define *demo-t* 0.0) ;; ── Tilemap builder ────────────────────────────────────────────────────────── (define (make-sandbox-tilemap ts tw th gw gh) (let* ((ncols (inexact->exact (ceiling (/ gw tw)))) (nrows (inexact->exact (ceiling (/ gh th)))) (empty-row (map (lambda (_) 0) (iota ncols))) (floor-row (map (lambda (_) 20) (iota ncols))) (map-data (append (map (lambda (_) empty-row) (iota (- nrows 1))) (list floor-row)))) (make-tilemap width: ncols height: nrows tilewidth: tw tileheight: th tileset-source: "" tileset: ts layers: (list (make-layer name: "ground" width: ncols height: nrows map: map-data)) objects: '()))) ;; ── Entity factories ───────────────────────────────────────────────────────── (define (make-box x y tw th) (plist->alist (list #:type 'box #:x x #:y y #:width tw #:height th #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f #:solid? #t #:immovable? #f #:tile-id 29))) (define (spawn-boxes tw th) (map (lambda (i) (make-box (+ 30 (* i 55)) (+ 10 (* (pseudo-random-integer 4) 20)) tw th)) (iota 8))) (define (make-demo-bot x y tw th id) (plist->alist (list #:type 'demo-bot #:x x #:y y #:width tw #:height th #:vx 0 #:vy 0 #:gravity? #t #:on-ground? #f #:solid? #t #:immovable? #f #:tile-id 1 #:demo-id id #:demo-since-jump 0))) ;; ── Per-entity intent ─────────────────────────────────────────────────────── (define (update-demo-bot e dt) (let* ((id (entity-ref e #:demo-id 0)) (phase (modulo (+ *demo-t* (* id 400.0)) +demo-bot-cycle-ms+)) (vx (if (< phase +demo-bot-half-cycle-ms+) 3.0 -3.0)) (ground? (entity-ref e #:on-ground? #f)) (since (+ (entity-ref e #:demo-since-jump 0) dt)) (jump? (and ground? (>= since +demo-bot-jump-interval-ms+))) (since (if jump? 0 since)) (ay (if jump? (- *jump-force*) 0))) (entity-set (entity-set (entity-set e #:vx vx) #:demo-since-jump since) #:ay ay))) ;; ── Scene builder ─────────────────────────────────────────────────────────── (define (attach-shelf-tween! shelf-list tw) (let* ((origin (car shelf-list)) (x-left (entity-ref origin #:x 0)) (x-right (+ x-left (* 6 tw))) (tweened (entity-set origin #:tween (make-tween origin props: `((#:x . ,x-right)) duration: 3500 ease: 'sine-in-out repeat: -1 yoyo?: #t)))) (cons tweened (cdr shelf-list)))) (define (make-sandbox-scene game) (let* ((reg (load-prefabs "demo/assets/sandbox-groups.scm" (engine-mixins) '())) (ts (game-load-tileset! game 'tileset "demo/assets/monochrome_transparent.tsx")) (tw (tileset-tilewidth ts)) (th (tileset-tileheight ts)) (tex (create-texture-from-tileset (game-renderer game) ts)) (gw (game-width game)) (gh (game-height game)) (tm (make-sandbox-tilemap ts tw th gw gh)) (shelf-list (attach-shelf-tween! (instantiate-group-prefab reg 'shelf-platform (* 10 tw) (- gh (* 6 th))) tw)) (raft-list (instantiate-group-prefab reg 'collision-raft 120 (- gh (* 14 th)))) (bots (list (make-demo-bot 80 80 tw th 0) (make-demo-bot 220 60 tw th 1) (make-demo-bot 380 100 tw th 2)))) (make-scene entities: (append shelf-list raft-list (spawn-boxes tw th) bots) tilemap: tm tileset: #f camera: (make-camera x: 0 y: 0) tileset-texture: tex camera-target: #f background: '(32 34 40)))) ;; ── Game ───────────────────────────────────────────────────────────────────── (define *game* (make-game title: "Demo: Physics Sandbox" width: +game-width+ height: +game-height+ create: (lambda (game) (game-scene-set! game (make-sandbox-scene game))) update: (lambda (game dt) (set! *demo-t* (+ *demo-t* dt)) (let ((scene (game-scene game))) (game-scene-set! game (scene-map-entities scene (lambda (scene_ e) (if (eq? (entity-type e) 'demo-bot) (update-demo-bot e dt) e)))))))) (game-run! *game*)