add tree chopping
This commit is contained in:
parent
73bf3c2d34
commit
7e3c8b4a75
313
src/app.zig
313
src/app.zig
@ -15,6 +15,8 @@ const Platform = @import("./platform.zig");
|
||||
const Gfx = Platform.Gfx;
|
||||
const ImGUI = Platform.ImGUI;
|
||||
const Rect = Platform.Rect;
|
||||
const Color = Platform.Color;
|
||||
const Nanoseconds = Platform.Nanoseconds;
|
||||
|
||||
const SlotMapType = @import("./slot_map.zig").SlotMapType;
|
||||
|
||||
@ -28,6 +30,7 @@ prng: Prng,
|
||||
|
||||
heat: u64 = 0,
|
||||
|
||||
entities_buffer: [2048]EntitySlotMap.Slot,
|
||||
entities: EntitySlotMap,
|
||||
|
||||
font: Gfx.Font.Id,
|
||||
@ -149,14 +152,21 @@ const Entity = struct {
|
||||
type: Type = .nil,
|
||||
flags: Flags = .{},
|
||||
|
||||
minion_state: MinionState = .chop,
|
||||
minion_target_station: ?EntityId = null,
|
||||
work_progress: f32 = 0,
|
||||
minion_chop_speed: f32 = 1,
|
||||
target_entity: ?EntityId = null,
|
||||
targeted_by_entity: ?EntityId = null,
|
||||
|
||||
minion_state: MinionState = .goto_tree_station,
|
||||
minion_work_progress: f32 = 0,
|
||||
minion_chop_cooldown: Nanoseconds = 0,
|
||||
minion_chop_damage: u32 = 2,
|
||||
minion_burn_speed: f32 = 1,
|
||||
|
||||
tree_health: u32 = 0,
|
||||
tree_max_health: u32 = 0,
|
||||
|
||||
station_subtype: StationSubtype = .chop,
|
||||
station_occupant: ?EntityId = null,
|
||||
station_spawn_tree_cooldown: Nanoseconds = 0,
|
||||
station_owned_tree: ?EntityId = null,
|
||||
|
||||
pos: Vec2 = .init(0, 0),
|
||||
vel: Vec2 = .init(0, 0),
|
||||
@ -168,10 +178,12 @@ const Entity = struct {
|
||||
animation: AnimationName = .idle,
|
||||
animation_dir: AnimationDirection = .down,
|
||||
animation_index: u32 = 0,
|
||||
animation_timer: Platform.Nanoseconds = 0,
|
||||
animation_timer: Nanoseconds = 0,
|
||||
|
||||
const MinionState = enum {
|
||||
chop,
|
||||
goto_tree_station,
|
||||
chop_tree,
|
||||
pickup_wood,
|
||||
burn
|
||||
};
|
||||
|
||||
@ -179,6 +191,8 @@ const Entity = struct {
|
||||
nil,
|
||||
minion,
|
||||
station,
|
||||
tree,
|
||||
wood,
|
||||
};
|
||||
|
||||
const StationSubtype = enum {
|
||||
@ -243,7 +257,8 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 {
|
||||
.idle_animations = try AnimationDirections.init(plt.arena, idle_sheet, ns_per_s / idle_fps, 2),
|
||||
.walk_animations = try AnimationDirections.init(plt.arena, walk_sheet, ns_per_s / walk_fps, 4),
|
||||
.death_animations = try AnimationDirections.init(plt.arena, death_sheet, ns_per_s / death_fps, 3),
|
||||
.entities = .init(.empty),
|
||||
.entities_buffer = undefined,
|
||||
.entities = .init(&self.entities_buffer),
|
||||
.prng = Prng.init(@truncate(@as(u96, @intCast(now.nanoseconds)))),
|
||||
.camera_pos = .init(0, 0),
|
||||
.font = font
|
||||
@ -261,15 +276,12 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 {
|
||||
}
|
||||
|
||||
pub fn spawnMinion(self: *App, pos: Vec2) !void {
|
||||
const minion_id = try self.entities.insert(self.gpa);
|
||||
const minion = self.entities.getAssumeExists(minion_id);
|
||||
minion.* = Entity{
|
||||
_ = try self.entities.insert(Entity{
|
||||
.type = .minion,
|
||||
.flags = .{
|
||||
.has_animation = true,
|
||||
.has_kinematics = true
|
||||
},
|
||||
.minion_state = .chop,
|
||||
|
||||
.pos = pos,
|
||||
.friction = 0.998,
|
||||
@ -277,18 +289,34 @@ pub fn spawnMinion(self: *App, pos: Vec2) !void {
|
||||
.max_walk_speed = 300,
|
||||
|
||||
.animation = .idle,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
pub fn spawnStation(self: *App, pos: f32, subtype: Entity.StationSubtype) !void {
|
||||
const id = try self.entities.insert(self.gpa);
|
||||
const station = self.entities.getAssumeExists(id);
|
||||
station.* = Entity{
|
||||
_ = try self.entities.insert(Entity{
|
||||
.type = .station,
|
||||
.station_subtype = subtype,
|
||||
|
||||
.pos = .init(pos, 0),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn spawnTree(self: *App, station_id: EntityId) !void {
|
||||
const station = self.entities.get(station_id) orelse return;
|
||||
assert(station.type == .station);
|
||||
if (station.station_owned_tree != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const health = 10;
|
||||
const id = try self.entities.insert(Entity{
|
||||
.type = .tree,
|
||||
.tree_health = health,
|
||||
.tree_max_health = health,
|
||||
|
||||
.pos = station.pos
|
||||
});
|
||||
station.station_owned_tree = id;
|
||||
}
|
||||
|
||||
fn getNearestFreeStation(self: *App, from: Vec2, subtype: Entity.StationSubtype) ?EntityId {
|
||||
@ -304,7 +332,7 @@ fn getNearestFreeStation(self: *App, from: Vec2, subtype: Entity.StationSubtype)
|
||||
if (station.station_subtype != subtype) {
|
||||
continue;
|
||||
}
|
||||
if (station.station_occupant != null) {
|
||||
if (station.targeted_by_entity != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -319,29 +347,57 @@ fn getNearestFreeStation(self: *App, from: Vec2, subtype: Entity.StationSubtype)
|
||||
return result;
|
||||
}
|
||||
|
||||
fn bindEntityToStation(self: *App, other_id: EntityId, station_id: EntityId) void {
|
||||
const station = self.entities.get(station_id) orelse return;
|
||||
assert(station.type == .station);
|
||||
fn getNearestFreeTree(self: *App, from: Vec2) ?EntityId {
|
||||
var result: ?EntityId = null;
|
||||
var result_distance: f32 = 0;
|
||||
|
||||
const other = self.entities.get(other_id) orelse return;
|
||||
assert(other.type != .station);
|
||||
|
||||
station.station_occupant = other_id;
|
||||
other.minion_target_station = station_id;
|
||||
var station_iter = self.entities.iterator();
|
||||
while (station_iter.next()) |id| {
|
||||
const station = self.entities.getAssumeExists(id);
|
||||
if (station.type != .tree) {
|
||||
continue;
|
||||
}
|
||||
if (station.targeted_by_entity != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fn unbindEntityFromStation(self: *App, station_id: EntityId) void {
|
||||
const station = self.entities.get(station_id) orelse return;
|
||||
if (station.type != .station) {
|
||||
return;
|
||||
const distance = station.pos.distanceSqr(from);
|
||||
|
||||
if (result == null or result_distance > distance) {
|
||||
result = id;
|
||||
result_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (station.station_occupant) |occupant_id| {
|
||||
station.station_occupant = null;
|
||||
if (self.entities.get(occupant_id)) |occupant| {
|
||||
occupant.minion_target_station = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn setEntityTarget(self: *App, entity_id: EntityId, target_id: EntityId) void {
|
||||
const entity = self.entities.get(entity_id) orelse return;
|
||||
const target = self.entities.get(target_id) orelse return;
|
||||
|
||||
self.unsetEntityTarget(entity_id);
|
||||
entity.target_entity = target_id;
|
||||
target.targeted_by_entity = entity_id;
|
||||
}
|
||||
|
||||
fn unsetEntityTarget(self: *App, entity_id: EntityId) void {
|
||||
const entity = self.entities.get(entity_id) orelse return;
|
||||
|
||||
if (entity.target_entity) |target_id| {
|
||||
if (self.entities.get(target_id)) |target| {
|
||||
if (target.targeted_by_entity == entity_id) {
|
||||
target.targeted_by_entity = null;
|
||||
}
|
||||
}
|
||||
|
||||
entity.target_entity = null;
|
||||
}
|
||||
}
|
||||
|
||||
fn drawProgressBar(progress: f32, center: Vec2, size: Vec2, color: Color) void {
|
||||
const from = center.sub(.init(size.x/2, 0));
|
||||
Gfx.drawLine(from, from.add(.init(size.x * progress, 0)), color, size.y);
|
||||
}
|
||||
|
||||
pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
@ -368,21 +424,56 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
Gfx.drawLine(.init(-800, 0), .init(800, 0), .white, 5);
|
||||
|
||||
var station_iter = self.entities.iterator();
|
||||
while (station_iter.next()) |minion_id| {
|
||||
const station = self.entities.getAssumeExists(minion_id);
|
||||
while (station_iter.next()) |station_id| {
|
||||
const station = self.entities.getAssumeExists(station_id);
|
||||
if (station.type != .station) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (station.station_occupant) |station_occupant| {
|
||||
if (!self.entities.exists(station_occupant)) {
|
||||
station.station_occupant = null;
|
||||
if (station.station_owned_tree) |station_owned_tree| {
|
||||
if (!self.entities.exists(station_owned_tree)) {
|
||||
station.station_owned_tree = null;
|
||||
station.station_spawn_tree_cooldown = std.time.ns_per_s;
|
||||
}
|
||||
}
|
||||
|
||||
if (station.station_subtype == .chop) {
|
||||
station.station_spawn_tree_cooldown = @max(station.station_spawn_tree_cooldown - plt.dt, 0);
|
||||
if (station.station_spawn_tree_cooldown == 0 and station.station_owned_tree == null) {
|
||||
try self.spawnTree(station_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const character_size = Vec2.init(96, 96);
|
||||
|
||||
{
|
||||
var entity_iter = self.entities.iterator();
|
||||
while (entity_iter.next()) |entity_id| {
|
||||
const e = self.entities.getAssumeExists(entity_id);
|
||||
|
||||
if (e.target_entity) |other_id| {
|
||||
if (self.entities.get(other_id)) |other| {
|
||||
if (other.targeted_by_entity != entity_id) {
|
||||
e.target_entity = null;
|
||||
}
|
||||
} else {
|
||||
e.target_entity = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.targeted_by_entity) |other_id| {
|
||||
if (self.entities.get(other_id)) |other| {
|
||||
if (other.target_entity != entity_id) {
|
||||
e.targeted_by_entity = null;
|
||||
}
|
||||
} else {
|
||||
e.targeted_by_entity = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var minion_iter = self.entities.iterator();
|
||||
while (minion_iter.next()) |minion_id| {
|
||||
const minion = self.entities.getAssumeExists(minion_id);
|
||||
@ -390,18 +481,69 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (minion.minion_target_station) |minion_target_station| {
|
||||
if (!self.entities.exists(minion_target_station)) {
|
||||
minion.minion_target_station = null;
|
||||
}
|
||||
}
|
||||
|
||||
const goal_distance_threshold = 10;
|
||||
var walk_goal: ?Vec2 = null;
|
||||
var station_goal: ?Entity.StationSubtype = null;
|
||||
|
||||
switch (minion.minion_state) {
|
||||
.goto_tree_station => {
|
||||
// TODO: Steal tree is it is nearer to you compared to the current minion that has it reserved.
|
||||
if (self.getNearestFreeTree(minion.pos)) |tree_id| {
|
||||
self.setEntityTarget(minion_id, tree_id);
|
||||
minion.minion_state = .chop_tree;
|
||||
} else {
|
||||
station_goal = .chop;
|
||||
|
||||
if (minion.target_entity) |station_id| {
|
||||
const station = self.entities.getAssumeExists(station_id);
|
||||
if (station.type != .station) {
|
||||
self.unsetEntityTarget(minion_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
.chop_tree => {
|
||||
if (minion.target_entity) |tree_id| {
|
||||
if (self.entities.getAssumeExists(tree_id).type != .tree) {
|
||||
self.unsetEntityTarget(minion_id);
|
||||
minion.minion_state = .goto_tree_station;
|
||||
}
|
||||
}
|
||||
|
||||
var is_at_tree = false;
|
||||
if (minion.target_entity) |tree_id| {
|
||||
const tree = self.entities.getAssumeExists(tree_id);
|
||||
|
||||
is_at_tree = minion.pos.distance(tree.pos) < goal_distance_threshold;
|
||||
if (is_at_tree) {
|
||||
minion.minion_chop_cooldown = @max(minion.minion_chop_cooldown - plt.dt, 0);
|
||||
if (minion.minion_chop_cooldown == 0) {
|
||||
minion.minion_chop_cooldown = std.time.ns_per_ms * 500;
|
||||
tree.tree_health -= minion.minion_chop_damage;
|
||||
}
|
||||
|
||||
if (tree.tree_health <= 0) {
|
||||
minion.minion_state = .burn;
|
||||
|
||||
self.unsetEntityTarget(minion_id);
|
||||
self.entities.removeAssumeExists(tree_id);
|
||||
}
|
||||
} else {
|
||||
walk_goal = tree.pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_at_tree) {
|
||||
minion.minion_work_progress = 0;
|
||||
}
|
||||
},
|
||||
.pickup_wood => {
|
||||
},
|
||||
.burn => {
|
||||
station_goal = .burn;
|
||||
|
||||
var is_at_station = false;
|
||||
if (minion.minion_target_station) |station_id| {
|
||||
if (minion.target_entity) |station_id| {
|
||||
const station = self.entities.getAssumeExists(station_id);
|
||||
if (station.pos.distance(minion.pos) < goal_distance_threshold) {
|
||||
is_at_station = true;
|
||||
@ -409,55 +551,44 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
}
|
||||
|
||||
if (!is_at_station) {
|
||||
minion.work_progress = 0;
|
||||
minion.minion_work_progress = 0;
|
||||
}
|
||||
|
||||
switch (minion.minion_state) {
|
||||
.chop => {
|
||||
station_goal = .chop;
|
||||
|
||||
if (is_at_station) {
|
||||
minion.work_progress += minion.minion_chop_speed * dt;
|
||||
minion.minion_work_progress += minion.minion_burn_speed * dt;
|
||||
}
|
||||
|
||||
minion.work_progress = std.math.clamp(minion.work_progress, 0, 1);
|
||||
if (minion.work_progress == 1) {
|
||||
minion.minion_state = .burn;
|
||||
minion.work_progress = 0;
|
||||
}
|
||||
},
|
||||
.burn => {
|
||||
station_goal = .burn;
|
||||
|
||||
if (is_at_station) {
|
||||
minion.work_progress += minion.minion_burn_speed * dt;
|
||||
}
|
||||
|
||||
minion.work_progress = std.math.clamp(minion.work_progress, 0, 1);
|
||||
if (minion.work_progress == 1) {
|
||||
minion.minion_state = .chop;
|
||||
minion.work_progress = 0;
|
||||
minion.minion_work_progress = std.math.clamp(minion.minion_work_progress, 0, 1);
|
||||
if (minion.minion_work_progress == 1) {
|
||||
minion.minion_state = .goto_tree_station;
|
||||
minion.minion_work_progress = 0;
|
||||
self.heat += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if (station_goal) |station_type| {
|
||||
if (minion.minion_target_station) |station_id| {
|
||||
if (minion.target_entity) |entity_id| {
|
||||
if (self.entities.get(entity_id)) |station| {
|
||||
if (station.type != .station) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minion.target_entity) |station_id| {
|
||||
const station = self.entities.getAssumeExists(station_id);
|
||||
if (station.station_subtype != station_type) {
|
||||
self.unbindEntityFromStation(station_id);
|
||||
minion.minion_target_station = null;
|
||||
self.unsetEntityTarget(minion_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (minion.minion_target_station == null) {
|
||||
if (minion.target_entity == null) {
|
||||
if (self.getNearestFreeStation(minion.pos, station_type)) |station_id| {
|
||||
self.bindEntityToStation(minion_id, station_id);
|
||||
self.setEntityTarget(minion_id, station_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (minion.minion_target_station) |station_id| {
|
||||
if (minion.target_entity) |station_id| {
|
||||
const station = self.entities.getAssumeExists(station_id);
|
||||
walk_goal = station.pos;
|
||||
}
|
||||
@ -484,6 +615,21 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var tree_iter = self.entities.iterator();
|
||||
while (tree_iter.next()) |id| {
|
||||
const e = self.entities.getAssumeExists(id);
|
||||
|
||||
if (e.type == .tree) {
|
||||
const tree_size = Vec2.init(40, 100);
|
||||
Gfx.drawRectangle(e.pos.sub(.init(tree_size.x/2, tree_size.y)), tree_size, .rgb(40, 200, 40));
|
||||
|
||||
const progress = @as(f32, @floatFromInt(e.tree_health)) / @as(f32, @floatFromInt(e.tree_max_health));
|
||||
drawProgressBar(progress, e.pos.add(.init(0, 20)), .init(40, 10), .rgb(0, 100, 20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var entity_iter = self.entities.iterator();
|
||||
while (entity_iter.next()) |id| {
|
||||
const e = self.entities.getAssumeExists(id);
|
||||
@ -542,12 +688,9 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
);
|
||||
Gfx.drawSprite(sprite, pos, size, .white);
|
||||
|
||||
const is_working = e.minion_state == .chop or e.minion_state == .burn;
|
||||
if (e.type == .minion and is_working and e.work_progress > 0) {
|
||||
const progressbar_size = 100;
|
||||
|
||||
const from = e.pos.add(.init(-progressbar_size/2, 20));
|
||||
Gfx.drawLine(from, from.add(.init(progressbar_size * e.work_progress, 0)), .rgb(0, 200, 20), 10);
|
||||
const is_working = e.minion_state == .chop_tree or e.minion_state == .burn;
|
||||
if (e.type == .minion and is_working and e.minion_work_progress > 0) {
|
||||
drawProgressBar(e.minion_work_progress, e.pos.add(.init(0, 20)), .init(100, 10), .rgb(0, 200, 20));
|
||||
}
|
||||
}
|
||||
|
||||
@ -557,6 +700,12 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
} else {
|
||||
Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(200, 20, 20));
|
||||
}
|
||||
} else if (e.type == .minion) {
|
||||
Gfx.drawText(self.font, .{
|
||||
.pos = e.pos.sub(.init(20, 50)),
|
||||
.height = 16,
|
||||
.text = try std.fmt.allocPrint(plt.arena, "{}", .{e.minion_state})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -576,6 +725,6 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *App, plt: Platform.Deinit) void {
|
||||
self.entities.deinit(self.gpa);
|
||||
_ = self; // autofix
|
||||
_ = plt; // autofix
|
||||
}
|
||||
|
||||
@ -168,19 +168,19 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void {
|
||||
.default_sampler = .nearest,
|
||||
|
||||
.textures_buffer = undefined,
|
||||
.textures = .init(.initBuffer(&self.textures_buffer)),
|
||||
.textures = .init(&self.textures_buffer),
|
||||
.nil_texture = undefined,
|
||||
|
||||
.sprites_buffer = undefined,
|
||||
.sprites = .init(.initBuffer(&self.sprites_buffer)),
|
||||
.sprites = .init(&self.sprites_buffer),
|
||||
.nil_sprite = undefined,
|
||||
|
||||
.spritesheets_buffer = undefined,
|
||||
.spritesheets = .init(.initBuffer(&self.spritesheets_buffer)),
|
||||
.spritesheets = .init(&self.spritesheets_buffer),
|
||||
.nil_spritesheet = undefined,
|
||||
|
||||
.fonts_buffer = undefined,
|
||||
.fonts = .init(.initBuffer(&self.fonts_buffer)),
|
||||
.fonts = .init(&self.fonts_buffer),
|
||||
.nil_font = undefined,
|
||||
};
|
||||
|
||||
@ -511,7 +511,7 @@ const TextureOptions = struct {
|
||||
|
||||
pub fn initTexture(opts: TextureOptions) Texture.Id {
|
||||
const self = &g_state;
|
||||
const id = self.textures.insertBounded() catch {
|
||||
const id = self.textures.insertUndefined() catch {
|
||||
log.warn("Failed to create texture, limit reached! limit: {}", .{self.textures.slots.capacity});
|
||||
return self.nil_texture;
|
||||
};
|
||||
@ -765,7 +765,7 @@ pub fn getNilSprite() Sprite.Id {
|
||||
|
||||
pub fn initSprite(opts: SpriteOptions) Sprite.Id {
|
||||
const self = &g_state;
|
||||
const id = self.sprites.insertBounded() catch {
|
||||
const id = self.sprites.insertUndefined() catch {
|
||||
log.warn("Failed to create sprite, limit reached! limit: {}", .{self.sprites.slots.capacity});
|
||||
return self.nil_sprite;
|
||||
};
|
||||
@ -1125,7 +1125,7 @@ pub const SpriteSheetOptions = struct {
|
||||
|
||||
pub fn initSpritesheet(opts: SpriteSheetOptions) Spritesheet.Id {
|
||||
const self = &g_state;
|
||||
const id = self.spritesheets.insertBounded() catch {
|
||||
const id = self.spritesheets.insertUndefined() catch {
|
||||
log.warn("Failed to create spritesheet, limit reached! limit: {}", .{self.spritesheets.slots.capacity});
|
||||
return self.nil_spritesheet;
|
||||
};
|
||||
@ -1159,7 +1159,7 @@ pub fn deinitSpritesheet(id: Spritesheet.Id) void {
|
||||
|
||||
pub fn initFont() Font.Id {
|
||||
const self = &g_state;
|
||||
const id = self.fonts.insertBounded() catch {
|
||||
const id = self.fonts.insertUndefined() catch {
|
||||
log.warn("Failed to create font, limit reached! limit: {}", .{self.fonts.slots.capacity});
|
||||
return self.nil_font;
|
||||
};
|
||||
|
||||
@ -287,6 +287,13 @@ fn PlatformType(App: type) type {
|
||||
}
|
||||
}
|
||||
|
||||
if (e == .mouse_leave and input.mouse_position == null) {
|
||||
return;
|
||||
}
|
||||
if (e == .mouse_enter and input.mouse_position != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.appendEvent(e);
|
||||
}
|
||||
|
||||
|
||||
@ -78,10 +78,7 @@ pub fn SlotMapType(Index: type, Generation: type, Value: type) type {
|
||||
};
|
||||
|
||||
pub fn clearRetainingCapacity(self: *Self) void {
|
||||
var slots = self.slots;
|
||||
slots.clearRetainingCapacity();
|
||||
|
||||
self.* = .init(slots);
|
||||
self.* = .init(self.slots.items);
|
||||
}
|
||||
|
||||
fn insertHole(self: *Self, index: Index) void {
|
||||
@ -121,19 +118,6 @@ pub fn SlotMapType(Index: type, Generation: type, Value: type) type {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn ensureUnusedCapacity(self: *Self, gpa: Allocator, additional_count: usize) Allocator.Error!void {
|
||||
if (additional_count > self.hole_count) {
|
||||
const new_capacity, const overflow = @addWithOverflow(
|
||||
self.slots.capacity,
|
||||
additional_count - self.hole_count
|
||||
);
|
||||
if (overflow != 0) return error.OutOfMemory;
|
||||
if (new_capacity >= std.math.maxInt(Index)) return error.OutOfMemory;
|
||||
|
||||
try self.slots.ensureTotalCapacity(gpa, new_capacity);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unusedCapacity(self: *Self) usize {
|
||||
const capacity = @min(self.slots.capacity, std.math.maxInt(Index));
|
||||
return capacity - self.slots.items.len + self.hole_count;
|
||||
@ -151,18 +135,19 @@ pub fn SlotMapType(Index: type, Generation: type, Value: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn insert(self: *Self, gpa: Allocator) Allocator.Error!Id {
|
||||
try self.ensureUnusedCapacity(gpa, 1);
|
||||
return self.insertAssumeCapacity();
|
||||
}
|
||||
|
||||
pub fn insertBounded(self: *Self) Allocator.Error!Id {
|
||||
pub fn insertUndefined(self: *Self) Allocator.Error!Id {
|
||||
if (self.unusedCapacity() == 0) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
return self.insertAssumeCapacity();
|
||||
}
|
||||
|
||||
pub fn insert(self: *Self, value: Value) Allocator.Error!Id {
|
||||
const id = try self.insertUndefined();
|
||||
self.getAssumeExists(id).* = value;
|
||||
return id;
|
||||
}
|
||||
|
||||
pub fn exists(self: *Self, id: Id) bool {
|
||||
if (id.index >= self.slots.items.len) {
|
||||
return false;
|
||||
@ -211,15 +196,11 @@ pub fn SlotMapType(Index: type, Generation: type, Value: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn init(slots: std.ArrayList(Slot)) Self {
|
||||
pub fn init(slots: []Slot) Self {
|
||||
var self: Self = .empty;
|
||||
self.slots = slots;
|
||||
self.slots = .initBuffer(slots);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, gpa: Allocator) void {
|
||||
self.slots.deinit(gpa);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -234,13 +215,13 @@ test "insert & remove" {
|
||||
var map: TestMap = .empty;
|
||||
defer map.deinit(gpa);
|
||||
|
||||
const id1 = try map.insert(gpa);
|
||||
const id1 = try map.insertUndefined(gpa);
|
||||
try expect(map.exists(id1));
|
||||
try expect(map.remove(id1));
|
||||
try expect(!map.exists(id1));
|
||||
try expect(!map.remove(id1));
|
||||
|
||||
const id2 = try map.insert(gpa);
|
||||
const id2 = try map.insertUndefined(gpa);
|
||||
try expect(map.exists(id2));
|
||||
try expect(!map.exists(id1));
|
||||
}
|
||||
@ -253,14 +234,14 @@ test "generation wrap around" {
|
||||
defer map.deinit(gpa);
|
||||
|
||||
// Grow array list so that at least 1 slot exists
|
||||
const id1 = try map.insert(gpa);
|
||||
const id1 = try map.insertUndefined(gpa);
|
||||
map.removeAssumeExists(id1);
|
||||
|
||||
// Artificially increase generation count
|
||||
map.slots.items[id1.index].generation = std.math.maxInt(@FieldType(TestMap.Id, "generation"));
|
||||
|
||||
// Check if generation wraps around
|
||||
const id2 = try map.insert(gpa);
|
||||
const id2 = try map.insertUndefined(gpa);
|
||||
map.removeAssumeExists(id2);
|
||||
try expectEqual(id1.index, id2.index);
|
||||
try expectEqual(0, map.slots.items[id1.index].generation);
|
||||
@ -274,9 +255,9 @@ test "iterator" {
|
||||
defer map.deinit(gpa);
|
||||
|
||||
// Create array which has a hole
|
||||
const id1 = try map.insert(gpa);
|
||||
const id2 = try map.insert(gpa);
|
||||
const id3 = try map.insert(gpa);
|
||||
const id1 = try map.insertUndefined(gpa);
|
||||
const id2 = try map.insertUndefined(gpa);
|
||||
const id3 = try map.insertUndefined(gpa);
|
||||
|
||||
map.removeAssumeExists(id2);
|
||||
|
||||
@ -294,11 +275,11 @@ test "clear retaining capacity" {
|
||||
var map: TestMap = .empty;
|
||||
defer map.deinit(gpa);
|
||||
|
||||
const id1 = try map.insert(gpa);
|
||||
const id1 = try map.insertUndefined(gpa);
|
||||
try expect(map.exists(id1));
|
||||
map.clearRetainingCapacity();
|
||||
|
||||
const id2 = try map.insert(gpa);
|
||||
const id2 = try map.insertUndefined(gpa);
|
||||
try expect(map.exists(id2));
|
||||
|
||||
try expectEqual(id1, id2);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user