ludic/packages
Orkuncakilkaya e909ec122c feat(character): 16.8 - on land a physics walker moves the body (CharacterGround.walk); the package's own gravity, ballistic air and slope-probe refusal are gone
The package keeps the rules and hands them over each step: speed and heading, whether a jump
leaves, CHAR_STEP and the boots' slope limit. The walker (Jolt CharacterVirtual) does gravity,
landing, steps, slopes and following the ground down. Too steep is ground too steep facing the way
the body wanted to go; stepping off an edge is not. ludic.physics: phys_walker_move (follow the body
if something else moved it, then step), the slope limit, the jump always honoured. The character's
tests run on a real Jolt walker over the same fake world: 15 pass, a knee-high boulder now a step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 00:47:14 +03:00
..
ludic.aim wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.anim wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.audio wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.base feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.character feat(character): 16.8 - on land a physics walker moves the body (CharacterGround.walk); the package's own gravity, ballistic air and slope-probe refusal are gone 2026-09-27 00:47:14 +03:00
ludic.clock feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.compass wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.core feat(engine): #90 atlas-aware Sprite component, #91 become from listeners, 0.3.x ergonomics batch 2026-09-04 01:36:08 +03:00
ludic.crafting feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.dungeon wip(0.S3): outside the runtime, its drawable size, scale, screen and tile size are read and set through its functions; a migration reports such a reference instead of threading the runtime's state 2026-09-25 15:47:21 +03:00
ludic.effects feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.fire feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.fishing feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.gameplay wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.gear feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.hints feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.i18n wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.inventory feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.jobs feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.lab wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.minimap wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.nativeecho feat(pkg): phase 15 - a package can carry a native library 2026-09-26 23:35:45 +03:00
ludic.needs feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.net feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.npc feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.npcai refactor(runtime,packages,examples): named constants, package enums, idiom sweep 2026-09-05 01:12:26 +03:00
ludic.photo feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.physics feat(character): 16.8 - on land a physics walker moves the body (CharacterGround.walk); the package's own gravity, ballistic air and slope-probe refusal are gone 2026-09-27 00:47:14 +03:00
ludic.platformer refactor(runtime,packages,examples): named constants, package enums, idiom sweep 2026-09-05 01:12:26 +03:00
ludic.prefs wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.render3d refactor(render3d): phase 16 - the collider store is gone; still things are bodies in ludic.physics 2026-09-27 00:08:36 +03:00
ludic.rpg wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.save feat(packages): ludic.save - versioned save files generic over their content: a version in the file, the steps a game declares into an open registry, a torn write told from a whole one, a backup of only a whole file, a write read back, a newer file refused and read-only 2026-09-25 08:28:30 +03:00
ludic.session feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.settings feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.shooter wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
ludic.shop feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.steps feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.telemetry feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.things feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.tracks feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.ui fix(ui): the pointer in the renderer's pixels - macOS reports it in window points, so on Retina every hit landed at half the cursor's position 2026-09-26 23:31:47 +03:00
ludic.update feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.vehicles feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.wallet feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.weather feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
ludic.wildlife feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
README.md feat(physics): phase 16 - ludic.physics over Jolt Physics v5.6.0 2026-09-26 23:41:37 +03:00

packages

