diff --git a/src/assets/textures/black_triangle.png b/src/assets/textures/black_triangle.png new file mode 100644 index 0000000..2e5db0d Binary files /dev/null and b/src/assets/textures/black_triangle.png differ diff --git a/src/assets/textures/proj.png b/src/assets/textures/proj.png new file mode 100644 index 0000000..64ede2b Binary files /dev/null and b/src/assets/textures/proj.png differ diff --git a/src/component/animate.zig b/src/component/animate.zig index 530ab8b..28d7be7 100644 --- a/src/component/animate.zig +++ b/src/component/animate.zig @@ -4,6 +4,10 @@ pub const Style = union(enum) { frequency: f32 = 6.0, phase: f32 = 0.0, }, + spawn: struct { + duration: f32 = 5.0, + next: *const Style, + }, }; style: Style, diff --git a/src/component/health.zig b/src/component/health.zig index 531c002..12eb384 100644 --- a/src/component/health.zig +++ b/src/component/health.zig @@ -1,2 +1,2 @@ max: u32 = 100, -current: u32 = 100, +current: u32 = 10, diff --git a/src/component/material.zig b/src/component/material.zig index dbd420a..47fcbe2 100644 --- a/src/component/material.zig +++ b/src/component/material.zig @@ -3,7 +3,11 @@ const Texture = @import("../resource/textures.zig").Texture; pub const Style = union(enum) { solid: @Vector(4, u8), - textured: Handle(Texture), + textured: struct { + handle: Handle(Texture), + tint: [3]u8 = .{ 255, 255, 255 }, + alpha: u8 = 255, + }, }; style: Style, diff --git a/src/component/transform.zig b/src/component/transform.zig index 811f0ec..9182433 100644 --- a/src/component/transform.zig +++ b/src/component/transform.zig @@ -1,2 +1,3 @@ pos: @Vector(2, f32) = .{ 0, 0 }, size: @Vector(2, f32) = .{ 32, 32 }, +angle: f32 = 0, diff --git a/src/event/juice/damage_flash.zig b/src/event/juice/damage_flash.zig new file mode 100644 index 0000000..8bd96e0 --- /dev/null +++ b/src/event/juice/damage_flash.zig @@ -0,0 +1,2 @@ +target: u64, +tint: [3]u8 = .{ 100, 220, 200 }, diff --git a/src/event/juice/root.zig b/src/event/juice/root.zig new file mode 100644 index 0000000..b0b486b --- /dev/null +++ b/src/event/juice/root.zig @@ -0,0 +1 @@ +pub const DamageFlash = @import("damage_flash.zig"); diff --git a/src/event/root.zig b/src/event/root.zig index 894e630..2644db7 100644 --- a/src/event/root.zig +++ b/src/event/root.zig @@ -1,2 +1,3 @@ pub const TakeDamage = @import("take_damage.zig"); pub const Collision = @import("collision.zig"); +pub const juice = @import("juice/root.zig"); diff --git a/src/plugin/default.zig b/src/plugin/default.zig index cbd38b6..e1612d9 100644 --- a/src/plugin/default.zig +++ b/src/plugin/default.zig @@ -1,3 +1,7 @@ +//! ---------------------------------------------------- +//! Default Plugin +//! ---------------------------------------------------- + const App = @import("app"); const component = @import("../component/root.zig"); @@ -28,14 +32,22 @@ fn build( // --- UPDATE --- try ctx.app.schedules.add(App.stage.inputs, system.inputs.pollEvents); try ctx.app.schedules.addMany(App.stage.update, &.{ - system.frame.checkAABB, - system.frame.resolveCollisions, - system.frame.resolveHealth, - system.frame.updateTimers, - system.frame.updateTransforms, - system.frame.cast, + // --- COLISION --- + system.collision.detect, + system.collision.resolve, + + // --- AI --- 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); } diff --git a/src/resource/assets.zig b/src/resource/assets.zig new file mode 100644 index 0000000..08ac618 --- /dev/null +++ b/src/resource/assets.zig @@ -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), diff --git a/src/resource/game.zig b/src/resource/game.zig index cb436dc..d514919 100644 --- a/src/resource/game.zig +++ b/src/resource/game.zig @@ -1,6 +1,6 @@ // --- WAVES --- current_wave: u32 = 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, enemy_multiplyer_each_wave: u32 = 2, diff --git a/src/resource/root.zig b/src/resource/root.zig index 6d9f421..3787f10 100644 --- a/src/resource/root.zig +++ b/src/resource/root.zig @@ -6,6 +6,7 @@ pub const Frame = @import("frame.zig"); pub const Time = @import("time.zig"); // --- ASSETS --- +pub const Assets = @import("assets.zig"); pub const Fonts = @import("fonts.zig"); pub const Textures = @import("textures.zig"); diff --git a/src/system/ai.zig b/src/system/ai.zig index ba14c50..9e2c669 100644 --- a/src/system/ai.zig +++ b/src/system/ai.zig @@ -1,3 +1,6 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + const std = @import("std"); const sdl = @import("sdl"); const App = @import("app"); @@ -13,6 +16,7 @@ pub fn spawn(ctx: App.scheduler.Context) !void { time: *resource.Time, game: *resource.Game, textures: *resource.Textures, + assets: *resource.Assets, }); // --- CURRENT TARGET --- @@ -58,14 +62,17 @@ pub fn spawn(ctx: App.scheduler.Context) !void { component.Velocity{}, component.Friction{}, component.Speed{ .value = 200 }, - component.Animate{ .style = .{ .bob = .{} } }, - - component.Material{ - .style = .{ - // .solid = .{ 200, 20, 20, 255 }, - .textured = try res.textures.load("src/assets/textures/player.png"), + component.Animate{ .style = .{ + .spawn = .{ + .next = &.{ + .bob = .{}, + }, }, - }, + } }, + + 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 { frame: *resource.Frame, time: *resource.Time, @@ -87,8 +94,14 @@ pub fn chaseTarget(ctx: App.scheduler.Context) !void { speed: *component.Speed, transform: *component.Transform, velocity: *component.Velocity, + animate: ?*component.Animate, }, .{}); while (query.next()) |e| { + // --- DO NO WALK ON ANIM PLS --- + if (e.animate) |animate| { + if (animate.style == .spawn) continue; + } + // --- GET TARGETS POSITION --- const target = ctx.app.world.getComponent( component.Transform, diff --git a/src/system/collision.zig b/src/system/collision.zig new file mode 100644 index 0000000..4863ad5 --- /dev/null +++ b/src/system/collision.zig @@ -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]; +} diff --git a/src/system/combat.zig b/src/system/combat.zig new file mode 100644 index 0000000..921419d --- /dev/null +++ b/src/system/combat.zig @@ -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; + } +} diff --git a/src/system/core.zig b/src/system/core.zig index facd23c..92fe8a8 100644 --- a/src/system/core.zig +++ b/src/system/core.zig @@ -44,10 +44,17 @@ pub fn init(ctx: App.scheduler.Context) !void { resource.Game{}, ); - // --- ASSETS --- + // --- ASSETS LOADERS --- _ = try ctx.app.resources.getOrSet(resource.Fonts.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 --- std.debug.print("\x1b[35m┏━ WINDOW\n", .{}); 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.Velocity{}, component.Transform{ .size = .{ 64, 64 } }, - component.Material{ .style = .{ - .textured = try textures.load("src/assets/textures/player.png"), - } }, + component.Material{ .style = .{ .textured = .{ + .handle = assets.player, + } } }, component.CameraTarget{}, }); diff --git a/src/system/draw.zig b/src/system/draw.zig index f0f824a..62d678e 100644 --- a/src/system/draw.zig +++ b/src/system/draw.zig @@ -21,9 +21,9 @@ pub fn draw(ctx: App.scheduler.Context) !void { // --- SET CLEAR COLOR --- _ = sdl.SDL_SetRenderDrawColor( res.renderer.raw, - 10, - 10, - 10, + 200, + 200, + 200, 255, ); @@ -50,6 +50,22 @@ pub fn draw(ctx: App.scheduler.Context) !void { .bob => |bob| { 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_RenderFillRect(res.renderer.raw, &rect); }, - .textured => |handle| { - const gpu = res.textures.data.get(handle) orelse continue; + .textured => |tex| { + const gpu = res.textures.data.get(tex.handle) orelse 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, raw.*, null, &rect, + e.transform.angle, + null, + 0, ); }, } diff --git a/src/system/frame.zig b/src/system/frame.zig index f9a7e50..7e4c758 100644 --- a/src/system/frame.zig +++ b/src/system/frame.zig @@ -1,3 +1,6 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + const std = @import("std"); const sdl = @import("sdl"); const gtl = @import("gtl"); @@ -127,230 +130,3 @@ pub fn endFrame(ctx: App.scheduler.Context) !void { 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]; -} diff --git a/src/system/health.zig b/src/system/health.zig new file mode 100644 index 0000000..e408c84 --- /dev/null +++ b/src/system/health.zig @@ -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(); + } +} diff --git a/src/system/inputs.zig b/src/system/inputs.zig index 541aa69..c864522 100644 --- a/src/system/inputs.zig +++ b/src/system/inputs.zig @@ -27,13 +27,33 @@ pub fn pollEvents(ctx: App.scheduler.Context) !void { var query = ctx.app.world.query(struct { player: *component.Player, + transform: *component.Transform, velocity: *component.Velocity, speed: *component.Speed, }, .{}); while (query.next()) |e| { - if (keyboard[sdl.SDL_SCANCODE_W]) e.velocity.value[1] = -e.speed.value; - 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; - if (keyboard[sdl.SDL_SCANCODE_D]) e.velocity.value[0] = e.speed.value; + e.transform.angle = 0; + + // --- UP --- + 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; + } } } diff --git a/src/system/juice.zig b/src/system/juice.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/system/root.zig b/src/system/root.zig index 97290a4..cbd8dee 100644 --- a/src/system/root.zig +++ b/src/system/root.zig @@ -3,3 +3,9 @@ pub const frame = @import("frame.zig"); pub const inputs = @import("inputs.zig"); pub const draw = @import("draw.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"); diff --git a/src/system/timer.zig b/src/system/timer.zig new file mode 100644 index 0000000..e0aff07 --- /dev/null +++ b/src/system/timer.zig @@ -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; + } + } + } +} diff --git a/src/system/transform.zig b/src/system/transform.zig new file mode 100644 index 0000000..230ceb3 --- /dev/null +++ b/src/system/transform.zig @@ -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; + } + } +}