add enemy spawning
This commit is contained in:
parent
4378f4421b
commit
8645f732c5
@ -257,7 +257,7 @@ pub const DrawTextOptions = struct {
|
||||
color: Vec4 = rgb(255, 255, 255),
|
||||
};
|
||||
|
||||
pub fn drawText(self: *Frame, position: Vec2, text: []const u8, opts: DrawTextOptions) void {
|
||||
pub fn drawText(self: *Frame, position: Vec2, opts: DrawTextOptions, text: []const u8) void {
|
||||
const arena = self.arena.allocator();
|
||||
const text_dupe = arena.dupe(u8, text) catch |e| {
|
||||
log.warn("Failed to draw text: {}", .{e});
|
||||
@ -275,6 +275,30 @@ pub fn drawText(self: *Frame, position: Vec2, text: []const u8, opts: DrawTextOp
|
||||
});
|
||||
}
|
||||
|
||||
pub fn drawTextFormat(
|
||||
self: *Frame,
|
||||
position: Vec2,
|
||||
opts: DrawTextOptions,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype
|
||||
) void {
|
||||
const arena = self.arena.allocator();
|
||||
const text = std.fmt.allocPrint(arena, fmt, args) catch |e| {
|
||||
log.warn("Failed to draw text: {}", .{e});
|
||||
return;
|
||||
};
|
||||
|
||||
self.pushGraphicsCommand(.{
|
||||
.draw_text = .{
|
||||
.pos = position,
|
||||
.text = text,
|
||||
.size = opts.size,
|
||||
.font = opts.font,
|
||||
.color = opts.color,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn pushTransform(self: *Frame, translation: Vec2, scale: Vec2) void {
|
||||
self.pushGraphicsCommand(.{
|
||||
.push_transformation = .{
|
||||
|
||||
@ -48,6 +48,12 @@ pub const Vec2 = extern struct {
|
||||
return .init(@floatFromInt(x), @floatFromInt(y));
|
||||
}
|
||||
|
||||
pub fn initRandomRect(rng: std.Random, rect: Rect) Vec2 {
|
||||
const x = Range.init(rect.left(), rect.right()).random(rng);
|
||||
const y = Range.init(rect.top(), rect.bottom()).random(rng);
|
||||
return init(x, y);
|
||||
}
|
||||
|
||||
pub fn initAngle(angle: f32) Vec2 {
|
||||
return Vec2{
|
||||
.x = @cos(angle),
|
||||
@ -431,6 +437,24 @@ pub const Rect = struct {
|
||||
return (lhs.left() < rhs.right() and lhs.right() > rhs.left()) and
|
||||
(lhs.top() < rhs.bottom() and lhs.bottom() > rhs.top());
|
||||
}
|
||||
|
||||
pub fn growX(self: Rect, amount: f32) Rect {
|
||||
return Rect{
|
||||
.pos = .init(self.pos.x - amount, self.pos.y),
|
||||
.size = .init(self.size.x + 2*amount, self.size.y)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn growY(self: Rect, amount: f32) Rect {
|
||||
return Rect{
|
||||
.pos = .init(self.pos.x, self.pos.y - amount),
|
||||
.size = .init(self.size.x, self.size.y + 2*amount)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn grow(self: Rect, amount: f32) Rect {
|
||||
return self.growX(self.growY(amount), amount);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Line = struct {
|
||||
|
||||
151
src/game.zig
151
src/game.zig
@ -26,6 +26,7 @@ const Sprite = Engine.Graphics.Sprite;
|
||||
const RaycastTileIterator = @import("./raycast_tile_iterator.zig");
|
||||
|
||||
const Game = @This();
|
||||
const world_size = Vec2.init(20 * 16, 15 * 16);
|
||||
|
||||
const RNGState = std.Random.DefaultPrng;
|
||||
|
||||
@ -74,6 +75,27 @@ const Enemy = struct {
|
||||
dead: bool = false
|
||||
};
|
||||
|
||||
const Wave = struct {
|
||||
started_at: Nanoseconds,
|
||||
duration: Nanoseconds,
|
||||
enemies_spawned: u32,
|
||||
total_enemies: u32,
|
||||
|
||||
const Info = struct {
|
||||
enemies: u32,
|
||||
duration_s: u32,
|
||||
starts_at_s: u32,
|
||||
};
|
||||
};
|
||||
|
||||
const wave_infos = [_]Wave.Info{
|
||||
.{
|
||||
.enemies = 10,
|
||||
.duration_s = 1,
|
||||
.starts_at_s = 0
|
||||
}
|
||||
};
|
||||
|
||||
arena: std.heap.ArenaAllocator,
|
||||
gpa: Allocator,
|
||||
rng: RNGState,
|
||||
@ -83,6 +105,11 @@ player: Player = .{},
|
||||
bullets: std.ArrayList(Bullet) = .empty,
|
||||
enemies: std.ArrayList(Enemy) = .empty,
|
||||
|
||||
wave_timer: Nanoseconds = 0,
|
||||
|
||||
waves: std.ArrayList(Wave) = .empty,
|
||||
spawned_waves: std.ArrayList(usize) = .empty,
|
||||
|
||||
pub fn init(gpa: Allocator, seed: u64, assets: *Assets) !Game {
|
||||
var arena = std.heap.ArenaAllocator.init(gpa);
|
||||
errdefer arena.deinit();
|
||||
@ -104,6 +131,8 @@ pub fn deinit(self: *Game) void {
|
||||
self.arena.deinit();
|
||||
self.bullets.deinit(self.gpa);
|
||||
self.enemies.deinit(self.gpa);
|
||||
self.waves.deinit(self.gpa);
|
||||
self.spawned_waves.deinit(self.gpa);
|
||||
}
|
||||
|
||||
fn getCenteredRect(pos: Vec2, size: f32) Rect {
|
||||
@ -113,11 +142,102 @@ fn getCenteredRect(pos: Vec2, size: f32) Rect {
|
||||
};
|
||||
}
|
||||
|
||||
fn spawnEnemy(self: *Game) !void {
|
||||
const spawn_area_margin = 20;
|
||||
const spawn_area_size = 10;
|
||||
|
||||
const top_spawn_area = (Rect{
|
||||
.pos = .init(0, -spawn_area_size - spawn_area_margin),
|
||||
.size = .init(world_size.x, spawn_area_size)
|
||||
}).growX(spawn_area_size);
|
||||
|
||||
const bottom_spawn_area = (Rect{
|
||||
.pos = .init(0, world_size.y + spawn_area_margin),
|
||||
.size = .init(world_size.x, spawn_area_size)
|
||||
}).growX(spawn_area_size);
|
||||
|
||||
const left_spawn_area = (Rect{
|
||||
.pos = .init(-spawn_area_margin-spawn_area_size, 0),
|
||||
.size = .init(spawn_area_size, world_size.y)
|
||||
}).growY(spawn_area_size);
|
||||
|
||||
const right_spawn_area = (Rect{
|
||||
.pos = .init(world_size.x + spawn_area_margin, 0),
|
||||
.size = .init(spawn_area_size, world_size.y)
|
||||
}).growY(spawn_area_size);
|
||||
|
||||
const spawn_areas = [_]Rect{
|
||||
top_spawn_area,
|
||||
bottom_spawn_area,
|
||||
left_spawn_area,
|
||||
right_spawn_area
|
||||
};
|
||||
|
||||
const rand = self.rng.random();
|
||||
const spawn_area = spawn_areas[rand.uintLessThan(usize, spawn_areas.len)];
|
||||
const pos = Vec2.initRandomRect(rand, spawn_area);
|
||||
|
||||
try self.enemies.append(self.gpa, .{
|
||||
.kinetic = .{ .pos = pos },
|
||||
.speed = 10,
|
||||
.size = 20
|
||||
});
|
||||
}
|
||||
|
||||
pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
||||
const dt = frame.deltaTime();
|
||||
|
||||
const canvas_size = Vec2.init(20 * 16, 15 * 16);
|
||||
frame.graphics.canvas_size = canvas_size;
|
||||
self.wave_timer += frame.dt_ns;
|
||||
const wave_timer_s = @divFloor(self.wave_timer, std.time.ns_per_s);
|
||||
|
||||
for (0.., wave_infos) |i, wave_info| {
|
||||
if (std.mem.indexOfScalar(usize, self.spawned_waves.items, i) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wave_info.starts_at_s > wave_timer_s) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try self.spawned_waves.append(self.gpa, i);
|
||||
try self.waves.append(self.gpa, .{
|
||||
.started_at = self.wave_timer,
|
||||
.duration = wave_info.duration_s * std.time.ns_per_s,
|
||||
.enemies_spawned = 0,
|
||||
.total_enemies = wave_info.enemies
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
var index: usize = 0;
|
||||
while (index < self.waves.items.len) {
|
||||
var wave_complete = false;
|
||||
const wave = &self.waves.items[index];
|
||||
|
||||
const wave_time_passed: f32 = @floatFromInt(self.wave_timer - wave.started_at);
|
||||
const wave_duration: f32 = @floatFromInt(wave.duration);
|
||||
const percent_complete = std.math.clamp(wave_time_passed / wave_duration, 0, 1);
|
||||
const wave_total_enemies: f32 = @floatFromInt(wave.total_enemies);
|
||||
const expected_enemies: u32 = @intFromFloat(wave_total_enemies * percent_complete);
|
||||
|
||||
while (wave.enemies_spawned < expected_enemies) {
|
||||
try self.spawnEnemy();
|
||||
wave.enemies_spawned += 1;
|
||||
}
|
||||
|
||||
if (wave.enemies_spawned == wave.total_enemies) {
|
||||
wave_complete = true;
|
||||
}
|
||||
|
||||
if (wave_complete) {
|
||||
_ = self.waves.swapRemove(index);
|
||||
} else {
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frame.graphics.canvas_size = world_size;
|
||||
|
||||
if (frame.isKeyPressed(.F3)) {
|
||||
frame.show_debug = !frame.show_debug;
|
||||
@ -128,7 +248,7 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
||||
}
|
||||
|
||||
frame.drawRectangle(.{
|
||||
.rect = .init(0, 0, canvas_size.x, canvas_size.y),
|
||||
.rect = .init(0, 0, world_size.x, world_size.y),
|
||||
.color = rgb(20, 20, 20)
|
||||
});
|
||||
|
||||
@ -183,16 +303,6 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
||||
.speed = 50
|
||||
});
|
||||
}
|
||||
|
||||
if (frame.isMousePressed(.right)) {
|
||||
try self.enemies.append(self.gpa, .{
|
||||
.kinetic = .{
|
||||
.pos = mouse,
|
||||
},
|
||||
.speed = 10,
|
||||
.size = 20
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
frame.drawRectangle(.{
|
||||
@ -253,6 +363,14 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const text_opts = Engine.Frame.DrawTextOptions{
|
||||
.font = self.assets.font_id.get(.regular)
|
||||
};
|
||||
frame.drawTextFormat(.init(10, 10), text_opts, "{d:02}:{d:02}", .{
|
||||
@divFloor(wave_timer_s, 60),
|
||||
@mod(wave_timer_s, 60)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn debug(self: *Game) !void {
|
||||
@ -265,5 +383,10 @@ pub fn debug(self: *Game) !void {
|
||||
}
|
||||
defer imgui.endWindow();
|
||||
|
||||
_ = self;
|
||||
if (imgui.button("Spawn enemy")) {
|
||||
try self.spawnEnemy();
|
||||
}
|
||||
|
||||
imgui.textFmt("Waves: {}\n", .{self.waves.items.len});
|
||||
imgui.textFmt("Enemies: {}\n", .{self.enemies.items.len});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user