diff options
| author | Gene Pasquet <dev@etenil.net> | 2026-04-18 02:47:10 +0100 |
|---|---|---|
| committer | Gene Pasquet <dev@etenil.net> | 2026-04-18 02:47:10 +0100 |
| commit | 38eee24832fe6da4f135cae455881ab97953b23a (patch) | |
| tree | cffc2bb3b45ac11d90f4a2de3e207f65862fb6fd /docs/api.org | |
| parent | a02b892e2ad1e1605ff942c63afdd618daa48be4 (diff) | |
Refresh docs and re-indent
Diffstat (limited to 'docs/api.org')
| -rw-r--r-- | docs/api.org | 1385 |
1 files changed, 0 insertions, 1385 deletions
diff --git a/docs/api.org b/docs/api.org deleted file mode 100644 index 673749f..0000000 --- a/docs/api.org +++ /dev/null @@ -1,1385 +0,0 @@ -#+TITLE: Downstroke API Reference - -This document describes the public API for the Downstroke game engine. All exported functions are organized by module. - -* Engine (~downstroke-engine~) - -#+begin_src scheme -(import downstroke-engine) -#+end_src - -The engine module provides the top-level game lifecycle and state management. - -** ~make-game~ - -#+begin_src scheme -(make-game #!key - (title "Downstroke Game") - (width 640) - (height 480) - (scale 1) - (frame-delay 16) - (input-config *default-input-config*) - (preload #f) - (create #f) - (update #f) - (engine-update 'default) - (render #f) - (debug? #f)) -#+end_src - -Creates and initializes a game object. All parameters are optional keywords. - -| Parameter | Type | Default | Description | -|-----------+------+---------+-------------| -| ~title~ | string | "Downstroke Game" | Window title | -| ~width~ | integer | 640 | Logical game width in pixels | -| ~height~ | integer | 480 | Logical game height in pixels | -| ~scale~ | positive integer | 1 | Whole-game pixel scaling factor (window = width×scale by height×scale) | -| ~frame-delay~ | integer | 16 | Delay between frames in milliseconds (30 FPS ≈ 33) | -| ~input-config~ | input-config | *default-input-config* | Keyboard/controller mappings | -| ~preload~ | procedure/false | #f | Hook: ~(lambda (game) ...)~ called once before create | -| ~create~ | procedure/false | #f | Hook: ~(lambda (game) ...)~ called once at startup | -| ~engine-update~ | procedure/false / ~'default~ | ~'default~ | Built-in physics pipeline (~default-engine-update~), ~#f~ to disable, or ~(lambda (game dt) ...)~ to replace | -| ~update~ | procedure/false | #f | Hook: ~(lambda (game dt) ...)~ called each frame *after* ~engine-update~ (see ~game-run!~) | -| ~render~ | procedure/false | #f | Hook: ~(lambda (game) ...)~ called after render-scene! | -| ~debug?~ | boolean | #f | Enable debug overlay drawing (collision boxes) | - -The game object is the central hub. Use it to store/retrieve assets, manage scenes, and access the current input state. - -*** Scaling - -The ~scale:~ parameter controls integer pixel scaling for the entire game. When ~scale:~ is greater than 1, the OS window is created at ~width×scale~ by ~height×scale~ pixels, but SDL2's logical renderer size is set to ~width~ by ~height~. This means all game code (rendering, physics, coordinates) works in the logical resolution — SDL2 handles the upscaling automatically. - -This affects everything uniformly: tiles, sprites, text, colored rectangles, and debug overlays. Mouse/touch input coordinates are also automatically mapped back to the logical resolution. - -#+begin_src scheme -;; 320×240 game rendered in a 640×480 window (2× pixel scaling) -(make-game title: "Pixel Art Game" width: 320 height: 240 scale: 2) - -;; 256×224 NES-style with 3× scaling → 768×672 window -(make-game title: "Retro Game" width: 256 height: 224 scale: 3) -#+end_src - -Only positive integers are accepted; fractional or zero values signal an error. - -*** ~engine-update:~ and ~default-engine-update~ - -Omit ~engine-update:~ (or pass ~engine-update: 'default~) to use ~default-engine-update~: the standard per-frame physics pipeline on the current scene. Pass ~engine-update: #f~ if your game does not use the built-in pipeline (for example a shmup or menu that moves entities entirely in ~update:~). Pass a custom procedure ~(lambda (game dt) ...)~ to run your own integration step instead; it should read and write the scene via ~(game-scene game)~ and ~(game-scene-set! game scene)~ like ~default-engine-update~ does. - -#+begin_src scheme -(default-engine-update game dt) -#+end_src - -Runs the built-in pipeline once. Order: ~step-tweens~ → ~apply-acceleration~ → ~apply-gravity~ → ~apply-velocity-x~ → ~resolve-tile-collisions-x~ → ~apply-velocity-y~ → ~resolve-tile-collisions-y~ → ~detect-on-solid~ → ~resolve-entity-collisions~ (whole entity list) → ~sync-groups~ (whole entity list). Per-entity steps use signature ~(entity scene dt)~; bulk steps use ~(entities)~ with ~scene-transform-entities~. - -**Typical ~update:~ pattern:** treat ~update:~ as game logic only: read input, set *intent* on entities (~#:vx~, ~#:vy~, one-shot ~#:ay~ for jumps, flags), animation, and sound. Each frame, ~engine-update~ runs *before* ~update:~, so your hook sees positions and ~#:on-ground?~ after that frame’s integration and collisions. The ~#:vx~ / ~#:vy~ / ~#:ay~ you set in ~update:~ are applied starting on the following frame’s ~engine-update~. - -*** Fullscreen - -Downstroke does not provide a built-in ~fullscreen:~ keyword, but you can make any game fullscreen by setting the SDL2 window flag after the game starts. Use the ~preload:~ hook (the window exists by then): - -#+begin_src scheme -(make-game - title: "My Game" width: 320 height: 240 scale: 2 - preload: (lambda (game) - ;; 'fullscreen-desktop scales to fill the screen without changing resolution - (sdl2:window-fullscreen-set! (game-window game) 'fullscreen-desktop))) -#+end_src - -The two fullscreen modes are: - -| Mode | SDL2 symbol | Behavior | -|------+-------------+-----------| -| Desktop fullscreen | ~'fullscreen-desktop~ | Fills the screen at the desktop resolution; SDL2 handles scaling and letterboxing. Best for most games. | -| Exclusive fullscreen | ~'fullscreen~ | Changes the display resolution to match ~width×scale~ by ~height×scale~. Use only when you need exclusive display control. | - -Combine with ~scale:~ to get pixel-perfect fullscreen: set your logical resolution small (e.g. 320×240), use ~scale:~ for the default windowed size, and let ~'fullscreen-desktop~ handle the rest. - -** ~game-run!~ - -#+begin_src scheme -(game-run! game) -#+end_src - -Starts the main event loop. Initializes SDL2, opens the window (at ~width×scale~ by ~height×scale~ pixels), sets the logical render size when ~scale~ > 1, and runs the frame loop indefinitely until the user quits or the ~quit~ action is pressed. Never returns. - -Lifecycle order within each frame (while not quitting): -1. Collect SDL2 events -2. Update input state -3. Call ~engine-update-hook~ if set (~default-engine-update~ runs the physics pipeline: tweens, acceleration, gravity, velocity, collisions, ground detection, entity collisions, group sync) -4. Call ~update:~ hook (or active state's ~update~) — runs *after* physics, so logic sees resolved positions and ~#:on-ground?~ -5. Apply camera follow (~update-scene~ with ~camera-target~, if set) -6. Set the renderer clear color from the current scene's ~background:~ (see ~make-scene~), then clear the framebuffer (~#f~ or invalid value uses opaque black) -7. Call ~render-scene!~ (if scene is set) -8. Call ~render:~ hook (or active state's ~render~) -9. Present renderer -10. Apply frame delay - -** ~game-camera~ - -#+begin_src scheme -(game-camera game) -#+end_src - -Returns the current scene's camera struct. Only valid after ~create:~ runs. Returns a camera record with ~x~ and ~y~ fields for the top-left viewport corner in world coordinates. - -** ~game-asset~ - -#+begin_src scheme -(game-asset game key) -#+end_src - -Retrieves an asset from the game's registry by key. Returns ~#f~ if the key is not found. - -** ~game-asset-set!~ - -#+begin_src scheme -(game-asset-set! game key value) -#+end_src - -Stores an asset in the game's registry. Overwrites any existing value at the key. - -** ~make-game-state~ - -#+begin_src scheme -(make-game-state #!key (create #f) (update #f) (render #f)) -#+end_src - -Creates a state record (plist) with optional lifecycle hooks. Used with ~game-add-state!~ and ~game-start-state!~ to build a state machine within the game. - -| Parameter | Type | Default | Description | -|-----------+------+---------+-------------| -| ~create~ | procedure/false | #f | Called when entering this state | -| ~update~ | procedure/false | #f | Called each frame while active | -| ~render~ | procedure/false | #f | Called each frame after rendering (overlay) | - -** ~game-add-state!~ - -#+begin_src scheme -(game-add-state! game name state) -#+end_src - -Registers a named state (created with ~make-game-state~) in the game's state table. ~name~ must be a symbol. - -** ~game-start-state!~ - -#+begin_src scheme -(game-start-state! game name) -#+end_src - -Transitions to a named state, activating its lifecycle hooks. Calls the state's ~create:~ hook (if present) immediately. - -** Example: Game with State Machine - -#+begin_src scheme -(define my-game (make-game title: "My Game")) - -(game-add-state! my-game 'playing - (make-game-state - create: (lambda (game) (print "Game started")) - update: (lambda (game dt) (print "Updating...")) - render: (lambda (game) (print "Rendering overlay")))) - -(game-add-state! my-game 'paused - (make-game-state - create: (lambda (game) (print "Paused")))) - -; Start the game in 'playing state -(game-start-state! my-game 'playing) - -; Later, transition to paused -(game-start-state! my-game 'paused) -#+end_src - -* World (~downstroke-world~) - -#+begin_src scheme -(import downstroke-world) -#+end_src - -The world module provides the scene (level) abstraction and camera management. - -** ~make-scene~ - -Auto-generated by defstruct. Use keyword arguments: - -#+begin_src scheme -(make-scene #!key - (entities '()) - (tilemap #f) - (tileset #f) - (camera #f) - (tileset-texture #f) - (camera-target #f) - (background #f)) -#+end_src - -Creates a scene record representing the current level state. - -| Parameter | Type | Default | Description | -|-----------+------+---------+-------------| -| ~entities~ | list | ~'()~ | List of entity plists | -| ~tilemap~ | tilemap/false | #f | Tile grid and collisions | -| ~tileset~ | tileset/false | #f | Tileset metadata (from ~load-tileset~) when there is no tilemap; required with ~tileset-texture~ to draw ~#:tile-id~ sprites without a TMX map | -| ~camera~ | camera/false | #f | Viewport position | -| ~tileset-texture~ | SDL2 texture/false | #f | Rendered tileset image | -| ~camera-target~ | symbol/false | #f | Tag symbol of the entity to follow (set via ~update-scene~) | -| ~background~ | list/false | #f | Framebuffer clear color: ~(r g b)~ or ~(r g b a)~ (0–255). ~#f~ means opaque black. Set each frame in ~game-run!~ before ~SDL_RenderClear~. | - -** ~make-camera~ - -Auto-generated by defstruct. Use keyword arguments: - -#+begin_src scheme -(make-camera #!key (x 0) (y 0)) -#+end_src - -Creates a camera record. ~x~ and ~y~ are the pixel coordinates of the viewport's top-left corner in world space. - -** ~camera-x~, ~camera-y~ - -#+begin_src scheme -(camera-x camera) -(camera-y camera) -#+end_src - -Accessors for camera position. - -** ~camera-x-set!~, ~camera-y-set!~ - -#+begin_src scheme -(camera-x-set! camera x) -(camera-y-set! camera y) -#+end_src - -Mutate camera position (in-place). - -** ~camera-follow~ - -#+begin_src scheme -(camera-follow camera entity viewport-w viewport-h) -#+end_src - -Returns a new camera centered on an entity, clamping to stay within world bounds (never negative). ~viewport-w~ and ~viewport-h~ are the game window dimensions. The original camera is not modified. - -** ~scene-add-entity~ - -#+begin_src scheme -(scene-add-entity scene entity) -#+end_src - -Appends an entity to the scene's entity list. Returns a new scene; the original is not modified. - -** ~scene-map-entities~ - -#+begin_src scheme -(scene-map-entities scene proc1 proc2 ...) -#+end_src - -Applies each procedure in sequence to all entities in the scene. Each procedure takes a single entity and returns a modified entity. Returns a new scene with the updated entity list; the original is not modified. - -Example: - -#+begin_src scheme -(define (increment-x entity) - (entity-set entity #:x (+ 1 (entity-ref entity #:x 0)))) - -;; Per-entity physics steps need scene and dt (e.g. from your update: hook): -(define dt 16) ; example: ms since last frame - -(scene-map-entities scene - increment-x - (lambda (e) (apply-gravity e scene dt))) -; Each entity is passed through increment-x, then apply-gravity with fixed scene/dt -#+end_src - -** ~scene-transform-entities~ - -#+begin_src scheme -(scene-transform-entities scene proc) -#+end_src - -Applies ~proc~ to the scene’s full entity list. ~proc~ must have signature ~(entities → entities)~: it receives the current list and returns a new list. Returns a new scene with that entity list; the original is not modified. Use this to run whole-list steps such as ~sync-groups~ or ~resolve-entity-collisions~ after ~scene-map-entities~ (or in any order your game needs). - -** ~sync-groups~ - -#+begin_src scheme -(sync-groups entities) -#+end_src - -For every entity with ~#:group-id~ that is not an origin (~#:group-origin?~ is false), sets ~#:x~ and ~#:y~ to the corresponding origin’s position plus that entity’s ~#:group-local-x~ and ~#:group-local-y~. Origins are read from the **entity list** argument (typically ~(scene-entities scene)~ when you compose with ~scene-transform-entities~), so after a tween or other motion that returns a *new* origin plist, replace that origin in the list (match on ~#:group-id~ / ~#:group-origin?~) before calling ~sync-groups~. Call after updating origin positions and before per-entity physics so platforms and collisions see a consistent pose. Returns a new entity list. - -Typical usage: - -#+begin_src scheme -(scene-transform-entities scene sync-groups) -#+end_src - -** ~scene-filter-entities~ - -#+begin_src scheme -(scene-filter-entities scene predicate) -#+end_src - -Keeps only entities that satisfy the predicate. Returns a new scene; the original is not modified. - -Example: - -#+begin_src scheme -; Remove all entities with type 'enemy -(scene-filter-entities scene - (lambda (e) (not (eq? (entity-type e) 'enemy)))) -#+end_src - -** ~scene-find-tagged~ - -#+begin_src scheme -(scene-find-tagged scene tag) -#+end_src - -Returns the first entity whose ~#:tags~ list (a list of symbols) contains the given tag, or ~#f~ if not found. - -** ~scene-find-all-tagged~ - -#+begin_src scheme -(scene-find-all-tagged scene tag) -#+end_src - -Returns a list of all entities whose ~#:tags~ list contains the given tag. Returns ~'()~ if none found. - -** Accessor functions (auto-generated by defstruct) - -Read accessors: -- ~scene-entities~, ~scene-tilemap~, ~scene-camera~, ~scene-tileset-texture~, ~scene-camera-target~, ~scene-background~ - -Functional updater (returns a new scene with the specified fields changed): - -#+begin_src scheme -(update-scene scene entities: new-entities camera-target: 'player) -#+end_src - -Mutating setters (~scene-entities-set!~, etc.) are also generated but should be avoided in favour of ~update-scene~ and the pure pipeline functions above. Use ~game-scene-set!~ at the boundary to store the final scene back on the game struct. - -** ~tilemap-tile-at~ - -#+begin_src scheme -(tilemap-tile-at tilemap col row) -#+end_src - -Returns the tile ID at grid position ~(col, row)~ across all layers. Returns ~0~ if the position is out of bounds or all layers have a 0 tile at that cell. Used internally by the physics module; also useful for manual tile queries. - -* Entity (~downstroke-entity~) - -#+begin_src scheme -(import downstroke-entity) -#+end_src - -The entity module provides property list (plist) accessors for game objects. Entities are immutable plists, never modified in place. It also defines ~entity-skips-pipeline?~ and the ~define-pipeline~ macro for frame pipeline steps that respect ~#:skip-pipelines~ (see ~docs/physics.org~ for the built-in physics step names). - -** ~entity-ref~ - -#+begin_src scheme -(entity-ref entity key #!optional default) -#+end_src - -Retrieves the value of a key from an entity plist. Returns ~default~ (or ~#f~) if the key is not found. If ~default~ is a procedure, it is called with no arguments to produce the default. - -Example: - -#+begin_src scheme -(entity-ref player #:x 0) ; Get x, defaulting to 0 -(entity-ref player #:tags '()) ; Get tags, defaulting to empty list -#+end_src - -** ~entity-type~ - -#+begin_src scheme -(entity-type entity) -#+end_src - -Shorthand for ~(entity-ref entity #:type #f)~. Returns the entity's ~#:type~ field or ~#f~. - -** ~entity-set~ - -#+begin_src scheme -(entity-set entity key value) -#+end_src - -Returns a new plist with the key/value updated. **Does not modify the original entity.** This is a functional (immutable) operation. - -Example: - -#+begin_src scheme -(define player (list #:x 100 #:y 200 #:type 'player)) -(define moved (entity-set player #:x 150)) -; player is still (list #:x 100 #:y 200 #:type 'player) -; moved is (list #:x 150 #:y 200 #:type 'player) -#+end_src - -** ~entity-update~ - -#+begin_src scheme -(entity-update entity key proc #!optional default) -#+end_src - -Functional update: applies ~proc~ to the current value of ~key~ and updates the entity with the result. Returns a new plist. - -Example: - -#+begin_src scheme -(entity-update player #:x (lambda (x) (+ x 10))) -; Equivalent to (entity-set player #:x (+ 10 (entity-ref player #:x 0))) -#+end_src - -** ~entity-skips-pipeline?~ - -#+begin_src scheme -(entity-skips-pipeline? entity step-symbol) -#+end_src - -Returns true if ~step-symbol~ appears in the entity’s ~#:skip-pipelines~ list (and that list is non-empty). The built-in physics step names are documented in ~docs/physics.org~; other engine modules may reserve additional symbols for their own frame phases (rendering, animation, etc.) using the same plist key. - -** ~define-pipeline~ - -#+begin_src scheme -(define-pipeline (procedure-name skip-symbol) (entity-formal extra-formal ...) - guard: guard-expr - body ...) -#+end_src - -The ~guard:~ clause is optional. When present, ~guard-expr~ is evaluated first; if it is false, the entity is returned unchanged and ~body ...~ does not run. When absent, the body applies to all entities (subject only to the skip-symbol check below). - -Syntax for authors of per-entity pipeline steps: expands to a ~define~ that returns the **first** formal (the entity) unchanged when ~skip-symbol~ is listed in ~#:skip-pipelines~; otherwise, if a guard is present and fails, returns the entity unchanged; otherwise runs ~body ...~ inside ~(let () ...)~. Used throughout ~downstroke-physics~ and ~step-tweens~ in ~downstroke-tween~; other modules can use it for consistent skip behavior. Extra formals after the entity are typically ~scene~ and ~dt~ so steps match ~(entity scene dt)~. The procedure name and skip symbol differ when needed (e.g. ~detect-on-solid~ vs ~on-solid~). - -** Shared Entity Keys - -All entities can have these keys. Not all are required: - -| Key | Type | Description | -|-----|------|-------------| -| ~#:type~ | symbol | Entity type (e.g., 'player, 'enemy) | -| ~#:x~ | number | X position in pixels | -| ~#:y~ | number | Y position in pixels | -| ~#:width~ | number | Bounding box width in pixels | -| ~#:height~ | number | Bounding box height in pixels | -| ~#:vx~ | number | X velocity in pixels/frame | -| ~#:vy~ | number | Y velocity in pixels/frame | -| ~#:tile-id~ | integer | Sprite index in tileset (1-indexed) | -| ~#:tags~ | list | List of symbols for lookup (e.g., '(player)) | -| ~#:gravity?~ | boolean | Apply gravity to this entity? | -| ~#:on-ground?~ | boolean | Is entity touching a solid tile below? | -| ~#:facing~ | integer | 1 (right) or -1 (left) | -| ~#:solid?~ | boolean | Participate in AABB entity collisions? | -| ~#:skip-pipelines~ | list | Symbols naming pipeline steps to skip; physics defines the built-in set (~docs/physics.org~); ~'tweens~ skips ~step-tweens~ | -| ~#:tween~ | tween/false | Active tween struct, auto-advanced by ~step-tweens~ | -| ~#:anim-name~ | symbol | Current animation name | -| ~#:anim-frame~ | integer | Current frame index | -| ~#:anim-tick~ | integer | Ticks in current frame | - -* Physics (~downstroke-physics~) - -#+begin_src scheme -(import downstroke-physics) -#+end_src - -The physics module provides functions for movement, collision detection, and ground sensing. With the default ~engine-update:~ hook (~default-engine-update~), they run automatically each frame in a fixed order. You can still call them yourself when building a custom ~engine-update:~ or when experimenting in the REPL (see ~docs/physics.org~). - -** Physics Pipeline Order (~default-engine-update~) - -The built-in pipeline runs this order each frame (inside ~engine-update~, after input, before ~update:~): - -1. ~step-tweens~ — advance ~#:tween~ (see ~downstroke-tween~) -2. ~apply-acceleration~ — consume ~#:ay~ into ~#:vy~ -3. ~apply-gravity~ — add gravity to ~#:vy~ -4. ~apply-velocity-x~ — move by ~#:vx~ -5. ~resolve-tile-collisions-x~ — snap against horizontal tile collisions (uses ~scene-tilemap~) -6. ~apply-velocity-y~ — move by ~#:vy~ -7. ~resolve-tile-collisions-y~ — snap against vertical tile collisions -8. ~detect-on-solid~ — set ~#:on-ground?~ from tiles below and/or other solids in ~scene-entities~ -9. ~resolve-entity-collisions~ — push apart solid entities (whole list; use ~scene-transform-entities~) -10. ~sync-groups~ — align grouped entities to origins (whole list) - -Per-entity steps take ~(entity scene dt)~. Jumping is *not* a built-in step: set ~#:ay~ in your ~update:~ hook (e.g. ~(entity-set player #:ay (- *jump-force*))~) when the player jumps; ~apply-acceleration~ consumes it on the following ~engine-update~. - -Entities may list ~#:skip-pipelines~ to omit specific steps; see ~entity-skips-pipeline?~ under ~downstroke-entity~ and ~docs/physics.org~. - -(This separation ensures smooth sliding along walls.) - -** ~apply-acceleration~ - -#+begin_src scheme -(apply-acceleration entity scene dt) -#+end_src - -Consumes ~#:ay~ (one-shot acceleration) into ~#:vy~ and clears ~#:ay~ to 0. Only applies if ~#:gravity?~ is true. ~scene~ and ~dt~ are accepted for pipeline uniformity; this step does not use them. - -** ~apply-gravity~ - -#+begin_src scheme -(apply-gravity entity scene dt) -#+end_src - -Adds the gravity constant (1 pixel/frame²) to ~#:vy~. Only applies if ~#:gravity?~ is true. - -** ~apply-velocity-x~ - -#+begin_src scheme -(apply-velocity-x entity scene dt) -#+end_src - -Updates ~#:x~ by adding ~#:vx~. Returns a new entity. - -** ~apply-velocity-y~ - -#+begin_src scheme -(apply-velocity-y entity scene dt) -#+end_src - -Updates ~#:y~ by adding ~#:vy~. Returns a new entity. - -** ~apply-velocity~ - -#+begin_src scheme -(apply-velocity entity) -#+end_src - -Legacy function: updates both ~#:x~ and ~#:y~ by their respective velocities. - -** ~resolve-tile-collisions-x~ - -#+begin_src scheme -(resolve-tile-collisions-x entity scene dt) -#+end_src - -Detects and resolves collisions between the entity's bounding box and solid tiles along the X axis using ~(scene-tilemap scene)~. If there is no tilemap, the step is skipped. Snaps the entity's ~#:x~ to the near/far tile edge and sets ~#:vx~ to 0. Returns a new entity. - -** ~resolve-tile-collisions-y~ - -#+begin_src scheme -(resolve-tile-collisions-y entity scene dt) -#+end_src - -Detects and resolves collisions between the entity's bounding box and solid tiles along the Y axis. Snaps the entity's ~#:y~ to the near/far tile edge and sets ~#:vy~ to 0. Returns a new entity. - -** ~detect-on-solid~ - -#+begin_src scheme -(detect-on-solid entity scene dt) -#+end_src - -Sets ~#:on-ground?~ to true if the entity is supported by a solid tile (probe below the feet) and/or by another solid's top surface in ~(scene-entities scene)~ (e.g. moving platforms). Only applies if ~#:gravity?~ is true. Returns a new entity (the ~?~-suffix denotes call-site readability, not a boolean return value). Runs after tile and entity collision resolution in ~default-engine-update~. - -** ~resolve-entity-collisions~ - -#+begin_src scheme -(resolve-entity-collisions entities) -#+end_src - -Detects and resolves AABB collisions between all pairs of entities with ~#:solid?~ true. Pushes overlapping entities apart along the axis of minimum penetration and sets their velocities in the push direction. Returns a new entity list. - -There is no scene-level wrapper; apply ~resolve-entity-collisions~ to the entity list via ~scene-transform-entities~: - -#+begin_src scheme -(scene-transform-entities scene resolve-entity-collisions) -#+end_src - -** Physics Constants - -- ~*gravity*~ = 1 (pixels per frame per frame) -- ~*jump-force*~ = 15 (conventional magnitude for a one-frame jump impulse; set ~#:ay~ to ~(- *jump-force*)~ in ~update:~ when jumping — there is no ~apply-jump~ helper) - -* Input (~downstroke-input~) - -#+begin_src scheme -(import downstroke-input) -#+end_src - -The input module handles keyboard, joystick, and game controller events. It maintains the current and previous input state to support pressed/released detection. - -** ~*default-input-config*~ - -The default input configuration record. Use as the ~input-config:~ parameter to ~make-game~, or create a custom one with ~make-input-config~. - -| Action | Keyboard | Joystick | Controller | -|--------|----------|----------|------------| -| ~up~ | W or Up | Y-axis negative | DPad Up, Left-Y negative | -| ~down~ | S or Down | Y-axis positive | DPad Down, Left-Y positive | -| ~left~ | A or Left | X-axis negative | DPad Left, Left-X negative | -| ~right~ | D or Right | X-axis positive | DPad Right, Left-X positive | -| ~a~ | J or Z | Button 0 | A button | -| ~b~ | K or X | Button 1 | B button | -| ~start~ | Return | Button 7 | Start button | -| ~select~ | (unmapped) | Button 6 | Back button | -| ~quit~ | Escape | (unmapped) | (unmapped) | - -** ~input-held?~ - -#+begin_src scheme -(input-held? state action) -#+end_src - -Returns true if the action is currently held down. ~action~ is a symbol like ~'left~ or ~'a~. - -** ~input-pressed?~ - -#+begin_src scheme -(input-pressed? state action) -#+end_src - -Returns true if the action was pressed in this frame (held now but not in the previous frame). - -** ~input-released?~ - -#+begin_src scheme -(input-released? state action) -#+end_src - -Returns true if the action was released in this frame (held previously but not now). - -** ~create-input-state~ - -#+begin_src scheme -(create-input-state config) -#+end_src - -Initializes an input state record from a configuration. All actions start as unpressed. - -** ~input-any-pressed?~ - -#+begin_src scheme -(input-any-pressed? state config) -#+end_src - -Returns true if any action in the configuration was pressed this frame. Useful for "press any key to continue" prompts. - -** ~input-state->string~ - -#+begin_src scheme -(input-state->string state config) -#+end_src - -Returns a human-readable string of all currently held actions, e.g., ~"[Input: left a]"~. Useful for debug displays. - -** ~set-facing-from-vx~ - -#+begin_src scheme -(set-facing-from-vx entity vx) -#+end_src - -Sets ~#:facing~ to ~1~ if ~vx > 0~, ~-1~ if ~vx < 0~, and returns the entity unchanged if ~vx = 0~. Convenience helper for keeping the facing direction in sync with horizontal velocity. - -** ~apply-input-to-entity~ - -#+begin_src scheme -(apply-input-to-entity entity held?) -#+end_src - -Applies an input map stored on the entity to compute a new ~#:vx~. The entity must have an ~#:input-map~ key: an alist of ~(action . (dvx . dvy))~ pairs. ~held?~ is a predicate ~(lambda (action) ...)~ (typically ~(lambda (a) (input-held? input a))~). Also updates ~#:facing~ from the resulting velocity. - -Example: - -#+begin_src scheme -;; Entity with embedded input map: -(list #:type 'player - #:input-map '((left . (-3 . 0)) (right . (3 . 0))) - #:move-speed 1 - ...) - -;; In update: -(apply-input-to-entity player (lambda (a) (input-held? input a))) -#+end_src - -** Example: Player Movement - -#+begin_src scheme -(define (update-player game dt) - (let* ((input (game-input game)) - (player (car (scene-entities (game-scene game)))) - (jump? (and (input-pressed? input 'a) - (entity-ref player #:on-ground? #f)))) - (if jump? - (entity-set player #:ay (- *jump-force*)) - player))) -#+end_src - -~#:on-ground?~ is set by ~detect-on-solid~ during ~engine-update~, before ~update:~ runs. ~*jump-force*~ is exported from ~downstroke-physics~ (default 15). - -* Renderer (~downstroke-renderer~) - -#+begin_src scheme -(import downstroke-renderer) -#+end_src - -The renderer module provides SDL2 drawing abstractions. - -** ~render-scene!~ - -#+begin_src scheme -(render-scene! renderer scene) -#+end_src - -Draws the entire scene: tilemap layers (if any), then every entity. Sprites use ~#:tile-id~ and the scene's tileset texture when both are available; otherwise an entity with ~#:color~ ~(r g b)~ or ~(r g b a)~ is drawn as a filled rectangle. Called automatically by ~game-run!~ before the user's ~render:~ hook. Does nothing if the scene is ~#f~. - -** ~entity-screen-coords~ - -#+begin_src scheme -(entity-screen-coords entity camera) -#+end_src - -Returns a list ~(x y width height)~ of the entity's bounding box in screen (viewport) coordinates. Pure function, testable without SDL2. - -Example: - -#+begin_src scheme -(entity-screen-coords player (make-camera x: 100 y: 50)) -; If player is at world (200, 100) with size (16, 16): -; Returns (100 50 16 16) -- offset by camera position -#+end_src - -** ~entity-flip~ - -#+begin_src scheme -(entity-flip entity) -#+end_src - -Returns an SDL2 flip list based on the entity's ~#:facing~ field. Returns ~'(horizontal)~ if facing is -1, ~'()~ otherwise. - -** ~draw-ui-text~ - -#+begin_src scheme -(draw-ui-text renderer font text color x y) -#+end_src - -Renders a single line of text to the screen at the given pixel coordinates. ~color~ is an SDL2 color struct. Positions are in screen (viewport) space, not world space. Does not cache; call once per frame for each text element. - -** Debug Drawing - -Debug drawing displays collision boxes and tile grid overlays to visualize physics during development. Enable with the ~debug?:~ keyword on ~make-game~. - -*** ~render-debug-scene!~ - -#+begin_src scheme -(render-debug-scene! renderer scene) -#+end_src - -Renders debug overlays for the entire scene: tile grid boundaries and entity collision boxes. Call this in your render hook to see what the physics engine sees. - -*** ~draw-debug-tiles~ - -#+begin_src scheme -(draw-debug-tiles renderer camera tilemap) -#+end_src - -Draws a grid outline around all non-zero tiles in the tilemap. Useful for understanding tilemap layout and collision geometry. Color: purple (~+debug-tile-color+~). - -*** ~draw-debug-entities~ - -#+begin_src scheme -(draw-debug-entities renderer camera scene) -#+end_src - -Draws colored bounding boxes around all entities in the scene. Also draws attack hitboxes when the entity's ~#:attack-timer~ is active. Entity colors are: - -| Type | Color | Value | -|------|-------|-------| -| Player | Blue (~+debug-player-color+~) | rgb(64, 128, 255) | -| Enemy | Red (~+debug-enemy-color+~) | rgb(220, 40, 40) | -| Attack Hitbox | Green (~+debug-attack-color+~) | rgb(0, 200, 80) | -| Tile | Purple (~+debug-tile-color+~) | rgb(140, 0, 220) | - -*** Example: Enable Debug Mode - -#+begin_src scheme -(define my-game - (make-game - title: "My Game" width: 600 height: 400 - debug?: #t)) ;; Enable debug overlay - -(game-run! my-game) -#+end_src - -With debug mode enabled, the game renders the normal scene first, then overlays collision boxes and tile boundaries on top. This is useful for finding collision bugs and understanding the physics layout. - -** Menus - -The ~draw-menu-items~ helper renders a list of menu options with a cursor indicator. - -*** ~draw-menu-items~ - -#+begin_src scheme -(draw-menu-items renderer font items cursor x y-start y-step - #!key (label-fn identity) (color #f) (prefix "> ") (no-prefix " ")) -#+end_src - -Renders a vertical menu with a cursor indicator on the currently selected item. - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| ~renderer~ | renderer | (required) | SDL2 renderer | -| ~font~ | TTF font | (required) | Font for rendering text | -| ~items~ | list | (required) | List of menu items (any values) | -| ~cursor~ | integer | (required) | Index of highlighted item (0-based) | -| ~x~ | integer | (required) | X position in screen space | -| ~y-start~ | integer | (required) | Y position of first item | -| ~y-step~ | integer | (required) | Pixel spacing between items | -| ~label-fn~ | procedure | ~identity~ | Function to convert item to string (called for each item) | -| ~color~ | SDL2 color | white (255, 255, 255) | Text color | -| ~prefix~ | string | "~> ~" | Prefix for highlighted item | -| ~no-prefix~ | string | "~ ~" | Prefix for non-highlighted items | - -*** Example: Menu State - -#+begin_src scheme -(import downstroke-engine - downstroke-world - (prefix sdl2 "sdl2:") - (prefix sdl2-ttf "ttf:")) - -(define menu-items '(("Start Game") ("Options") ("Quit"))) -(define cursor (make-parameter 0)) - -(define my-game - (make-game - title: "Menu Game" width: 600 height: 400 - - preload: (lambda (game) - (let ((font (ttf:open-font "assets/font.ttf" 24))) - (game-asset-set! game 'menu-font font))) - - create: (lambda (game) - (game-add-state! game 'menu - (make-game-state - render: (lambda (game) - (let ((font (game-asset game 'menu-font))) - (draw-menu-items (game-renderer game) font menu-items - (cursor) - 100 100 40)))))))) - -(game-run! my-game) -#+end_src - -** Sprite Fonts - -Sprite fonts render text using a bitmap tileset instead of system fonts. Characters are stored as tiles in your tileset image, making them pixel-perfect and zero-dependency on TTF files. - -*** ~make-sprite-font*~ - -#+begin_src scheme -(make-sprite-font* #!key tile-size spacing ranges) -#+end_src - -Creates a sprite font from character ranges. - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| ~tile-size~ | integer | (required) | Pixel width/height of each character tile | -| ~spacing~ | integer | 1 | Pixels between characters when rendered | -| ~ranges~ | list | (required) | List of ~(start-char end-char first-tile-id)~ triples | - -The ~ranges~ parameter defines which tile IDs correspond to which characters. For example, ~(list (#\A #\M 917) (#\N #\Z 966) (#\0 #\9 868))~ maps characters A–M to tile IDs 917–929, N–Z to 966–978, and 0–9 to 868–877. - -Characters are automatically uppercased when rendered (all lookups use uppercase). - -*** ~sprite-font-char->tile-id~ - -#+begin_src scheme -(sprite-font-char->tile-id font ch) -#+end_src - -Returns the tile ID for a character, or ~#f~ if not found. The character is automatically uppercased before lookup. - -*** ~sprite-text-width~ - -#+begin_src scheme -(sprite-text-width font text) -#+end_src - -Computes the pixel width of a string when rendered with the given font. Useful for centering text. Formula: ~(* n tile-size) + (* (- n 1) spacing)~ where ~n~ is the string length. - -*** ~draw-sprite-text~ - -#+begin_src scheme -(draw-sprite-text renderer tileset-texture tileset font text x y) -#+end_src - -Renders sprite-font text to the screen. ~tileset-texture~ is the SDL2 texture of your tileset image, and ~tileset~ is the tilemap structure containing tile layout information. Characters not in the font are silently skipped. - -| Parameter | Type | Description | -|-----------|------|-------------| -| ~renderer~ | renderer | SDL2 renderer | -| ~tileset-texture~ | SDL2 texture | Rendered tileset image | -| ~tileset~ | tileset struct | Tileset from loaded tilemap | -| ~font~ | sprite-font struct | Font created with ~make-sprite-font*~ | -| ~text~ | string | Text to render | -| ~x, y~ | integers | Screen position in pixels | - -*** Example: Sprite Font for Score Display - -#+begin_src scheme -(import downstroke-engine - downstroke-scene-loader - downstroke-renderer) - -;; Create a sprite font with A-Z at tiles 917–942, 0-9 at tiles 868–877 -(define score-font - (make-sprite-font* - tile-size: 16 - spacing: 1 - ranges: (list - (list #\A #\Z 917) - (list #\0 #\9 868)))) - -(define my-game - (make-game - title: "Score Game" width: 600 height: 400 - - create: (lambda (game) - (game-load-scene! game "assets/level.tmx")) - - render: (lambda (game) - (let ((scene (game-scene game))) - ;; Display "SCORE: 1500" using sprite font - (draw-sprite-text (game-renderer game) - (scene-tileset-texture scene) - (tilemap-tileset (scene-tilemap scene)) - score-font - "SCORE: 1500" - 10 10))))) - -(game-run! my-game) -#+end_src - -* Assets (~downstroke-assets~) - -#+begin_src scheme -(import downstroke-assets) -#+end_src - -The assets module provides a simple registry for game resources. - -** ~make-asset-registry~ - -#+begin_src scheme -(make-asset-registry) -#+end_src - -Creates an empty asset registry (hash-table). Returned by ~make-game~ automatically. - -** ~asset-set!~ - -#+begin_src scheme -(asset-set! registry key value) -#+end_src - -Stores a value in the registry by key (any hashable value). Overwrites any existing value. - -** ~asset-ref~ - -#+begin_src scheme -(asset-ref registry key) -#+end_src - -Retrieves a value from the registry by key. Returns ~#f~ if not found. - -** Example: Storing Fonts - -#+begin_src scheme -(define (preload game) - (let ((font (ttf:open-font "assets/font.ttf" 16))) - (game-asset-set! game 'main-font font))) - -(define (render-overlay game) - (let ((font (game-asset game 'main-font))) - (draw-ui-text (game-renderer game) font "Score: 100" - (sdl2:make-color 255 255 255) 10 10))) -#+end_src - -* Sound (~downstroke-sound~) - -#+begin_src scheme -(import downstroke-sound) -#+end_src - -The sound module provides music and sound effect playback via SDL_mixer. - -** ~init-audio!~ - -#+begin_src scheme -(init-audio!) -#+end_src - -Initializes SDL_mixer with default settings (44100 Hz, stereo, 512 buffer). Call once in the ~preload:~ hook. - -** ~load-sounds!~ - -#+begin_src scheme -(load-sounds! sound-alist) -#+end_src - -Loads sound effects from an alist of ~(name . path)~ pairs, where ~name~ is a symbol and ~path~ is a file path. Stores them globally for playback. - -Example: - -#+begin_src scheme -(load-sounds! '((jump . "assets/jump.wav") - (hit . "assets/hit.wav"))) -#+end_src - -** ~play-sound~ - -#+begin_src scheme -(play-sound name) -#+end_src - -Plays a loaded sound effect by name (symbol). Plays on the first available channel. No-op if the sound is not found. - -** ~load-music!~ - -#+begin_src scheme -(load-music! path) -#+end_src - -Loads background music from a file. Replaces any previously loaded music. - -** ~play-music!~ - -#+begin_src scheme -(play-music! volume) -#+end_src - -Plays the loaded music on an infinite loop. ~volume~ is a number from 0.0 to 1.0. - -** ~stop-music!~ - -#+begin_src scheme -(stop-music!) -#+end_src - -Stops the currently playing music immediately. - -** ~set-music-volume!~ - -#+begin_src scheme -(set-music-volume! volume) -#+end_src - -Changes the music volume while it is playing. ~volume~ is 0.0 to 1.0. - -** ~cleanup-audio!~ - -#+begin_src scheme -(cleanup-audio!) -#+end_src - -Releases all audio resources. Call at shutdown or in a cleanup hook. - -* Tweens (~downstroke-tween~) - -#+begin_src scheme -(import downstroke-tween) -#+end_src - -Time-based interpolation of numeric entity properties. ~step-tweens~ runs inside ~default-engine-update~ before acceleration/gravity; you can still call ~tween-step~ from ~update:~ for manual tweening — see ~docs/tweens.org~ for patterns with ~#:skip-pipelines~. - -** ~make-tween~ - -#+begin_src scheme -(make-tween entity #!key props duration (delay 0) ease - (on-complete #f) (repeat 0) (yoyo? #f)) -#+end_src - -| Keyword | Description | -|---------+-------------| -| ~props~ | Alist ~((#:key . target-number) ...)~ | -| ~duration~ | Milliseconds of interpolation after ~delay~ | -| ~delay~ | Initial wait in ms (default 0, first cycle only) | -| ~ease~ | Symbol (e.g. ~quad-in-out~) or ~(lambda (t) ...)~ with ~t~ in [0,1] | -| ~on-complete~ | Optional ~(lambda (entity) ...)~ once at final completion (not called with ~repeat: -1~) | -| ~repeat~ | ~0~ = play once (default), ~N~ = replay N extra times, ~-1~ = loop forever | -| ~yoyo?~ | ~#f~ (default) = same direction, ~#t~ = reverse each cycle | - -** ~tween-step~ - -#+begin_src scheme -(tween-step tween entity dt) -#+end_src - -Returns two values: updated tween struct and updated entity. ~dt~ is elapsed milliseconds for this frame. - -** ~tween-finished?~ / ~tween-active?~ - -** ~step-tweens~ - -#+begin_src scheme -(step-tweens entity scene dt) -#+end_src - -Pipeline step: auto-advances ~#:tween~ on an entity. No-op if ~#:tween~ is absent. Removes ~#:tween~ when the tween finishes. Skipped when ~'tweens~ is in ~#:skip-pipelines~. ~scene~ is accepted for uniformity with other pipeline steps. See ~docs/tweens.org~ for patterns. - -** Easing exports - -~ease-linear~, ~ease-quad-in~, ~ease-quad-out~, ~ease-quad-in-out~, ~ease-cubic-in~, ~ease-cubic-out~, ~ease-cubic-in-out~, ~ease-sine-in-out~, ~ease-expo-in~, ~ease-expo-out~, ~ease-expo-in-out~, ~ease-back-out~, ~ease-named~, ~ease-resolve~. - -* Animation (~downstroke-animation~) - -#+begin_src scheme -(import downstroke-animation) -#+end_src - -The animation module provides simple frame-based sprite animation. - -** ~animation-frames~ - -#+begin_src scheme -(animation-frames anim) -#+end_src - -Returns the ~#:frames~ list from an animation plist. Each element is a 0-indexed tile ID. - -** ~animation-duration~ - -#+begin_src scheme -(animation-duration anim) -#+end_src - -Returns the ~#:duration~ value from an animation plist (ticks per frame). - -** ~set-animation~ - -#+begin_src scheme -(set-animation entity name) -#+end_src - -Switches the entity to a named animation, resetting frame and tick counters to 0. No-op if the animation is already active (prevents restarting mid-loop). Returns a new entity. - -** ~animate-entity~ - -#+begin_src scheme -(animate-entity entity animations) -#+end_src - -Advances the entity's animation frame based on elapsed ticks. ~animations~ is an alist of ~(name . animation-plist)~ pairs. Each animation plist has ~#:frames~ (list of tile IDs, 0-indexed) and ~#:duration~ (ticks per frame). Returns a new entity with updated ~#:anim-tick~, ~#:anim-frame~, and ~#:tile-id~. - -Example: - -#+begin_src scheme -(define player-animations - '((idle #:frames (28) #:duration 10) - (walk #:frames (27 28) #:duration 10) - (jump #:frames (29) #:duration 1))) - -(define player (list #:anim-name 'walk #:anim-frame 0 #:anim-tick 0)) -(animate-entity player player-animations) -; After 10 ticks, frame advances to 1 (wraps to 0 after reaching length) -#+end_src - -** ~frame->tile-id~ - -#+begin_src scheme -(frame->tile-id frames frame-idx) -#+end_src - -Converts a frame index to a tile ID (1-indexed). Used internally by ~animate-entity~. - -* Tilemap (~downstroke-tilemap~) - -#+begin_src scheme -(import downstroke-tilemap) -#+end_src - -The tilemap module parses Tiled TMX and TSX files using the expat XML library, and provides struct accessors for tile and map data. - -** ~load-tilemap~ - -#+begin_src scheme -(load-tilemap filename) -#+end_src - -Loads and parses a TMX file. Automatically resolves and loads the referenced TSX tileset and its image. Returns a ~tilemap~ struct. - -** ~load-tileset~ - -#+begin_src scheme -(load-tileset filename) -#+end_src - -Loads and parses a TSX tileset file. Loads the image referenced in the tileset. Returns a ~tileset~ struct. - -** ~tileset-tile~ - -#+begin_src scheme -(tileset-tile tileset tile-id) -#+end_src - -Returns a ~tile~ struct for the given 1-indexed tile ID. The tile struct has ~tile-id~ and ~tile-rect~ (an SDL2 rect) fields with the source rectangle in the tileset image. - -** ~tileset-rows~ - -#+begin_src scheme -(tileset-rows tileset) -#+end_src - -Returns the number of rows in the tileset image (computed from ~tilecount / columns~). - -** Tileset Accessors (auto-generated by defstruct) - -- ~tileset-tilewidth~, ~tileset-tileheight~ — tile dimensions in pixels -- ~tileset-spacing~ — pixel gap between tiles in the source image -- ~tileset-tilecount~ — total number of tiles -- ~tileset-columns~ — number of tile columns -- ~tileset-image-source~ — path to the tileset image file -- ~tileset-image~ — loaded SDL2 surface - -** Tilemap Accessors (auto-generated by defstruct) - -- ~tilemap-width~, ~tilemap-height~ — map dimensions in tiles -- ~tilemap-tilewidth~, ~tilemap-tileheight~ — tile dimensions in pixels -- ~tilemap-tileset~ — the embedded ~tileset~ struct -- ~tilemap-layers~ — list of ~layer~ structs -- ~tilemap-objects~ — list of ~object~ structs (from the Tiled object layer) - -** Layer Accessors (auto-generated by defstruct) - -- ~layer-name~ — layer name string -- ~layer-width~, ~layer-height~ — layer dimensions in tiles -- ~layer-map~ — 2D list of tile GIDs (rows × columns) - -** Object Accessors (auto-generated by defstruct) - -- ~object-name~, ~object-type~ — object name and type (strings from XML) -- ~object-x~, ~object-y~ — position in pixels -- ~object-width~, ~object-height~ — size in pixels -- ~object-properties~ — alist of custom properties from Tiled - -* Scene Loader (~downstroke-scene-loader~) - -#+begin_src scheme -(import downstroke-scene-loader) -#+end_src - -The scene-loader module provides utilities for loading Tiled maps and instantiating entities from prefabs. - -** ~game-load-scene!~ - -#+begin_src scheme -(game-load-scene! game filename) -#+end_src - -Loads a TMX (Tiled) map from a file and creates a scene. Steps: - -1. Loads the tilemap from the file -2. Creates an SDL2 texture from the tileset image -3. Creates an empty scene with the tilemap and camera -4. Stores the tilemap in game assets under key ~'tilemap~ -5. Sets the scene on the game -6. Returns the scene - -Example: - -#+begin_src scheme -(define (create-game game) - (game-load-scene! game "assets/level1.tmx") - ; Now add entities to the scene... - ) -#+end_src - -** ~game-load-tilemap!~ - -#+begin_src scheme -(game-load-tilemap! game key filename) -#+end_src - -Loads a TMX tilemap file, stores it in the game asset registry under ~key~, and returns the tilemap struct. - -** ~game-load-tileset!~ - -#+begin_src scheme -(game-load-tileset! game key filename) -#+end_src - -Loads a TSX tileset file, stores it in the game asset registry under ~key~, and returns the tileset struct. - -** ~game-load-font!~ - -#+begin_src scheme -(game-load-font! game key filename size) -#+end_src - -Opens a TTF font at ~size~ points, stores it in the game asset registry under ~key~, and returns the font. Wraps ~ttf:open-font~. - -** ~create-texture-from-tileset~ - -#+begin_src scheme -(create-texture-from-tileset renderer tileset) -#+end_src - -Creates an SDL2 texture from a tileset struct's image surface (after ~load-tileset~ / ~game-load-tileset!~). Use with ~make-scene~ when ~tilemap~ is ~#f~ but entities use ~#:tile-id~. - -** ~create-tileset-texture~ - -#+begin_src scheme -(create-tileset-texture renderer tilemap) -#+end_src - -Creates an SDL2 texture from the tileset image embedded in a tilemap struct. Useful when you need the texture independently of ~game-load-scene!~. - -** ~load-prefabs~ - -#+begin_src scheme -(load-prefabs filename engine-mixin-table user-hooks) -#+end_src - -Loads a prefab definition file and returns a ~prefab-registry~ struct. The file must contain a Scheme expression with ~mixins~ and ~prefabs~ sections; an optional ~group-prefabs~ section defines multi-entity assemblies (see ~docs/entities.org~). - -| Parameter | Type | Description | -|-----------|------|-------------| -| ~filename~ | string | Path to the prefab data file (e.g., ~"assets/prefabs.scm"~) | -| ~engine-mixin-table~ | alist | Extra engine-level mixins to merge; pass ~'()~ if none | -| ~user-hooks~ | alist | Extra instantiation hooks; pass ~'()~ if none | - -Example: - -#+begin_src scheme -(define *prefabs* (load-prefabs "assets/prefabs.scm" '() '())) -#+end_src - -** ~reload-prefabs!~ - -#+begin_src scheme -(reload-prefabs! registry) -#+end_src - -Reloads the prefab file that the registry was originally loaded from. Returns a new registry. Useful for hot-reloading prefab data during development without restarting the game. - -** ~instantiate-prefab~ - -#+begin_src scheme -(instantiate-prefab registry type x y w h) -#+end_src - -Looks up a prefab by type symbol in the registry and returns a fresh entity plist at the given position and size. Returns ~#f~ if the type is not registered. If the resulting entity has an ~#:on-instantiate~ hook, it is called with the entity before returning. - -** ~instantiate-group-prefab~ - -#+begin_src scheme -(instantiate-group-prefab registry type origin-x origin-y) -#+end_src - -Looks up a *group prefab* by type symbol and returns a list ~(origin member ...)~: one origin entity plus one entity per part. Optional group-level flags ~#:pose-only-origin?~ and ~#:static-parts?~ select origin/part profiles (see ~docs/entities.org~); defaults are ~#f~ (physics-driving origin, non-static parts). Each instance receives a fresh gensym ~#:group-id~ shared by the origin and all members. Returns ~#f~ if the type is not in ~group-prefabs~. After moving origins (tween and/or physics), ensure updated origins are stored in the scene’s entity list, then ~(scene-transform-entities scene sync-groups)~ so member ~#:x~ / ~#:y~ match ~origin + #:group-local-x/y~. - -** ~tilemap-objects->entities~ - -#+begin_src scheme -(tilemap-objects->entities tilemap registry) -#+end_src - -Converts the TMX object list (from Tiled) into entity plists using the prefab registry. Each object's type string is converted to a symbol and passed to ~instantiate-prefab~. Objects whose type has no registered prefab are silently filtered out. - -Example: - -#+begin_src scheme -(let* ((registry (load-prefabs "assets/prefabs.scm" '() '())) - (entities (tilemap-objects->entities tilemap registry))) - (update-scene scene entities: entities)) -#+end_src |
