diff --git a/src/app.zig b/src/app.zig index 33972ed..86b52dd 100644 --- a/src/app.zig +++ b/src/app.zig @@ -166,6 +166,7 @@ const AnimationDirection = enum { const Entity = struct { type: Type = .nil, flags: Flags = .{}, + seed: u64 = 0, target_entity: ?EntityId = null, targeted_by_entity: ?EntityId = null, @@ -180,7 +181,6 @@ const Entity = struct { tree_health: u32 = 0, tree_max_health: u32 = 0, - tree_seed: u64 = 0, station_spawn_tree_cooldown: Nanoseconds = 0, station_owned_tree: ?EntityId = null, @@ -189,7 +189,9 @@ const Entity = struct { wood_burning: bool = false, wood_health: f32 = 0, wood_max_health: f32 = 0, + wood_prev_carried: ?EntityId = null, wood_next_carried: ?EntityId = null, + wood_visual_pos: Vec2 = .init(0, 0), pos: Vec2 = .init(0, 0), vel: Vec2 = .init(0, 0), @@ -716,7 +718,7 @@ fn spawnTree(self: *App, station_id: EntityId) void { .type = .tree, .tree_health = health, .tree_max_health = health, - .tree_seed = rng.int(u64), + .seed = rng.int(u64), .pos = station.pos })) |id| { @@ -728,8 +730,11 @@ fn destroyTree(self: *App, tree_id: EntityId) void { const tree = self.entities.get(tree_id) orelse return; assert(tree.type == .tree); + var rng = self.prng.random(); + for (0..self.upgrades.tree_wood_amount) |_| { - self.spawnWood(tree.pos); + const offset = (rng.float(f32)-0.5)*2 * 30; + self.spawnWood(tree.pos.add(.init(offset, 0))); } self.entities.removeAssumeExists(tree_id); @@ -738,11 +743,15 @@ fn destroyTree(self: *App, tree_id: EntityId) void { fn spawnWood(self: *App, pos: Vec2) void { const health = self.upgrades.tree_wood_health; + const rng = self.prng.random(); + _ = self.spawnEntity(Entity{ .type = .wood, .pos = pos, + .seed = rng.int(u64), .wood_health = health, - .wood_max_health = health + .wood_max_health = health, + .wood_visual_pos = pos, }); } @@ -917,10 +926,16 @@ fn minionPushWood(self: *App, minion_id: EntityId, wood_id: EntityId) void { const minion = self.entities.get(minion_id) orelse return; const wood = self.entities.get(wood_id) orelse return; - wood.wood_carried_by = minion_id; + if (minion.minion_carried_wood_head) |head_id| { + if (self.entities.get(head_id)) |head| { + head.wood_prev_carried = wood_id; + } + } wood.wood_next_carried = minion.minion_carried_wood_head; - wood.flags.has_kinematics = false; minion.minion_carried_wood_head = wood_id; + + wood.wood_carried_by = minion_id; + wood.flags.has_kinematics = false; minion.minion_carried_wood_len += 1; } @@ -931,6 +946,11 @@ fn minionPopWood(self: *App, minion_id: EntityId) ?EntityId { minion.minion_carried_wood_len -= 1; if (self.entities.get(wood_id)) |wood| { minion.minion_carried_wood_head = wood.wood_next_carried; + if (minion.minion_carried_wood_head) |head_id| { + if (self.entities.get(head_id)) |head| { + head.wood_prev_carried = null; + } + } wood.wood_carried_by = null; wood.wood_next_carried = null; return wood_id; @@ -1427,6 +1447,23 @@ fn showDebug(self: *App, plt: Platform.Frame) !void { ImGUI.separator(); self.showDebugShopItems("Fire", self.shop.fire_upgrades); } + + if (ImGUI.beginTabItem("Wood")) { + defer ImGUI.endTabItem(); + + var minion_iter = self.initEntityIteratorByType(.minion); + while (minion_iter.next()) |minion_id| { + const minion = self.entities.getAssumeExists(minion_id); + ImGUI.text("{f}, wood_head: {?f}", .{minion_id, minion.minion_carried_wood_head}); + } + + ImGUI.separator(); + var wood_iter = self.initEntityIteratorByType(.wood); + while (wood_iter.next()) |wood_id| { + const wood = self.entities.getAssumeExists(wood_id); + ImGUI.text("{f}, carried_by: {?f}, next: {?f}", .{wood_id, wood.wood_carried_by, wood.wood_next_carried}); + } + } } } @@ -1775,8 +1812,21 @@ pub fn frame(self: *App, plt: Platform.Frame) !void { if (wood.wood_carried_by) |minion_id| { const minion = self.entities.getAssumeExists(minion_id); - wood.pos = minion.pos.add(.init(0, -50)); + + if (wood.wood_prev_carried) |prev_wood_id| { + const follow_distance = 20; + const next_wood = self.entities.getAssumeExists(prev_wood_id); + var dir = next_wood.pos.sub(wood.pos); + if (dir.length() > follow_distance) { + wood.pos = wood.pos.add(dir.normalized().multiplyScalar(dir.length() - follow_distance)); + } + } else { + wood.pos = minion.pos.add(.init(0, -60)); + } } + + wood.wood_visual_pos.x = std.math.lerp(wood.wood_visual_pos.x, wood.pos.x, 0.03); + wood.wood_visual_pos.y = std.math.lerp(wood.wood_visual_pos.y, wood.pos.y, 0.03); } } @@ -1799,7 +1849,7 @@ pub fn frame(self: *App, plt: Platform.Frame) !void { while (tree_iter.next()) |id| { const e = self.entities.getAssumeExists(id); - self.drawTree(e.tree_seed, e.pos); + self.drawTree(e.seed, e.pos); const progress = @as(f32, @floatFromInt(e.tree_health)) / @as(f32, @floatFromInt(e.tree_max_health)); drawProgressBar(progress, e.pos.add(.init(0, 20)), .init(40, 10), .rgb(0, 100, 20)); @@ -1915,7 +1965,26 @@ pub fn frame(self: *App, plt: Platform.Frame) !void { while (wood_iter.next()) |id| { const wood = self.entities.getAssumeExists(id); - Gfx.drawRectangle(wood.pos.sub(.init(10, 10)), .init(20, 20), .rgb(100, 100, 20)); + var prng = Prng.init(wood.seed); + const rng = prng.random(); + + const sprites = &[_]Gfx.Sprite.Id{ + self.terrain_sheet.get(125, 41), + self.terrain_sheet.get(125, 40), + self.terrain_sheet.get(125, 39), + }; + + var rect = Gfx.Quad.initRect( + terrain_tile_size.divideScalar(-2), + terrain_tile_size + ); + rect.applyRotate(rng.float(f32) * std.math.pi * 2, .init(0, 0)); + rect.applyOffset(wood.wood_visual_pos); + Gfx.drawSpriteQuad( + sprites[rng.uintLessThan(u32, sprites.len)], + rect, + .white + ); } } diff --git a/src/graphics.zig b/src/graphics.zig index 5dabf8a..8230612 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -2350,13 +2350,13 @@ pub const Quad = struct { }; } - pub fn rotate(self: *Quad, rad: f32, origin: Vec2) void { + pub fn applyRotate(self: *Quad, rad: f32, origin: Vec2) void { for (&self.positions) |*pos| { pos.* = pos.sub(origin).rotate(rad).add(origin); } } - pub fn offset(self: *Quad, vec2: Vec2) void { + pub fn applyOffset(self: *Quad, vec2: Vec2) void { for (&self.positions) |*pos| { pos.* = pos.add(vec2); }