ludic/packages
2026-09-30 02:29:55 +03:00
..
ludic.aim ludic.character: the look goes up and down to 89 degrees (was -55..35), and the orbit's heading holds when the camera is straight over or under the pivot; ludic.aim: aim_ahead, whether a point is before the view 2026-09-29 12:14:04 +03:00
ludic.anim ludic.anim: animset_choice names the playing choice's clip as the set does, animset_can says whether the rig carries any of a state's clips (a one-shot with none is not asked for) 2026-09-29 20:19:41 +03:00
ludic.audio audio: a volume per channel under the master (effects, wildlife, ambient, interface) - aud_channel_set / aud_emit_on, aud_ui on the interface channel, and Audio.music_volume for the ambience loop on the runtime's music channel 2026-09-29 12:11:29 +03:00
ludic.base ludic.base: bake_maps refuses a first input without {map} (it would test one path for every map: all or none); bake_expand says a {map} row is expanded only with a key from bake_maps 2026-09-29 22:23:44 +03:00
ludic.character ludic.things: a Thing's seats (a run of the game's seat rows, saved; a bit per seat taken, not); ludic.character: char_sit takes the seat's top and its height above the feet, char_sit_leave names where standing up goes 2026-09-29 20:19:41 +03:00
ludic.clock ludic.fishing, net, clock: their dice made once and seeded again, never made again per reset; ludic.minimap's near list kept, its marks and grid declared 2026-09-28 20:13:58 +03:00
ludic.compass @max on the bounded lists: TextRing's slots, the pack's counts and change ring, the net's fact ring, the compass pools, ludic.ui's memo (its 3/4 of MM_CAP), screen pools, answer and pointer rings; ludic.base's intern test holds the table to its cap now that past it is Mem.over 2026-09-28 16:48: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 packages: the editor's schema attributes on every .lres-bound record (@Ref, @OneOf, @Asset, @Unit, @Range, @Color, @Text, @Derived, @Key; @AppendOnly/@ByKey) - annotation only 2026-09-29 18:36:11 +03:00
ludic.devlink ludic.devlink: the interface's verbs (ui_screen, ui_model, ui_tree, ui_override) through a DevlinkUi port 2026-09-29 22:48:34 +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 Merge branch 'lang/memory-burn3' into lang/foundations 2026-09-28 20:13:59 +03:00
ludic.fire ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.fishing packages: the editor's schema attributes on every .lres-bound record (@Ref, @OneOf, @Asset, @Unit, @Range, @Color, @Text, @Derived, @Key; @AppendOnly/@ByKey) - annotation only 2026-09-29 18:36:11 +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 packages: the registry text a game fills is keys (Maroon Lake's phase 26.4) - ThingKind.name, GearKind blurb/howto/names, HintCard's words, JobDef title/text/how, NpcLine and NpcChoice text, WeatherKind.name are @Text Key fields (a compiled row that leaves one out takes its derived key, the English is the game's en.po); what a package hands back as text is the key's marked text, "" for none; NpcName.name, NpcChoice.line and SettingDef.text are not text and lose @Text; ludic.i18n's tr/trf/trn/key_text say @alloc_ok (tr is a cast); the packages' tests with key literals (built, not run); changes/text-keys-data.md 2026-09-30 00:29:03 +03:00
ludic.hints packages: the registry text a game fills is keys (Maroon Lake's phase 26.4) - ThingKind.name, GearKind blurb/howto/names, HintCard's words, JobDef title/text/how, NpcLine and NpcChoice text, WeatherKind.name are @Text Key fields (a compiled row that leaves one out takes its derived key, the English is the game's en.po); what a package hands back as text is the key's marked text, "" for none; NpcName.name, NpcChoice.line and SettingDef.text are not text and lose @Text; ludic.i18n's tr/trf/trn/key_text say @alloc_ok (tr is a cast); the packages' tests with key literals (built, not run); changes/text-keys-data.md 2026-09-30 00:29:03 +03:00
ludic.i18n ludic.i18n: inside a bracket an argument's bytes ride raw - trf escapes a 27 or 31 only outside brackets, and the decoder unescapes only there, so a bracketed line carrying a key with holes (Ada's brief: its legend line's escaped arguments) keeps its own escapes whichever way it was put in (a game's txt_key raw, or trf). 81e1f7d8 unescaped inside the bracket and split the legend's arguments out of the brief. nest_test holds a key whose hole holds a key with two holes: bare, bracketed raw, bracketed through trf, and escaped. 2026-09-30 02:00:20 +03:00
ludic.inventory ludic.ui: a select matches a number as a number (its text was made every frame per select); the fired-event queue's lists made with room; ludic.inventory: the counts made at full length at once 2026-09-28 21:09:08 +03:00
ludic.jobs packages: the registry text a game fills is keys (Maroon Lake's phase 26.4) - ThingKind.name, GearKind blurb/howto/names, HintCard's words, JobDef title/text/how, NpcLine and NpcChoice text, WeatherKind.name are @Text Key fields (a compiled row that leaves one out takes its derived key, the English is the game's en.po); what a package hands back as text is the key's marked text, "" for none; NpcName.name, NpcChoice.line and SettingDef.text are not text and lose @Text; ludic.i18n's tr/trf/trn/key_text say @alloc_ok (tr is a cast); the packages' tests with key literals (built, not run); changes/text-keys-data.md 2026-09-30 00:29:03 +03:00
ludic.lab ludic.lab: 16-bit PNGs (lab_png_write16_from), so a test can write a height map render3d reads back as R16 2026-09-29 22:08:22 +03:00
ludic.minimap ludic.minimap: minimap_seen_text writes the explored grid into bytes the state keeps (a string per cell before, every save) 2026-09-28 22:33:49 +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 nav: nav_tiles_keep_near - a file-backed mesh's tiles kept by a distance the game answers (its players' zones, ludic.zones' zones_nearest): in within pad of it, out only past pad + hyst; the tile counters in resident_count.ludic 2026-09-29 15:43:22 +03:00
ludic.needs ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.net ludic.fishing, net, clock: their dice made once and seeded again, never made again per reset; ludic.minimap's near list kept, its marks and grid declared 2026-09-28 20:13:58 +03:00
ludic.npc packages: the registry text a game fills is keys (Maroon Lake's phase 26.4) - ThingKind.name, GearKind blurb/howto/names, HintCard's words, JobDef title/text/how, NpcLine and NpcChoice text, WeatherKind.name are @Text Key fields (a compiled row that leaves one out takes its derived key, the English is the game's en.po); what a package hands back as text is the key's marked text, "" for none; NpcName.name, NpcChoice.line and SettingDef.text are not text and lose @Text; ludic.i18n's tr/trf/trn/key_text say @alloc_ok (tr is a cast); the packages' tests with key literals (built, not run); changes/text-keys-data.md 2026-09-30 00:29:03 +03:00
ludic.npcai refactor(runtime,packages,examples): named constants, package enums, idiom sweep 2026-09-05 01:12:26 +03:00
ludic.photo photo: a frame's tags written into a kept buffer and interned, one string per distinct text - a viewfinder composes every frame, and each tag concatenated a new text the frame dropped 2026-09-29 13:29:00 +03:00
ludic.physics checker: a function named like a built-in a call always takes is refused at its declaration - function words(st, k) compiled and every call became words(n) with a pointer for n (invalid IR, far from the cause); the table is selfhost/check/check_builtins.ludic, emit_call's built-ins no find_fn guard lets a declared function take, and ludic-dev syntax --check holds it to emit_call both ways; every other built-in (buffer, floats, double, ...) yields to a declared function in the checker as it already did in codegen. string_temps' keep and cross_heap_test's keep renamed (hand, keep_cell); rejected/builtin_call_name and functions/builtin_yield with their cases (added, not run); reseeded, bootstrap-cfree fixpoint holds 2026-09-30 01:10:24 +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: terrain_height_file - terrain_height's exact bilinear that never pages: a resident tile is read in place, any other from tt_file into four tiles of scratch of its own (in the state's defaults, forgotten when the file closes), so no slot is taken and no recency bumped - a sweep at boot no longer changes which tiles the cache holds, and so what is drawn from it. With the whole copy it is terrain_height. tests/height_file_test: equal to terrain_height over a grid across many tiles, resident and not, at tile corners and past the edge; a whole-map sweep leaves slot_of, tile_in, ref, the heights, the hand and the read counts unchanged 2026-09-30 02:29:55 +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 play 2's frame keeps and a dropped file: a var() join made at once, the hint rail emptied in place, save_read gives its text back 2026-09-29 13:33:54 +03:00
ludic.session ludic.session: ply_rest_least, the party's rest for the clock (the valley's selftest57 down into the packages): the roster's freed slot reused and ply_leave_all, the nearest player alone, and ludic.shop's week of demand leaving the shared Random stream untouched 2026-09-29 22:51:01 +03:00
ludic.settings packages: the registry text a game fills is keys (Maroon Lake's phase 26.4) - ThingKind.name, GearKind blurb/howto/names, HintCard's words, JobDef title/text/how, NpcLine and NpcChoice text, WeatherKind.name are @Text Key fields (a compiled row that leaves one out takes its derived key, the English is the game's en.po); what a package hands back as text is the key's marked text, "" for none; NpcName.name, NpcChoice.line and SettingDef.text are not text and lose @Text; ludic.i18n's tr/trf/trn/key_text say @alloc_ok (tr is a cast); the packages' tests with key literals (built, not run); changes/text-keys-data.md 2026-09-30 00:29:03 +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 ludic.session: ply_rest_least, the party's rest for the clock (the valley's selftest57 down into the packages): the roster's freed slot reused and ply_leave_all, the nearest player alone, and ludic.shop's week of demand leaving the shared Random stream untouched 2026-09-29 22:51:01 +03:00
ludic.steps packages: the editor's schema attributes on every .lres-bound record (@Ref, @OneOf, @Asset, @Unit, @Range, @Color, @Text, @Derived, @Key; @AppendOnly/@ByKey) - annotation only 2026-09-29 18:36:11 +03:00
ludic.telemetry fix(allocs): ludic.update, steps, effects and telemetry at 0 frame allocs and 0 keeps 2026-09-28 20:13:06 +03:00
ludic.things packages: the registry text a game fills is keys (Maroon Lake's phase 26.4) - ThingKind.name, GearKind blurb/howto/names, HintCard's words, JobDef title/text/how, NpcLine and NpcChoice text, WeatherKind.name are @Text Key fields (a compiled row that leaves one out takes its derived key, the English is the game's en.po); what a package hands back as text is the key's marked text, "" for none; NpcName.name, NpcChoice.line and SettingDef.text are not text and lose @Text; ludic.i18n's tr/trf/trn/key_text say @alloc_ok (tr is a cast); the packages' tests with key literals (built, not run); changes/text-keys-data.md 2026-09-30 00:29:03 +03:00
ludic.tracks ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.ui ludic.ui: t(key, holes...) in a template - the key marked (byte 1, a marked key kept as it is) and each hole's value after a byte 31, the text the translator decodes at draw time; joined through the text memo, so nothing is made while it reads the same (phase 26) 2026-09-29 23:16:05 +03:00
ludic.update ludic.update: update_notes answers only for the language update_notes_for kept (none for another), and the tests follow the contract 3ac1179 set - the notes are kept by update_notes_for and whether this copy is installed is worked out when the updater is configured; they read the old per-call answers and failed 2 of 12 2026-09-29 21:02:21 +03:00
ludic.vehicles vehicles: a kind's numbers are a VehicleSpec the game hands over (vehicles_spec), not constants 2026-09-29 20:22:02 +03:00
ludic.wallet ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.weather packages: the registry text a game fills is keys (Maroon Lake's phase 26.4) - ThingKind.name, GearKind blurb/howto/names, HintCard's words, JobDef title/text/how, NpcLine and NpcChoice text, WeatherKind.name are @Text Key fields (a compiled row that leaves one out takes its derived key, the English is the game's en.po); what a package hands back as text is the key's marked text, "" for none; NpcName.name, NpcChoice.line and SettingDef.text are not text and lose @Text; ludic.i18n's tr/trf/trn/key_text say @alloc_ok (tr is a cast); the packages' tests with key literals (built, not run); changes/text-keys-data.md 2026-09-30 00:29:03 +03:00
ludic.wildlife ludic.wildlife: painted habitats and ranges through the port (habitat_in, range_at), the rule where none is painted 2026-09-29 20:35:13 +03:00
ludic.zones ludic.zones: a player's radius never past no fog's 900 m 2026-09-29 16:42:43 +03:00
README.md ludic.devlink: an editor's live link into a running dev build (protocol v1, stage 1) 2026-09-29 19:09:59 +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); ozz-animation underneath as a native library (phase 19)
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.zones where the world is simulated: a disc per player from their fog, merged into clusters
ludic.devlink an editor's live link into a running dev build: loopback UDP, one request a frame, verbs as ports
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