refactor(runtime,packages,examples): named constants, package enums, idiom sweep
- runtime: HEADLESS_FRAME_PATH, STICK_DEADZONE / STICK_LEFT_X/Y, key and byte codes as char literals throughout (`k == 'w'`, `fill(rt_map, ' ', …)`) - ludic.gameplay/stats: drop the duplicate `stat_field` (it answered "atk" for every build stat); Stats.base uses stats_field_name - ludic.shooter: compare aim modes and fire patterns with AimMode.* and WeaponPattern.* instead of raw ints; STICK_RIGHT_X/Y - ludic.npcai: DecisionMade / brain_set_state use AiState.* - examples/games/menu.ludic uses Font.load / Ui.* with FONT_PATH and BACKDROP named; strings.ludic header says what it prints - whole tree: `x = x + 1` → `x += 1` (single-term right-hand sides only), `0 - x` → `-x`, ASCII codes → char literals; every .ludic and every ```ludic fence reformatted with the fixed formatter (whitespace only) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
e3e1bc7784
commit
bf36bc8a8f
103 changed files with 1256 additions and 1252 deletions
|
|
@ -20,6 +20,9 @@
|
|||
# ============================================================================
|
||||
|
||||
# ---- state ----------------------------------------------------------------
|
||||
# where a headless build leaves its last frame (relative to the working directory)
|
||||
const HEADLESS_FRAME_PATH: string = "build/out.ppm"
|
||||
|
||||
var rt_fb: words = null # framebuffer, one i32 (0x00RRGGBB) per pixel
|
||||
var rt_fbw: int = 320
|
||||
var rt_fbh: int = 240
|
||||
|
|
@ -63,7 +66,7 @@ function rt_init() -> void {
|
|||
rt_regs = words(64)
|
||||
fill(rt_regs, 0, 64 * 4)
|
||||
rt_map = bytes(96 * 64)
|
||||
fill(rt_map, 32, 96 * 64)
|
||||
fill(rt_map, ' ', 96 * 64)
|
||||
rt_clip_x1 = rt_fbw
|
||||
rt_clip_y1 = rt_fbh
|
||||
rt_statusbuf = bytes(96)
|
||||
|
|
@ -82,7 +85,7 @@ function rt_shutdown() -> void {
|
|||
win_close()
|
||||
return
|
||||
}
|
||||
rt_dump_ppm("build/out.ppm")
|
||||
rt_dump_ppm(HEADLESS_FRAME_PATH)
|
||||
}
|
||||
|
||||
# ---- framebuffer ----------------------------------------------------------
|
||||
|
|
@ -153,9 +156,9 @@ function rt_fill_rect(x: int, y: int, w: int, h: int, c: int) -> void {
|
|||
while i < x1 {
|
||||
if rt_blend == 1 { rt_fb[row + i] = rt_blend_add(rt_fb[row + i], c) }
|
||||
else { rt_fb[row + i] = c }
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
j = j + 1
|
||||
j += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,16 +172,16 @@ function rt_frame_rect(x: int, y: int, w: int, h: int, c: int) -> void {
|
|||
# A straight line by Bresenham's algorithm — integer only, any direction.
|
||||
function rt_line(x0: int, y0: int, x1: int, y1: int, c: int) -> void {
|
||||
var x = x0; var y = y0
|
||||
let dx = abs(x1 - x0); let dy = 0 - abs(y1 - y0)
|
||||
var sx = 0 - 1; if x0 < x1 { sx = 1 }
|
||||
var sy = 0 - 1; if y0 < y1 { sy = 1 }
|
||||
let dx = abs(x1 - x0); let dy = -abs(y1 - y0)
|
||||
var sx = -1; if x0 < x1 { sx = 1 }
|
||||
var sy = -1; if y0 < y1 { sy = 1 }
|
||||
var err = dx + dy
|
||||
while true {
|
||||
rt_put_px(x, y, c)
|
||||
if (x == x1) and (y == y1) { return }
|
||||
let e2 = 2 * err
|
||||
if e2 >= dy { err = err + dy; x = x + sx }
|
||||
if e2 <= dx { err = err + dx; y = y + sy }
|
||||
if e2 >= dy { err += dy; x += sx }
|
||||
if e2 <= dx { err += dx; y += sy }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,9 +194,9 @@ function rt_circle(cx: int, cy: int, r: int, c: int) -> void {
|
|||
rt_put_px(cx - y, cy + x, c); rt_put_px(cx - x, cy + y, c)
|
||||
rt_put_px(cx - x, cy - y, c); rt_put_px(cx - y, cy - x, c)
|
||||
rt_put_px(cx + y, cy - x, c); rt_put_px(cx + x, cy - y, c)
|
||||
y = y + 1
|
||||
y += 1
|
||||
if err < 0 { err = err + 2 * y + 1 }
|
||||
else { x = x - 1; err = err + 2 * (y - x) + 1 }
|
||||
else { x -= 1; err = err + 2 * (y - x) + 1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,12 +204,12 @@ function rt_circle(cx: int, cy: int, r: int, c: int) -> void {
|
|||
function rt_fill_circle(cx: int, cy: int, r: int, c: int) -> void {
|
||||
if r < 0 { return }
|
||||
let r2 = r * r
|
||||
var dy = 0 - r
|
||||
var dy = -r
|
||||
while dy <= r {
|
||||
var dx = 0
|
||||
while (dx + 1) * (dx + 1) + dy * dy <= r2 { dx = dx + 1 }
|
||||
while (dx + 1) * (dx + 1) + dy * dy <= r2 { dx += 1 }
|
||||
rt_fill_rect(cx - dx, cy + dy, 2 * dx + 1, 1, c)
|
||||
dy = dy + 1
|
||||
dy += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -231,9 +234,9 @@ function rt_fill_triangle(x0: int, y0: int, x1: int, y1: int, x2: int, y2: int,
|
|||
let neg = (d0 < 0) or (d1 < 0) or (d2 < 0)
|
||||
let pos = (d0 > 0) or (d1 > 0) or (d2 > 0)
|
||||
if not (neg and pos) { rt_put_px(px, py, c) }
|
||||
px = px + 1
|
||||
px += 1
|
||||
}
|
||||
py = py + 1
|
||||
py += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -261,18 +264,18 @@ function rt_oval(x: int, y: int, rx: int, ry: int, c: int) -> void {
|
|||
rt_oval_pts(x, y, ex, ey, c)
|
||||
var p = ry2 - rx2 * ry + rx2 / 4 # region 1
|
||||
while px < py {
|
||||
ex = ex + 1
|
||||
px = px + two_ry2
|
||||
ex += 1
|
||||
px += two_ry2
|
||||
if p < 0 { p = p + ry2 + px }
|
||||
else { ey = ey - 1; py = py - two_rx2; p = p + ry2 + px - py }
|
||||
else { ey -= 1; py -= two_rx2; p = p + ry2 + px - py }
|
||||
rt_oval_pts(x, y, ex, ey, c)
|
||||
}
|
||||
p = ry2 * (ex * 2 + 1) * (ex * 2 + 1) / 4 + rx2 * (ey - 1) * (ey - 1) - rx2 * ry2 # region 2
|
||||
while ey > 0 {
|
||||
ey = ey - 1
|
||||
py = py - two_rx2
|
||||
ey -= 1
|
||||
py -= two_rx2
|
||||
if p > 0 { p = p + rx2 - py }
|
||||
else { ex = ex + 1; px = px + two_ry2; p = p + rx2 - py + px }
|
||||
else { ex += 1; px += two_ry2; p = p + rx2 - py + px }
|
||||
rt_oval_pts(x, y, ex, ey, c)
|
||||
}
|
||||
}
|
||||
|
|
@ -291,7 +294,7 @@ function rt_get_px(x: int, y: int) -> int {
|
|||
# glyph (5 wide + 1 gap), matching rt_text's cursor step.
|
||||
function rt_measure_text(text: string) -> int {
|
||||
var i = 0
|
||||
while text[i] != 0 { i = i + 1 }
|
||||
while text[i] != 0 { i += 1 }
|
||||
return i * 6
|
||||
}
|
||||
|
||||
|
|
@ -316,8 +319,8 @@ function rt_camera_follow(x: int, y: int, lerp: fixed) -> void {
|
|||
let ty = y - rt_fbh / 2
|
||||
let sx = fixed(tx - rt_cam_x) * lerp # fixed * fixed, then floor to whole pixels
|
||||
let sy = fixed(ty - rt_cam_y) * lerp
|
||||
rt_cam_x = rt_cam_x + floor(sx)
|
||||
rt_cam_y = rt_cam_y + floor(sy)
|
||||
rt_cam_x += floor(sx)
|
||||
rt_cam_y += floor(sy)
|
||||
}
|
||||
|
||||
# Add a random screen shake of up to +/- amount pixels, drawn from the seeded
|
||||
|
|
@ -325,8 +328,8 @@ function rt_camera_follow(x: int, y: int, lerp: fixed) -> void {
|
|||
# amount <= 0 clears it.
|
||||
function rt_camera_shake(amount: int) -> void {
|
||||
if amount <= 0 { rt_shake_x = 0; rt_shake_y = 0; return }
|
||||
rt_shake_x = rt_rng_range(0 - amount, amount)
|
||||
rt_shake_y = rt_rng_range(0 - amount, amount)
|
||||
rt_shake_x = rt_rng_range(-amount, amount)
|
||||
rt_shake_y = rt_rng_range(-amount, amount)
|
||||
}
|
||||
|
||||
# Camera.shake_for(amount, frames): shake by up to +/- amount pixels for `frames`
|
||||
|
|
@ -340,7 +343,7 @@ function rt_camera_shake_for(amount: int, frames: int) -> void {
|
|||
}
|
||||
function rt_camera_tick() -> void {
|
||||
if rt_shake_left > 0 {
|
||||
rt_shake_left = rt_shake_left - 1
|
||||
rt_shake_left -= 1
|
||||
rt_camera_shake(rt_shake_amount)
|
||||
return
|
||||
}
|
||||
|
|
@ -377,11 +380,11 @@ function rt_present() -> void {
|
|||
# ---- text -----------------------------------------------------------------
|
||||
function rt_glyph(x: int, y: int, ch: int, colour: int, sc: int) -> void {
|
||||
var c = ch
|
||||
if c >= 97 {
|
||||
if c <= 122 { c = c - 32 }
|
||||
if c >= 'a' {
|
||||
if c <= 'z' { c -= 32 }
|
||||
}
|
||||
if c < 32 { return }
|
||||
if c > 90 { return }
|
||||
if c < ' ' { return }
|
||||
if c > 'Z' { return }
|
||||
let base = (c - 32) * 7
|
||||
let font = rt_font()
|
||||
for row in 0 .. 7 {
|
||||
|
|
@ -404,38 +407,38 @@ function rt_text(x: int, y: int, s: string, colour: int, sc: int) -> void {
|
|||
while ch != 0 {
|
||||
rt_glyph(cx, y, ch, colour, sc)
|
||||
cx = cx + 6 * sc
|
||||
i = i + 1
|
||||
i += 1
|
||||
ch = s[i]
|
||||
}
|
||||
}
|
||||
|
||||
function rt_text_int(x: int, y: int, n: int, colour: int, sc: int) -> void {
|
||||
if n == 0 {
|
||||
rt_glyph(x, y, 48, colour, sc)
|
||||
rt_glyph(x, y, '0', colour, sc)
|
||||
return
|
||||
}
|
||||
var v = n
|
||||
var cx = x
|
||||
if v < 0 {
|
||||
rt_glyph(cx, y, 45, colour, sc)
|
||||
rt_glyph(cx, y, '-', colour, sc)
|
||||
cx = cx + 6 * sc
|
||||
v = 0 - v
|
||||
v = -v
|
||||
}
|
||||
var digits = 0
|
||||
var t = v
|
||||
while t > 0 {
|
||||
digits = digits + 1
|
||||
t = t / 10
|
||||
digits += 1
|
||||
t /= 10
|
||||
}
|
||||
var p = digits
|
||||
while p > 0 {
|
||||
var div = 1
|
||||
for k in 1 .. p {
|
||||
div = div * 10
|
||||
div *= 10
|
||||
}
|
||||
rt_glyph(cx, y, 48 + (v / div) % 10, colour, sc)
|
||||
cx = cx + 6 * sc
|
||||
p = p - 1
|
||||
p -= 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -496,7 +499,7 @@ function rt_rng_int(max: int) -> int {
|
|||
# a deterministic +1 or -1
|
||||
function rt_rng_sign() -> int {
|
||||
if rt_rng_chance(50) { return 1 }
|
||||
return 0 - 1
|
||||
return -1
|
||||
}
|
||||
|
||||
# ---- platform: input ------------------------------------------------------
|
||||
|
|
@ -509,7 +512,7 @@ function rt_poll() -> int {
|
|||
rt_alive = 0
|
||||
return 0
|
||||
}
|
||||
if c == 113 { # 'q' quits, as in the headless C platform
|
||||
if c == 'q' { # 'q' quits, as in the headless C platform
|
||||
rt_alive = 0
|
||||
}
|
||||
return c
|
||||
|
|
@ -529,8 +532,8 @@ function rt_put_str(buf: pointer, at: int, s: string) -> int {
|
|||
var ch = s[0]
|
||||
while ch != 0 {
|
||||
buf[n] = ch
|
||||
n = n + 1
|
||||
i = i + 1
|
||||
n += 1
|
||||
i += 1
|
||||
ch = s[i]
|
||||
}
|
||||
return n
|
||||
|
|
@ -538,25 +541,25 @@ function rt_put_str(buf: pointer, at: int, s: string) -> int {
|
|||
|
||||
function rt_put_int(buf: pointer, at: int, v: int) -> int {
|
||||
if v == 0 {
|
||||
buf[at] = 48
|
||||
buf[at] = '0'
|
||||
return at + 1
|
||||
}
|
||||
var digits = 0
|
||||
var t = v
|
||||
while t > 0 {
|
||||
digits = digits + 1
|
||||
t = t / 10
|
||||
digits += 1
|
||||
t /= 10
|
||||
}
|
||||
var n = at
|
||||
var p = digits
|
||||
while p > 0 {
|
||||
var div = 1
|
||||
for k in 1 .. p {
|
||||
div = div * 10
|
||||
div *= 10
|
||||
}
|
||||
buf[n] = 48 + (v / div) % 10
|
||||
n = n + 1
|
||||
p = p - 1
|
||||
n += 1
|
||||
p -= 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
|
@ -604,7 +607,7 @@ var rt_maph: int = 0
|
|||
function rt_map_size(w: int, h: int) -> void {
|
||||
rt_mapw = clamp(w, 0, 96)
|
||||
rt_maph = clamp(h, 0, 64)
|
||||
fill(rt_map, 32, 96 * 64)
|
||||
fill(rt_map, ' ', 96 * 64)
|
||||
}
|
||||
|
||||
function rt_map_row(y: int, s: string) -> void {
|
||||
|
|
@ -615,7 +618,7 @@ function rt_map_row(y: int, s: string) -> void {
|
|||
while ch != 0 {
|
||||
if x >= 96 { return }
|
||||
rt_map[y * 96 + x] = ch
|
||||
x = x + 1
|
||||
x += 1
|
||||
ch = s[x]
|
||||
}
|
||||
}
|
||||
|
|
@ -627,12 +630,12 @@ function rt_map_set(x: int, y: int, glyph: int) -> void {
|
|||
}
|
||||
function rt_map_fill(glyph: int) -> void {
|
||||
var y = 0
|
||||
while y < rt_maph { var x = 0; while x < rt_mapw { rt_map[y * 96 + x] = glyph; x = x + 1 }; y = y + 1 }
|
||||
while y < rt_maph { var x = 0; while x < rt_mapw { rt_map[y * 96 + x] = glyph; x += 1 }; y += 1 }
|
||||
}
|
||||
# every cell of the rectangle (x, y, w, h)
|
||||
function rt_map_rect(x: int, y: int, w: int, h: int, glyph: int) -> void {
|
||||
var yy = y
|
||||
while yy < y + h { var xx = x; while xx < x + w { rt_map_set(xx, yy, glyph); xx = xx + 1 }; yy = yy + 1 }
|
||||
while yy < y + h { var xx = x; while xx < x + w { rt_map_set(xx, yy, glyph); xx += 1 }; yy += 1 }
|
||||
}
|
||||
# the outermost ring of cells
|
||||
function rt_map_border(glyph: int) -> void {
|
||||
|
|
@ -648,11 +651,11 @@ function rt_map_random_cell(glyph: int) -> IVec2 {
|
|||
let x = rt_rng_range(0, rt_mapw - 1)
|
||||
let y = rt_rng_range(0, rt_maph - 1)
|
||||
if rt_tile(x, y) == glyph { return IVec2.make(x, y) }
|
||||
tries = tries + 1
|
||||
tries += 1
|
||||
}
|
||||
var y2 = 0
|
||||
while y2 < rt_maph { var x2 = 0; while x2 < rt_mapw { if rt_tile(x2, y2) == glyph { return IVec2.make(x2, y2) }; x2 = x2 + 1 }; y2 = y2 + 1 }
|
||||
return IVec2.make(0 - 1, 0 - 1)
|
||||
while y2 < rt_maph { var x2 = 0; while x2 < rt_mapw { if rt_tile(x2, y2) == glyph { return IVec2.make(x2, y2) }; x2 += 1 }; y2 += 1 }
|
||||
return IVec2.make(-1, -1)
|
||||
}
|
||||
# a random cell holding `glyph` at least `min_tiles` from `from` (tiles); falls back to any such cell
|
||||
function rt_map_random_cell_far(glyph: int, from: IVec2, min_tiles: int) -> IVec2 {
|
||||
|
|
@ -661,7 +664,7 @@ function rt_map_random_cell_far(glyph: int, from: IVec2, min_tiles: int) -> IVec
|
|||
while tries < 40 {
|
||||
if not IVec2.within(tile, from, min_tiles - 1) { return tile }
|
||||
tile = rt_map_random_cell(glyph)
|
||||
tries = tries + 1
|
||||
tries += 1
|
||||
}
|
||||
return tile
|
||||
}
|
||||
|
|
@ -696,8 +699,8 @@ function rt_ivec_step(degrees: int) -> IVec2 {
|
|||
}
|
||||
function rt_angle_diff_degrees(a: int, b: int) -> int {
|
||||
var d = (b - a) % 360
|
||||
if d > 180 { d = d - 360 }
|
||||
if d <= 0 - 180 { d = d + 360 }
|
||||
if d > 180 { d -= 360 }
|
||||
if d <= -180 { d += 360 }
|
||||
return d
|
||||
}
|
||||
|
||||
|
|
@ -714,18 +717,18 @@ function rt_bar(x: int, y: int, w: int, h: int, value: int, max: int, color: int
|
|||
function rt_rng_weighted(weights: []int) -> int {
|
||||
var total = 0
|
||||
var i = 0
|
||||
while i < len(weights) { if weights[i] > 0 { total = total + weights[i] }; i = i + 1 }
|
||||
if total <= 0 { return 0 - 1 }
|
||||
while i < len(weights) { if weights[i] > 0 { total += weights[i] }; i += 1 }
|
||||
if total <= 0 { return -1 }
|
||||
var roll = rt_rng_range(0, total - 1)
|
||||
i = 0
|
||||
while i < len(weights) {
|
||||
if weights[i] > 0 {
|
||||
if roll < weights[i] { return i }
|
||||
roll = roll - weights[i]
|
||||
roll -= weights[i]
|
||||
}
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
return 0 - 1
|
||||
return -1
|
||||
}
|
||||
|
||||
# List.sample(pool, count): `count` picks from an int slice, distinct while the
|
||||
|
|
@ -735,20 +738,20 @@ function rt_list_sample(pool: []int, count: int) -> []int {
|
|||
let n = len(pool)
|
||||
var i = 0
|
||||
while i < count {
|
||||
if n == 0 { push(out, 0); i = i + 1; continue }
|
||||
if n == 0 { push(out, 0); i += 1; continue }
|
||||
var pick = rt_rng_range(0, n - 1)
|
||||
var distinct = n > i
|
||||
var tries = 0
|
||||
while distinct and (tries < 64) {
|
||||
var seen = false
|
||||
var j = 0
|
||||
while j < len(out) { if out[j] == pool[pick] { seen = true }; j = j + 1 }
|
||||
while j < len(out) { if out[j] == pool[pick] { seen = true }; j += 1 }
|
||||
if not seen { break }
|
||||
pick = rt_rng_range(0, n - 1)
|
||||
tries = tries + 1
|
||||
tries += 1
|
||||
}
|
||||
push(out, pool[pick])
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
@ -773,7 +776,7 @@ function rt_status(s: string) -> void {
|
|||
if i >= 95 { ch = 0 }
|
||||
if ch != 0 {
|
||||
rt_statusbuf[i] = ch
|
||||
i = i + 1
|
||||
i += 1
|
||||
ch = s[i]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue