simple juice and cleanup

This commit is contained in:
abux 2026-07-14 14:14:27 +01:00
parent c4d8770e25
commit 4a7df22256
25 changed files with 428 additions and 259 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,002 B

View file

@ -4,6 +4,10 @@ pub const Style = union(enum) {
frequency: f32 = 6.0, frequency: f32 = 6.0,
phase: f32 = 0.0, phase: f32 = 0.0,
}, },
spawn: struct {
duration: f32 = 5.0,
next: *const Style,
},
}; };
style: Style, style: Style,

View file

@ -1,2 +1,2 @@
max: u32 = 100, max: u32 = 100,
current: u32 = 100, current: u32 = 10,

View file

@ -3,7 +3,11 @@ const Texture = @import("../resource/textures.zig").Texture;
pub const Style = union(enum) { pub const Style = union(enum) {
solid: @Vector(4, u8), solid: @Vector(4, u8),
textured: Handle(Texture), textured: struct {
handle: Handle(Texture),
tint: [3]u8 = .{ 255, 255, 255 },
alpha: u8 = 255,
},
}; };
style: Style, style: Style,

View file

@ -1,2 +1,3 @@
pos: @Vector(2, f32) = .{ 0, 0 }, pos: @Vector(2, f32) = .{ 0, 0 },
size: @Vector(2, f32) = .{ 32, 32 }, size: @Vector(2, f32) = .{ 32, 32 },
angle: f32 = 0,

View file

@ -0,0 +1,2 @@
target: u64,
tint: [3]u8 = .{ 100, 220, 200 },

1
src/event/juice/root.zig Normal file
View file

@ -0,0 +1 @@
pub const DamageFlash = @import("damage_flash.zig");

View file

@ -1,2 +1,3 @@
pub const TakeDamage = @import("take_damage.zig"); pub const TakeDamage = @import("take_damage.zig");
pub const Collision = @import("collision.zig"); pub const Collision = @import("collision.zig");
pub const juice = @import("juice/root.zig");

View file

@ -1,3 +1,7 @@
//! ----------------------------------------------------
//! Default Plugin
//! ----------------------------------------------------
const App = @import("app"); const App = @import("app");
const component = @import("../component/root.zig"); const component = @import("../component/root.zig");
@ -28,14 +32,22 @@ fn build(
// --- UPDATE --- // --- UPDATE ---
try ctx.app.schedules.add(App.stage.inputs, system.inputs.pollEvents); try ctx.app.schedules.add(App.stage.inputs, system.inputs.pollEvents);
try ctx.app.schedules.addMany(App.stage.update, &.{ try ctx.app.schedules.addMany(App.stage.update, &.{
system.frame.checkAABB, // --- COLISION ---
system.frame.resolveCollisions, system.collision.detect,
system.frame.resolveHealth, system.collision.resolve,
system.frame.updateTimers,
system.frame.updateTransforms, // --- AI ---
system.frame.cast,
system.ai.spawn, system.ai.spawn,
system.ai.chaseTarget, system.ai.chase,
// --- JUICE ---
system.health.juice,
// --- GAMEPLAY ---
system.timer.update,
system.transform.update,
system.health.resolve,
system.combat.cast,
}); });
try ctx.app.schedules.add(App.stage.draw, system.draw.draw); try ctx.app.schedules.add(App.stage.draw, system.draw.draw);
} }

15
src/resource/assets.zig Normal file
View file

@ -0,0 +1,15 @@
//! ----------------------------------------------------
//! Assets Storage
//! ----------------------------------------------------
// --- # ---
const Handle = @import("gtl").Handle;
const Texture = @import("textures.zig").Texture;
//
// FIELDS
//
enemy: Handle(Texture),
player: Handle(Texture),
projectile: Handle(Texture),

View file

@ -1,6 +1,6 @@
// --- WAVES --- // --- WAVES ---
current_wave: u32 = 0, current_wave: u32 = 0,
current_wave_time: f32 = 0, current_wave_time: f32 = 0,
time_for_next_wave: f32 = 20.0, time_for_next_wave: f32 = 10.0,
next_wave_enemy_count: u32 = 3, next_wave_enemy_count: u32 = 3,
enemy_multiplyer_each_wave: u32 = 2, enemy_multiplyer_each_wave: u32 = 2,

View file

@ -6,6 +6,7 @@ pub const Frame = @import("frame.zig");
pub const Time = @import("time.zig"); pub const Time = @import("time.zig");
// --- ASSETS --- // --- ASSETS ---
pub const Assets = @import("assets.zig");
pub const Fonts = @import("fonts.zig"); pub const Fonts = @import("fonts.zig");
pub const Textures = @import("textures.zig"); pub const Textures = @import("textures.zig");

