add goals with colors

This commit is contained in:
Rokas Puzonas 2026-10-03 17:55:50 +03:00
parent 930a7dc275
commit 7b99f3344c
3 changed files with 197 additions and 70 deletions

View File

@ -239,7 +239,7 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,98,98,98,98,98,98,0,0,0,0,0,0,0,0, 0,0,98,98,98,98,98,98,0,0,0,0,0,0,0,0,
0,0,98,0,0,0,0,98,98,98,98,98,98,0,0,0, 0,0,98,0,0,0,0,98,98,98,98,98,98,0,0,0,
0,0,98,0,0,0,0,0,0,0,0,0,98,98,0,0 0,0,98,0,0,8,42,0,12,0,0,0,98,98,0,0
</chunk> </chunk>
<chunk x="16" y="0" width="16" height="16"> <chunk x="16" y="0" width="16" height="16">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -332,7 +332,7 @@
0,0,0,99,0,98,98,0,0,0,0,0,0,0,0,0 0,0,0,99,0,98,98,0,0,0,0,0,0,0,0,0
</chunk> </chunk>
<chunk x="0" y="16" width="16" height="16"> <chunk x="0" y="16" width="16" height="16">
0,0,98,0,72,0,7,0,0,0,0,40,0,98,0,0, 0,0,98,0,72,0,7,7,0,0,0,40,0,98,0,0,
0,0,98,0,0,0,0,0,0,0,0,0,98,98,0,0, 0,0,98,0,0,0,0,0,0,0,0,0,98,98,0,0,
0,0,98,98,98,98,98,98,98,98,98,98,98,0,0,0, 0,0,98,98,98,98,98,98,98,98,98,98,98,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -350,7 +350,7 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk> </chunk>
<chunk x="16" y="16" width="16" height="16"> <chunk x="16" y="16" width="16" height="16">
0,0,98,0,72,0,0,7,0,0,0,75,0,98,0,0, 0,0,98,0,72,0,0,7,0,0,0,40,0,98,0,0,
0,0,98,0,0,0,0,7,0,0,0,0,98,98,0,0, 0,0,98,0,0,0,0,7,0,0,0,0,98,98,0,0,
0,0,98,98,98,98,98,98,98,98,98,98,98,0,0,0, 0,0,98,98,98,98,98,98,98,98,98,98,98,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -368,7 +368,7 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk> </chunk>
<chunk x="32" y="16" width="16" height="16"> <chunk x="32" y="16" width="16" height="16">
0,98,0,72,0,0,7,0,0,0,75,0,98,0,0,98, 0,98,0,72,0,0,7,0,0,0,40,0,98,0,0,98,
0,98,0,0,0,0,7,0,0,99,0,98,98,0,0,98, 0,98,0,0,0,0,7,0,0,99,0,98,98,0,0,98,
0,98,98,98,98,98,98,98,98,98,98,98,0,0,0,98, 0,98,98,98,98,98,98,98,98,98,98,98,0,0,0,98,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -386,7 +386,7 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk> </chunk>
<chunk x="48" y="16" width="16" height="16"> <chunk x="48" y="16" width="16" height="16">
0,72,0,0,0,0,0,12,75,0,98,0,0,98,0,72, 0,72,0,0,0,0,0,12,40,0,98,0,0,98,0,72,
0,0,0,0,0,0,0,99,0,98,98,0,0,98,0,0, 0,0,0,0,0,0,0,99,0,98,98,0,0,98,0,0,
98,98,98,98,98,98,98,98,98,98,0,0,0,98,98,98, 98,98,98,98,98,98,98,98,98,98,0,0,0,98,98,98,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -404,7 +404,7 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk> </chunk>
<chunk x="64" y="16" width="16" height="16"> <chunk x="64" y="16" width="16" height="16">
0,7,7,0,12,12,75,0,98,0,0,98,0,72,0,0, 0,7,7,0,12,12,40,0,98,0,0,98,0,72,0,0,
0,0,0,0,99,99,0,98,98,0,0,98,0,0,0,0, 0,0,0,0,99,99,0,98,98,0,0,98,0,0,0,0,
98,98,98,98,98,98,98,98,0,0,0,98,98,98,98,98, 98,98,98,98,98,98,98,98,0,0,0,98,98,98,98,98,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -422,7 +422,7 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk> </chunk>
<chunk x="80" y="16" width="16" height="16"> <chunk x="80" y="16" width="16" height="16">
0,0,0,12,75,0,98,0,0,0,0,0,0,0,0,0, 0,0,0,12,40,0,98,0,0,0,0,0,0,0,0,0,
7,0,0,99,0,98,98,0,0,0,0,0,0,0,0,0, 7,0,0,99,0,98,98,0,0,0,0,0,0,0,0,0,
98,98,98,98,98,98,0,0,0,0,0,0,0,0,0,0, 98,98,98,98,98,98,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

