const std = @import("std"); const Artificer = @import("artificer"); const rl = @import("raylib"); const Allocator = std.mem.Allocator; const srcery = @import("./srcery.zig"); const UI = @import("./ui.zig"); const UIStack = @import("./ui_stack.zig"); const RectUtils = @import("./rect_utils.zig"); fn getAPITokenFromArgs(allocator: Allocator) !?[]u8 { const args = try std.process.argsAlloc(allocator); defer std.process.argsFree(allocator, args); if (args.len < 2) { return null; } const filename = args[1]; const cwd = std.fs.cwd(); var token_buffer: [256]u8 = undefined; const token = try cwd.readFile(filename, &token_buffer); return try allocator.dupe(u8, std.mem.trim(u8,token,"\n\t ")); } fn drawCharacterInfo(ui: *UI, rect: rl.Rectangle, artificer: *Artificer, brain: *Artificer.Brain) !void { var buffer: [256]u8 = undefined; const name_height = 20; UI.drawTextCentered(ui.font, brain.name, .{ .x = RectUtils.center(rect).x, .y = rect.y + name_height/2 }, 20, 2, srcery.bright_white); var label_stack = UIStack.init(RectUtils.shrinkTop(rect, name_height + 4), .top_to_bottom); label_stack.gap = 4; const now = std.time.milliTimestamp(); const cooldown = brain.cooldown(&artificer.server); const seconds_left: f32 = @as(f32, @floatFromInt(@max(cooldown - now, 0))) / std.time.ms_per_s; const cooldown_label = try std.fmt.bufPrint(&buffer, "Cooldown: {d:.3}", .{ seconds_left }); UI.drawTextEx(ui.font, cooldown_label, RectUtils.topLeft(label_stack.next(16)), 16, 2, srcery.bright_white); var task_label: []u8 = undefined; if (brain.task) |task| { task_label = try std.fmt.bufPrint(&buffer, "Task: {s}", .{ @tagName(task) }); } else { task_label = try std.fmt.bufPrint(&buffer, "Task: -", .{ }); } UI.drawTextEx(ui.font, task_label, RectUtils.topLeft(label_stack.next(16)), 16, 2, srcery.bright_white); const actions_label = try std.fmt.bufPrint(&buffer, "Actions: {}", .{ brain.action_queue.items.len }); UI.drawTextEx(ui.font, actions_label, RectUtils.topLeft(label_stack.next(16)), 16, 2, srcery.bright_white); } pub fn main() anyerror!void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const token = (try getAPITokenFromArgs(allocator)) orelse return error.MissingToken; defer allocator.free(token); var artificer = try Artificer.init(allocator, token); defer artificer.deinit(); const cache_path = try std.fs.cwd().realpathAlloc(allocator, "api-store.bin"); defer allocator.free(cache_path); std.log.info("Prefetching server data", .{}); try artificer.server.prefetchCached(cache_path); rl.initWindow(800, 450, "Artificer"); defer rl.closeWindow(); rl.setTargetFPS(60); var ui = UI.init(); defer ui.deinit(); while (!rl.windowShouldClose()) { if (std.time.milliTimestamp() > artificer.nextStepAt()) { try artificer.step(); } const screen_size = rl.Vector2.init( @floatFromInt(rl.getScreenWidth()), @floatFromInt(rl.getScreenHeight()) ); rl.beginDrawing(); defer rl.endDrawing(); rl.clearBackground(srcery.black); var info_stack = UIStack.init(rl.Rectangle.init(0, 0, screen_size.x, screen_size.y), .left_to_right); for (artificer.characters.items) |*brain| { const info_width = screen_size.x / @as(f32, @floatFromInt(artificer.characters.items.len)); try drawCharacterInfo(&ui, info_stack.next(info_width), &artificer, brain); } } }