View file

@ -1,3 +1,6 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const std = @import("std"); const std = @import("std");
const sdl = @import("sdl"); const sdl = @import("sdl");
const App = @import("app"); const App = @import("app");
@ -13,6 +16,7 @@ pub fn spawn(ctx: App.scheduler.Context) !void {
time: *resource.Time, time: *resource.Time,
game: *resource.Game, game: *resource.Game,
textures: *resource.Textures, textures: *resource.Textures,
assets: *resource.Assets,
}); });
// --- CURRENT TARGET --- // --- CURRENT TARGET ---
@ -58,14 +62,17 @@ pub fn spawn(ctx: App.scheduler.Context) !void {
component.Velocity{}, component.Velocity{},
component.Friction{}, component.Friction{},
component.Speed{ .value = 200 }, component.Speed{ .value = 200 },
component.Animate{ .style = .{ .bob = .{} } }, component.Animate{ .style = .{
.spawn = .{
component.Material{ .next = &.{
.style = .{ .bob = .{},
// .solid = .{ 200, 20, 20, 255 }, },
.textured = try res.textures.load("src/assets/textures/player.png"),
}, },
}, } },
component.Material{ .style = .{ .textured = .{
.handle = res.assets.enemy,
} } },
}); });
} }
@ -76,7 +83,7 @@ pub fn spawn(ctx: App.scheduler.Context) !void {
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn chaseTarget(ctx: App.scheduler.Context) !void { pub fn chase(ctx: App.scheduler.Context) !void {
const res = try ctx.app.resources.getMany(struct { const res = try ctx.app.resources.getMany(struct {
frame: *resource.Frame, frame: *resource.Frame,
time: *resource.Time, time: *resource.Time,
@ -87,8 +94,14 @@ pub fn chaseTarget(ctx: App.scheduler.Context) !void {
speed: *component.Speed, speed: *component.Speed,
transform: *component.Transform, transform: *component.Transform,
velocity: *component.Velocity, velocity: *component.Velocity,
animate: ?*component.Animate,
}, .{}); }, .{});
while (query.next()) |e| { while (query.next()) |e| {
// --- DO NO WALK ON ANIM PLS ---
if (e.animate) |animate| {
if (animate.style == .spawn) continue;
}
// --- GET TARGETS POSITION --- // --- GET TARGETS POSITION ---
const target = ctx.app.world.getComponent( const target = ctx.app.world.getComponent(
component.Transform, component.Transform,

113
src/system/collision.zig Normal file
View file

@ -0,0 +1,113 @@
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");
const event = @import("../event/root.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn detect(ctx: App.scheduler.Context) !void {
var outer = ctx.app.world.query(struct {
aabb: *component.AABB,
transform: *component.Transform,
}, .{});
while (outer.next()) |a| {
const entity_a = outer.entity();
var inner = ctx.app.world.query(struct {
aabb: *component.AABB,
transform: *component.Transform,
}, .{});
while (inner.next()) |b| {
const entity_b = inner.entity();
// --- PREVENT SAME ENTITY ---
if (entity_a == entity_b)
continue;
// --- CHECK IF OVERLAPS ---
if (overlaps(
a.transform.pos,
a.aabb,
b.transform.pos,
b.aabb,
)) {
try ctx.app.events.send(event.Collision{
.a = entity_a,
.b = entity_b,
});
}
}
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn resolve(ctx: App.scheduler.Context) !void {
if (ctx.app.events.get(event.Collision)) |queue| {
for (queue.items()) |coll| {
// --- GET PROJECTILES ---
const proj_a = ctx.app.world.getComponent(component.Projectile, coll.a);
const proj_b = ctx.app.world.getComponent(component.Projectile, coll.b);
if (proj_a) |projectile| {
if (projectile.owner != coll.b and
ctx.app.world.hasComponent(component.Health, coll.b))
{
try ctx.app.events.sendMany(.{
event.TakeDamage{
.target = coll.b,
.source = coll.a,
.amount = projectile.damage,
},
event.juice.DamageFlash{
.target = coll.a,
},
});
ctx.app.world.despawn(coll.a);
}
}
if (proj_b) |projectile| {
if (projectile.owner != coll.a and
ctx.app.world.hasComponent(component.Health, coll.a))
{
try ctx.app.events.sendMany(.{
event.TakeDamage{
.target = coll.b,
.source = coll.a,
.amount = projectile.damage,
},
event.juice.DamageFlash{
.target = coll.a,
},
});
ctx.app.world.despawn(coll.b);
}
}
}
queue.clear();
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn overlaps(
a_pos: @Vector(2, f32),
a: *component.AABB,
b_pos: @Vector(2, f32),
b: *component.AABB,
) bool {
return a_pos[0] + a.offset[0] < b_pos[0] + b.offset[0] + b.size[0] and
a_pos[0] + a.offset[0] + a.size[0] > b_pos[0] + b.offset[0] and
a_pos[1] + a.offset[1] < b_pos[1] + b.offset[1] + b.size[1] and
a_pos[1] + a.offset[1] + a.size[1] > b_pos[1] + b.offset[1];
}

54
src/system/combat.zig Normal file
View file

@ -0,0 +1,54 @@
const std = @import("std");
const App = @import("app");
const component = @import("../component/root.zig");
const resource = @import("../resource/root.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn cast(ctx: App.scheduler.Context) !void {
const res = try ctx.app.resources.getMany(struct {
time: *resource.Time,
assets: *resource.Assets,
});
// --- 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),
},
.size = .{ 109, 32 },
},
component.AABB{},
component.Velocity{
.value = .{ 600, 0 },
},
component.Material{ .style = .{ .textured = .{
.handle = res.assets.projectile,
} } },
component.Projectile{
.owner = query.entity(),
},
});
// --- SET COOLDOWN ---
e.caster.current_cooldown =
e.caster.cooldown;
}
}

View file

@ -44,10 +44,17 @@ pub fn init(ctx: App.scheduler.Context) !void {
resource.Game{}, resource.Game{},
); );
// --- ASSETS --- // --- ASSETS LOADERS ---
_ = try ctx.app.resources.getOrSet(resource.Fonts.init(ctx.app.alloc)); _ = try ctx.app.resources.getOrSet(resource.Fonts.init(ctx.app.alloc));
const textures = try ctx.app.resources.getOrSet(resource.Textures.init(ctx.app.alloc)); const textures = try ctx.app.resources.getOrSet(resource.Textures.init(ctx.app.alloc));
// --- ASSETS ---
const assets = try ctx.app.resources.getOrSet(resource.Assets{
.enemy = try textures.load("src/assets/textures/black_triangle.png"),
.player = try textures.load("src/assets/textures/player.png"),
.projectile = try textures.load("src/assets/textures/projectile.png"),
});
// --- DEBUG --- // --- DEBUG ---
std.debug.print("\x1b[35m┏━ WINDOW\n", .{}); std.debug.print("\x1b[35m┏━ WINDOW\n", .{});
std.debug.print("\x1b[35m┃\x1b[0m Title: \"{s}\"\n", .{window.title}); std.debug.print("\x1b[35m┃\x1b[0m Title: \"{s}\"\n", .{window.title});
@ -71,9 +78,9 @@ pub fn init(ctx: App.scheduler.Context) !void {
component.Friction{}, component.Friction{},
component.Velocity{}, component.Velocity{},
component.Transform{ .size = .{ 64, 64 } }, component.Transform{ .size = .{ 64, 64 } },
component.Material{ .style = .{ component.Material{ .style = .{ .textured = .{
.textured = try textures.load("src/assets/textures/player.png"), .handle = assets.player,
} }, } } },
component.CameraTarget{}, component.CameraTarget{},
}); });

View file

@ -21,9 +21,9 @@ pub fn draw(ctx: App.scheduler.Context) !void {
// --- SET CLEAR COLOR --- // --- SET CLEAR COLOR ---
_ = sdl.SDL_SetRenderDrawColor( _ = sdl.SDL_SetRenderDrawColor(
res.renderer.raw, res.renderer.raw,
10, 200,
10, 200,
10, 200,
255, 255,
); );
@ -50,6 +50,22 @@ pub fn draw(ctx: App.scheduler.Context) !void {
.bob => |bob| { .bob => |bob| {
draw_pos[1] += @sin(res.time.elapsed * bob.frequency + bob.phase) * bob.amplitude; draw_pos[1] += @sin(res.time.elapsed * bob.frequency + bob.phase) * bob.amplitude;
}, },
.spawn => |spawn| {
const tmp_height: @Vector(2, f32) = .{ 32, 32 };
e.transform.size[1] = 0;
const t = @min(res.time.elapsed / spawn.duration, 1.0);
const scale = 1.0 - @exp(-8.0 * t) * @cos(20.0 * t);
e.transform.size = tmp_height * @as(
@Vector(2, f32),
@splat(scale),
);
if (t >= 1.0) {
animate.style = spawn.next.*;
}
},
} }
} }
@ -67,15 +83,20 @@ pub fn draw(ctx: App.scheduler.Context) !void {
_ = sdl.SDL_SetRenderDrawColor(res.renderer.raw, albedo[0], albedo[1], albedo[2], albedo[3]); _ = sdl.SDL_SetRenderDrawColor(res.renderer.raw, albedo[0], albedo[1], albedo[2], albedo[3]);
_ = sdl.SDL_RenderFillRect(res.renderer.raw, &rect); _ = sdl.SDL_RenderFillRect(res.renderer.raw, &rect);
}, },
.textured => |handle| { .textured => |tex| {
const gpu = res.textures.data.get(handle) orelse continue; const gpu = res.textures.data.get(tex.handle) orelse continue;
const raw = if (gpu.raw) |*v| v else continue; const raw = if (gpu.raw) |*v| v else continue;
_ = sdl.SDL_RenderTexture( _ = sdl.SDL_SetTextureColorMod(raw.*, tex.tint[0], tex.tint[1], tex.tint[2]);
_ = sdl.SDL_SetTextureAlphaMod(raw.*, tex.alpha);
_ = sdl.SDL_RenderTextureRotated(
res.renderer.raw, res.renderer.raw,
raw.*, raw.*,
null, null,
&rect, &rect,
e.transform.angle,
null,
0,
); );
}, },
} }

