feat(engine): #90 atlas-aware Sprite component, #91 become from listeners, 0.3.x ergonomics batch
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 32s
ci / build-and-test (push) Successful in 2m49s
commit-lint / conventional-commits (push) Successful in 5s
docs / build-and-deploy (push) Successful in 30s

Closes the two open issues and lands the pending unreleased batch:

- #90: `Sprite { atlas: 1 }` routes esys_sprite through atlas_draw_ex
  (scale/flip/tint), so cell / cell_span / strip ids of any size draw
  through the engine sprite-render system. examples/library/sprite_atlas
  is the pixel-readback regression.
- #91: `become` from an @On(Event) listener / global handler / plain
  function no longer segfaults the compiler; it emits @L_scene_leave()
  (a dispatch on the live scene id) so the leaving scene's on-exit runs.
  UI_* handles are readable from any code (widget table built on first
  use). examples/library/scene_menus covers it.
- fix: a windowed `ludicc -o` build that reaches the audio runtime only
  through the atlas/Assets preload import now links audio.ll +
  AVFoundation (the audio backend link was gated on a game-level
  Audio.* call, so any windowed game declaring Sprite failed to link).
- the hand-written "Unreleased" CHANGELOG section is converted to
  changesets under changes/ so `x release` generates it.
- plus the batch: engine-driven retained UI + UiClicked event, Overlay
  phase, TileSkin tilemap-render system, Key.* constants, Font/Ui/File
  namespaces, Sprite.strip, prefabs, managers, countdown fields,
  enum-typed machines, layer @Queries, ludic.prefs / ludic.dungeon
  packages, Ai.seek pathing, Solids.solid2, cursor confine (mode 3)
  fix, shooter centre-aim fix, reserved-word function diagnostic.

Verified: x test (124/124), x test-tools, check-impl, check-vocabulary,
check-docs, docs-gen + docs-check, bootstrap-cfree (seed is a fixpoint).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-04 01:36:08 +03:00
parent e9c2c51bc3
commit ad548840c7
139 changed files with 58981 additions and 43671 deletions

View file

@ -0,0 +1,7 @@
---
id: file
title: File
order: 44
---
Raw file access over the C stdio calls, for programs that read and write their own formats. <a href="file-open"><code>File.open</code></a> returns a handle (or <code>null</code>), <a href="file-read"><code>File.read</code></a> / <a href="file-write"><code>File.write</code></a> move bytes through a <code>bytes</code> buffer, <a href="file-seek"><code>File.seek</code></a> / <a href="file-tell"><code>File.tell</code></a> position the cursor, and <a href="file-close"><code>File.close</code></a> releases the handle. For text and structured data prefer the higher-level <code>Fs</code>, <code>Json</code> and <code>Prefs</code> APIs.

View file

@ -0,0 +1,19 @@
---
id: file-close
name: File.close
category: file
kind: namespace-method
tokens: File.close
sig: File.close(file: f)
tip: Close a handle from File.open.
order: 6
ns: File
member: close
---
Flushes and releases the handle. Every successful <code>File.open</code> pairs with one <code>File.close</code>.
```ludic
# doc-check: skip — illustrative
File.close(file: f)
```

View file

@ -0,0 +1,20 @@
---
id: file-open
name: File.open
category: file
kind: namespace-method
tokens: File.open
sig: File.open(path: p, mode: m) -> pointer
tip: Open a file (fopen); null when it cannot be opened.
order: 1
ns: File
member: open
---
Opens <code>path</code> with a C <code>fopen</code> mode string (<code>"rb"</code>, <code>"wb"</code>, <code>"ab"</code>). Returns the handle, or <code>null</code> when the file cannot be opened — always test for it.
```ludic
# doc-check: skip — illustrative
let f = File.open(path: "save/best.txt", mode: "rb")
if f == null { return }
```

View file

@ -0,0 +1,20 @@
---
id: file-read
name: File.read
category: file
kind: namespace-method
tokens: File.read
sig: File.read(file: f, buffer: b, count: n) -> int
tip: Read up to n bytes into a buffer; returns the bytes read.
order: 2
ns: File
member: read
---
Reads at most <code>count</code> bytes from the handle into <code>buffer</code> (a <code>bytes</code> allocation) and returns how many arrived; fewer than asked means the end of the file.
```ludic
# doc-check: skip — illustrative
let buf = bytes(size + 1)
let got = File.read(file: f, buffer: buf, count: size)
```

View file

@ -0,0 +1,21 @@
---
id: file-seek
name: File.seek
category: file
kind: namespace-method
tokens: File.seek
sig: File.seek(file: f, offset: o, whence: w)
tip: Move the file cursor (0 start, 1 current, 2 end).
order: 4
ns: File
member: seek
---
Positions the cursor at <code>offset</code> bytes from <code>whence</code>: 0 the start, 1 the current position, 2 the end. Seeking to the end and reading <code>File.tell</code> measures a file.
```ludic
# doc-check: skip — illustrative
File.seek(file: f, offset: 0, whence: 2)
let size = File.tell(file: f)
File.seek(file: f, offset: 0, whence: 0)
```

View file

@ -0,0 +1,19 @@
---
id: file-tell
name: File.tell
category: file
kind: namespace-method
tokens: File.tell
sig: File.tell(file: f) -> int
tip: The cursor position in bytes.
order: 5
ns: File
member: tell
---
Returns the current cursor position in bytes from the start of the file.
```ludic
# doc-check: skip — illustrative
let size = File.tell(file: f)
```

View file

@ -0,0 +1,20 @@
---
id: file-write
name: File.write
category: file
kind: namespace-method
tokens: File.write
sig: File.write(file: f, buffer: b, count: n) -> int
tip: Write n bytes from a buffer; returns the bytes written.
order: 3
ns: File
member: write
---
Writes <code>count</code> bytes of <code>buffer</code> (a string or a <code>bytes</code> allocation) to the handle and returns how many were written.
```ludic
# doc-check: skip — illustrative
let line = "best_floor=3\n"
File.write(file: f, buffer: line, count: len(line))
```