idk man
parent
7e0e3f7501
commit
d07d2e1a40
50
build.zig
50
build.zig
|
|
@ -5,16 +5,17 @@ pub fn build(b: *std.Build) void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
// === [ C IMPORTS ] ===
|
||||||
const c = b.addModule("c", .{
|
const c = b.addModule("c", .{
|
||||||
.root_source_file = b.path("src/c.zig"),
|
.root_source_file = b.path("src/c.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
// c.addLibraryPath(b.path("/usr/local/lib"));
|
|
||||||
c.linkSystemLibrary("SDL3", .{});
|
c.linkSystemLibrary("SDL3", .{});
|
||||||
c.addIncludePath(b.path("open-simplex/include"));
|
c.addIncludePath(b.path("open-simplex/include"));
|
||||||
c.linkSystemLibrary("SDL3_image", .{});
|
c.linkSystemLibrary("SDL3_image", .{});
|
||||||
|
|
||||||
|
// === [ ENGINE ] ===
|
||||||
const engine = b.addModule("engine", .{
|
const engine = b.addModule("engine", .{
|
||||||
.root_source_file = b.path("src/engine/root.zig"),
|
.root_source_file = b.path("src/engine/root.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
|
|
@ -23,14 +24,27 @@ pub fn build(b: *std.Build) void {
|
||||||
engine.addImport("engine", engine);
|
engine.addImport("engine", engine);
|
||||||
engine.addImport("c", c);
|
engine.addImport("c", c);
|
||||||
|
|
||||||
const exe = b.addExecutable(.{
|
// === [ GAME CODE ] ===
|
||||||
.name = "hadean",
|
const hadean = b.addModule("hadean", .{
|
||||||
.root_source_file = b.path("src/hadean/main.zig"),
|
.root_source_file = b.path("src/hadean/hadean.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.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("engine", engine);
|
||||||
|
exe.root_module.addImport("hadean", hadean);
|
||||||
exe.root_module.addImport("c", c);
|
exe.root_module.addImport("c", c);
|
||||||
exe.addCSourceFile(.{ .file = b.path("open-simplex/src/OpenSimplex2F.c") });
|
exe.addCSourceFile(.{ .file = b.path("open-simplex/src/OpenSimplex2F.c") });
|
||||||
exe.addIncludePath(b.path("open-simplex/include"));
|
exe.addIncludePath(b.path("open-simplex/include"));
|
||||||
|
|
@ -49,14 +63,28 @@ pub fn build(b: *std.Build) void {
|
||||||
"assets/textures.png"
|
"assets/textures.png"
|
||||||
)).step);
|
)).step);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
b.installArtifact(exe);
|
b.installArtifact(exe);
|
||||||
const run_cmd = b.addRunArtifact(exe);
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
run_cmd.step.dependOn(b.getInstallStep());
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
if (b.args) |args| {
|
if (b.args) |args| {
|
||||||
run_cmd.addArgs(args);
|
run_cmd.addArgs(args);
|
||||||
}
|
}
|
||||||
const run_step = b.step("run", "Run the app");
|
const run_step = b.step("run", "Run the app");
|
||||||
run_step.dependOn(&run_cmd.step);
|
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
|
// because engine and hadean are diff modules their c modules
|
||||||
// get different caches, causing opaques to be different between them.
|
// get different caches, causing opaques to be different between them.
|
||||||
|
|
|
||||||
52
src/Pawn.zig
52
src/Pawn.zig
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
@ -3,8 +3,8 @@ const c = @import("c");
|
||||||
const engine = @import("engine");
|
const engine = @import("engine");
|
||||||
const Control = engine.Control;
|
const Control = engine.Control;
|
||||||
|
|
||||||
pub const camera = true;
|
pub var camera = true;
|
||||||
pub const mouse = true;
|
pub var mouse = true;
|
||||||
|
|
||||||
// later make this expandable in some way using an allocator.
|
// later make this expandable in some way using an allocator.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,21 @@ pub fn create(renderer: *c.SDL_Renderer, path: []const u8) Texture {
|
||||||
// @panic("Failed to create IO for Texture");
|
// @panic("Failed to create IO for Texture");
|
||||||
// };
|
// };
|
||||||
// c.IMG_Load()
|
// 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() });
|
std.debug.print("SDL Error: {s}\n", .{ c.SDL_GetError() });
|
||||||
@panic("Failed to create Surface for Texture");
|
@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);
|
defer c.SDL_DestroySurface(surface);
|
||||||
|
|
||||||
const texture = c.SDL_CreateTextureFromSurface(renderer, surface) orelse {
|
const texture = c.SDL_CreateTextureFromSurface(renderer, surface) orelse {
|
||||||
std.debug.print("SDL Error: {s}\n", .{ c.SDL_GetError() });
|
std.debug.print("SDL Error: {s}\n", .{ c.SDL_GetError() });
|
||||||
@panic("Failed to create Texture");
|
@panic("Failed to create Texture");
|
||||||
|
|
|
||||||
|
|
@ -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 ZERO = create(0, 0);
|
||||||
pub const ONE = create(1, 1);
|
pub const ONE = create(1, 1);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -156,6 +156,7 @@ pub fn set_focus(self: *Camera, f: Vec2f) void {
|
||||||
pub fn mouse_properties(_: *const Camera) MouseListener.Properties {
|
pub fn mouse_properties(_: *const Camera) MouseListener.Properties {
|
||||||
return MouseListener.Properties {
|
return MouseListener.Properties {
|
||||||
.area = Recti.from_xywh(0, 0, engine.Control.window_width, engine.Control.window_height),
|
.area = Recti.from_xywh(0, 0, engine.Control.window_width, engine.Control.window_height),
|
||||||
|
// .area = Recti.from_xywh(100, 100, 100, 100),
|
||||||
.layer = .CAMERA,
|
.layer = .CAMERA,
|
||||||
.button = .Middle,
|
.button = .Middle,
|
||||||
.click_through = true,
|
.click_through = true,
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ const Terrain = @This();
|
||||||
const engine = @import("engine");
|
const engine = @import("engine");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const c = @import("c");
|
const c = @import("c");
|
||||||
|
const hadean = @import("hadean");
|
||||||
const Scene = engine.Scene;
|
const Scene = engine.Scene;
|
||||||
const Entity = engine.Entity;
|
const Entity = engine.Entity;
|
||||||
const Vec2i = engine.geometry.Vec2i;
|
const Vec2i = engine.geometry.Vec2i;
|
||||||
|
|
@ -12,11 +13,11 @@ const Recti = engine.geometry.Recti;
|
||||||
const Noise = engine.Noise;
|
const Noise = engine.Noise;
|
||||||
const Layer = engine.Layer;
|
const Layer = engine.Layer;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Camera = hadean.entities.Camera;
|
||||||
|
const assets = hadean.assets;
|
||||||
|
|
||||||
var prng = std.rand.DefaultPrng.init(0);
|
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 = 48;
|
||||||
pub const CHUNK_SIZE = 24;
|
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.camera.set_focus(Vec2f.create(CHUNK_SIZE / 2, CHUNK_SIZE / 2));
|
||||||
self.scene = scene;
|
self.scene = scene;
|
||||||
|
|
||||||
self.gen_chunk(Vec2i { .x = 0, .y = 0 }) catch @panic("OOM");
|
for (0..3) |x| {
|
||||||
self.gen_chunk(Vec2i { .x = -1, .y = 0 }) catch @panic("OOM");
|
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;
|
// 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, .{}));
|
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 {
|
pub fn destroy(self: *const Terrain) void {
|
||||||
self.generator.destroy();
|
self.generator.destroy();
|
||||||
|
|
@ -82,6 +90,24 @@ pub fn destroy(self: *const Terrain) void {
|
||||||
// Scene.EntityPool.deallocate(self);
|
// 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 {
|
// fn chance(percent: f32) bool {
|
||||||
// return prng.random().float(f32) < percent;
|
// 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 {
|
pub const Tile = struct {
|
||||||
texture_idx: usize,
|
texture_idx: usize,
|
||||||
growth: f32,
|
growth: f32,
|
||||||
|
|
@ -193,7 +219,7 @@ const Chunk = struct {
|
||||||
fn generate_tiles(self: *Chunk, generator: *const WorldGenerator) void {
|
fn generate_tiles(self: *Chunk, generator: *const WorldGenerator) void {
|
||||||
for(0..CHUNK_LENGTH) |idx| {
|
for(0..CHUNK_LENGTH) |idx| {
|
||||||
const x, const y = index_to_xy(@intCast(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);
|
const tile = generator.gen_tile(abs_pos);
|
||||||
|
|
||||||
self.tiles[idx] = tile;
|
self.tiles[idx] = tile;
|
||||||
|
|
@ -212,15 +238,15 @@ const Chunk = struct {
|
||||||
const x, const y = index_to_xy(@intCast(idx));
|
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 rect = Recti.from_xywh(x + chunk_offset_x, y + chunk_offset_y, 1, 1);
|
||||||
const sprite = assets.terrain[0][tile.texture_idx];
|
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) {
|
// switch(tile.texture_idx) {
|
||||||
0 => self.camera.draw_sprite_i(sprite, rect, Color.WHITE),
|
// 0 => self.camera.draw_sprite_i(sprite, rect, Color.WHITE),
|
||||||
1 => self.camera.draw_sprite_i(sprite, rect, Color.RED),
|
// 1 => self.camera.draw_sprite_i(sprite, rect, Color.RED),
|
||||||
2 => self.camera.draw_sprite_i(sprite, rect, Color.GREEN),
|
// 2 => self.camera.draw_sprite_i(sprite, rect, Color.GREEN),
|
||||||
3 => self.camera.draw_sprite_i(sprite, rect, Color.BLUE),
|
// 3 => self.camera.draw_sprite_i(sprite, rect, Color.BLUE),
|
||||||
else => {}
|
// else => {}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
|
|
@ -237,6 +263,14 @@ const Chunk = struct {
|
||||||
const x: i32 = @rem(index, CHUNK_SIZE);
|
const x: i32 = @rem(index, CHUNK_SIZE);
|
||||||
return .{ x, y };
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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");
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const engine = @import("engine");
|
const engine = @import("engine");
|
||||||
|
const hadean = @import("hadean");
|
||||||
const Scene = engine.Scene;
|
const Scene = engine.Scene;
|
||||||
const Camera = @import("Camera.zig");
|
const entities = hadean.entities;
|
||||||
const Selection = @import("Selection.zig");
|
|
||||||
const Terrain = @import("Terrain.zig");
|
|
||||||
const Entity = engine.Entity;
|
const Entity = engine.Entity;
|
||||||
const Tag = engine.Tag;
|
const Tag = engine.Tag;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub fn game(scene: *Scene) Allocator.Error!void {
|
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 }));
|
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(Entity.from(terrain, .{ .tag = Tag.TERRAIN }));
|
||||||
|
|
||||||
// try scene.add(try Selection.create());
|
// try scene.add(try Selection.create());
|
||||||
// for (0..5) |_| {
|
for (0..1) |_| {
|
||||||
// try scene.add(try Pawn.random());
|
const pawn = try entities.Pawn.random(scene.arena.allocator());
|
||||||
// }
|
try scene.add(Entity.from(pawn, .{}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue