simple juice and cleanup
This commit is contained in:
parent
c4d8770e25
commit
4a7df22256
25 changed files with 428 additions and 259 deletions
BIN
src/assets/textures/black_triangle.png
Normal file
BIN
src/assets/textures/black_triangle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 312 B |
BIN
src/assets/textures/proj.png
Normal file
BIN
src/assets/textures/proj.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1,002 B |
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
max: u32 = 100,
|
max: u32 = 100,
|
||||||
current: u32 = 100,
|
current: u32 = 10,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
2
src/event/juice/damage_flash.zig
Normal file
2
src/event/juice/damage_flash.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
target: u64,
|
||||||
|
tint: [3]u8 = .{ 100, 220, 200 },
|
||||||
1
src/event/juice/root.zig
Normal file
1
src/event/juice/root.zig
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub const DamageFlash = @import("damage_flash.zig");
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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
15
src/resource/assets.zig
Normal 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),
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 = .{
|
||||||
|
.next = &.{
|
||||||
|
.bob = .{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} },
|
||||||
|
|
||||||
component.Material{
|
component.Material{ .style = .{ .textured = .{
|
||||||
.style = .{
|
.handle = res.assets.enemy,
|
||||||
// .solid = .{ 200, 20, 20, 255 },
|
} } },
|
||||||
.textured = try res.textures.load("src/assets/textures/player.png"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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
113
src/system/collision.zig
Normal 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
54
src/system/combat.zig
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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{},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
51
src/system/health.zig
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
0
src/system/juice.zig
Normal 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
35
src/system/timer.zig
Normal 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
32
src/system/transform.zig
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue