const std = @import("std"); const sdl = @import("sdl"); const gtl = @import("gtl"); 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, textures: *resource.Textures, renderer: *resource.Renderer, }); // // 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, }; } } { // BUILD TEXTURES for (res.textures.data.items()) |*entry| { // --- SKIP IF ALREADY BUILD --- if (entry.value.raw != null) continue; // --- SURFACE --- const surface = sdl.IMG_Load(entry.value.path.ptr) orelse { gtl.log.err("Texture: {s}", .{sdl.SDL_GetError()}); continue; }; defer sdl.SDL_DestroySurface(surface); // --- TEXTURE --- const raw = sdl.SDL_CreateTextureFromSurface( res.renderer.raw, surface, ) orelse return error.Texture; // --- SET TEXTURE FILTER --- _ = sdl.SDL_SetTextureScaleMode( raw, sdl.SDL_SCALEMODE_PIXELART, ); // --- ASSIGN --- entry.value.raw = raw; entry.value.size = .{ raw.*.w, raw.*.h }; // --- DEBUG --- std.debug.print("{s}┏━ TEXTURE{s}\n", .{ gtl.ansi.purple, gtl.ansi.reset }); std.debug.print("{s}┃{s} Path: \"{s}\"\n", .{ gtl.ansi.purple, gtl.ansi.reset, entry.value.path }); std.debug.print("{s}┃{s} Size: {}x{}\n", .{ gtl.ansi.purple, gtl.ansi.reset, raw.*.w, raw.*.h }); std.debug.print("{s}┃{s} Filter: PixelArt\n", .{ gtl.ansi.purple, gtl.ansi.reset }); std.debug.print("{s}┗━{s}\n", .{ gtl.ansi.purple, gtl.ansi.reset }); } } } /// ---------------------------------------------------- /// ---------------------------------------------------- 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; res.time.elapsed += res.time.dt; 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, textures: *resource.Textures, }); // --- 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[0] + (e.transform.size[0] / 2.0), e.transform.pos[1] + (e.transform.size[1] / 2.0), }, }, component.Velocity{ .value = .{ 200, 0 }, }, component.Material{ .style = .{ .textured = try res.textures.load("src/assets/textures/projectile.png"), } }, component.Projectile{}, }); // --- SET COOLDOWN --- e.caster.current_cooldown = e.caster.cooldown; } }