View file

@ -1,3 +1,6 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const std = @import("std"); const std = @import("std");
const sdl = @import("sdl"); const sdl = @import("sdl");
const gtl = @import("gtl"); const gtl = @import("gtl");
@ -127,230 +130,3 @@ pub fn endFrame(ctx: App.scheduler.Context) !void {
sdl.SDL_Delay(@intFromFloat(delay_ms)); 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.AABB{},
component.Velocity{
.value = .{ 600, 0 },
},
component.Material{ .style = .{
.textured = try res.textures.load("src/assets/textures/projectile.png"),
} },
component.Projectile{
.owner = query.entity(),
},
});
// --- SET COOLDOWN ---
e.caster.current_cooldown =
e.caster.cooldown;
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn resolveHealth(ctx: App.scheduler.Context) !void {
if (ctx.app.events.get(event.TakeDamage)) |queue| {
for (queue.items()) |take_dmg| {
const health = ctx.app.world.getComponent(component.Health, take_dmg.target) orelse continue;
if (health.current <= 0) continue;
health.current -= take_dmg.amount;
std.debug.print("{s}[TAKE_DAMAGE]{s} source({}) target({}) amount({})\n", .{
gtl.ansi.red,
gtl.ansi.reset,
take_dmg.source,
take_dmg.target,
take_dmg.amount,
});
}
queue.clear();
}
var query = ctx.app.world.query(struct {
health: *component.Health,
}, .{});
while (query.next()) |e| {
if (e.health.current <= 0) {
ctx.app.world.despawn(query.entity());
}
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn resolveCollisions(ctx: App.scheduler.Context) !void {
if (ctx.app.events.get(event.Collision)) |queue| {
for (queue.items()) |coll| {
const proj_a = ctx.app.world.getComponent(component.Projectile, coll.a);
const proj_b = ctx.app.world.getComponent(component.Projectile, coll.b);
if (proj_a) |projectile| {
if (projectile.owner != coll.b and
ctx.app.world.hasComponent(component.Health, coll.b))
{
try ctx.app.events.send(event.TakeDamage{
.target = coll.b,
.source = coll.a,
.amount = projectile.damage,
});
ctx.app.world.despawn(coll.a);
}
}
if (proj_b) |projectile| {
if (projectile.owner != coll.a and
ctx.app.world.hasComponent(component.Health, coll.a))
{
try ctx.app.events.send(event.TakeDamage{
.target = coll.a,
.source = coll.b,
.amount = projectile.damage,
});
ctx.app.world.despawn(coll.b);
}
}
}
queue.clear();
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn checkAABB(ctx: App.scheduler.Context) !void {
var outer = ctx.app.world.query(struct {
aabb: *component.AABB,
transform: *component.Transform,
}, .{});
while (outer.next()) |a| {
const entity_a = outer.entity();
var inner = ctx.app.world.query(struct {
aabb: *component.AABB,
transform: *component.Transform,
}, .{});
while (inner.next()) |b| {
const entity_b = inner.entity();
// --- PREVENT SAME ENTITY ---
if (entity_a == entity_b)
continue;
// --- CHECK IF OVERLAPS ---
if (overlaps(
a.transform.pos,
a.aabb,
b.transform.pos,
b.aabb,
)) {
try ctx.app.events.send(event.Collision{
.a = entity_a,
.b = entity_b,
});
}
}
}
}
fn overlaps(
a_pos: @Vector(2, f32),
a: *component.AABB,
b_pos: @Vector(2, f32),
b: *component.AABB,
) bool {
return a_pos[0] + a.offset[0] < b_pos[0] + b.offset[0] + b.size[0] and
a_pos[0] + a.offset[0] + a.size[0] > b_pos[0] + b.offset[0] and
a_pos[1] + a.offset[1] < b_pos[1] + b.offset[1] + b.size[1] and
a_pos[1] + a.offset[1] + a.size[1] > b_pos[1] + b.offset[1];
}

51
src/system/health.zig Normal file
View file

@ -0,0 +1,51 @@
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");
const event = @import("../event/root.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn resolve(ctx: App.scheduler.Context) !void {
if (ctx.app.events.get(event.TakeDamage)) |queue| {
for (queue.items()) |take_dmg| {
const health = ctx.app.world.getComponent(component.Health, take_dmg.target) orelse continue;
if (health.current <= 0) continue;
health.current -= take_dmg.amount;
std.debug.print("{s}[TAKE_DAMAGE]{s} source({}) target({}) amount({})\n", .{
gtl.ansi.red,
gtl.ansi.reset,
take_dmg.source,
take_dmg.target,
take_dmg.amount,
});
}
queue.clear();
}
var query = ctx.app.world.query(struct {
health: *component.Health,
}, .{});
while (query.next()) |e| {
if (e.health.current <= 0) {
ctx.app.world.despawn(query.entity());
}
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn juice(ctx: App.scheduler.Context) !void {
if (ctx.app.events.get(event.juice.DamageFlash)) |queue| {
for (queue.items()) |dmg_flash| {
const material = ctx.app.world.getComponent(component.Material, dmg_flash.target) orelse continue;
material.style.textured.tint = dmg_flash.tint;
}
queue.clear();
}
}

View file

@ -27,13 +27,33 @@ pub fn pollEvents(ctx: App.scheduler.Context) !void {
var query = ctx.app.world.query(struct { var query = ctx.app.world.query(struct {
player: *component.Player, player: *component.Player,
transform: *component.Transform,
velocity: *component.Velocity, velocity: *component.Velocity,
speed: *component.Speed, speed: *component.Speed,
}, .{}); }, .{});
while (query.next()) |e| { while (query.next()) |e| {
if (keyboard[sdl.SDL_SCANCODE_W]) e.velocity.value[1] = -e.speed.value; e.transform.angle = 0;
if (keyboard[sdl.SDL_SCANCODE_S]) e.velocity.value[1] = e.speed.value;
if (keyboard[sdl.SDL_SCANCODE_A]) e.velocity.value[0] = -e.speed.value; // --- UP ---
if (keyboard[sdl.SDL_SCANCODE_D]) e.velocity.value[0] = e.speed.value; if (keyboard[sdl.SDL_SCANCODE_W]) {
e.velocity.value[1] = -e.speed.value;
}
// --- DOWN ---
if (keyboard[sdl.SDL_SCANCODE_S]) {
e.velocity.value[1] = e.speed.value;
}
// --- LEFT ---
if (keyboard[sdl.SDL_SCANCODE_A]) {
e.velocity.value[0] = -e.speed.value;
e.transform.angle = -5;
}
// --- RIGHT ---
if (keyboard[sdl.SDL_SCANCODE_D]) {
e.velocity.value[0] = e.speed.value;
e.transform.angle = 5;
}
} }
} }

0
src/system/juice.zig Normal file
View file

View file

@ -3,3 +3,9 @@ pub const frame = @import("frame.zig");
pub const inputs = @import("inputs.zig"); pub const inputs = @import("inputs.zig");
pub const draw = @import("draw.zig"); pub const draw = @import("draw.zig");
pub const ai = @import("ai.zig"); pub const ai = @import("ai.zig");
pub const collision = @import("collision.zig");
pub const health = @import("health.zig");
pub const transform = @import("transform.zig");
pub const combat = @import("combat.zig");
pub const timer = @import("timer.zig");
pub const juice = @import("juice.zig");

35
src/system/timer.zig Normal file
View file

@ -0,0 +1,35 @@
const std = @import("std");
const App = @import("app");
const component = @import("../component/root.zig");
const resource = @import("../resource/root.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn update(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;
}
}
}
}

32
src/system/transform.zig Normal file
View file

@ -0,0 +1,32 @@
const std = @import("std");
const App = @import("app");
const component = @import("../component/root.zig");
const resource = @import("../resource/root.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn update(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;
}
}
}