From a11710fbc9293129ae976d3a54c3af1039f4ccf8 Mon Sep 17 00:00:00 2001 From: adrien Date: Fri, 10 Apr 2026 10:45:25 +0200 Subject: [PATCH] CHanged into a multi files NodeKind, one per file. And added a models.zig --- src/Engine.zig | 26 ++-------- src/Node.zig | 126 +++++++++++++++++---------------------------- src/main.zig | 22 ++++---- src/models.zig | 46 +++++++++++++++++ src/nodes/Base.zig | 23 +++++++++ src/nodes/Rect.zig | 43 ++++++++++++++++ 6 files changed, 175 insertions(+), 111 deletions(-) create mode 100644 src/models.zig create mode 100644 src/nodes/Base.zig create mode 100644 src/nodes/Rect.zig diff --git a/src/Engine.zig b/src/Engine.zig index 56818b6..e57cf87 100644 --- a/src/Engine.zig +++ b/src/Engine.zig @@ -1,11 +1,11 @@ const std = @import("std"); const rl = @import("raylib"); -const Node = @import("Node.zig"); +const md = @import("models.zig"); const Engine = @This(); allocator: std.mem.Allocator, -nodes: std.ArrayList(Node) = .{}, +nodes: std.ArrayList(md.Node) = .{}, pub fn init(allocator: std.mem.Allocator) !Engine { return .{ @@ -18,29 +18,11 @@ pub fn deinit(self: *Engine) void { self.nodes.deinit(self.allocator); } -pub fn addNode(self: *Engine, name: []const u8, script: []const u8) !void { - const node = try Node.init(self.allocator, name, .base, script); +pub fn addNode(self: *Engine, name: []const u8, script: []const u8, kind: md.Node.NodeKind) !void { + const node = try md.Node.init(self.allocator, name, kind, script); try self.nodes.append(self.allocator, node); } -/// A rectangle node. Engine auto-draws the rect from Zig properties. -/// Script receives a RectNode self and can change width/height/color. -pub fn addRectNode( - self: *Engine, - name: []const u8, - width: f32, - height: f32, - color: rl.Color, - script: []const u8, -) !void { - const kind: Node.NodeKind = .{ .rectangle = .{ - .width = width, - .height = height, - .color = color, - } }; - try self.nodes.append(self.allocator, try Node.init(self.allocator, name, kind, script)); -} - /// Poll every node's file watcher; reload changed scripts. pub fn checkReloads(self: *Engine) !void { for (self.nodes.items) |*node| { diff --git a/src/Node.zig b/src/Node.zig index 89d8cfa..d3de409 100644 --- a/src/Node.zig +++ b/src/Node.zig @@ -1,9 +1,8 @@ const std = @import("std"); const rl = @import("raylib"); const zlua = @import("zlua"); +const md = @import("models.zig"); const Lua = zlua.Lua; -const lua_api = @import("lua_api.zig"); -const FileWatcher = @import("FileWatcher.zig"); pub const RectData = struct { width: f32 = 50, @@ -12,9 +11,8 @@ pub const RectData = struct { }; pub const NodeKind = union(enum) { - base: void, - rectangle: RectData, - // extend here: sprite, audio_emitter, tilemap, ... + base: @import("nodes/Base.zig"), + rectangle: @import("nodes/Rect.zig"), }; /// A scene node with an attached Lua script. @@ -25,14 +23,15 @@ allocator: std.mem.Allocator, name: []const u8, script_path: []const u8, lua: *Lua, -watcher: FileWatcher, +watcher: md.FileWatcher, /// Lua registry key for the persistent self table. /// Survives hot-reloads so script-side state is preserved. +/// FIXME: Not working, state is lost self_ref: i32 = 0, x: f32 = 400, y: f32 = 300, -kind: NodeKind = .base, +kind: NodeKind = .{ .base = .{} }, pub fn init( allocator: std.mem.Allocator, @@ -42,7 +41,7 @@ pub fn init( ) !Node { const lua = try Lua.init(allocator); lua.openLibs(); - lua_api.registerAll(lua); + md.lua_api.registerAll(lua); var node = Node{ .allocator = allocator, @@ -50,7 +49,7 @@ pub fn init( .kind = kind, .script_path = script_path, .lua = lua, - .watcher = FileWatcher.init(script_path), + .watcher = .init(script_path), }; try node.createSelfTable(); try node.loadScript(); @@ -72,7 +71,7 @@ pub fn loadScript(self: *Node) !void { const msg = self.lua.toString(-1) catch "?"; std.log.err("[{s}] {s} ({})", .{ self.script_path, msg, err }); self.lua.pop(1); - return; // keep old functions running — don't crash on syntax error + return; }; self.syncToLua(); @@ -87,6 +86,19 @@ pub fn update(self: *Node, dt: f32) void { self.lua.pop(1); return; } + + switch (self.kind) { + inline else => |*p| { + const T = @TypeOf(p.*); + const info = @typeInfo(T); + switch (info) { + .@"struct" => if (std.meta.hasFn(T, "update")) + p.update(self), + else => {}, + } + }, + } + _ = self.lua.rawGetIndex(zlua.registry_index, self.self_ref); self.lua.pushNumber(@floatCast(dt)); self.lua.protectedCall(.{ .args = 2 }) catch |err| self.logErr("on_update", err); @@ -94,19 +106,19 @@ pub fn update(self: *Node, dt: f32) void { } pub fn render(self: *Node) void { - // 1. Automatic draw from Zig-side properties + // Comptime check that the NodeKind has a function render then use it switch (self.kind) { - .rectangle => |r| { - rl.drawRectangle( - @intFromFloat(self.x), - @intFromFloat(self.y), - @intFromFloat(r.width), - @intFromFloat(r.height), - r.color, - ); + inline else => |*p| { + const T = @TypeOf(p.*); + const info = @typeInfo(T); + switch (info) { + .@"struct" => if (std.meta.hasFn(T, "render")) + p.render(self), + else => {}, + } }, - .base => {}, } + // 2. Script overlay (labels, effects, custom shapes, etc.) self.syncToLua(); self.callWithSelf("on_render"); @@ -126,17 +138,16 @@ fn syncToLua(self: *Node) void { _ = self.lua.rawGetIndex(zlua.registry_index, self.self_ref); // self table is now at stack -1 - setNum(self.lua, "x", self.x); - setNum(self.lua, "y", self.y); + md.setNum(self.lua, "x", self.x); + md.setNum(self.lua, "y", self.y); + // Comptime check that the NodeKind has a function render then use it switch (self.kind) { - .rectangle => |r| { - setNum(self.lua, "width", r.width); - setNum(self.lua, "height", r.height); - pushColorTable(self.lua, r.color); // pushes {r,g,b,a} at -1 - self.lua.setField(-2, "color"); // self_tbl.color = color_tbl; pops color_tbl + inline else => |*p| { + const T = @TypeOf(p.*); + if (std.meta.hasFn(T, "syncToLua")) + p.syncToLua(self.lua); }, - .base => {}, } self.lua.pop(1); // pop self table @@ -147,30 +158,21 @@ fn syncFromLua(self: *Node) void { _ = self.lua.rawGetIndex(zlua.registry_index, self.self_ref); // self table at -1 - self.x = getNum(self.lua, "x", self.x); - self.y = getNum(self.lua, "y", self.y); + self.x = md.getNum(self.lua, "x", self.x); + self.y = md.getNum(self.lua, "y", self.y); + // Comptime check that the NodeKind has a function then use it switch (self.kind) { - .rectangle => |*r| { - r.width = getNum(self.lua, "width", r.width); - r.height = getNum(self.lua, "height", r.height); - _ = self.lua.getField(-1, "color"); // stack: [..., self_tbl, color_tbl] - if (self.lua.typeOf(-1) == .table) { - r.color.r = getU8(self.lua, "r", r.color.r); - r.color.g = getU8(self.lua, "g", r.color.g); - r.color.b = getU8(self.lua, "b", r.color.b); - r.color.a = getU8(self.lua, "a", r.color.a); - } - self.lua.pop(1); // pop color_tbl (or nil) + inline else => |*p| { + const T = @TypeOf(p.*); + if (std.meta.hasFn(T, "syncFromLua")) + p.syncFromLua(self.lua); }, - .base => {}, } self.lua.pop(1); // pop self table } -// ── Callback helpers ────────────────────────────────────────────────────── - /// Call lua_fn_name(self) — no-op if the function is not defined. fn callWithSelf(self: *Node, name: [:0]const u8) void { const t = self.lua.getGlobal(name) catch return; @@ -187,39 +189,3 @@ fn logErr(self: *Node, ctx: []const u8, err: anyerror) void { std.log.err("[{s}] {s}: {s} ({})", .{ self.script_path, ctx, msg, err }); self.lua.pop(1); } - -/// Set a number field on the table currently at stack -1. -fn setNum(lua: *Lua, key: [:0]const u8, val: f32) void { - lua.pushNumber(@floatCast(val)); - lua.setField(-2, key); // pops num, sets table[-2][key] = num -} - -/// Get a number field from the table currently at stack -1. -fn getNum(lua: *Lua, key: [:0]const u8, default: f32) f32 { - _ = lua.getField(-1, key); - const v = lua.toNumber(-1) catch @as(f64, @floatCast(default)); - lua.pop(1); - return @floatCast(v); -} - -/// Get a u8 color component (0-255) from the table currently at stack -1. -fn getU8(lua: *Lua, key: [:0]const u8, default: u8) u8 { - _ = lua.getField(-1, key); - const v = lua.toNumber(-1) catch @as(f64, @floatFromInt(default)); - lua.pop(1); - return @intFromFloat(std.math.clamp(v, 0, 255)); -} - -/// Push a new {r, g, b, a} table onto the stack. -fn pushColorTable(lua: *Lua, c: rl.Color) void { - lua.newTable(); - lua.pushNumber(@floatFromInt(c.r)); - lua.setField(-2, "r"); - lua.pushNumber(@floatFromInt(c.g)); - lua.setField(-2, "g"); - lua.pushNumber(@floatFromInt(c.b)); - lua.setField(-2, "b"); - lua.pushNumber(@floatFromInt(c.a)); - lua.setField(-2, "a"); - // color table is now at stack top — caller sets it as a field -} diff --git a/src/main.zig b/src/main.zig index 0325932..ffd6ff1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -15,20 +15,24 @@ pub fn main() !void { defer engine.deinit(); // Attach Lua scripts to nodes — add as many as you like - try engine.addNode("Player", "scripts/player.lua"); - try engine.addRectNode( + try engine.addNode("Player", "scripts/player.lua", .{ .base = .{} }); + try engine.addNode( "HealthBar", - 200, - 20, - .{ .r = 60, .g = 200, .b = 80, .a = 255 }, "scripts/healthbar.lua", + .{ .rectangle = .{ + .width = 200, + .height = 20, + .color = .{ .r = 60, .g = 200, .b = 80, .a = 255 }, + } }, ); - try engine.addRectNode( + try engine.addNode( "Enemy", - 55, - 55, - .{ .r = 220, .g = 60, .b = 60, .a = 255 }, "scripts/enemy_rect.lua", + .{ .rectangle = .{ + .width = 55, + .height = 55, + .color = .{ .r = 220, .g = 60, .b = 60, .a = 255 }, + } }, ); while (!rl.windowShouldClose()) { diff --git a/src/models.zig b/src/models.zig new file mode 100644 index 0000000..c0fc014 --- /dev/null +++ b/src/models.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +const rl = @import("raylib"); +const zlua = @import("zlua"); +pub const Lua = zlua.Lua; + +pub const lua_api = @import("lua_api.zig"); + +pub const Node = @import("Node.zig"); +pub const FileWatcher = @import("FileWatcher.zig"); + +// ── Callback helpers ────────────────────────────────────────────────────── + +/// Set a number field on the table currently at stack -1. +pub fn setNum(lua: *zlua.Lua, key: [:0]const u8, val: f32) void { + lua.pushNumber(@floatCast(val)); + lua.setField(-2, key); // pops num, sets table[-2][key] = num +} + +/// Get a number field from the table currently at stack -1. +pub fn getNum(lua: *zlua.Lua, key: [:0]const u8, default: f32) f32 { + _ = lua.getField(-1, key); + const v = lua.toNumber(-1) catch @as(f64, @floatCast(default)); + lua.pop(1); + return @floatCast(v); +} + +/// Get a u8 color component (0-255) from the table currently at stack -1. +pub fn getU8(lua: *zlua.Lua, key: [:0]const u8, default: u8) u8 { + _ = lua.getField(-1, key); + const v = lua.toNumber(-1) catch @as(f64, @floatFromInt(default)); + lua.pop(1); + return @intFromFloat(std.math.clamp(v, 0, 255)); +} + +/// Push a new {r, g, b, a} table onto the stack. +pub fn pushColorTable(lua: *zlua.Lua, c: rl.Color) void { + lua.newTable(); + lua.pushNumber(@floatFromInt(c.r)); + lua.setField(-2, "r"); + lua.pushNumber(@floatFromInt(c.g)); + lua.setField(-2, "g"); + lua.pushNumber(@floatFromInt(c.b)); + lua.setField(-2, "b"); + lua.pushNumber(@floatFromInt(c.a)); + lua.setField(-2, "a"); +} diff --git a/src/nodes/Base.zig b/src/nodes/Base.zig new file mode 100644 index 0000000..ba643c0 --- /dev/null +++ b/src/nodes/Base.zig @@ -0,0 +1,23 @@ +const rl = @import("raylib"); +const md = @import("../models.zig"); + +pub fn render(self: *@This(), node: *md.Node) void { + _ = self; + _ = node; +} + +pub fn update(self: *@This(), node: *md.Node) void { + _ = self; + _ = node; +} + +pub fn syncToLua(self: *@This(), lua: *md.Lua) void { + _ = self; + _ = lua; +} + +/// Read back Lua self table → Zig state after any callback. +pub fn syncFromLua(self: *@This(), lua: *md.Lua) void { + _ = self; + _ = lua; +} diff --git a/src/nodes/Rect.zig b/src/nodes/Rect.zig new file mode 100644 index 0000000..1368aaf --- /dev/null +++ b/src/nodes/Rect.zig @@ -0,0 +1,43 @@ +const std = @import("std"); +const rl = @import("raylib"); +const md = @import("../models.zig"); + +width: f32 = 50, +height: f32 = 50, +color: rl.Color = rl.Color.white, + +pub fn render(self: *const @This(), node: *const md.Node) void { + rl.drawRectangle( + @intFromFloat(node.x), + @intFromFloat(node.y), + @intFromFloat(self.width), + @intFromFloat(self.height), + self.color, + ); +} + +pub fn update(self: *@This(), node: *md.Node) void { + _ = self; + _ = node; +} + +pub fn syncToLua(self: *@This(), lua: *md.Lua) void { + md.setNum(lua, "width", self.width); + md.setNum(lua, "height", self.height); + md.pushColorTable(lua, self.color); + lua.setField(-2, "color"); +} + +/// Read back Lua self table → Zig state after any callback. +pub fn syncFromLua(self: *@This(), lua: *md.Lua) void { + self.width = md.getNum(lua, "width", self.width); + self.height = md.getNum(lua, "height", self.height); + _ = lua.getField(-1, "color"); // stack: [..., self_tbl, color_tbl] + if (lua.typeOf(-1) == .table) { + self.color.r = md.getU8(lua, "r", self.color.r); + self.color.g = md.getU8(lua, "g", self.color.g); + self.color.b = md.getU8(lua, "b", self.color.b); + self.color.a = md.getU8(lua, "a", self.color.a); + } + lua.pop(1); // pop color_tbl (or nil) +}