This commit is contained in:
Rokas Puzonas 2026-01-31 19:23:48 +02:00
parent ba264d7bc5
commit c9543c72ad
4 changed files with 160 additions and 17 deletions

View File

@ -76,6 +76,13 @@ const Laser = struct {
duration: Nanoseconds
};
const Bomb = struct {
kinetic: Kinetic,
size: f32,
explode_at: Nanoseconds,
explosion_radius: f32
};
const Enemy = struct {
kinetic: Kinetic,
speed: f32,
@ -126,6 +133,7 @@ enemies: std.ArrayList(Enemy) = .empty,
player: Player = .{},
bullets: std.ArrayList(Bullet) = .empty,
lasers: std.ArrayList(Laser) = .empty,
bombs: std.ArrayList(Bomb) = .empty,
pickups: std.ArrayList(Pickup) = .empty,
next_pickup_spawn_at: Nanoseconds,
@ -164,6 +172,7 @@ pub fn deinit(self: *CombatScreen) void {
self.pickups.deinit(self.gpa);
self.spawned_waves.deinit(self.gpa);
self.lasers.deinit(self.gpa);
self.bombs.deinit(self.gpa);
}
pub fn spawnEnemy(self: *CombatScreen) !void {
@ -360,6 +369,15 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul
});
},
.bomb => {
try self.bombs.append(self.gpa, .{
.kinetic = .{
.pos = self.player.kinetic.pos,
.vel = bullet_dir.multiplyScalar(800)
},
.size = 10,
.explode_at = frame.time_ns + std.time.ns_per_s,
.explosion_radius = 50
});
},
.laser => {
try self.lasers.append(self.gpa, .{
@ -400,7 +418,12 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul
});
}
for (self.lasers.items) |*laser| {
{
var index: usize = 0;
while (index < self.lasers.items.len) {
var destroy = false;
var laser = &self.lasers.items[index];
const laser_length = 1000;
const laser_line = Line{
.p0 = laser.origin,
@ -415,12 +438,59 @@ pub fn tick(self: *CombatScreen, state: *State, frame: *Engine.Frame) !TickResul
}
}
if (frame.time_ns > (laser.created_at + laser.duration)) {
destroy = true;
}
frame.drawLine(
laser_line.p0,
laser_line.p1,
rgb(200, 20, 255),
laser.size
);
if (destroy) {
_ = self.lasers.swapRemove(index);
} else {
index += 1;
}
}
}
{
var index: usize = 0;
while (index < self.bombs.items.len) {
var destroy = false;
var bomb = &self.bombs.items[index];
bomb.kinetic.update(dt, .{
.friction = 0.998
});
if (frame.time_ns >= bomb.explode_at) {
destroy = true;
for (self.enemies.items) |*enemy| {
const enemy_rect = getCenteredRect(enemy.kinetic.pos, enemy.size);
if (enemy_rect.checkCircleOverlap(bomb.kinetic.pos, bomb.explosion_radius)) {
enemy.dead = true;
frame.drawCircle(bomb.kinetic.pos, bomb.explosion_radius, rgb(200, 20, 20));
}
}
}
frame.drawCircle(bomb.kinetic.pos, bomb.explosion_radius, rgba(200, 20, 20, 0.2));
const bomb_rect = getCenteredRect(bomb.kinetic.pos, bomb.size);
frame.drawRectangle(.{
.rect = bomb_rect,
.color = rgb(200, 20, 255)
});
if (destroy) {
_ = self.bombs.swapRemove(index);
} else {
index += 1;
}
}
}
for (self.enemies.items) |*enemy| {

View File

@ -230,6 +230,16 @@ pub fn popScissor(self: *Frame) void {
});
}
pub fn drawCircle(self: *Frame, center: Vec2, radius: f32, color: Vec4) void {
self.pushGraphicsCommand(.{
.draw_circle = .{
.center = center,
.radius = radius,
.color = color,
}
});
}
pub fn drawRectangle(self: *Frame, opts: GraphicsCommand.DrawRectangle) void {
self.pushGraphicsCommand(.{ .draw_rectangle = opts });
}

View File

@ -48,6 +48,12 @@ pub const Command = union(enum) {
origin: Vec2 = .init(0, 0),
};
pub const DrawCircle = struct {
center: Vec2,
radius: f32,
color: Vec4,
};
set_scissor: Rect,
draw_rectangle: DrawRectangle,
draw_line: struct { pos1: Vec2, pos2: Vec2, color: Vec4, width: f32 },
@ -60,6 +66,7 @@ pub const Command = union(enum) {
},
push_transformation: struct { translation: Vec2, scale: Vec2 },
pop_transformation: void,
draw_circle: DrawCircle
};
const Texture = struct {
@ -206,6 +213,25 @@ pub fn drawCommand(command: Command) void {
font_context.setColor(color);
font_context.drawText(opts.pos.x * font_resolution_scale.x, opts.pos.y * font_resolution_scale.y, opts.text);
},
.draw_circle => |opts| {
sgl.beginTriangleStrip();
defer sgl.end();
const angle_step: f32 = std.math.pi * 2.0 / 32.0;
sgl.c4f(opts.color.x, opts.color.y, opts.color.z, opts.color.w);
sgl.v2f(opts.center.x, opts.center.y);
var angle: f32 = 0;
while (angle < std.math.pi * 2) {
const point_on_circle = Vec2.initAngle(angle).multiplyScalar(opts.radius).add(opts.center);
sgl.v2f(point_on_circle.x, point_on_circle.y);
sgl.v2f(opts.center.x, opts.center.y);
angle += angle_step;
}
sgl.v2f(opts.center.x + opts.radius, opts.center.y);
}
}
}

View File

@ -472,6 +472,43 @@ pub const Rect = struct {
return left_overlap or right_overlap or top_overlap or bottom_overlap;
}
pub fn checkCircleOverlap(self: Rect, circle_center: Vec2, circle_radius: f32) bool {
const cx = circle_center.x;
const cy = circle_center.y;
const rx = self.pos.x;
const ry = self.pos.y;
const rw = self.size.x;
const rh = self.size.y;
// temporary variables to set edges for testing
var testX = cx;
var testY = cy;
// which edge is closest?
if (cx < rx) {
testX = rx; // test left edge
} else if (cx > rx+rw) {
testX = rx+rw; // right edge
}
if (cy < ry) {
testY = ry; // top edge
} else if (cy > ry+rh) {
testY = ry+rh; // bottom edge
}
// get distance from closest edges
const distX = cx-testX;
const distY = cy-testY;
const distance = @sqrt( (distX*distX) + (distY*distY) );
// if the distance is less than the radius, collision!
if (distance <= circle_radius) {
return true;
}
return false;
}
pub fn hasOverlap(lhs: Rect, rhs: Rect) bool {
return (lhs.left() < rhs.right() and lhs.right() > rhs.left()) and
(lhs.top() < rhs.bottom() and lhs.bottom() > rhs.top());