CHanged into a multi files NodeKind, one per file. And added a models.zig
This commit is contained in:
parent
0ab4a47ebc
commit
a11710fbc9
@ -1,11 +1,11 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const rl = @import("raylib");
|
const rl = @import("raylib");
|
||||||
const Node = @import("Node.zig");
|
const md = @import("models.zig");
|
||||||
|
|
||||||
const Engine = @This();
|
const Engine = @This();
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
nodes: std.ArrayList(Node) = .{},
|
nodes: std.ArrayList(md.Node) = .{},
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) !Engine {
|
pub fn init(allocator: std.mem.Allocator) !Engine {
|
||||||
return .{
|
return .{
|
||||||
@ -18,29 +18,11 @@ pub fn deinit(self: *Engine) void {
|
|||||||
self.nodes.deinit(self.allocator);
|
self.nodes.deinit(self.allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addNode(self: *Engine, name: []const u8, script: []const u8) !void {
|
pub fn addNode(self: *Engine, name: []const u8, script: []const u8, kind: md.Node.NodeKind) !void {
|
||||||
const node = try Node.init(self.allocator, name, .base, script);
|
const node = try md.Node.init(self.allocator, name, kind, script);
|
||||||
try self.nodes.append(self.allocator, node);
|
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.
|
/// Poll every node's file watcher; reload changed scripts.
|
||||||
pub fn checkReloads(self: *Engine) !void {
|
pub fn checkReloads(self: *Engine) !void {
|
||||||
for (self.nodes.items) |*node| {
|
for (self.nodes.items) |*node| {
|
||||||
|
|||||||
128
src/Node.zig
128
src/Node.zig
@ -1,9 +1,8 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const rl = @import("raylib");
|
const rl = @import("raylib");
|
||||||
const zlua = @import("zlua");
|
const zlua = @import("zlua");
|
||||||
|
const md = @import("models.zig");
|
||||||
const Lua = zlua.Lua;
|
const Lua = zlua.Lua;
|
||||||
const lua_api = @import("lua_api.zig");
|
|
||||||
const FileWatcher = @import("FileWatcher.zig");
|
|
||||||
|
|
||||||
pub const RectData = struct {
|
pub const RectData = struct {
|
||||||
width: f32 = 50,
|
width: f32 = 50,
|
||||||
@ -12,9 +11,8 @@ pub const RectData = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const NodeKind = union(enum) {
|
pub const NodeKind = union(enum) {
|
||||||
base: void,
|
base: @import("nodes/Base.zig"),
|
||||||
rectangle: RectData,
|
rectangle: @import("nodes/Rect.zig"),
|
||||||
// extend here: sprite, audio_emitter, tilemap, ...
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A scene node with an attached Lua script.
|
/// A scene node with an attached Lua script.
|
||||||
@ -25,14 +23,15 @@ allocator: std.mem.Allocator,
|
|||||||
name: []const u8,
|
name: []const u8,
|
||||||
script_path: []const u8,
|
script_path: []const u8,
|
||||||
lua: *Lua,
|
lua: *Lua,
|
||||||
watcher: FileWatcher,
|
watcher: md.FileWatcher,
|
||||||
/// Lua registry key for the persistent self table.
|
/// Lua registry key for the persistent self table.
|
||||||
/// Survives hot-reloads so script-side state is preserved.
|
/// Survives hot-reloads so script-side state is preserved.
|
||||||
|
/// FIXME: Not working, state is lost
|
||||||
self_ref: i32 = 0,
|
self_ref: i32 = 0,
|
||||||
|
|
||||||
x: f32 = 400,
|
x: f32 = 400,
|
||||||
y: f32 = 300,
|
y: f32 = 300,
|
||||||
kind: NodeKind = .base,
|
kind: NodeKind = .{ .base = .{} },
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
@ -42,7 +41,7 @@ pub fn init(
|
|||||||
) !Node {
|
) !Node {
|
||||||
const lua = try Lua.init(allocator);
|
const lua = try Lua.init(allocator);
|
||||||
lua.openLibs();
|
lua.openLibs();
|
||||||
lua_api.registerAll(lua);
|
md.lua_api.registerAll(lua);
|
||||||
|
|
||||||
var node = Node{
|
var node = Node{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
@ -50,7 +49,7 @@ pub fn init(
|
|||||||
.kind = kind,
|
.kind = kind,
|
||||||
.script_path = script_path,
|
.script_path = script_path,
|
||||||
.lua = lua,
|
.lua = lua,
|
||||||
.watcher = FileWatcher.init(script_path),
|
.watcher = .init(script_path),
|
||||||
};
|
};
|
||||||
try node.createSelfTable();
|
try node.createSelfTable();
|
||||||
try node.loadScript();
|
try node.loadScript();
|
||||||
@ -72,7 +71,7 @@ pub fn loadScript(self: *Node) !void {
|
|||||||
const msg = self.lua.toString(-1) catch "?";
|
const msg = self.lua.toString(-1) catch "?";
|
||||||
std.log.err("[{s}] {s} ({})", .{ self.script_path, msg, err });
|
std.log.err("[{s}] {s} ({})", .{ self.script_path, msg, err });
|
||||||
self.lua.pop(1);
|
self.lua.pop(1);
|
||||||
return; // keep old functions running — don't crash on syntax error
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.syncToLua();
|
self.syncToLua();
|
||||||
@ -87,6 +86,19 @@ pub fn update(self: *Node, dt: f32) void {
|
|||||||
self.lua.pop(1);
|
self.lua.pop(1);
|
||||||
return;
|
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.rawGetIndex(zlua.registry_index, self.self_ref);
|
||||||
self.lua.pushNumber(@floatCast(dt));
|
self.lua.pushNumber(@floatCast(dt));
|
||||||
self.lua.protectedCall(.{ .args = 2 }) catch |err| self.logErr("on_update", err);
|
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 {
|
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) {
|
switch (self.kind) {
|
||||||
.rectangle => |r| {
|
inline else => |*p| {
|
||||||
rl.drawRectangle(
|
const T = @TypeOf(p.*);
|
||||||
@intFromFloat(self.x),
|
const info = @typeInfo(T);
|
||||||
@intFromFloat(self.y),
|
switch (info) {
|
||||||
@intFromFloat(r.width),
|
.@"struct" => if (std.meta.hasFn(T, "render"))
|
||||||
@intFromFloat(r.height),
|
p.render(self),
|
||||||
r.color,
|
else => {},
|
||||||
);
|
|
||||||
},
|
|
||||||
.base => {},
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Script overlay (labels, effects, custom shapes, etc.)
|
// 2. Script overlay (labels, effects, custom shapes, etc.)
|
||||||
self.syncToLua();
|
self.syncToLua();
|
||||||
self.callWithSelf("on_render");
|
self.callWithSelf("on_render");
|
||||||
@ -126,17 +138,16 @@ fn syncToLua(self: *Node) void {
|
|||||||
_ = self.lua.rawGetIndex(zlua.registry_index, self.self_ref);
|
_ = self.lua.rawGetIndex(zlua.registry_index, self.self_ref);
|
||||||
// self table is now at stack -1
|
// self table is now at stack -1
|
||||||
|
|
||||||
setNum(self.lua, "x", self.x);
|
md.setNum(self.lua, "x", self.x);
|
||||||
setNum(self.lua, "y", self.y);
|
md.setNum(self.lua, "y", self.y);
|
||||||
|
|
||||||
|
// Comptime check that the NodeKind has a function render then use it
|
||||||
switch (self.kind) {
|
switch (self.kind) {
|
||||||
.rectangle => |r| {
|
inline else => |*p| {
|
||||||
setNum(self.lua, "width", r.width);
|
const T = @TypeOf(p.*);
|
||||||
setNum(self.lua, "height", r.height);
|
if (std.meta.hasFn(T, "syncToLua"))
|
||||||
pushColorTable(self.lua, r.color); // pushes {r,g,b,a} at -1
|
p.syncToLua(self.lua);
|
||||||
self.lua.setField(-2, "color"); // self_tbl.color = color_tbl; pops color_tbl
|
|
||||||
},
|
},
|
||||||
.base => {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.lua.pop(1); // pop self table
|
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.lua.rawGetIndex(zlua.registry_index, self.self_ref);
|
||||||
// self table at -1
|
// self table at -1
|
||||||
|
|
||||||
self.x = getNum(self.lua, "x", self.x);
|
self.x = md.getNum(self.lua, "x", self.x);
|
||||||
self.y = getNum(self.lua, "y", self.y);
|
self.y = md.getNum(self.lua, "y", self.y);
|
||||||
|
|
||||||
|
// Comptime check that the NodeKind has a function then use it
|
||||||
switch (self.kind) {
|
switch (self.kind) {
|
||||||
.rectangle => |*r| {
|
inline else => |*p| {
|
||||||
r.width = getNum(self.lua, "width", r.width);
|
const T = @TypeOf(p.*);
|
||||||
r.height = getNum(self.lua, "height", r.height);
|
if (std.meta.hasFn(T, "syncFromLua"))
|
||||||
_ = self.lua.getField(-1, "color"); // stack: [..., self_tbl, color_tbl]
|
p.syncFromLua(self.lua);
|
||||||
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)
|
|
||||||
},
|
},
|
||||||
.base => {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.lua.pop(1); // pop self table
|
self.lua.pop(1); // pop self table
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Callback helpers ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// Call lua_fn_name(self) — no-op if the function is not defined.
|
/// Call lua_fn_name(self) — no-op if the function is not defined.
|
||||||
fn callWithSelf(self: *Node, name: [:0]const u8) void {
|
fn callWithSelf(self: *Node, name: [:0]const u8) void {
|
||||||
const t = self.lua.getGlobal(name) catch return;
|
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 });
|
std.log.err("[{s}] {s}: {s} ({})", .{ self.script_path, ctx, msg, err });
|
||||||
self.lua.pop(1);
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
22
src/main.zig
22
src/main.zig
@ -15,20 +15,24 @@ pub fn main() !void {
|
|||||||
defer engine.deinit();
|
defer engine.deinit();
|
||||||
|
|
||||||
// Attach Lua scripts to nodes — add as many as you like
|
// Attach Lua scripts to nodes — add as many as you like
|
||||||
try engine.addNode("Player", "scripts/player.lua");
|
try engine.addNode("Player", "scripts/player.lua", .{ .base = .{} });
|
||||||
try engine.addRectNode(
|
try engine.addNode(
|
||||||
"HealthBar",
|
"HealthBar",
|
||||||
200,
|
|
||||||
20,
|
|
||||||
.{ .r = 60, .g = 200, .b = 80, .a = 255 },
|
|
||||||
"scripts/healthbar.lua",
|
"scripts/healthbar.lua",
|
||||||
|
.{ .rectangle = .{
|
||||||
|
.width = 200,
|
||||||
|
.height = 20,
|
||||||
|
.color = .{ .r = 60, .g = 200, .b = 80, .a = 255 },
|
||||||
|
} },
|
||||||
);
|
);
|
||||||
try engine.addRectNode(
|
try engine.addNode(
|
||||||
"Enemy",
|
"Enemy",
|
||||||
55,
|
|
||||||
55,
|
|
||||||
.{ .r = 220, .g = 60, .b = 60, .a = 255 },
|
|
||||||
"scripts/enemy_rect.lua",
|
"scripts/enemy_rect.lua",
|
||||||
|
.{ .rectangle = .{
|
||||||
|
.width = 55,
|
||||||
|
.height = 55,
|
||||||
|
.color = .{ .r = 220, .g = 60, .b = 60, .a = 255 },
|
||||||
|
} },
|
||||||
);
|
);
|
||||||
|
|
||||||
while (!rl.windowShouldClose()) {
|
while (!rl.windowShouldClose()) {
|
||||||
|
|||||||
46
src/models.zig
Normal file
46
src/models.zig
Normal file
@ -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");
|
||||||
|
}
|
||||||
23
src/nodes/Base.zig
Normal file
23
src/nodes/Base.zig
Normal file
@ -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;
|
||||||
|
}
|
||||||
43
src/nodes/Rect.zig
Normal file
43
src/nodes/Rect.zig
Normal file
@ -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)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user