From d07d2e1a4002cb8dd0bb89ccc2f95cd31a1fb54f Mon Sep 17 00:00:00 2001 From: Ivory Date: Sat, 15 Feb 2025 06:17:46 -0500 Subject: [PATCH] idk man --- build.zig | 50 ++++- src/Pawn.zig | 52 ----- src/engine/Debug.zig | 4 +- src/engine/Texture.zig | 12 +- src/engine/geometry/Vec2i.zig | 9 + src/hadean/PathFinder.zig | 272 ++++++++++++++++++++++++ src/hadean/Tile.zig | 0 src/hadean/WorldGenerator.zig | 0 src/hadean/{ => entities}/Camera.zig | 1 + src/hadean/entities/Chunk.zig | 0 src/hadean/entities/Pawn.zig | 69 ++++++ src/hadean/{ => entities}/Selection.zig | 0 src/hadean/{ => entities}/Terrain.zig | 62 ++++-- src/hadean/hadean.zig | 14 ++ src/hadean/main.zig | 15 -- src/hadean/scenes.zig | 16 +- src/main.zig | 13 ++ 17 files changed, 486 insertions(+), 103 deletions(-) delete mode 100644 src/Pawn.zig create mode 100644 src/hadean/PathFinder.zig create mode 100644 src/hadean/Tile.zig create mode 100644 src/hadean/WorldGenerator.zig rename src/hadean/{ => entities}/Camera.zig (99%) create mode 100644 src/hadean/entities/Chunk.zig create mode 100644 src/hadean/entities/Pawn.zig rename src/hadean/{ => entities}/Selection.zig (100%) rename src/hadean/{ => entities}/Terrain.zig (83%) create mode 100644 src/hadean/hadean.zig delete mode 100644 src/hadean/main.zig create mode 100644 src/main.zig diff --git a/build.zig b/build.zig index bf9a4d0..68f3153 100644 --- a/build.zig +++ b/build.zig @@ -5,16 +5,17 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + // === [ C IMPORTS ] === const c = b.addModule("c", .{ .root_source_file = b.path("src/c.zig"), .target = target, .optimize = optimize, }); - // c.addLibraryPath(b.path("/usr/local/lib")); c.linkSystemLibrary("SDL3", .{}); c.addIncludePath(b.path("open-simplex/include")); c.linkSystemLibrary("SDL3_image", .{}); + // === [ ENGINE ] === const engine = b.addModule("engine", .{ .root_source_file = b.path("src/engine/root.zig"), .target = target, @@ -23,14 +24,27 @@ pub fn build(b: *std.Build) void { engine.addImport("engine", engine); engine.addImport("c", c); - const exe = b.addExecutable(.{ - .name = "hadean", - .root_source_file = b.path("src/hadean/main.zig"), + // === [ GAME CODE ] === + const hadean = b.addModule("hadean", .{ + .root_source_file = b.path("src/hadean/hadean.zig"), .target = target, .optimize = optimize, }); + hadean.addImport("c", c); + hadean.addImport("engine", engine); + hadean.addImport("hadean", hadean); + + // === [ EXECUTABLE ] === + const exe = b.addExecutable(.{ + .name = "hadean", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + exe.root_module.addImport("engine", engine); + exe.root_module.addImport("hadean", hadean); exe.root_module.addImport("c", c); exe.addCSourceFile(.{ .file = b.path("open-simplex/src/OpenSimplex2F.c") }); exe.addIncludePath(b.path("open-simplex/include")); @@ -49,14 +63,28 @@ pub fn build(b: *std.Build) void { "assets/textures.png" )).step); + + b.installArtifact(exe); - const run_cmd = b.addRunArtifact(exe); - run_cmd.step.dependOn(b.getInstallStep()); - if (b.args) |args| { - run_cmd.addArgs(args); - } - const run_step = b.step("run", "Run the app"); - run_step.dependOn(&run_cmd.step); + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const pathfinder_perf_test = b.addTest(.{ + .root_source_file = b.path("src/hadean/PathFinder.zig"), + .target = target, + .optimize = optimize, + }); + pathfinder_perf_test.root_module.addImport("engine", engine); + const run_pathfinder_perf_test = b.addRunArtifact(pathfinder_perf_test); + + + const test_step = b.step("pathfinder_perf_test", "Run PathFinder Performance Test"); + test_step.dependOn(&run_pathfinder_perf_test.step); // because engine and hadean are diff modules their c modules // get different caches, causing opaques to be different between them. diff --git a/src/Pawn.zig b/src/Pawn.zig deleted file mode 100644 index 662a03d..0000000 --- a/src/Pawn.zig +++ /dev/null @@ -1,52 +0,0 @@ -const Pawn = @This(); - -const std = @import("std"); -const Entity = @import("Entity.zig"); -const Scene = @import("Scene.zig"); -const Camera = @import("Camera.zig"); -const Terrain = @import("Terrain.zig"); -const Tag = @import("Tag.zig"); -const Vec2i = @import("geometry/Vec2i.zig"); -const assets = @import("assets.zig"); -const Layer = @import("Layer.zig"); -const Color = @import("Color.zig"); -var prng = std.rand.DefaultPrng.init(0); - -camera: *const Camera = undefined, -position: Vec2i = Vec2i.ZERO, - -pub fn random() !*Pawn { - var self: Pawn = Pawn {}; - self.position = Vec2i.create( - @intFromFloat(@floor(prng.random().float(f32) * @as(f32, @floatFromInt(Terrain.CHUNK_SIZE)))), - @intFromFloat(@floor(prng.random().float(f32) * @as(f32, @floatFromInt(Terrain.CHUNK_SIZE)))) - ); - return try Scene.EntityPool.allocate(self); -} - -pub fn create() !*Pawn { - const self: Pawn = Pawn {}; - return try Scene.allocate(self); -} - -pub fn start(self: *Pawn, scene: *const Scene) void { - self.camera = scene.get(Camera.TAG, Camera); -} - -pub fn destroy(self: *const Pawn) void { - Scene.EntityPool.deallocate(self); -} - -pub fn entity(self: *Pawn) Entity { - return Entity.init(self, .{}); -} - -// pub fn tile(self: *Pawn) Tile { -// return Tile.init(.{ -// .solid = true -// }); -// } - -pub fn draw(self: *const Pawn) void { - self.camera.draw_sprite_i(assets.pawn, self.position.to_unit_recti(), Layer.ENTITIES, Color.WHITE); -} diff --git a/src/engine/Debug.zig b/src/engine/Debug.zig index 79edefe..5de5c76 100644 --- a/src/engine/Debug.zig +++ b/src/engine/Debug.zig @@ -3,8 +3,8 @@ const c = @import("c"); const engine = @import("engine"); const Control = engine.Control; -pub const camera = true; -pub const mouse = true; +pub var camera = true; +pub var mouse = true; // later make this expandable in some way using an allocator. diff --git a/src/engine/Texture.zig b/src/engine/Texture.zig index b6309e0..9ae1137 100644 --- a/src/engine/Texture.zig +++ b/src/engine/Texture.zig @@ -34,11 +34,21 @@ pub fn create(renderer: *c.SDL_Renderer, path: []const u8) Texture { // @panic("Failed to create IO for Texture"); // }; // c.IMG_Load() - const surface: *c.SDL_Surface = c.IMG_Load(new_path) orelse { + var surface: *c.SDL_Surface = c.IMG_Load(new_path) orelse { std.debug.print("SDL Error: {s}\n", .{ c.SDL_GetError() }); @panic("Failed to create Surface for Texture"); }; + + const keys = [_]u32 { 0xFFFF00FF, 0xFF0000FF, }; + for (keys) |key| { + _ = c.SDL_SetSurfaceColorKey(surface, true, key); + const new_surface: *c.SDL_Surface = c.SDL_CreateSurface(surface.w, surface.h, surface.format); + _ = c.SDL_BlitSurface(surface, null, new_surface, null); + c.SDL_DestroySurface(surface); + surface = new_surface; + } defer c.SDL_DestroySurface(surface); + const texture = c.SDL_CreateTextureFromSurface(renderer, surface) orelse { std.debug.print("SDL Error: {s}\n", .{ c.SDL_GetError() }); @panic("Failed to create Texture"); diff --git a/src/engine/geometry/Vec2i.zig b/src/engine/geometry/Vec2i.zig index ace83dc..db49127 100644 --- a/src/engine/geometry/Vec2i.zig +++ b/src/engine/geometry/Vec2i.zig @@ -96,6 +96,15 @@ pub fn to_vec2f(self: *const Vec2i) Vec2f { }; } +pub fn distance_from(self: Vec2i, other: Vec2i) f32 { + const x: f32 = @floatFromInt(@abs(self.x - other.x)); + const y: f32 = @floatFromInt(@abs(self.y - other.y)); + return @sqrt(x * x + y * y); +} + +pub fn eql(self: Vec2i, other: Vec2i) bool { + return self.x == other.x and self.y == other.y; +} pub const ZERO = create(0, 0); pub const ONE = create(1, 1); diff --git a/src/hadean/PathFinder.zig b/src/hadean/PathFinder.zig new file mode 100644 index 0000000..3953471 --- /dev/null +++ b/src/hadean/PathFinder.zig @@ -0,0 +1,272 @@ +const engine = @import("engine"); +const std = @import("std"); +const Vec2i = engine.geometry.Vec2i; +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; +const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator; +const ArrayListUnmanaged = std.ArrayListUnmanaged; + +var gpa = GeneralPurposeAllocator(.{}) {}; +var nodes_arena = ArenaAllocator.init(gpa.allocator()); + + +pub const Surface = struct { + const VTable = struct { + _is_pathable: *const fn (*const anyopaque, Vec2i) bool, + _is_valid: *const fn (*const anyopaque, Vec2i) bool, + }; + + ptr: *const anyopaque, + vtable: VTable, + + fn InputFunctions(comptime T: type) type { + return struct { + _is_pathable: fn (T, Vec2i) bool, + _is_valid: fn (T, Vec2i) bool, + }; + } + + pub fn from(impl: anytype, functions: InputFunctions(@TypeOf(impl))) Surface { + const PtrType = @TypeOf(impl); + + const gen = struct { + pub fn _is_pathable(ptr: *const anyopaque, pos: Vec2i) bool { + const self: PtrType = @ptrCast(@alignCast(ptr)); + return functions._is_pathable(self, pos); + } + pub fn _is_valid(ptr: *const anyopaque, pos: Vec2i) bool { + const self: PtrType = @ptrCast(@alignCast(ptr)); + return functions._is_valid(self, pos); + } + }; + + return Surface { + .ptr = impl, + .vtable = .{ + ._is_valid = gen._is_valid, + ._is_pathable = gen._is_pathable, + } + }; + } + + pub fn is_valid(self: Surface, pos: Vec2i) bool { + return self.vtable._is_valid(self.ptr, pos); + } + + pub fn is_pathable(self: Surface, pos: Vec2i) bool { + return self.vtable._is_pathable(self.ptr, pos); + } +}; + +const Node = struct { + pos: Vec2i, + g: u32, + h: u32, + parent: ?*Node, + + pub const HeapContext = struct {}; + + pub fn compare(_: HeapContext, a: *const Node, b: *const Node) std.math.Order { + if (a.cost() < b.cost()) return std.math.Order.lt; + if (a.cost() > b.cost()) return std.math.Order.gt; + return std.math.Order.eq; + } + + pub fn get_key(self: *const Node) Vec2i { + return self.pos; + } + + pub fn to_vec2i(self: Node) Vec2i { + return Vec2i { .x = self.x, .y = self.y }; + } + + pub fn cost(self: Node) u32 { + return self.g + self.h; + } + + pub fn asc(self: *const Node, other: *const Node) bool { + return self.cost() < other.cost(); + } +}; + +fn heuristic(src: Vec2i, dst: Vec2i) u32 { + return @intFromFloat(@floor(src.distance_from(dst) * 10.0)); +} + +fn get_neighbor_positions(from: Vec2i) [4]Vec2i { + return .{ + from.add(Vec2i.NORTH), + from.add(Vec2i.EAST), + from.add(Vec2i.SOUTH), + from.add(Vec2i.WEST), + }; +} + +// this function ONLY works for adjacent / diagonal positions +fn get_cost_between(src: Vec2i, dst: Vec2i) u32 { + if (src.x == dst.x or src.y == dst.y) return 10; + return 14; +} + +fn HashedPriorityQueue( + comptime K: type, + comptime V: type, + comptime PriorityContext: type, + comptime compare_fn: fn (context: PriorityContext, V, V) std.math.Order, + comptime value_to_key: fn (V) K, +) type { + return struct { + const Self = @This(); + + priority_queue: std.PriorityQueue(V, PriorityContext, compare_fn), + hash_map: std.AutoHashMap(K, V), + + pub fn init(allocator: Allocator) Self { + return Self { + .priority_queue = std.PriorityQueue(V, PriorityContext, compare_fn).init(allocator, .{}), + .hash_map = std.AutoHashMap(K, V).init(allocator), + }; + } + + pub fn put(self: *Self, key: K, value: V) !void { + try self.hash_map.put(key, value); + try self.priority_queue.add(value); + } + + pub fn contains_key(self: Self, key: K) bool { + return self.hash_map.contains(key); + } + + pub fn pop(self: *Self) V { + const value: V = self.priority_queue.remove(); + const key = value_to_key(value); + _ = self.hash_map.remove(key); + return value; + } + + pub fn get(self: Self, key: K) ?V { + return self.hash_map.get(key); + } + + pub fn count(self: Self) usize { + return self.priority_queue.items.len; + } + }; +} + +pub fn get_path(return_allocator: Allocator, surface: Surface, src: Vec2i, dst: Vec2i) !?[]Vec2i { + // std.debug.print("pathfinding ({}, {}) => ({}, {})\n", .{ src.x, src.y, dst.x, dst.y }); + defer _ = nodes_arena.reset(.retain_capacity); + const allocator = nodes_arena.allocator(); + + var open = HashedPriorityQueue(Vec2i, *Node, Node.HeapContext, Node.compare, Node.get_key).init(allocator); + var pos_lookup_table = std.AutoHashMap(Vec2i, *Node).init(allocator); + var closed = std.AutoHashMap(Vec2i, *Node).init(allocator); + + const starting_node: *Node = try allocator.create(Node); + starting_node.* = Node { + .pos = src, + .g = 0, + .h = heuristic(src, dst), + .parent = null, + }; + try pos_lookup_table.put(src, starting_node); + try open.put(starting_node.pos, starting_node); + + while(open.count() != 0) { + const current_node: *Node = open.pop(); + try closed.put(current_node.pos, current_node); + + if (current_node.pos.eql(dst)) { + var reverse_path = std.ArrayList(*Node).init(allocator); + + var walk_node: *Node = current_node; + while (walk_node.parent != null) { + try reverse_path.append(walk_node); + walk_node = walk_node.parent.?; + } + var path = std.ArrayList(Vec2i).init(return_allocator); + for (0..reverse_path.items.len) |rev_idx| { + const idx = reverse_path.items.len - 1 - rev_idx; + try path.append(reverse_path.items[idx].*.pos); + } + + return try path.toOwnedSlice(); + } + + const neighbor_positions = get_neighbor_positions(current_node.pos); + + for (neighbor_positions) |pos| { + if (pos.eql(current_node.pos)) continue; + if (!surface.is_valid(pos)) continue; + if (!surface.is_pathable(pos)) continue; + + // the new g cost of this node is the g cost of the parent node + the g + // cost from the parent node, to this node. + const gCost: u32 = current_node.g + get_cost_between(current_node.pos, pos); + + if (pos_lookup_table.contains(pos)) { + const known_node: *Node = pos_lookup_table.get(pos) orelse unreachable; + if (gCost < known_node.g) { + known_node.*.g = gCost; + known_node.*.parent = current_node; + + if(closed.contains(pos)) _ = closed.remove(pos); + if(!open.contains_key(pos)) try open.put(pos, known_node); + } + } else { + const hCost: u32 = heuristic(pos, dst); + const new_node: *Node = try allocator.create(Node); + new_node.* = Node { + .pos = pos, + .g = gCost, + .h = hCost, + .parent = current_node, + }; + try pos_lookup_table.put(pos, new_node); + try open.put(pos, new_node); + } + } + } + return null; +} + + + +test "PathFinder" { + var test_gpa = std.heap.GeneralPurposeAllocator(.{}) {}; + var allocator = test_gpa.allocator(); + + const Terrain = struct { + const Terrain = @This(); + + pub fn is_pathable(self: *const Terrain, pos: Vec2i) bool { + return Terrain.is_valid(self, pos); + } + + pub fn is_valid(self: *const Terrain, pos: Vec2i) bool { + _ = self; + return pos.x >= 0 and pos.x < 100 and pos.y >= 0 and pos.y < 100; + } + }; + + const terrain = Terrain {}; + const surface: Surface = Surface.from(&terrain, .{ + ._is_pathable = Terrain.is_pathable, + ._is_valid = Terrain.is_valid, + }); + + const start: std.time.Instant = try std.time.Instant.now(); + const iterations = 1000; + for (0..iterations) |_| { + const path: []Vec2i = try get_path(allocator, surface, Vec2i.ZERO, Vec2i.ONE.scale(40)) orelse continue; + allocator.free(path); + } + const end = try std.time.Instant.now(); + const elapsed: f32 = @floatFromInt(end.since(start)); + const average: f32 = elapsed / @as(f32, @floatFromInt(iterations)); + const ms: f32 = average / 1_000_000.0; + std.debug.print("average path time: {d:.2}ms\n", .{ ms }); + + try std.testing.expect(true); +} diff --git a/src/hadean/Tile.zig b/src/hadean/Tile.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/hadean/WorldGenerator.zig b/src/hadean/WorldGenerator.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/hadean/Camera.zig b/src/hadean/entities/Camera.zig similarity index 99% rename from src/hadean/Camera.zig rename to src/hadean/entities/Camera.zig index 5bb1d16..650abb3 100644 --- a/src/hadean/Camera.zig +++ b/src/hadean/entities/Camera.zig @@ -156,6 +156,7 @@ pub fn set_focus(self: *Camera, f: Vec2f) void { pub fn mouse_properties(_: *const Camera) MouseListener.Properties { return MouseListener.Properties { .area = Recti.from_xywh(0, 0, engine.Control.window_width, engine.Control.window_height), + // .area = Recti.from_xywh(100, 100, 100, 100), .layer = .CAMERA, .button = .Middle, .click_through = true, diff --git a/src/hadean/entities/Chunk.zig b/src/hadean/entities/Chunk.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/hadean/entities/Pawn.zig b/src/hadean/entities/Pawn.zig new file mode 100644 index 0000000..10087b7 --- /dev/null +++ b/src/hadean/entities/Pawn.zig @@ -0,0 +1,69 @@ +const Pawn = @This(); + +const std = @import("std"); +const engine = @import("engine"); +const hadean = @import("hadean"); +const Camera = hadean.entities.Camera; +const Layer = engine.Layer; +const Color = engine.Color; +const Terrain = hadean.entities.Terrain; +const Chunk = hadean.entities.Terrain.Chunk; +const assets = hadean.assets; +const Vec2i = engine.geometry.Vec2i; +const Allocator = std.mem.Allocator; +const Tag = engine.Tag; +const Scene = engine.Scene; + +var prng = std.rand.DefaultPrng.init(0); + +camera: *const Camera = undefined, +terrain: *const Terrain = undefined, +scene: *const Scene = undefined, + +pos: Vec2i = Vec2i.ZERO, +randomize_position_on_start: bool = true, + +current_path: ?[]Vec2i = null, + +pub fn random(allocator: Allocator) !*Pawn { + const self: *Pawn = try allocator.create(Pawn); + self.* = .{}; + return self; +} + +pub fn start(self: *Pawn, scene: *const Scene) void { + self.scene = scene; + self.camera = scene.get(Tag.CAMERA, Camera); + self.terrain = scene.get(Tag.TERRAIN, Terrain); + + if (self.randomize_position_on_start) { + const chunk: *const Chunk = self.terrain.get_random_chunk(); + const pos: Vec2i = chunk.get_random_pos_absolute(); + self.pos = pos; + } +} + +pub fn update(self: *Pawn, _: f32) void { + const dst = self.terrain.get_random_chunk().get_random_pos_absolute(); + const surface = self.terrain.surface(); + const allocator = self.scene.arena.allocator(); + const new_path = hadean.PathFinder.get_path(allocator, surface, self.pos, dst) + catch unreachable + orelse unreachable; + + defer allocator.free(new_path); +} + +pub fn draw(self: *const Pawn, layer: Layer) void { + switch(layer) { + .ENTITIES => { + self.camera.draw_sprite_i(assets.pawn, self.pos.to_unit_recti(), Color.WHITE); + }, + else => {}, + } +} + +fn set_path(self: *Pawn, path: []Vec2i) void { + if (self.current_path != null) self.scene.arena.allocator().destroy(self.current_path); + self.current_path = path; +} diff --git a/src/hadean/Selection.zig b/src/hadean/entities/Selection.zig similarity index 100% rename from src/hadean/Selection.zig rename to src/hadean/entities/Selection.zig diff --git a/src/hadean/Terrain.zig b/src/hadean/entities/Terrain.zig similarity index 83% rename from src/hadean/Terrain.zig rename to src/hadean/entities/Terrain.zig index 32a2c14..9ed1cdf 100644 --- a/src/hadean/Terrain.zig +++ b/src/hadean/entities/Terrain.zig @@ -3,6 +3,7 @@ const Terrain = @This(); const engine = @import("engine"); const std = @import("std"); const c = @import("c"); +const hadean = @import("hadean"); const Scene = engine.Scene; const Entity = engine.Entity; const Vec2i = engine.geometry.Vec2i; @@ -12,11 +13,11 @@ const Recti = engine.geometry.Recti; const Noise = engine.Noise; const Layer = engine.Layer; const Allocator = std.mem.Allocator; +const Camera = hadean.entities.Camera; +const assets = hadean.assets; var prng = std.rand.DefaultPrng.init(0); -const Camera = @import("Camera.zig"); -const assets = @import("assets.zig"); // pub const CHUNK_SIZE = 48; pub const CHUNK_SIZE = 24; @@ -47,8 +48,11 @@ pub fn start(self: *Terrain, scene: *Scene) void { self.camera.set_focus(Vec2f.create(CHUNK_SIZE / 2, CHUNK_SIZE / 2)); self.scene = scene; - self.gen_chunk(Vec2i { .x = 0, .y = 0 }) catch @panic("OOM"); - self.gen_chunk(Vec2i { .x = -1, .y = 0 }) catch @panic("OOM"); + for (0..3) |x| { + for (0..3) |y| { + self.gen_chunk(Vec2i { .x = @as(i32, @intCast(x)) - 1, .y = @as(i32, @intCast(y)) - 1 }) catch @panic("OOM"); + } + } // for (self.chunks.items) |chunk| scene.add(Entity.from(chunk, .{})) catch unreachable; } @@ -75,6 +79,10 @@ fn gen_chunk(self: *Terrain, chunk_pos: Vec2i) !void { try self.scene.add(Entity.from(chunk, .{})); } +pub fn get_random_chunk(self: *const Terrain) *const Chunk { + const idx = prng.random().uintAtMost(usize, self.chunks.items.len - 1); + return self.chunks.items[idx]; +} pub fn destroy(self: *const Terrain) void { self.generator.destroy(); @@ -82,6 +90,24 @@ pub fn destroy(self: *const Terrain) void { // Scene.EntityPool.deallocate(self); } +const Surface = struct { + pub fn is_pathable(self: *const Terrain, pos: Vec2i) bool { + return Surface.is_valid(self, pos); + } + + pub fn is_valid(self: *const Terrain, pos: Vec2i) bool { + _ = self; + return pos.x >= -24 and pos.x < 48 and pos.y >= -24 and pos.y < 48; + } +}; + +pub fn surface(self: *const Terrain) hadean.PathFinder.Surface { + return hadean.PathFinder.Surface.from(self, .{ + ._is_pathable = Surface.is_pathable, + ._is_valid = Surface.is_valid, + }); +} + // fn chance(percent: f32) bool { // return prng.random().float(f32) < percent; // } @@ -140,7 +166,7 @@ pub fn destroy(self: *const Terrain) void { // } // } -const Chunk = struct { +pub const Chunk = struct { pub const Tile = struct { texture_idx: usize, growth: f32, @@ -193,7 +219,7 @@ const Chunk = struct { fn generate_tiles(self: *Chunk, generator: *const WorldGenerator) void { for(0..CHUNK_LENGTH) |idx| { const x, const y = index_to_xy(@intCast(idx)); - const abs_pos = self.chunk_pos.add(Vec2i.create(x, y)); + const abs_pos = self.chunk_pos.add(Vec2i.create(x, y).add(self.chunk_pos.scale(CHUNK_SIZE))); const tile = generator.gen_tile(abs_pos); self.tiles[idx] = tile; @@ -212,15 +238,15 @@ const Chunk = struct { const x, const y = index_to_xy(@intCast(idx)); const rect = Recti.from_xywh(x + chunk_offset_x, y + chunk_offset_y, 1, 1); const sprite = assets.terrain[0][tile.texture_idx]; - // self.camera.draw_sprite_i(sprite, rect, tile.grass_color); + self.camera.draw_sprite_i(sprite, rect, tile.grass_color); - switch(tile.texture_idx) { - 0 => self.camera.draw_sprite_i(sprite, rect, Color.WHITE), - 1 => self.camera.draw_sprite_i(sprite, rect, Color.RED), - 2 => self.camera.draw_sprite_i(sprite, rect, Color.GREEN), - 3 => self.camera.draw_sprite_i(sprite, rect, Color.BLUE), - else => {} - } + // switch(tile.texture_idx) { + // 0 => self.camera.draw_sprite_i(sprite, rect, Color.WHITE), + // 1 => self.camera.draw_sprite_i(sprite, rect, Color.RED), + // 2 => self.camera.draw_sprite_i(sprite, rect, Color.GREEN), + // 3 => self.camera.draw_sprite_i(sprite, rect, Color.BLUE), + // else => {} + // } } }, else => {}, @@ -237,6 +263,14 @@ const Chunk = struct { const x: i32 = @rem(index, CHUNK_SIZE); return .{ x, y }; } + + pub fn get_random_pos_absolute(self: *const Chunk) Vec2i { + return Vec2i.create( + prng.random().intRangeAtMost(i32, 0, Terrain.CHUNK_SIZE - 1), + prng.random().intRangeAtMost(i32, 0, Terrain.CHUNK_SIZE - 1) + ).add(self.chunk_pos.scale(Terrain.CHUNK_SIZE)); + } + }; diff --git a/src/hadean/hadean.zig b/src/hadean/hadean.zig new file mode 100644 index 0000000..1efeda2 --- /dev/null +++ b/src/hadean/hadean.zig @@ -0,0 +1,14 @@ +pub const assets = @import("assets.zig"); +pub const scenes = @import("scenes.zig"); + +pub const entities = struct { + pub const Terrain = @import("entities/Terrain.zig"); + pub const Chunk = @import("entities/Chunk.zig"); + pub const Camera = @import("entities/Camera.zig"); + pub const Pawn = @import("entities/Pawn.zig"); + pub const Selection = @import("entities/Selection.zig"); +}; + +pub const PathFinder = @import("PathFinder.zig"); + + diff --git a/src/hadean/main.zig b/src/hadean/main.zig deleted file mode 100644 index dfb0357..0000000 --- a/src/hadean/main.zig +++ /dev/null @@ -1,15 +0,0 @@ - -const std = @import("std"); -const Engine = @import("engine"); -// const assets = @import("assets.zig"); -const scenes = @import("scenes.zig"); -const assets = @import("assets.zig"); - -pub fn main() !void { - Engine.Control.set_loader(assets.load); - try Engine.Control.setup(); - defer Engine.Control.destroy(); - - try Engine.Control.set_scene(scenes.game); - try Engine.Control.run(); -} diff --git a/src/hadean/scenes.zig b/src/hadean/scenes.zig index c35a7d7..616b536 100644 --- a/src/hadean/scenes.zig +++ b/src/hadean/scenes.zig @@ -1,23 +1,23 @@ const std = @import("std"); const engine = @import("engine"); +const hadean = @import("hadean"); const Scene = engine.Scene; -const Camera = @import("Camera.zig"); -const Selection = @import("Selection.zig"); -const Terrain = @import("Terrain.zig"); +const entities = hadean.entities; const Entity = engine.Entity; const Tag = engine.Tag; const Allocator = std.mem.Allocator; pub fn game(scene: *Scene) Allocator.Error!void { - const camera = try Camera.create(scene.arena.allocator()); + const camera = try entities.Camera.create(scene.arena.allocator()); try scene.add(Entity.from(camera, .{ .tag = Tag.CAMERA })); - const terrain = try Terrain.create(scene.arena.allocator(), 123); + const terrain = try entities.Terrain.create(scene.arena.allocator(), 123); try scene.add(Entity.from(terrain, .{ .tag = Tag.TERRAIN })); // try scene.add(try Selection.create()); - // for (0..5) |_| { - // try scene.add(try Pawn.random()); - // } + for (0..1) |_| { + const pawn = try entities.Pawn.random(scene.arena.allocator()); + try scene.add(Entity.from(pawn, .{})); + } } diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..50739f4 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,13 @@ + +const std = @import("std"); +const engine = @import("engine"); +const hadean = @import("hadean"); + +pub fn main() !void { + engine.Control.set_loader(hadean.assets.load); + try engine.Control.setup(); + defer engine.Control.destroy(); + + try engine.Control.set_scene(hadean.scenes.game); + try engine.Control.run(); +}