View File

@ -3,26 +3,31 @@
<image source="../sokoban/sokoban_tilesheet.png" width="832" height="512"/> <image source="../sokoban/sokoban_tilesheet.png" width="832" height="512"/>
<tile id="6"> <tile id="6">
<properties> <properties>
<property name="color" value="brown"/>
<property name="type" value="box"/> <property name="type" value="box"/>
</properties> </properties>
</tile> </tile>
<tile id="7"> <tile id="7">
<properties> <properties>
<property name="color" value="red"/>
<property name="type" value="box"/> <property name="type" value="box"/>
</properties> </properties>
</tile> </tile>
<tile id="8"> <tile id="8">
<properties> <properties>
<property name="color" value="blue"/>
<property name="type" value="box"/> <property name="type" value="box"/>
</properties> </properties>
</tile> </tile>
<tile id="9"> <tile id="9">
<properties> <properties>
<property name="color" value="green"/>
<property name="type" value="box"/> <property name="type" value="box"/>
</properties> </properties>
</tile> </tile>
<tile id="10"> <tile id="10">
<properties> <properties>
<property name="color" value="gray"/>
<property name="type" value="box"/> <property name="type" value="box"/>
</properties> </properties>
</tile> </tile>
@ -31,11 +36,23 @@
<property name="type" value="box_hole"/> <property name="type" value="box_hole"/>
</properties> </properties>
</tile> </tile>
<tile id="24">
<properties>
<property name="color" value="brown"/>
<property name="type" value="box_hole"/>
</properties>
</tile>
<tile id="25"> <tile id="25">
<properties> <properties>
<property name="type" value="goal_marker"/> <property name="type" value="goal_marker"/>
</properties> </properties>
</tile> </tile>
<tile id="37">
<properties>
<property name="color" value="red"/>
<property name="type" value="box_hole"/>
</properties>
</tile>
<tile id="38"> <tile id="38">
<properties> <properties>
<property name="type" value="goal_marker"/> <property name="type" value="goal_marker"/>
@ -48,34 +65,51 @@
</tile> </tile>
<tile id="40"> <tile id="40">
<properties> <properties>
<property name="color" value="brown"/>
<property name="type" value="box_goal"/> <property name="type" value="box_goal"/>
</properties> </properties>
</tile> </tile>
<tile id="41"> <tile id="41">
<properties> <properties>
<property name="color" value="red"/>
<property name="type" value="box_goal"/> <property name="type" value="box_goal"/>
</properties> </properties>
</tile> </tile>
<tile id="42"> <tile id="42">
<properties> <properties>
<property name="color" value="blue"/>
<property name="type" value="box_goal"/> <property name="type" value="box_goal"/>
</properties> </properties>
</tile> </tile>
<tile id="43"> <tile id="43">
<properties> <properties>
<property name="color" value="green"/>
<property name="type" value="box_goal"/> <property name="type" value="box_goal"/>
</properties> </properties>
</tile> </tile>
<tile id="44"> <tile id="44">
<properties> <properties>
<property name="color" value="gray"/>
<property name="type" value="box_goal"/> <property name="type" value="box_goal"/>
</properties> </properties>
</tile> </tile>
<tile id="50">
<properties>
<property name="color" value="blue"/>
<property name="type" value="box_hole"/>
</properties>
</tile>
<tile id="51"> <tile id="51">
<properties> <properties>
<property name="type" value="goal_marker"/> <property name="type" value="goal_marker"/>
</properties> </properties>
</tile> </tile>
<tile id="63">
<properties>
<property name="color" value="green"/>
<property name="type" value="box_hole"/>
</properties>
</tile>
<tile id="64"> <tile id="64">
<properties> <properties>
<property name="type" value="goal_marker"/> <property name="type" value="goal_marker"/>
@ -91,6 +125,12 @@
<property name="type" value="coin"/> <property name="type" value="coin"/>
</properties> </properties>
</tile> </tile>
<tile id="76">
<properties>
<property name="color" value="gray"/>
<property name="type" value="box_hole"/>
</properties>
</tile>
<tile id="77"> <tile id="77">
<properties> <properties>
<property name="type" value="goal_marker"/> <property name="type" value="goal_marker"/>