The Ludic packages that ship with the toolchain, found by import "ludic.<name>" from $LUDIC_HOME/packages (or a project's ludic_modules). Each has a README of its own. ludic test packages runs every package's tests, each test block in a process of its own.

The mechanic packages

A mechanic says module ludic_<name> uses ludic_base and so reaches nothing else; it asks the game through ports (bind ClockWorld { ... }), reports through queues of facts, and saves its own section. The rules are in ludic.base.

ludic.base the shared vocabulary: Tick and phases, Queue<T>, rng streams, the save tree, the system runner (Systems, an open registry)
ludic.aim what the crosshair is on: a forgiving ray against cylinders, the ground in front, reach floors, which thing the use key means
ludic.anim glTF animation clips sampled and cross-faded onto a render3d Skin (uses ludic_render3d)
ludic.character a third-person walking body: gait, steps and slopes, a jump, wading, swimming, sitting, a safe put-down, the orbit camera
ludic.audio world sounds with a gain, pan and pitch from a listener port, flat interface sounds, packed clips loaded, the one door to Audio.*
ludic.compass bearing marks on a strip: gathered from registered providers, gated by tier, sorted by priority, bearings, distances and a radar
ludic.clock the hour, the day, the moon and a calendar of seasons
ludic.crafting recipes: what goes in and out, where and how long, making, refunding, hold-to-make
ludic.effects timed modifiers that run down in game time (a meal's warmth, a drink's legs)
ludic.fire a camp fire: fuel, the rain on it, warmth at a distance, whether it will cook
ludic.fishing a rod at the water: cast, bite, the reel's fight, the landing; species as an open registry
ludic.hints a rail of what is true now, most urgent first, each a card that teaches it and can be muted by key
ludic.i18n a game in any language: gettext .po files, patterns with holes, plurals, a mod folder, a font per language
ludic.inventory a pack: a count per kind, with room the game decides
ludic.minimap a map's logic: the explored fog over a grid and its land share, the player's marks, and the view (world to map and back, zoom, pan, the camp, you, focus)
ludic.needs a body's warmth, food, water and energy, and the countdown to a collapse
ludic.npc the other people in a place: a routine by the hour, walking round what is in the way, facing a player who comes near, lines as data, a guest's copy
ludic.physics rigid bodies, removable still shapes, queries and buoyancy over Jolt Physics (a native library, phase 16)
ludic.photo a camera's photographs: what is in the frame, a grade on size, framing, light and the moment, the roll, its worth, the best of each subject
ludic.save versioned save files: a migration chain the game declares, torn writes told apart, a backup, a newer file refused and read-only
ludic.settings a game's settings as data: one store, a fact per change, ranges, a safe set
ludic.shop vendors: prices by standing and weekly demand, stock, buying and selling
ludic.steps chapters of steps (kind, param, need), progress, and a party's shares pooled
ludic.telemetry events queued, batched to a PostHog-shaped endpoint, backed off when offline
ludic.things the placed things of a world and their kinds (an open registry), queries, facts, a save
ludic.tracks prints and sign animals leave, their age, and reading them
ludic.update a Velopack-installed game updating itself a frame at a time
ludic.vehicles each player's boat and horse: called to berths and bays, owner and rider, riding, getting off onto a safe spot
ludic.wallet money carried: earned, spent, lost, every change a fact
ludic.weather spells of weather described as data: odds, wetness, wind, storms, a forecast
ludic.wildlife a valley's animals: ranges, their day, senses and alert, feeding, lures, spawning

Engine and UI

ludic.render3d the physically based 3D renderer over OpenGL and Vulkan (named ludic_render3d under uses)
ludic.ui components: templates, stylesheets, layout, input and focus (module ludic_ui)
ludic.lab the visual lab: a scene on a lit plate, fixed cameras, PNG shots and a contact sheet
ludic.core the canonical engine-ABI components
ludic.prefs a small key=value store for preferences and records
ludic.nativeecho the worked example of a package carrying a native library: a C sum over a slice, callbacks drained as facts

Native libraries

A package may carry a C or C++ library (native lines in its package.ludic, docs/PACKAGES.md). The library is never bound directly: a thin C shim in the package's native/shim/ is, and every shim keeps the same shape so the Ludic side stays safe.

  • Only int, long, float and opaque handles cross. A Ludic float is a C float, a long an int64_t, a pointer a void *. No struct is passed by value, no C++ type, no exception: the shim catches and turns it into a return code.
  • A handle is the library's pointer, kept in the package's state and never exported. The game names what the package hands it - an int id - and the package maps it to the handle.
  • Nothing calls back into Ludic. No function pointer crosses the boundary. What a library reports through a callback (a contact, a finished path, a mixed buffer) the shim records in a buffer of its own, and the package drains it after the call returns (ne_scan then ne_drain), pushing each record onto its facts queue. A record that did not fit is counted, not dropped silently.
  • Bulk data crosses as a slice the package owns. A []float or []int passed where the shim takes a pointer is its data; the package makes it once and reuses it (L7), and passes its length beside it.
  • Errors are return codes. 0 is success and a negative number says what failed; nothing aborts, nothing prints.
  • Threads a library starts stay inside C (a job system, an audio device). The Ludic side is called on the game's thread only.
  • The symbols are prefixed with the package's short name (ne_, jph_), exported explicitly (__attribute__((visibility("default"))), __declspec(dllexport)), and everything else is hidden.

native/build.sh builds lib/<target>/ from the pinned upstream source and the shim through tools/native/lib.sh; run it on the Mac and, in Git Bash, on the PC. The package's tests run the real library - there is no fake for it.

The builtin controllers

ludic.gameplay the shared foundation of the builtin gameplay controllers
ludic.platformer the Platformer controller
ludic.shooter the top-down Shooter controller
ludic.rpg the RPG systems suite
ludic.dungeon arena rooms for a room-to-room roguelite
ludic.npcai NPC AI: perception, decision, action