diff --git a/src/Engine.zig b/src/Engine.zig index cb8232b..6ca080b 100644 --- a/src/Engine.zig +++ b/src/Engine.zig @@ -151,7 +151,7 @@ pub fn set_scene(scene: Scene) void { next_scene = scene; } -pub fn run() Error!void { +pub fn run() !void { if (window == null) return Error.WindowNotInitialized; if (current_scene != null) return Error.AlreadyRunning; if (next_scene == null) return Error.NoScene; diff --git a/src/Entity.zig b/src/Entity.zig index 1b5cfe9..196c288 100644 --- a/src/Entity.zig +++ b/src/Entity.zig @@ -174,7 +174,15 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity { pub fn _start(pointer: *anyopaque, scene: *Scene) void { const self: MutablePointer = @ptrCast(@alignCast(pointer)); if(@hasDecl(type_info.Pointer.child, "start")) { - type_info.Pointer.child.start(self, scene); + const return_type = @typeInfo(@typeInfo(@TypeOf(type_info.Pointer.child.start)).Fn.return_type.?); + // @compileLog(return_type); + switch (return_type) { + .Void => type_info.Pointer.child.start(self, scene), + .ErrorUnion => { + type_info.Pointer.child.start(self, scene) catch @panic("fuck"); + }, + else => {} + } } } pub fn _resize(pointer: *anyopaque, width: i32, height: i32) void { diff --git a/src/Scene.zig b/src/Scene.zig index 80905a6..95b7833 100644 --- a/src/Scene.zig +++ b/src/Scene.zig @@ -14,18 +14,24 @@ const AutoHashMap = std.AutoHashMap; // cache and list bothe store entities. treat entities as fat pointers. // Pointers into an arraylist WILL become invalid. -mouse_ready_entities: ArrayList(Entity) = undefined, -entities: ArrayList(Entity) = undefined, -tagged_entities: AutoHashMap(Tag.ID, Entity) = undefined, +mouse_ready_entities: ArrayList(Entity), +entities: ArrayList(Entity), +entities_to_add: ArrayList(Entity), +tagged_entities: AutoHashMap(Tag.ID, Entity), mouse_inside: bool = false, current_hover_item: ?Entity = null, mouse_pos: ?Vec2i = null, +started: bool = false, + + pub fn create() Scene { - var self = Scene {}; - self.entities = ArrayList(Entity).init(std.heap.page_allocator); - self.mouse_ready_entities = ArrayList(Entity).init(std.heap.page_allocator); - self.tagged_entities = AutoHashMap(Tag.ID, Entity).init(std.heap.page_allocator); + const self = Scene { + .entities = ArrayList(Entity).init(std.heap.page_allocator), + .entities_to_add = ArrayList(Entity).init(std.heap.page_allocator), + .mouse_ready_entities = ArrayList(Entity).init(std.heap.page_allocator), + .tagged_entities = AutoHashMap(Tag.ID, Entity).init(std.heap.page_allocator), + }; return self; } @@ -40,6 +46,7 @@ pub fn start(self: *Scene) !void { std.debug.print("[Scene:start] Scene starting...\n", .{}); std.debug.print("[Scene:start] entities: {}\n", .{ self.entities.items.len }); + self.started = true; for (self.entities.items) |*entity| { if (entity.tag.id == Tag.NONE.id) { std.debug.print("[Scene:start] - {s}\n", .{ entity.name() }); @@ -51,6 +58,8 @@ pub fn start(self: *Scene) !void { for (self.entities.items) |entity| { entity.start(self); } + + try self.add_queued(); } pub fn get(self: *const Scene, tag: Tag, comptime T: type) *T { @@ -84,6 +93,26 @@ pub fn add(self: *Scene, instance_ptr: anytype) !void { // again, entities are fat pointers. they are okay to be copied const entity: Entity = instance_ptr.entity(); + + if (self.started) { + try self.entities_to_add.append(entity); + } else { + try self.add_unsafe(entity); + } +} + +inline fn add_queued(self: *Scene) !void { + for (self.entities_to_add.items) |entity| { + try self.entities.append(entity); + entity.start(self); + } + self.entities_to_add.clearAndFree(); +} + +inline fn add_unsafe(self: *Scene, entity: Entity) !void { + std.debug.print("[Scene:add] Adding {s}\n", .{ + entity.name() + }); try self.entities.append(entity); if (entity.tag.id != Tag.NONE.id) { try self.tagged_entities.put(entity.tag.id, entity); diff --git a/src/Terrain.zig b/src/Terrain.zig index 6e78801..0f597a5 100644 --- a/src/Terrain.zig +++ b/src/Terrain.zig @@ -4,6 +4,7 @@ const Layer = @import("Layer.zig"); const Color = @import("Color.zig"); const Recti = @import("geometry/Recti.zig"); const Vec2f = @import("geometry/Vec2f.zig"); +const Vec2i = @import("geometry/Vec2i.zig"); const Entity = @import("Entity.zig"); const Scene = @import("Scene.zig"); const Camera = @import("Camera.zig"); @@ -19,52 +20,16 @@ const CHUNK_LENGTH = CHUNK_SIZE * CHUNK_SIZE; const tile_size: usize = 16; -pub inline fn xy_to_index(x: i32, y: i32) i32 { - return x + y * CHUNK_SIZE; -} - -pub inline fn index_to_xy(index: i32) struct { i32, i32 } { - const y: i32 = @divFloor(index, CHUNK_SIZE); - const x: i32 = @rem(index, CHUNK_SIZE); - return .{ x, y }; -} - const GROWS = true; const GROWTH_RATE: f32 = 0.5; -pub const Tile = struct { - texture_idx: usize, - growth: f32, - stone_decoration: f32, - grass_color: Color, - dirt_color: Color, - stone_color: Color, - pub const NULL = Tile { - .texture_idx = 0, - .growth = 0, - .stone_decoration = 0, - .grass_color = Color.WHITE, - .dirt_color = Color.WHITE, - .stone_color = Color.WHITE, - }; -}; - -tiles: []Tile = undefined, - -// the noise for deciding random textures -texture_noise: Noise, -// noise for deciding grass growth -growth_noise: Noise, -// noise for stone decoration placement -stone_decoration_noise: Noise.Custom, -// noise for colors -dirt_color_noise: Noise.Color, -grass_color_noise: Noise.Color, -stone_color_noise: Noise.Color, +chunks: std.ArrayList(*Chunk), +generator: WorldGenerator, camera: *Camera = undefined, +scene: ?*Scene = null, pub fn entity(self: *Terrain) Entity { return Entity.init(self, .{ @@ -72,9 +37,12 @@ pub fn entity(self: *Terrain) Entity { }); } -pub fn start(self: *Terrain, scene: *Scene) void { +pub fn start(self: *Terrain, scene: *Scene) !void { self.camera = scene.get(Camera.TAG, Camera); - 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; + + for (self.chunks.items) |chunk| try scene.add(chunk); } fn contrast(n: f32, blend_strength: f32) f32 { @@ -89,107 +57,72 @@ fn contrast(n: f32, blend_strength: f32) f32 { return (unadjusted_total + 1.0) / 2.0; } -fn generate_tiles(self: *Terrain) void { - for(0..CHUNK_LENGTH) |idx| { - const x, const y = index_to_xy(@intCast(idx)); - const fx: f32 = @floatFromInt(x); - const fy: f32 = @floatFromInt(y); - const texture_random = self.texture_noise.get(fx, fy); - self.tiles[idx] = Tile { - .texture_idx = @intFromFloat(@floor(texture_random * 4)), - .growth = contrast(self.growth_noise.get(fx, fy), 10), - .stone_decoration = self.stone_decoration_noise.get(fx, fy), - .dirt_color = self.dirt_color_noise.get(fx, fy), - .grass_color = self.grass_color_noise.get(fx, fy), - .stone_color = self.stone_color_noise.get(fx, fy), - }; - } +fn gen_chunk(self: *Terrain, chunk_pos: Vec2i) !void { + std.debug.print("[Terrain:gen_chunk] {}\n", .{ chunk_pos }); + const chunk = try Chunk.generate(chunk_pos, &self.generator); + try self.chunks.append(chunk); + if (self.scene != null) try self.scene.?.add(chunk); } pub fn create(seed: i64) !*Terrain { var self: *Terrain = try Scene.EntityPool.allocate(Terrain { - .tiles = try Entity.allocate_array(Tile, CHUNK_LENGTH, Tile.NULL), - .growth_noise = Noise.create(seed, 10, 0.03), - .texture_noise = Noise.create(seed + 1, 1, 3), - .stone_decoration_noise = try Noise.Custom.create(seed + 2, 1, &.{ - .{ .frequency = 0.03, .amplitude = 1 }, - .{ .frequency = 1, .amplitude = 1 } - }), - .dirt_color_noise = try Noise.Color.create(seed + 3, Noise.ColorRange.DIRT), - .grass_color_noise = try Noise.Color.create(seed + 4, Noise.ColorRange.GRASS), - .stone_color_noise = try Noise.Color.create(seed + 5, Noise.ColorRange.STONE) + .chunks = std.ArrayList(*Chunk).init(std.heap.page_allocator), + .generator = try WorldGenerator.create(seed), }); - self.generate_tiles(); + try self.gen_chunk(Vec2i.ZERO); + try self.gen_chunk(Vec2i { .x = -1, .y = 0 }); return self; } pub fn destroy(self: *const Terrain) void { - self.texture_noise.destroy(); - self.growth_noise.destroy(); - self.stone_decoration_noise.destroy(); - - self.dirt_color_noise.destroy(); - self.grass_color_noise.destroy(); - self.stone_color_noise.destroy(); - - Entity.free_array(self.tiles); + self.generator.destroy(); + self.chunks.deinit(); Scene.EntityPool.deallocate(self); } -fn chance(percent: f32) bool { - return prng.random().float(f32) < percent; -} +// fn chance(percent: f32) bool { +// return prng.random().float(f32) < percent; +// } -fn grow(self: *Terrain, x: i32, y: i32, amt: f32) void { - if (x < 0 or x >= CHUNK_SIZE) return; - if (y < 0 or y >= CHUNK_SIZE) return; - const idx: usize = @intCast(xy_to_index(x, y)); - const tile = &self.tiles[idx]; - // @compileLog(tile); - tile.*.growth = @min(@max(tile.growth + amt, 0), 1); -} +// fn grow(self: *Terrain, x: i32, y: i32, amt: f32) void { +// if (x < 0 or x >= CHUNK_SIZE) return; +// if (y < 0 or y >= CHUNK_SIZE) return; +// const idx: usize = @intCast(xy_to_index(x, y)); +// const tile = &self.tiles[idx]; +// // @compileLog(tile); +// tile.*.growth = @min(@max(tile.growth + amt, 0), 1); +// } -pub fn update(self: *Terrain, dt: f32) void { - if (GROWS) { - for (0..CHUNK_LENGTH) |idx| { - const x, const y = index_to_xy(@intCast(idx)); - const tile = self.tiles[idx]; - const should_grow = chance(dt * GROWTH_RATE) and chance(tile.growth); - if (should_grow) { - self.grow(x, y, 0.010); - - self.grow(x + 1, y, 0.008); - self.grow(x - 1, y, 0.008); - self.grow(x, y + 1, 0.008); - self.grow(x, y - 1, 0.008); - - self.grow(x + 1, y + 1, 0.004); - self.grow(x + 1, y - 1, 0.004); - self.grow(x - 1, y + 1, 0.004); - self.grow(x - 1, y - 1, 0.004); - } - } - } -} +// pub fn update(self: *Terrain, dt: f32) void { +// if (GROWS) { +// for (0..CHUNK_LENGTH) |idx| { +// const x, const y = index_to_xy(@intCast(idx)); +// const tile = self.tiles[idx]; +// const should_grow = chance(dt * GROWTH_RATE) and chance(tile.growth); +// if (should_grow) { +// self.grow(x, y, 0.010); +// +// self.grow(x + 1, y, 0.008); +// self.grow(x - 1, y, 0.008); +// self.grow(x, y + 1, 0.008); +// self.grow(x, y - 1, 0.008); +// +// self.grow(x + 1, y + 1, 0.004); +// self.grow(x + 1, y - 1, 0.004); +// self.grow(x - 1, y + 1, 0.004); +// self.grow(x - 1, y - 1, 0.004); +// } +// } +// } +// } +// - -pub fn draw(self: *const Terrain) void { - for(0..CHUNK_LENGTH) |idx| { - const tile = self.tiles[idx]; - const x, const y = index_to_xy(@intCast(idx)); - const rect = Recti.from_xywh(x, y, 1, 1); - const sprite = assets.terrain[0][tile.texture_idx]; - self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, tile.grass_color); - // switch(tile.texture_idx) { - // 0 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.WHITE), - // 1 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.RED), - // 2 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.GREEN), - // 3 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.BLUE), - // else => {} - // } - } -} +// pub fn draw(self: *const Terrain) void { +// for (self.chunks.items) |chunk| { +// chunk.draw... we dont need this. chunks draw themselves... +// } +// } // pub fn draw_opacity(self: *const Terrain) void { // for(0..CHUNK_LENGTH) |idx| { @@ -205,3 +138,149 @@ pub fn draw(self: *const Terrain) void { // self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, color); // } // } + + +const Chunk = struct { + pub const Tile = struct { + texture_idx: usize, + growth: f32, + stone_decoration: f32, + grass_color: Color, + dirt_color: Color, + stone_color: Color, + + pub const NULL = Tile { + .texture_idx = 0, + .growth = 0, + .stone_decoration = 0, + .grass_color = Color.WHITE, + .dirt_color = Color.WHITE, + .stone_color = Color.WHITE, + }; + }; + + tiles: []Tile = undefined, + chunk_pos: Vec2i, + chunk_pos_world: Vec2i, + + camera: *Camera = undefined, + + pub fn generate(chunk_pos: Vec2i, generator: *const WorldGenerator) !*Chunk { + var self = try Scene.EntityPool.allocate(Chunk { + .chunk_pos = chunk_pos, + .chunk_pos_world = Vec2i { + .x = chunk_pos.x * CHUNK_SIZE, + .y = chunk_pos.y * CHUNK_SIZE, + }, + .tiles = try Entity.allocate_array(Tile, CHUNK_LENGTH, Tile.NULL), + }); + + self.generate_tiles(generator); + + return self; + } + + pub fn destroy(self: *const Chunk) void { + Entity.free_array(self.tiles); + Scene.EntityPool.deallocate(self); + } + + pub fn entity(self: *Chunk) Entity { + return Entity.init(self, .{}); + } + + pub fn start(self: *Chunk, scene: *Scene) void { + self.camera = scene.get(Camera.TAG, Camera); + } + + 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 tile = generator.gen_tile(abs_pos); + + self.tiles[idx] = tile; + } + } + + pub fn draw(self: *const Chunk) void { + for(0..CHUNK_LENGTH) |idx| { + const tile = self.tiles[idx]; + const x, const y = index_to_xy(@intCast(idx)); + const rect = Recti.from_xywh(x, y, 1, 1); + const sprite = assets.terrain[0][tile.texture_idx]; + self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, tile.grass_color); + // switch(tile.texture_idx) { + // 0 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.WHITE), + // 1 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.RED), + // 2 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.GREEN), + // 3 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.BLUE), + // else => {} + // } + } + } + + // some helper functions + pub inline fn xy_to_index(x: i32, y: i32) i32 { + return x + y * CHUNK_SIZE; + } + + pub inline fn index_to_xy(index: i32) struct { i32, i32 } { + const y: i32 = @divFloor(index, CHUNK_SIZE); + const x: i32 = @rem(index, CHUNK_SIZE); + return .{ x, y }; + } +}; + + +const WorldGenerator = struct { + + // the noise for deciding random textures + texture_noise: Noise, + // noise for deciding grass growth + growth_noise: Noise, + // noise for stone decoration placement + stone_decoration_noise: Noise.Custom, + // noise for colors + dirt_color_noise: Noise.Color, + grass_color_noise: Noise.Color, + stone_color_noise: Noise.Color, + + pub fn create(seed: i64) !WorldGenerator { + return WorldGenerator { + .growth_noise = Noise.create(seed, 10, 0.03), + .texture_noise = Noise.create(seed + 1, 1, 3), + .stone_decoration_noise = try Noise.Custom.create(seed + 2, 1, &.{ + .{ .frequency = 0.03, .amplitude = 1 }, + .{ .frequency = 1, .amplitude = 1 } + }), + .dirt_color_noise = try Noise.Color.create(seed + 3, Noise.ColorRange.DIRT), + .grass_color_noise = try Noise.Color.create(seed + 4, Noise.ColorRange.GRASS), + .stone_color_noise = try Noise.Color.create(seed + 5, Noise.ColorRange.STONE) + }; + } + + pub fn destroy(self: *const WorldGenerator) void { + self.texture_noise.destroy(); + self.growth_noise.destroy(); + self.stone_decoration_noise.destroy(); + + self.dirt_color_noise.destroy(); + self.grass_color_noise.destroy(); + self.stone_color_noise.destroy(); + } + + pub fn gen_tile(self: *const WorldGenerator, position: Vec2i) Chunk.Tile { + const fx: f32 = @floatFromInt(position.x); + const fy: f32 = @floatFromInt(position.y); + const texture_random = self.texture_noise.get(fx, fy); + return Chunk.Tile { + .texture_idx = @intFromFloat(@floor(texture_random * 4)), + .growth = contrast(self.growth_noise.get(fx, fy), 10), + .stone_decoration = self.stone_decoration_noise.get(fx, fy), + .dirt_color = self.dirt_color_noise.get(fx, fy), + .grass_color = self.grass_color_noise.get(fx, fy), + .stone_color = self.stone_color_noise.get(fx, fy), + }; + } +}; diff --git a/src/geometry/Vec2i.zig b/src/geometry/Vec2i.zig index aad1319..6e4ca33 100644 --- a/src/geometry/Vec2i.zig +++ b/src/geometry/Vec2i.zig @@ -32,6 +32,13 @@ pub fn to_unit_recti(self: *const Vec2i) Recti { return Recti.from_xywh(self.x, self.y, 1, 1); } +pub fn add(self: Vec2i, other: Vec2i) Vec2i { + return Vec2i { + .x = self.x + other.x, + .y = self.y + other.y, + }; +} + pub fn draw(self: *const Vec2i, radius: f32, layer: Layer, color: Color) void { shaders.disable_textures(); diff --git a/src/scenes.zig b/src/scenes.zig index edaf56c..a29d403 100644 --- a/src/scenes.zig +++ b/src/scenes.zig @@ -11,9 +11,9 @@ pub fn game() !Scene { try scene.add(try Camera.create()); try scene.add(try Terrain.create(123)); try scene.add(try Selection.create()); - for (0..5) |_| { - try scene.add(try Pawn.random()); - } + // for (0..5) |_| { + // try scene.add(try Pawn.random()); + // } return scene; }