VampireSurvivors/src/system/frame.zig

230 lines
7.2 KiB
Zig
Raw Normal View History

2026-07-11 18:45:05 +01:00
const std = @import("std");
const sdl = @import("sdl");
2026-07-13 01:33:26 +01:00
const gtl = @import("gtl");
2026-07-11 18:45:05 +01:00
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,
2026-07-13 01:33:26 +01:00
textures: *resource.Textures,
renderer: *resource.Renderer,
2026-07-11 18:45:05 +01:00
});
//
// 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,
};
}
}
2026-07-13 01:33:26 +01:00
{ // 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(
entry.value.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 });
}
}
2026-07-11 18:45:05 +01:00
}
/// ----------------------------------------------------
/// ----------------------------------------------------
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,
2026-07-13 01:33:26 +01:00
textures: *resource.Textures,
2026-07-11 18:45:05 +01:00
});
// --- 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{
2026-07-13 01:33:26 +01:00
.style = .{
// .solid = .{ 200, 20, 20, 255 },
.textured = try res.textures.load(resource.Textures.Player, "src/assets/textures/player.png"),
},
2026-07-11 18:45:05 +01:00
},
component.Projectile{},
});
// --- SET COOLDOWN ---
e.caster.current_cooldown =
e.caster.cooldown;
}
}