diff --git a/src/assets/tiled/first.tmx b/src/assets/tiled/first.tmx index ff64a00..f129e7f 100644 --- a/src/assets/tiled/first.tmx +++ b/src/assets/tiled/first.tmx @@ -1,5 +1,5 @@ - + @@ -7,15 +7,15 @@ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,91,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5,0,0,0,0,0,0,53,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,40,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,53,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @@ -27,19 +27,38 @@ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,68,2,68,2,2,2,67,2,2,2,4,0,0,0,0, +0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0, +0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0, +0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0, +0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0, +0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0, +0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0, +0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0, +0,0,0,0,83,2,2,68,2,67,2,2,2,67,2,36,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,1,2,2,2,2,2,2,2,2,4,0,0,0,0,0,0, -0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0, -0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0, -0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0, -0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0, -0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0, -0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0, -0,0,0,0,83,2,2,68,0,2,2,2,67,36,0,0,0,0,0,0, -0,0,0,0,0,0,17,0,0,0,20,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,17,0,0,0,20,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,33,2,2,2,36,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + + +0,85,85,0,0,0,0,85,85,85,85,85,85,0,0,0,0,85,85,85, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85, +0,0,85,0,0,0,0,0,0,0,0,0,0,0,70,0,0,0,0,85, +85,0,0,0,0,0,69,0,0,70,0,0,0,70,0,0,0,0,0,85, +85,0,0,0,0,0,0,0,0,0,0,0,69,0,0,0,0,0,0,85, +85,0,0,0,0,0,0,0,0,69,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,0,0, +0,85,0,0,0,0,69,0,0,0,70,0,69,0,0,0,0,85,0,0, +0,85,0,0,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85, +0,0,0,0,85,0,0,0,0,0,0,0,0,0,85,0,85,0,0,85, +85,0,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85, +0,0,0,0,0,0,85,85,85,85,85,85,0,0,0,0,0,0,0,85 diff --git a/src/assets/tiled/main.tiled-session b/src/assets/tiled/main.tiled-session index 036a136..dcdfedd 100644 --- a/src/assets/tiled/main.tiled-session +++ b/src/assets/tiled/main.tiled-session @@ -8,11 +8,19 @@ ], "fileStates": { "first.tmx": { - "scale": 3, - "selectedLayer": 0, + "scale": 4, + "selectedLayer": 1, "viewCenter": { - "x": 141.49999999999997, - "y": 68 + "x": 70, + "y": 71.5 + } + }, + "second.tmx": { + "scale": 4, + "selectedLayer": 2, + "viewCenter": { + "x": 93, + "y": 71.25 } } }, diff --git a/src/assets/tiled/second.tmx b/src/assets/tiled/second.tmx new file mode 100644 index 0000000..a0e6647 --- /dev/null +++ b/src/assets/tiled/second.tmx @@ -0,0 +1,61 @@ + + + + + +0,0,0,85,0,0,0,85,85,85,85,0,0,0,0,0,0,0,85,0, +0,0,85,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,85, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,85,0,0,0,0,0,0,0,0,0,0,0,85,0,0, +85,0,0,0,85,85,0,0,0,0,0,0,0,0,0,0,0,85,85,0, +85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,0,0,85, +0,0,0,70,69,0,0,0,0,0,0,70,70,0,0,0,0,0,0,0, +85,0,0,0,0,0,0,0,69,0,69,0,70,0,0,0,0,0,85,0, +85,0,0,70,70,0,0,69,0,0,0,0,69,0,0,0,0,0,85,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,85,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,85,85,0,0,0,0,85,85,85,85,85,0,0,0,0,0,85 + + + + +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,53,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,40,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,91,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + + +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,1,2,2,2,4,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,17,0,0,0,20,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,17,0,0,0,20,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,17,0,0,0,20,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,65,2,0,2,66,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,17,0,69,0,20,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,17,0,69,0,20,0,0,0,0,0,0, +0,0,1,2,2,2,2,2,2,50,0,0,0,20,0,0,0,0,0,0, +0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0, +0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0, +0,0,17,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0, +0,0,33,2,2,2,2,2,2,2,2,2,2,36,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + diff --git a/src/assets/tiled/tileset.tsx b/src/assets/tiled/tileset.tsx index 2db6d48..9229b73 100644 --- a/src/assets/tiled/tileset.tsx +++ b/src/assets/tiled/tileset.tsx @@ -1,11 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -16,6 +66,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/entity.zig b/src/entity.zig index 40392cb..6eb2294 100644 --- a/src/entity.zig +++ b/src/entity.zig @@ -15,6 +15,7 @@ pub const Type = enum { player, solid, pot, + staircase, door, key, }; diff --git a/src/game.zig b/src/game.zig index ebe5713..79a61ed 100644 --- a/src/game.zig +++ b/src/game.zig @@ -19,7 +19,6 @@ const tiled = @import("./tiled.zig"); const Game = @This(); pub const Input = struct { - allocator: Allocator, dt: f64, move_up: Window.KeyState, move_down: Window.KeyState, @@ -56,6 +55,8 @@ pub const Level = struct { } }; +gpa: Allocator, + canvas_size: Vec2, level: Level, @@ -69,37 +70,60 @@ last_right_repeat_at: ?f64 = null, show_grid: bool = false, +timers: Timer.List = .empty, +level_exit_transition: ?Timer.Id = null, +level_enter_transition: ?Timer.Id = null, + pub fn init(gpa: Allocator) !Game { var self = Game{ + .gpa = gpa, .canvas_size = (Vec2.init(20, 15)), .level = .empty, .levels = .empty, .current_level = 0, }; - errdefer self.deinit(gpa); + errdefer self.deinit(); const manager = try tiled.ResourceManager.init(); defer manager.deinit(); try manager.loadTilesetFromBuffer(@embedFile("assets/tiled/tileset.tsx"), "tileset.tsx"); - const map = try manager.loadMapFromBuffer(@embedFile("assets/tiled/first.tmx")); - defer map.deinit(); + try self.levels.append(gpa, try loadLevelFromEmbedFile(gpa, manager, "assets/tiled/first.tmx")); + try self.levels.append(gpa, try loadLevelFromEmbedFile(gpa, manager, "assets/tiled/second.tmx")); - try self.levels.append(gpa, try loadLevelFromTiled(gpa, map)); - try self.restartLevel(gpa); + try self.restartLevel(); return self; } -fn restartLevel(self: *Game, gpa: Allocator) !void { - const level_copy = try self.levels.items[self.current_level].clone(gpa); - errdefer level_copy.deinit(gpa); +fn restartLevel(self: *Game) !void { + const level_copy = try self.levels.items[self.current_level].clone(self.gpa); + errdefer level_copy.deinit(self.gpa); - self.level.deinit(gpa); + self.level.deinit(self.gpa); self.level = level_copy; } +fn nextLevel(self: *Game) !void { + if (self.level_exit_transition != null) { + return; + } + + if (self.current_level < self.levels.items.len) { + self.level_exit_transition = try self.timers.start(self.gpa, .{ + .duration = 1 + }); + } +} + +fn loadLevelFromEmbedFile(gpa: Allocator, manager: tiled.ResourceManager, comptime path: []const u8) !Level { + const map = try manager.loadMapFromBuffer(@embedFile(path)); + defer map.deinit(); + + return try loadLevelFromTiled(gpa, map); +} + fn loadLevelFromTiled(gpa: Allocator, map: tiled.Map) !Level { var level: Level = .empty; errdefer level.deinit(gpa); @@ -113,9 +137,6 @@ fn loadLevelFromTiled(gpa: Allocator, map: tiled.Map) !Level { continue; } - const layer_props = tiled.Properties{ .inner = layer.layer.properties }; - const is_layer_solid = layer_props.getPropertyBool("solid") orelse false; - const map_width = map.map.width; for (0..map.map.height) |y| { for (0..map_width) |x| { @@ -139,7 +160,9 @@ fn loadLevelFromTiled(gpa: Allocator, map: tiled.Map) !Level { entity.locked = true; } else if (std.mem.eql(u8, tile_type, "pot")) { entity.type = .pot; - } else if (is_layer_solid) { + } else if (std.mem.eql(u8, tile_type, "staircase")) { + entity.type = .staircase; + } else if (std.mem.eql(u8, tile_type, "solid")) { entity.type = .solid; } @@ -151,12 +174,13 @@ fn loadLevelFromTiled(gpa: Allocator, map: tiled.Map) !Level { return level; } -pub fn deinit(self: *Game, gpa: Allocator) void { - self.level.deinit(gpa); +pub fn deinit(self: *Game) void { + self.timers.deinit(self.gpa); + self.level.deinit(self.gpa); for (self.levels.items) |*level| { - level.deinit(gpa); + level.deinit(self.gpa); } - self.levels.deinit(gpa); + self.levels.deinit(self.gpa); } fn drawGrid(self: *Game, size: Vec2, color: Vec4, line_width: f32) void { @@ -187,6 +211,10 @@ fn getEntityAt(self: *Game, pos: Vec2) ?Entity.Id { var iter = self.level.entities.iterator(); while (iter.next()) |tuple| { const entity = tuple.item; + if (entity.type == .nil) { + continue; + } + if (entity.position.eql(pos)) { return tuple.id; } @@ -226,6 +254,10 @@ fn moveEntity(self: *Game, entity_id: Entity.Id, dir: Vec2) bool { return false; } + if (entity.type == .nil) { + return true; + } + if (dir.x == 0 and dir.y == 0) { return true; } @@ -247,7 +279,7 @@ fn moveEntity(self: *Game, entity_id: Entity.Id, dir: Vec2) bool { if (next_entity.locked) { return false; } - } else if (next_entity.type == .nil) { + } else if (next_entity.type == .solid) { return false; } } @@ -262,7 +294,6 @@ pub fn getInput(self: *Game, window: *Window) Input { const dt = @as(f32, @floatFromInt(window.frame_dt_ns)) / std.time.ns_per_s; return Input{ - .allocator = window.gpa, .dt = dt, .move_up = window.getKeyState(.W), .move_down = window.getKeyState(.S), @@ -292,36 +323,77 @@ fn drawEntity(self: *Game, entity: *Entity) void { } } +fn hasStaricaseAt(self: *Game, position: Vec2) bool { + var iter = self.level.entities.iterator(); + while (iter.nextItem()) |entity| { + if (entity.type == .staircase and entity.position.eql(position)) { + return true; + } + } + + return false; +} + pub fn tick(self: *Game, input: Input) !void { - Gfx.drawRectangle(.init(0, 0), .init(self.canvas_size.x, self.canvas_size.y), rgb_hex("#222323").?); + const bg_color = rgb_hex("#222323").?; + + Gfx.drawRectangle(.init(0, 0), .init(self.canvas_size.x, self.canvas_size.y), bg_color); if (self.show_grid) { self.drawGrid(.init(1, 1), rgb(20, 20, 20), 0.1); } if (input.restart) { - try self.restartLevel(input.allocator); + try self.restartLevel(); } self.level.timers.now += input.dt; + self.timers.now += input.dt; + + var cover_opacity: f32 = 0; + var can_move = true; + + if (self.level_exit_transition) |timer| { + can_move = false; + + cover_opacity = self.timers.percent_passed(timer); + + if (self.timers.finished(timer)) { + self.current_level += 1; + try self.restartLevel(); + self.level_exit_transition = null; + + self.level_enter_transition = try self.timers.start(self.gpa, .{ + .duration = 1 + }); + } + } + + if (self.level_enter_transition) |timer| { + cover_opacity = 1 - self.timers.percent_passed(timer); + if (self.timers.finished(timer)) { + self.level_enter_transition = null; + } + } var move: Vec2 = .init(0, 0); + if (can_move) { + const repeat_options = Window.KeyState.RepeatOptions{ + .first_at = 0.3, + .period = 0.1 + }; - const repeat_options = Window.KeyState.RepeatOptions{ - .first_at = 0.3, - .period = 0.1 - }; - - if (input.move_up.pressed or input.move_up.repeat(&self.last_up_repeat_at, repeat_options)) { - move.y -= 1; - } - if (input.move_down.pressed or input.move_down.repeat(&self.last_down_repeat_at, repeat_options)) { - move.y += 1; - } - if (input.move_left.pressed or input.move_left.repeat(&self.last_left_repeat_at, repeat_options)) { - move.x -= 1; - } - if (input.move_right.pressed or input.move_right.repeat(&self.last_right_repeat_at, repeat_options)) { - move.x += 1; + if (input.move_up.pressed or input.move_up.repeat(&self.last_up_repeat_at, repeat_options)) { + move.y -= 1; + } + if (input.move_down.pressed or input.move_down.repeat(&self.last_down_repeat_at, repeat_options)) { + move.y += 1; + } + if (input.move_left.pressed or input.move_left.repeat(&self.last_left_repeat_at, repeat_options)) { + move.x -= 1; + } + if (input.move_right.pressed or input.move_right.repeat(&self.last_right_repeat_at, repeat_options)) { + move.x += 1; + } } var iter = self.level.entities.iterator(); @@ -330,21 +402,41 @@ pub fn tick(self: *Game, input: Input) !void { const entity_id = tuple.id; if (entity.type == .player) { _ = self.moveEntity(entity_id, move); + if (self.hasStaricaseAt(entity.position)) { + try self.nextLevel(); + } } } + var top_layer: std.ArrayList(Entity.Id) = .empty; + defer top_layer.deinit(self.gpa); + + var bottom_layer: std.ArrayList(Entity.Id) = .empty; + defer bottom_layer.deinit(self.gpa); + iter = self.level.entities.iterator(); - while (iter.nextItem()) |entity| { - if (entity.type != .player) { - self.drawEntity(entity); + while (iter.next()) |tuple| { + const entity = tuple.item; + const entity_id = tuple.id; + if (entity.type == .player or entity.type == .key or entity.type == .pot) { + try top_layer.append(self.gpa, entity_id); + } else { + try bottom_layer.append(self.gpa, entity_id); } } - iter = self.level.entities.iterator(); - while (iter.nextItem()) |entity| { - if (entity.type == .player) { - self.drawEntity(entity); - } + for (bottom_layer.items) |entity_id| { + const entity = self.level.entities.getAssumeExists(entity_id); + self.drawEntity(entity); + } + + for (top_layer.items) |entity_id| { + const entity = self.level.entities.getAssumeExists(entity_id); + self.drawEntity(entity); + } + + if (cover_opacity != 0) { + Gfx.drawRectangle(.init(0, 0), .init(self.canvas_size.x, self.canvas_size.y), bg_color.multiply(Vec4.init(1, 1, 1, cover_opacity))); } } @@ -361,4 +453,8 @@ pub fn debug(self: *Game) !void { imgui.textFmt("Entities: {}", .{self.level.entities.len}); imgui.textFmt("Timers: {}", .{self.level.timers.array_list.len}); _ = imgui.checkbox("Show grid", &self.show_grid); + + if (imgui.button("Skip level")) { + try self.nextLevel(); + } } diff --git a/src/timer.zig b/src/timer.zig index 4460617..f5177f4 100644 --- a/src/timer.zig +++ b/src/timer.zig @@ -60,6 +60,14 @@ pub const List = struct { return timer.finishes_at > self.now; } + pub fn percent_passed(self: *List, id: Id) f32 { + const timer = self.array_list.get(id) orelse return 0; + + const time_passed = self.now - timer.started_at; + const duration = timer.finishes_at - timer.started_at; + return @floatCast(std.math.clamp(time_passed / duration, 0, 1)); + } + pub fn finished(self: *List, id: Id) bool { const timer = self.array_list.get(id) orelse return false; if (timer.finishes_at > self.now) { diff --git a/src/window.zig b/src/window.zig index 8fbbb05..a2db1b4 100644 --- a/src/window.zig +++ b/src/window.zig @@ -240,7 +240,7 @@ pub fn init(self: *Window, gpa: Allocator) !void { }); var game = try Game.init(gpa); - errdefer game.deinit(gpa); + errdefer game.deinit(); self.* = Window{ .gpa = gpa, @@ -255,7 +255,7 @@ pub fn init(self: *Window, gpa: Allocator) !void { pub fn deinit(self: *Window) void { const gpa = self.gpa; - self.game.deinit(gpa); + self.game.deinit(); self.events.deinit(gpa); Gfx.deinit();