diff --git a/src/app.zig b/src/app.zig index 931575d..78df39b 100644 --- a/src/app.zig +++ b/src/app.zig @@ -44,21 +44,22 @@ const PlayerSprites = struct { } }; +const Tile = struct { + ground_sprite: ?Gfx.Sprite.Id, + + entity: Entity.Id, + + const empty = Tile{ + .ground_sprite = null, + .entity = .nil + }; +}; + const Tilemap = struct { tiles: []Tile, width: usize, height: usize, - const Tile = struct { - ground_sprite: ?Gfx.Sprite.Id, - wall_sprite: ?Gfx.Sprite.Id, - - const empty = Tile{ - .ground_sprite = null, - .wall_sprite = null - }; - }; - pub fn init(gpa: Allocator, width: usize, height: usize) !Tilemap { const tiles = try gpa.alloc(Tile, width * height); @memset(tiles, .empty); @@ -84,8 +85,9 @@ const Tilemap = struct { const Entity = struct { type: Type, - pos_x: i32 = 0, - pos_y: i32 = 0, + pos_x: i32 = -1, + pos_y: i32 = -1, + visual_pos: Vec2 = .init(0, 0), sprite: ?Gfx.Sprite.Id = null, player_direction: Direction = .down, @@ -93,17 +95,83 @@ const Entity = struct { const Type = enum { player, - box + box, + wall }; - const SlotMap = Platform.SlotMapType(u8, u16, Entity); + const SlotMap = Platform.SlotMapType(u16, u8, Entity); const Id = SlotMap.Id; }; -roboto_font: Font, -tilemap: Tilemap, -entities: Entity.SlotMap, +const World = struct { + tilemap: Tilemap, + entities: Entity.SlotMap, + fn spawnPlayer(self: *World) !Entity.Id { + return try self.entities.insert(Entity{ + .type = .player, + }); + } + + fn spawnBox(self: *World, sprite: Gfx.Sprite.Id) !Entity.Id { + return try self.entities.insert(Entity{ + .type = .box, + .sprite = sprite + }); + } + + fn spawnWall(self: *World, sprite: Gfx.Sprite.Id) !Entity.Id { + return try self.entities.insert(Entity{ + .type = .wall, + .sprite = sprite + }); + } + + fn getTile(self: *World, x: i32, y: i32) ?*Tile { + const width: i32 = @intCast(self.tilemap.width); + const height: i32 = @intCast(self.tilemap.height); + if (0 <= x and x < width and 0 <= y and y < height) { + return &self.tilemap.tiles[@intCast(x + y * width)]; + } + return null; + } + + fn moveEntity(self: *World, id: Entity.Id, x: i32, y: i32) bool { + const e = self.entities.get(id) orelse return false; + const destination_tile = self.getTile(x, y) orelse return false; + + if (self.entities.exists(destination_tile.entity)) { + // Cna't overwrite entity + return false; + } + destination_tile.entity = id; + + if (self.getTile(e.pos_x, e.pos_y)) |tile| { + tile.entity = .nil; + } + + e.pos_x = x; + e.pos_y = y; + + return true; + } + + fn movePlayer(self: *World, id: Entity.Id, dir_x: i32, dir_y: i32) bool { + const e = self.entities.get(id) orelse return false; + + const next_x = e.pos_x+dir_x; + const next_y = e.pos_y+dir_y; + if (!self.moveEntity(id, next_x, next_y)) { + return false; + } + + return true; + } +}; + +world: World, + +roboto_font: Font, tilesheet: Tilesheet, player_sprites: PlayerSprites, @@ -167,12 +235,13 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { .tilesheet = tilesheet, .footstep_buffer = footstep_sound, .sfx_bus = Audio.addBus(.{ .label = "sfx" }), - .tilemap = undefined, - .entities = try .initCapacity(plt.gpa, 256) + .world = .{ + .tilemap = undefined, + .entities = try .initCapacity(plt.gpa, 256) + } }; - var player_pos_x: i32 = 0; - var player_pos_y: i32 = 0; + var player_pos: Vec2 = .zero; { var scratch = std.heap.ArenaAllocator.init(plt.gpa); defer scratch.deinit(); @@ -190,7 +259,7 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { const bounds = tilemap.getTileBounds(); - self.tilemap = try Tilemap.init(plt.gpa, bounds.width(), bounds.height()); + self.world.tilemap = try Tilemap.init(plt.gpa, bounds.width(), bounds.height()); const ground = tilemap.getLayer("Ground").?; const walls = tilemap.getLayer("Walls").?; @@ -199,7 +268,7 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { const x = bounds.min_x + ox; const y = bounds.min_y + oy; - const tile = self.tilemap.get(ox, oy); + const tile = self.world.tilemap.get(ox, oy); if (tilemap.getTile(ground, tilesets, x, y)) |tiled_tile| { const pos = tiled_tile.getPosition(); @@ -208,17 +277,18 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { if (tilemap.getTile(walls, tilesets, x, y)) |tiled_tile| { const pos = tiled_tile.getPosition(); - tile.wall_sprite = tilesheet.get(pos.x, pos.y); + const wall_id = try self.world.spawnWall(tilesheet.get(pos.x, pos.y)); + _ = self.world.moveEntity(wall_id, @intCast(ox), @intCast(oy)); } } } const markers = &tilemap.getLayer("Markers").?.variant.object; const spawn = markers.getByName("spawn").?; - player_pos_x = @divFloor(@as(i32, @intFromFloat(spawn.shape.point.x)), @as(i32, @intCast(tilemap.tile_width))); - player_pos_x -= @intCast(bounds.min_x); - player_pos_y = @divFloor(@as(i32, @intFromFloat(spawn.shape.point.y)),@as(i32, @intCast(tilemap.tile_height))); - player_pos_y -= @intCast(bounds.min_y); + player_pos.x = @divFloor(spawn.shape.point.x, @as(f32, @floatFromInt(tilemap.tile_width))); + player_pos.x -= @floatFromInt(bounds.min_x); + player_pos.y = @divFloor(spawn.shape.point.y, @as(f32, @floatFromInt(tilemap.tile_height))); + player_pos.y -= @floatFromInt(bounds.min_y); const boxes = tilemap.getLayer("Boxes").?; for (0..bounds.height()) |oy| { @@ -228,35 +298,19 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { if (tilemap.getTile(boxes, tilesets, x, y)) |tiled_tile| { const pos = tiled_tile.getPosition(); - _ = try self.spawnBox(@intCast(ox), @intCast(oy), tilesheet.get(pos.x, pos.y)); + const box_id = try self.world.spawnBox(tilesheet.get(pos.x, pos.y)); + _ = self.world.moveEntity(box_id, @intCast(ox), @intCast(oy)); } } } } - const player_id = try self.spawnPlayer(); - const player = self.entities.getAssumeExists(player_id); - player.pos_x = player_pos_x; - player.pos_y = player_pos_y; + const player_id = try self.world.spawnPlayer(); + _ = self.world.moveEntity(player_id, @intFromFloat(player_pos.x), @intFromFloat(player_pos.y)); return null; } -fn spawnPlayer(self: *App) !Entity.Id { - return try self.entities.insert(Entity{ - .type = .player, - }); -} - -fn spawnBox(self: *App, x: i32, y: i32, sprite: Gfx.Sprite.Id) !Entity.Id { - return try self.entities.insert(Entity{ - .type = .box, - .pos_x = x, - .pos_y = y, - .sprite = sprite - }); -} - pub fn frame(self: *App, plt: Platform.Frame) !void { const input = plt.input; @@ -283,46 +337,43 @@ pub fn frame(self: *App, plt: Platform.Frame) !void { const tile_size = Vec2.init(48, 48); var canvas = CanvasScaler{ - .size = Vec2.initFromInt(usize, self.tilemap.width, self.tilemap.height).multiply(tile_size) + .size = Vec2.initFromInt(usize, self.world.tilemap.width, self.world.tilemap.height).multiply(tile_size) }; canvas.push(); defer canvas.pop(clear_color); - for (0..self.tilemap.height) |y| { - for (0..self.tilemap.width) |x| { - const tile = self.tilemap.get(x, y); + for (0..self.world.tilemap.height) |y| { + for (0..self.world.tilemap.width) |x| { + const tile = self.world.tilemap.get(x, y); if (tile.ground_sprite) |sprite| { Gfx.drawSprite(sprite, Vec2.initFromInt(usize, x, y).multiply(tile_size), tile_size, .white); } - - if (tile.wall_sprite) |sprite| { - Gfx.drawSprite(sprite, Vec2.initFromInt(usize, x, y).multiply(tile_size), tile_size, .white); - } } } - var entity_iter = self.entities.iterator(); + var entity_iter = self.world.entities.iterator(); while (entity_iter.next()) |entity_id| { - const entity = self.entities.getAssumeExists(entity_id); + const entity = self.world.entities.getAssumeExists(entity_id); if (entity.type == .player) { - entity.pos_x += dir_x; - entity.pos_y += dir_y; + if ((dir_x != 0 or dir_y != 0) and self.world.movePlayer(entity_id, dir_x, dir_y)) { + var new_direction = entity.player_direction; + if (dir_x < 0) { + new_direction = .left; + } else if (dir_x > 0) { + new_direction = .right; + } else if (dir_y > 0) { + new_direction = .down; + } else if (dir_y < 0) { + new_direction = .up; + } - var new_direction = entity.player_direction; - if (dir_x < 0) { - new_direction = .left; - } else if (dir_x > 0) { - new_direction = .right; - } else if (dir_y > 0) { - new_direction = .down; - } else if (dir_y < 0) { - new_direction = .up; - } + if (entity.player_direction != new_direction) { + entity.player_frame_index = 0; + entity.player_direction = new_direction; + } - if (entity.player_direction != new_direction) { - entity.player_frame_index = 0; - entity.player_direction = new_direction; + self.footstep_sound = Audio.play(.{ .buffer = self.footstep_buffer, .bus = self.sfx_bus }); } const frames = self.player_sprites.getDirection(entity.player_direction); @@ -339,35 +390,32 @@ pub fn frame(self: *App, plt: Platform.Frame) !void { entity.player_frame_index = 0; } - if (dir_x != 0 or dir_y != 0) { - self.footstep_sound = Audio.play(.{ .buffer = self.footstep_buffer, .bus = self.sfx_bus }); - } - entity.sprite = frames[entity.player_frame_index]; } + entity.visual_pos = .initFromInt(i32, entity.pos_x, entity.pos_y); if (entity.sprite) |sprite| { Gfx.drawSprite( sprite, - Vec2.initFromInt(i32, entity.pos_x, entity.pos_y).multiply(tile_size), + entity.visual_pos.multiply(tile_size), tile_size, .white ); } } - self.roboto_font.drawText(.{ - .text = try std.fmt.allocPrint(plt.frame, "Hello {:.0}!", .{plt.time()}), - .pos = .init(0, 0), - .height = 32, - }); + // self.roboto_font.drawText(.{ + // .text = try std.fmt.allocPrint(plt.frame, "Hello {:.0}!", .{plt.time()}), + // .pos = .init(0, 0), + // .height = 32, + // }); } pub fn deinit(self: *App, plt: Platform.Deinit) void { self.roboto_font.deinit(plt.gpa); Gfx.deinitSpritesheet(self.roboto_font.spritesheet); - self.tilemap.deinit(plt.gpa); - self.entities.slots.deinit(plt.gpa); + self.world.tilemap.deinit(plt.gpa); + self.world.entities.slots.deinit(plt.gpa); }