View File

@ -21,6 +21,23 @@ const Tilesheet = @import("./tilesheet.zig");
const App = @This(); const App = @This();
const BoxColor = enum {
none,
brown,
red,
blue,
green,
gray,
var string_mapping = std.StaticStringMap(BoxColor).initComptime(.{
.{ "brown", .brown },
.{ "red", .red },
.{ "blue", .blue },
.{ "green", .green },
.{ "gray", .gray },
});
};
const Direction = enum { const Direction = enum {
up, up,
down, down,
@ -71,6 +88,7 @@ const Entity = struct {
pos: Point = .{ .x = -1, .y = -1 }, pos: Point = .{ .x = -1, .y = -1 },
box_hole_id: Entity.Id = .nil, box_hole_id: Entity.Id = .nil,
color: BoxColor = .none,
const Type = enum { const Type = enum {
nil, nil,
@ -139,26 +157,28 @@ const UndoFrame = struct {
}; };
const World = struct { const World = struct {
entities_buffer: [256]Entity.SlotMap.Slot,
entities: Entity.SlotMap, entities: Entity.SlotMap,
width: u32, width: u32,
height: u32, height: u32,
level_number: i32,
pub fn clone(self: World, gpa: Allocator) !World { pub fn init(self: *World) void {
var entity_slots = try self.entities.slots.clone(gpa); self.* = World{
errdefer entity_slots.deinit(gpa); .entities_buffer = undefined,
.entities = .initBuffer(&self.entities_buffer),
var entities = self.entities; .width = 0,
entities.slots = entity_slots; .height = 0,
.level_number = 0
return World{
.width = self.width,
.height = self.height,
.entities = entities,
}; };
} }
pub fn deinit(self: *World, gpa: Allocator) void { pub fn copy(self: *World, other: World) void {
self.entities.deinit(gpa); self.* = other;
const self_slots = self.entities_buffer[0..other.entities.slots.items.len];
self.entities.slots.items = self_slots;
@memcpy(self_slots, other.entities.slots.items);
} }
fn spawnEntity(self: *World, opts: Entity) Entity.Id { fn spawnEntity(self: *World, opts: Entity) Entity.Id {
@ -301,21 +321,28 @@ const TickContext = struct {
} }
}; };
const Level = struct { const BoxSprites = struct {
number: i32, regular: Gfx.Sprite.Id,
world: World, in_hole: Gfx.Sprite.Id,
darker: Gfx.Sprite.Id,
pub const nil = BoxSprites{
.regular = .nil,
.in_hole = .nil,
.darker = .nil,
};
}; };
world: World, world: World,
undo_stack: std.ArrayList(UndoFrame) = .empty, undo_stack: std.ArrayList(UndoFrame) = .empty,
current_level: u32 = 0, current_level: u32 = 0,
levels: std.ArrayList(Level), levels: std.ArrayList(World),
roboto_font: Font, roboto_font: Font,
tilesheet: Tilesheet, tilesheet: Tilesheet,
player_sprites: PlayerSprites, player_sprites: PlayerSprites,
hole_with_box_sprite: Gfx.Sprite.Id, box_sprites_by_color: std.EnumArray(BoxColor, BoxSprites),
animation_timer: Platform.Nanoseconds, animation_timer: Platform.Nanoseconds,
footstep_buffer: Audio.BufferId, footstep_buffer: Audio.BufferId,
@ -386,19 +413,46 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 {
.animation_timer = 0, .animation_timer = 0,
.tilesheet = tilesheet, .tilesheet = tilesheet,
.footstep_buffer = footstep_sound, .footstep_buffer = footstep_sound,
.hole_with_box_sprite = tilesheet.get(6, 4), .box_sprites_by_color = .init(.{
.none = .nil,
.brown = .{
.regular = tilesheet.get(6, 0),
.darker = tilesheet.get(6, 1),
.in_hole = tilesheet.get(6, 4),
},
.red = .{
.regular = tilesheet.get(7, 0),
.darker = tilesheet.get(7, 1),
.in_hole = tilesheet.get(7, 4),
},
.blue = .{
.regular = tilesheet.get(8, 0),
.darker = tilesheet.get(8, 1),
.in_hole = tilesheet.get(8, 4),
},
.green = .{
.regular = tilesheet.get(9, 0),
.darker = tilesheet.get(9, 1),
.in_hole = tilesheet.get(9, 4),
},
.gray = .{
.regular = tilesheet.get(10, 0),
.darker = tilesheet.get(10, 1),
.in_hole = tilesheet.get(10, 4),
}
}),
.sfx_bus = Audio.addBus(.{ .label = "sfx" }), .sfx_bus = Audio.addBus(.{ .label = "sfx" }),
.world = undefined, .world = undefined,
.levels = .empty, .levels = .empty,
}; };
self.levels = try self.loadLevels(plt.gpa, plt.assets); self.levels = try self.loadLevels(plt.gpa, plt.assets);
self.world = try self.levels.items[0].world.clone(plt.gpa); self.world.copy(self.levels.items[0]);
return null; return null;
} }
fn loadLevels(self: *App, gpa: Allocator, assets: *Platform.Assets) !std.ArrayList(Level) { fn loadLevels(self: *App, gpa: Allocator, assets: *Platform.Assets) !std.ArrayList(World) {
var xml_parser = try Tiled.xml.Parser.initCapacity(gpa, .{}); var xml_parser = try Tiled.xml.Parser.initCapacity(gpa, .{});
defer xml_parser.deinit(gpa); defer xml_parser.deinit(gpa);
@ -418,9 +472,11 @@ fn loadLevels(self: *App, gpa: Allocator, assets: *Platform.Assets) !std.ArrayLi
.root .root
)); ));
var levels: std.ArrayList(Level) = .empty; var levels: std.ArrayList(World) = .empty;
const level_bounds = tilemap.getLayer("Level bounds").?; const level_bounds = tilemap.getLayer("Level bounds").?;
try levels.ensureTotalCapacity(gpa, level_bounds.variant.object.items.len);
for (level_bounds.variant.object.items) |obj| { for (level_bounds.variant.object.items) |obj| {
if (obj.shape != .rectangle) { if (obj.shape != .rectangle) {
continue; continue;
@ -445,37 +501,32 @@ fn loadLevels(self: *App, gpa: Allocator, assets: *Platform.Assets) !std.ArrayLi
}; };
level_bound = tilemap.trimBoundsByEmptyTiles(level_bound); level_bound = tilemap.trimBoundsByEmptyTiles(level_bound);
var level = try self.loadLevel(gpa, tilemap, tilesets, level_bound); var level = levels.addOneAssumeCapacity();
level.number = level_index; try self.loadLevel(level, tilemap, tilesets, level_bound);
try levels.append(gpa, level); level.level_number = level_index;
} }
const Context = struct { const Context = struct {
fn lessThan(_: @This(), lhs: Level, rhs: Level) bool { fn lessThan(_: @This(), lhs: World, rhs: World) bool {
return lhs.number < rhs.number; return lhs.level_number < rhs.level_number;
} }
}; };
std.mem.sort(Level, levels.items, Context{}, Context.lessThan); std.mem.sort(World, levels.items, Context{}, Context.lessThan);
return levels; return levels;
} }
fn loadLevel( fn loadLevel(
self: *App, self: *App,
gpa: Allocator, world: *World,
tilemap: Tiled.Tilemap, tilemap: Tiled.Tilemap,
tilesets: Tiled.Tileset.List, tilesets: Tiled.Tileset.List,
bounds: Tiled.Tilemap.Bounds bounds: Tiled.Tilemap.Bounds
) !Level { ) !void {
var level = Level{ world.init();
.number = 0, world.width = bounds.width();
.world = .{ world.height = bounds.height();
.width = bounds.width(),
.height = bounds.height(),
.entities = try .initCapacity(gpa, 256)
}
};
const ground = tilemap.getLayer("Ground").?; const ground = tilemap.getLayer("Ground").?;
const foreground = tilemap.getLayer("Foreground").?; const foreground = tilemap.getLayer("Foreground").?;
@ -487,7 +538,7 @@ fn loadLevel(
if (tilemap.getTile(ground, tilesets, x, y)) |tiled_tile| { if (tilemap.getTile(ground, tilesets, x, y)) |tiled_tile| {
const pos = tiled_tile.getPosition(); const pos = tiled_tile.getPosition();
_ = level.world.spawnEntity(Entity{ _ = world.spawnEntity(Entity{
.type = .nil, .type = .nil,
.visual_layer = .ground, .visual_layer = .ground,
.pos = .{ .pos = .{
@ -502,29 +553,34 @@ fn loadLevel(
const pos = tiled_tile.getPosition(); const pos = tiled_tile.getPosition();
const props = tiled_tile.getProperties(); const props = tiled_tile.getProperties();
const props_type = props.getString("type") orelse ""; const props_type = props.getString("type") orelse "";
const props_color = props.getString("color") orelse "";
var entity_type: ?Entity.Type = null; var entity_type: ?Entity.Type = null;
var layer: Entity.Layer = .surface; var layer: Entity.Layer = .surface;
var color: BoxColor = .none;
if (std.mem.eql(u8, props_type, "wall")) { if (std.mem.eql(u8, props_type, "wall")) {
entity_type = .wall; entity_type = .wall;
} else if (std.mem.eql(u8, props_type, "box")) { } else if (std.mem.eql(u8, props_type, "box")) {
entity_type = .box; entity_type = .box;
color = BoxColor.string_mapping.get(props_color) orelse return error.UnknownBoxColor;
} else if (std.mem.eql(u8, props_type, "player")) { } else if (std.mem.eql(u8, props_type, "player")) {
entity_type = .player; entity_type = .player;
} else if (std.mem.eql(u8, props_type, "coin")) { } else if (std.mem.eql(u8, props_type, "coin")) {
entity_type = .coin; entity_type = .coin;
} else if (std.mem.eql(u8, props_type, "box_hole")) { } else if (std.mem.eql(u8, props_type, "box_hole")) {
entity_type = .box_hole; entity_type = .box_hole;
color = BoxColor.string_mapping.get(props_color) orelse .none;
layer = .on_ground; layer = .on_ground;
} else if (std.mem.eql(u8, props_type, "box_goal")) { } else if (std.mem.eql(u8, props_type, "box_goal")) {
entity_type = .box_goal; entity_type = .box_goal;
layer = .on_ground; layer = .on_ground;
color = BoxColor.string_mapping.get(props_color) orelse .none;
} else { } else {
log.warn("Unknown tile type: '{s}' at {} {}", .{props_type, x, y}); log.warn("Unknown tile type: '{s}' at {} {}", .{props_type, x, y});
} }
if (entity_type != null) { if (entity_type != null) {
_ = level.world.spawnEntity(Entity{ _ = world.spawnEntity(Entity{
.type = entity_type.?, .type = entity_type.?,
.visual_layer = layer, .visual_layer = layer,
.pos = .{ .pos = .{
@ -532,25 +588,26 @@ fn loadLevel(
.y = @intCast(oy), .y = @intCast(oy),
}, },
.visual_pos = .initFromInt(usize, ox, oy), .visual_pos = .initFromInt(usize, ox, oy),
.sprite = self.tilesheet.get(pos.x, pos.y) .sprite = self.tilesheet.get(pos.x, pos.y),
.color = color
}); });
} }
} }
} }
} }
return level;
} }
const PlayerInput = struct { const PlayerInput = struct {
move_dir: Point, move_dir: Point,
undo: bool undo: bool,
restart: bool
}; };
fn getPlayerInput(input: *Platform.Input) PlayerInput { fn getPlayerInput(input: *Platform.Input) PlayerInput {
var result: PlayerInput = .{ var result: PlayerInput = .{
.move_dir = .{ .x = 0, .y = 0 }, .move_dir = .{ .x = 0, .y = 0 },
.undo = false .undo = false,
.restart = false
}; };
if (input.isKeyPressedOrRepeat(.S)) { if (input.isKeyPressedOrRepeat(.S)) {
@ -570,6 +627,7 @@ fn getPlayerInput(input: *Platform.Input) PlayerInput {
} }
result.undo = input.isKeyPressedOrRepeat(.Z); result.undo = input.isKeyPressedOrRepeat(.Z);
result.restart = input.isKeyPressed(.R);
return result; return result;
} }
@ -596,12 +654,14 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
} }
} }
if (input.restart) {
try self.setWorldByLevel(self.current_level);
}
var tick_context: TickContext = .{ var tick_context: TickContext = .{
.world = &self.world .world = &self.world
}; };
var goal_count: u32 = 0;
var box_at_goal_count: u32 = 0;
{ {
var entity_iter = self.world.entities.iterator(); var entity_iter = self.world.entities.iterator();
while (entity_iter.next()) |entity_id| { while (entity_iter.next()) |entity_id| {
@ -620,12 +680,6 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
self.footstep_sound = Audio.play(.{ .buffer = self.footstep_buffer, .bus = self.sfx_bus }); self.footstep_sound = Audio.play(.{ .buffer = self.footstep_buffer, .bus = self.sfx_bus });
} }
} else if (entity.type == .box_goal) {
goal_count += 1;
if (self.world.entityExists(.{ .pos = entity.pos, .type = .box })) {
box_at_goal_count += 1;
}
} else if (entity.type == .box) { } else if (entity.type == .box) {
interpolate_pos = true; interpolate_pos = true;
} }
@ -662,6 +716,28 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
try self.undo_stack.append(plt.gpa, tick_context.undo_frame); try self.undo_stack.append(plt.gpa, tick_context.undo_frame);
} }
var goal_count: u32 = 0;
var box_at_goal_count: u32 = 0;
{
var goal_iter = self.world.iterEntities(.{ .type = .box_goal });
while (goal_iter.next()) |goal_id| {
const goal = self.world.entities.getAssumeExists(goal_id);
goal_count += 1;
if (self.world.findEntity(.{ .pos = goal.pos, .type = .box })) |box| {
var has_correct_color = true;
if (goal.color != .none) {
has_correct_color = goal.color == box.color;
}
const is_on_top = box.visual_pos.distance(goal.visual_pos) < 0.05;
if (is_on_top and has_correct_color) {
box_at_goal_count += 1;
}
}
}
}
inline for (.{ Entity.Layer.ground, Entity.Layer.on_ground, Entity.Layer.surface }) |layer| { inline for (.{ Entity.Layer.ground, Entity.Layer.on_ground, Entity.Layer.surface }) |layer| {
var entity_iter = self.world.entities.iterator(); var entity_iter = self.world.entities.iterator();
while (entity_iter.next()) |entity_id| { while (entity_iter.next()) |entity_id| {
@ -672,7 +748,7 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
var sprite = entity.sprite; var sprite = entity.sprite;
const box_hole_margin = 0.2; const box_hole_margin = 0.1;
if (entity.type == .box) { if (entity.type == .box) {
const box_hole_id = self.world.findEntityId(.{ .type = .box_hole, .pos = entity.pos }); const box_hole_id = self.world.findEntityId(.{ .type = .box_hole, .pos = entity.pos });
if (self.world.entities.get(box_hole_id)) |box_hole| { if (self.world.entities.get(box_hole_id)) |box_hole| {
@ -680,12 +756,25 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
sprite = null; sprite = null;
} }
} }
if (self.world.findEntity(.{ .type = .box_goal, .pos = entity.pos })) |box_goal| {
if (box_goal.visual_pos.distance(entity.visual_pos) < 0.2) {
var is_correct = true;
if (box_goal.color != .none) {
is_correct = box_goal.color == entity.color;
}
if (is_correct) {
sprite = self.box_sprites_by_color.get(entity.color).darker;
}
}
}
} else if (entity.type == .box_hole) { } else if (entity.type == .box_hole) {
var box_iter = self.world.iterEntities(.{ .type = .box, .pos = entity.pos }); var box_iter = self.world.iterEntities(.{ .type = .box, .pos = entity.pos });
while (box_iter.next()) |box_id| { while (box_iter.next()) |box_id| {
const box = self.world.entities.getAssumeExists(box_id); const box = self.world.entities.getAssumeExists(box_id);
if (box.visual_pos.distance(entity.visual_pos) < box_hole_margin) { if (box.visual_pos.distance(entity.visual_pos) < box_hole_margin) {
sprite = self.hole_with_box_sprite; sprite = self.box_sprites_by_color.get(box.color).in_hole;
} }
} }
} }
@ -703,21 +792,19 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
if (goal_count > 0 and goal_count == box_at_goal_count and self.current_level+1 < self.levels.items.len) { if (goal_count > 0 and goal_count == box_at_goal_count and self.current_level+1 < self.levels.items.len) {
self.current_level += 1; self.current_level += 1;
self.world.deinit(plt.gpa); try self.setWorldByLevel(self.current_level);
self.world = try self.levels.items[self.current_level].world.clone(plt.gpa);
self.undo_stack.clearRetainingCapacity();
} }
} }
pub fn setWorldByLevel(self: *App, index: u32) !void {
self.world.copy(self.levels.items[index]);
self.undo_stack.clearRetainingCapacity();
}
pub fn deinit(self: *App, plt: Platform.Deinit) void { pub fn deinit(self: *App, plt: Platform.Deinit) void {
self.roboto_font.deinit(plt.gpa); self.roboto_font.deinit(plt.gpa);
Gfx.deinitSpritesheet(self.roboto_font.spritesheet); Gfx.deinitSpritesheet(self.roboto_font.spritesheet);
self.world.deinit(plt.gpa);
for (self.levels.items) |*level| {
level.world.deinit(plt.gpa);
}
self.levels.deinit(plt.gpa); self.levels.deinit(plt.gpa);
self.undo_stack.deinit(plt.gpa); self.undo_stack.deinit(plt.gpa);
} }