const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const Entity = @import("./entity.zig"); const GenerationalArrayList = @import("./generational_array_list.zig").GenerationalArrayList; const Timer = @This(); const ArrayList = GenerationalArrayList(Timer); pub const Id = ArrayList.Id; pub const Options = struct { duration: f64, entity: ?Entity.Id = null }; started_at: f64, finishes_at: f64, entity: ?Entity.Id, pub const List = struct { array_list: GenerationalArrayList(Timer), now: f64, pub const empty = List{ .array_list = .empty, .now = 0 }; pub fn deinit(self: *List, gpa: Allocator) void { self.array_list.deinit(gpa); } pub fn clone(self: *List, gpa: Allocator) !List { const array_list = try self.array_list.clone(gpa); errdefer array_list.deinit(gpa); return List{ .now = self.now, .array_list = array_list }; } pub fn start(self: *List, gpa: Allocator, opts: Options) !Id { assert(opts.duration > 0); return try self.array_list.insert(gpa, .{ .started_at = self.now, .finishes_at = self.now + opts.duration, .entity = opts.entity }); } pub fn stop(self: *List, id: Id) void { _ = self.array_list.remove(id); } pub fn running(self: *List, id: Id) bool { const timer = self.array_list.get(id) orelse return false; 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) { return false; } self.array_list.removeAssumeExists(id); return true; } };