ludic/packages
Orkuncakilkaya de85f201ad render3d: gltf_cached - a still model loaded once by dir, file and node
A game placing props while it plays (a sign an animal leaves, a fish, a bobber) loaded each through
gltf_load, which reads the file and parses its document every time. gltf_cached finds the same
(dir, file, node) by comparing the names in place and hands back the model the first load made;
a model whose document is read after the load (ludic.anim's clips) still goes through gltf_load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:39:41 +03:00
..
ludic.aim feat(senses): walkers slide round still things, and lines of sight are the world's to answer 2026-09-27 00:59:39 +03:00
ludic.anim refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +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 ludic.base: StrBuf and StrTable - a line written into a kept buffer and interned, one string per distinct text 2026-09-28 11:04:07 +03:00
ludic.character ludic.physics, ludic.character, ludic.vehicles: fact records reused, and phys_sink allocates nothing 2026-09-28 02:03:09 +03:00
ludic.clock refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
ludic.compass docs(compass): what a capture hands back lasts until the next one 2026-09-28 00:58:53 +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 refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +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 fix(leaks): effects_live_into / effects_live_count and hints_rail_into - a HUD's read-outs into lists it keeps 2026-09-28 01:21:53 +03:00
ludic.fire refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
ludic.fishing refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
ludic.gameplay refactor(packages): 0.R5 - the rest of the lazy starts are defaults - hints' rail (sized by hints_config), lab's CRC table, rpg's crafting / items / inventory / quests / dialog, shooter's weapons, prefs, and npc's and gameplay's empty ensures gone; gameplay's faction table sits beside its state, since a default names only what is declared before it; --tighten over their tests and examples 2026-09-27 03:07:29 +03:00
ludic.gear refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
ludic.hints fix(leaks): effects_live_into / effects_live_count and hints_rail_into - a HUD's read-outs into lists it keeps 2026-09-28 01:21:53 +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 refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
ludic.jobs feat(jobs): phase 3 - a job can be given up: jobs_give_up puts a written job back on offer from the next day (the day back is saved with it, so giving up is never a reroll), jobs_post_give_up takes a post down for the morning to replace; an auto board's posts are not taken and not given up; JOBS_F_GIVEN_UP tells the game to take down what it placed 2026-09-27 15:28:06 +03:00
ludic.lab feat(render3d): 23.3 - a .dds beside a .png is uploaded BC-compressed with its whole mip chain 2026-09-28 01:23:45 +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): windows-x64 builds of ludic.physics (Jolt v5.6.0) and ludic.nativeecho, built on the PC by their native/build.sh 2026-09-27 13:29:55 +03:00
ludic.nav ludic.nav (23.5b): a baked mesh resident by chunk 2026-09-28 11:07:27 +03:00
ludic.needs refactor(packages): 0.R5 - the shop's week is rolled by its tick and asking reads it; needs start filled and vehicles start with their list; character, physics, vehicles and nativeecho's lazy starts are defaults; --tighten over all four 2026-09-27 13:25:57 +03:00
ludic.net refactor(packages): 0.R5 - jobs, session, settings, hints and net make their tables when the state is made, not on the first question (jobs__ensure, session__init and ply_init, sg_init, hn_mute_ensure, net__init gone); their questions read 2026-09-27 01:29:32 +03:00
ludic.npc ludic.base, ludic.wildlife, ludic.npc: fact records reused, not made per fact 2026-09-28 01:58:19 +03:00
ludic.npcai refactor(runtime,packages,examples): named constants, package enums, idiom sweep 2026-09-05 01:12:26 +03:00
ludic.photo merge lang/physics-senses: animals and people slide round still things, the aim's, photo's and animals' sight lines are one Jolt ray 2026-09-27 01:23:17 +03:00
ludic.physics ludic.physics, ludic.character, ludic.vehicles: fact records reused, and phys_sink allocates nothing 2026-09-28 02:03:09 +03:00
ludic.platformer refactor(runtime,packages,examples): named constants, package enums, idiom sweep 2026-09-05 01:12:26 +03:00
ludic.prefs refactor(packages): 0.R5 - the rest of the lazy starts are defaults - hints' rail (sized by hints_config), lab's CRC table, rpg's crafting / items / inventory / quests / dialog, shooter's weapons, prefs, and npc's and gameplay's empty ensures gone; gameplay's faction table sits beside its state, since a default names only what is declared before it; --tighten over their tests and examples 2026-09-27 03:07:29 +03:00
ludic.render3d render3d: gltf_cached - a still model loaded once by dir, file and node 2026-09-28 11:39:41 +03:00
ludic.rpg refactor(packages): 0.R5 - the rest of the lazy starts are defaults - hints' rail (sized by hints_config), lab's CRC table, rpg's crafting / items / inventory / quests / dialog, shooter's weapons, prefs, and npc's and gameplay's empty ensures gone; gameplay's faction table sits beside its state, since a default names only what is declared before it; --tighten over their tests and examples 2026-09-27 03:07:29 +03:00
ludic.save ludic.save, ludic.telemetry: a parsed tree they refuse is freed (23.2) 2026-09-27 22:51:06 +03:00
ludic.session refactor(packages): 0.R5 - jobs, session, settings, hints and net make their tables when the state is made, not on the first question (jobs__ensure, session__init and ply_init, sg_init, hn_mute_ensure, net__init gone); their questions read 2026-09-27 01:29:32 +03:00
ludic.settings refactor(packages): 0.R5 - jobs, session, settings, hints and net make their tables when the state is made, not on the first question (jobs__ensure, session__init and ply_init, sg_init, hn_mute_ensure, net__init gone); their questions read 2026-09-27 01:29:32 +03:00
ludic.shooter refactor(packages): 0.R5 - the rest of the lazy starts are defaults - hints' rail (sized by hints_config), lab's CRC table, rpg's crafting / items / inventory / quests / dialog, shooter's weapons, prefs, and npc's and gameplay's empty ensures gone; gameplay's faction table sits beside its state, since a default names only what is declared before it; --tighten over their tests and examples 2026-09-27 03:07:29 +03:00
ludic.shop refactor(packages): 0.R5 - the shop's week is rolled by its tick and asking reads it; needs start filled and vehicles start with their list; character, physics, vehicles and nativeecho's lazy starts are defaults; --tighten over all four 2026-09-27 13:25:57 +03:00
ludic.steps refactor(steps): 0.R5 - a seat's share is a question (steps__share): seat 0 read live, a teammate's column as sent for this chapter; only steps_share, the verb, clears a stale table - steps_met, steps_each, steps_lacking, steps_my_shares and steps_share_of read 2026-09-27 15:06:33 +03:00
ludic.telemetry ludic.save, ludic.telemetry: a parsed tree they refuse is freed (23.2) 2026-09-27 22:51:06 +03:00
ludic.things feat(ecs): thing_nearest_within - the nearest of a kind strictly within a radius, from the grid or the kind's list 2026-09-27 22:25:34 +03:00
ludic.tracks wip(tracks): phase 5 - a print names its maker, and a read print points at the next one (the chain); goes with maroon-lake phase-5 2026-09-27 21:31:14 +03:00
ludic.ui ludic.ui: a float in a text's hole written to one decimal from kept texts (its whole part from mm_int, its tenth a literal) - a bar's width moving every frame made a new string every frame, and read 7.470598e-07% 2026-09-28 11:29:11 +03:00
ludic.update refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
ludic.vehicles ludic.physics, ludic.character, ludic.vehicles: fact records reused, and phys_sink allocates nothing 2026-09-28 02:03:09 +03:00
ludic.wallet refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
ludic.weather refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
ludic.wildlife ludic.base, ludic.wildlife, ludic.npc: fact records reused, not made per fact 2026-09-28 01:58:19 +03:00
README.md feat(nav): 17.1/17.3 - ludic.nav over Recast & Detour v1.6.0 (zlib), built here from the pinned tag on the Mac and the PC (the DLL imports KERNEL32 alone): a navmesh per kind of walker from triangles with an area byte each and cylinders nothing stands in, saved and loaded as bytes; the nearest point, a path as corners (partial when the end cannot be reached), whether a straight line stays walkable, a cost per kind of ground. Tests on a hand-built meadow: round a post, over a ford, stopped at a bank, a saved mesh answering alike - Mac and PC 2026-09-27 15:29:11 +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.nav a navmesh per kind of walker and the ways across it, over Recast & Detour (a native library, phase 17)
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