const std = @import("std"); const sdl = @import("sdl"); const App = @import("app"); const component = @import("../component/root.zig"); const resource = @import("../resource/root.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn beginFrame(ctx: App.scheduler.Context) !void { const res = try ctx.app.resources.getMany(struct { window: *resource.Window, frame: *resource.Frame, time: *resource.Time, }); // // TIME // res.time.last = res.time.now; res.time.now = sdl.SDL_GetPerformanceCounter(); res.time.dt = @as(f32, @floatFromInt(res.time.now - res.time.last)) / @as(f32, @floatFromInt(res.time.frequency)); // // CAMERA // var target_transform: ?*const component.Transform = null; { // GATHER CAMERAS TARGET var query = ctx.app.world.query(struct { transform: *component.Transform, camera_target: ?*component.CameraTarget, }, .{}); while (query.next()) |e| { if (e.camera_target) |_| { target_transform = e.transform; } } } { // GATHER CAMERAS var query = ctx.app.world.query(struct { camera: *component.Camera, transform: *component.Transform, }, .{}); while (query.next()) |e| { const window_width: f32 = @floatFromInt(res.window.width); const window_height: f32 = @floatFromInt(res.window.height); const target = if (target_transform) |v| v else continue; e.transform.pos[0] += ((window_width / 2.0) - (target.pos[0] + target.size[0] / 2.0) - e.transform.pos[0]) / 20.0; e.transform.pos[1] += ((window_height / 2.0) - (target.pos[1] + target.size[1] / 2.0) - e.transform.pos[1]) / 20.0; // --- SET CURRENT FRAME CAMERA --- res.frame.camera = .{ .value = e.camera, .transform = e.transform, }; } } } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn endFrame(ctx: App.scheduler.Context) !void { const res = try ctx.app.resources.getMany(struct { frame: *resource.Frame, time: *resource.Time, }); res.frame.camera = null; const frame_time = @as(f32, @floatFromInt(sdl.SDL_GetPerformanceCounter() - res.time.now)) / @as(f32, @floatFromInt(res.time.frequency)); if (frame_time < res.time.target_dt) { const delay_ms = (res.time.target_dt - frame_time) * 1000.0; sdl.SDL_Delay(@intFromFloat(delay_ms)); } } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn updateTransforms(ctx: App.scheduler.Context) !void { const res = try ctx.app.resources.getMany(struct { time: *resource.Time, }); // --- QUERY --- var query = ctx.app.world.query(struct { transform: *component.Transform, velocity: *component.Velocity, friction: ?*component.Friction, }, .{}); while (query.next()) |e| { // --- APPLY VELOCITY --- e.transform.pos[0] += e.velocity.value[0] * res.time.dt; e.transform.pos[1] += e.velocity.value[1] * res.time.dt; // --- APPLY FRICTION --- if (e.friction) |friction| { const damping = std.math.exp(-friction.value * res.time.dt); e.velocity.value[0] *= damping; e.velocity.value[1] *= damping; } } } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn updateTimers(ctx: App.scheduler.Context) !void { const res = try ctx.app.resources.getMany(struct { time: *resource.Time, }); var query = ctx.app.world.query(struct { timer: *component.Timer, }, .{}); while (query.next()) |e| { // --- # --- if (e.timer.finished and !e.timer.repeat) continue; // --- PROGRESS --- e.timer.remaining -= res.time.dt; if (e.timer.remaining <= 0.0) { e.timer.finished = true; if (e.timer.repeat) { e.timer.remaining = e.timer.duration; e.timer.finished = false; } } } } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn cast(ctx: App.scheduler.Context) !void { const res = try ctx.app.resources.getMany(struct { time: *resource.Time, }); // --- QUERY --- var query = ctx.app.world.query(struct { caster: *component.Caster, transform: *component.Transform, }, .{}); while (query.next()) |e| { // --- SUBTRACT COOLDOWN --- e.caster.current_cooldown -= @max(0, res.time.dt); // --- SKIP IF ON COOLDOWN --- if (e.caster.current_cooldown >= 0) continue; // --- SPAWN PROJECTILE --- try ctx.app.world.spawn(.{ component.Transform{ .pos = e.transform.pos, }, component.Velocity{ .value = .{ 400, 0 }, }, component.Material{ .style = .{ .solid = .{ 200, 20, 20, 255 } }, }, component.Projectile{}, }); // --- SET COOLDOWN --- e.caster.current_cooldown = e.caster.cooldown; } }