diff --git a/api/errors.zig b/api/errors.zig index 1f9cf4f..d0089c4 100644 --- a/api/errors.zig +++ b/api/errors.zig @@ -43,11 +43,13 @@ const MapNotFound = ErrorDefinition.init("MapNotFound", 404); const ItemNotFound = ErrorDefinition.init("ItemNotFound", 404); const RecipeNotFound = ErrorDefinition.init("RecipeNotFound", 404); -const BankIsBusy = ErrorDefinition.init("BankIsBusy", 461); -const NotEnoughItems = ErrorDefinition.init("NotEnoughItems", 478); -const SlotIsFull = ErrorDefinition.init("SlotIsFull", 485); -const CharacterIsBusy = ErrorDefinition.init("CharacterIsBusy", 486); -const AlreadyHasTask = ErrorDefinition.init("AlreadyHasTask", 486); +const BankIsBusy = ErrorDefinition.init("BankIsBusy", 461); +const NotEnoughItems = ErrorDefinition.init("NotEnoughItems", 478); +const SlotIsFull = ErrorDefinition.init("SlotIsFull", 485); +const CharacterIsBusy = ErrorDefinition.init("CharacterIsBusy", 486); +const AlreadyHasTask = ErrorDefinition.init("AlreadyHasTask", 486); +const HasNoTask = ErrorDefinition.init("HasNoTask", 487); +const TaskNotCompleted = ErrorDefinition.init("TaskNotCompleted", 488); const CharacterAtDestination = ErrorDefinition.init("CharacterAtDestination", 490); const SlotIsEmpty = ErrorDefinition.init("SlotIsEmpty", 491); @@ -183,12 +185,24 @@ const EquipErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{ pub const EquipError = FetchError || EquipErrorDef.ErrorSet; pub const parseEquipError = EquipErrorDef.parse; -const TaskAcceptErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{ +const AcceptTaskErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{ CharacterIsBusy, AlreadyHasTask, CharacterNotFound, CharacterInCooldown, TaskMasterNotFound }); -pub const TaskAcceptError = FetchError || TaskAcceptErrorDef.ErrorSet; -pub const parseTaskAcceptError = TaskAcceptErrorDef.parse; +pub const AcceptTaskError = FetchError || AcceptTaskErrorDef.ErrorSet; +pub const parseAcceptTaskError = AcceptTaskErrorDef.parse; + +const TaskCompleteErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{ + CharacterIsBusy, + HasNoTask, + TaskNotCompleted, + CharacterIsFull, + CharacterNotFound, + CharacterInCooldown, + TaskMasterNotFound +}); +pub const TaskCompleteError = FetchError || TaskCompleteErrorDef.ErrorSet; +pub const parseTaskCompleteError = TaskCompleteErrorDef.parse; diff --git a/api/root.zig b/api/root.zig index a149f1f..c9bb65f 100644 --- a/api/root.zig +++ b/api/root.zig @@ -4,6 +4,7 @@ pub const parseDateTime = @import("./date_time/parse.zig").parseDateTime; pub const Server = @import("server.zig"); pub const Store = @import("store.zig"); pub const Character = @import("./schemas/character.zig"); +pub const Map = @import("./schemas/map.zig"); pub const Position = @import("position.zig"); pub const BoundedSlotsArray = @import("schemas/slot_array.zig").BoundedSlotsArray; @@ -20,3 +21,4 @@ pub const BankDepositGoldError = errors.BankDepositGoldError; pub const BankDepositItemError = errors.BankDepositItemError; pub const BankWithdrawItemError = errors.BankWithdrawItemError; pub const CraftError = errors.CraftError; +pub const AcceptTaskError = errors.AcceptTaskError; diff --git a/api/schemas/character.zig b/api/schemas/character.zig index 0f72c4e..86903a6 100644 --- a/api/schemas/character.zig +++ b/api/schemas/character.zig @@ -10,6 +10,7 @@ const assert = std.debug.assert; const SkillStats = @import("./skill_stats.zig"); const CombatStats = @import("./combat_stats.zig"); const Equipment = @import("./equipment.zig"); +const Task = @import("./task.zig"); const BoundedSlotsArray = @import("./slot_array.zig").BoundedSlotsArray; const Inventory = BoundedSlotsArray(20); @@ -17,10 +18,32 @@ const Inventory = BoundedSlotsArray(20); const Character = @This(); const TaskMasterTask = struct { - target: []u8, - type: []u8, + target_id: Store.CodeId, + type: Task.Type, progress: u64, total: u64, + + fn parse(store: *Store, obj: json.ObjectMap) !TaskMasterTask { + const task_target = try json_utils.getStringRequired(obj, "task"); + const task_type = try json_utils.getStringRequired(obj, "task_type"); + + const progress = try json_utils.getIntegerRequired(obj, "task_progress"); + if (progress < 0) { + return error.InvalidTaskProgress; + } + + const total = try json_utils.getIntegerRequired(obj, "task_total"); + if (total < 0) { + return error.InvalidTaskTotal; + } + + return TaskMasterTask{ + .target_id = try store.getCodeId(task_target), + .type = Task.TypeUtils.fromString(task_type) orelse return error.InvalidTaskType, + .total = @intCast(total), + .progress = @intCast(progress), + }; + } }; allocator: Allocator, @@ -74,22 +97,7 @@ pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Characte var task: ?TaskMasterTask = null; const task_target = try json_utils.getStringRequired(obj, "task"); if (task_target.len > 0) { - const progress = try json_utils.getIntegerRequired(obj, "task_progress"); - if (progress < 0) { - return error.InvalidTaskProgress; - } - - const total = try json_utils.getIntegerRequired(obj, "task_total"); - if (total < 0) { - return error.InvalidTaskTotal; - } - - task = TaskMasterTask{ - .target = try allocator.dupe(u8, task_target), - .type = try json_utils.dupeStringRequired(allocator, obj, "task_type"), - .total = @intCast(total), - .progress = @intCast(progress), - }; + task = try TaskMasterTask.parse(store, obj); } return Character{ @@ -131,11 +139,6 @@ pub fn deinit(self: *Character) void { if (self.account) |str| self.allocator.free(str); self.allocator.free(self.name); self.allocator.free(self.skin); - - if (self.task) |task| { - self.allocator.free(task.type); - self.allocator.free(task.target); - } } pub fn getItemCount(self: *const Character) u64 { diff --git a/api/schemas/map.zig b/api/schemas/map.zig index 9f293a8..56dc58e 100644 --- a/api/schemas/map.zig +++ b/api/schemas/map.zig @@ -24,7 +24,7 @@ pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Map { .name = (try json_utils.dupeString(allocator, obj, "name")) orelse return error.MissingProperty, .skin = (try json_utils.dupeString(allocator, obj, "skin")) orelse return error.MissingProperty, .position = Position.init(x, y), - .content = try MapContent.parse(store, content) + .content = if (content) |c| try MapContent.parse(store, c) else null }; } diff --git a/api/schemas/map_content.zig b/api/schemas/map_content.zig index 1ac4a82..e887b95 100644 --- a/api/schemas/map_content.zig +++ b/api/schemas/map_content.zig @@ -27,11 +27,11 @@ pub const TypeUtils = EnumStringUtils(Type, .{ type: Type, code_id: Store.CodeId, -pub fn parse(store: *Store, obj: json.ObjectMap) MapContent { +pub fn parse(store: *Store, obj: json.ObjectMap) !MapContent { const content_type = json_utils.getString(obj, "type") orelse return error.MissingProperty; return MapContent{ .type = TypeUtils.fromString(content_type) orelse return error.InvalidContentType, - .code = (try store.getCodeIdJson(obj, "code")) orelse return error.MissingProperty + .code_id = (try store.getCodeIdJson(obj, "code")) orelse return error.MissingProperty }; } diff --git a/api/schemas/skill_info.zig b/api/schemas/skill_info.zig index ccdfa3f..327c547 100644 --- a/api/schemas/skill_info.zig +++ b/api/schemas/skill_info.zig @@ -8,14 +8,18 @@ const Items = BoundedSlotsArray(8); const SkillInfo = @This(); -xp: i64, +xp: u64, items: Items, pub fn parse(store: *Store, obj: json.ObjectMap) !SkillInfo { const items = json_utils.getArray(obj, "items") orelse return error.MissingProperty; + const xp = try json_utils.getIntegerRequired(obj, "xp"); + if (xp < 0) { + return error.InvalidXp; + } return SkillInfo{ - .xp = try json_utils.getIntegerRequired(obj, "xp"), + .xp = @intCast(xp), .items = try Items.parse(store, items), }; } diff --git a/api/schemas/task_reward_data.zig b/api/schemas/task_reward_data.zig new file mode 100644 index 0000000..e51cc25 --- /dev/null +++ b/api/schemas/task_reward_data.zig @@ -0,0 +1,27 @@ +const std = @import("std"); +const Store = @import("../store.zig"); +const json_utils = @import("../json_utils.zig"); +const json = std.json; +const Allocator = std.mem.Allocator; + +const Cooldown = @import("./cooldown.zig"); +const Character = @import("./character.zig"); +const ItemQuantity = @import("./item_quantity.zig"); + +const TaskRewardData = @This(); + +cooldown: Cooldown, +character: Character, +reward: ItemQuantity, + +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !TaskRewardData { + const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty; + const character = json_utils.getObject(obj, "character") orelse return error.MissingProperty; + const task = json_utils.getObject(obj, "task") orelse return error.MissingProperty; + + return TaskRewardData{ + .cooldown = try Cooldown.parse(cooldown), + .character = try Character.parse(store, character, allocator), + .reward = (try ItemQuantity.parse(store, task)) orelse return error.MissinReward + }; +} diff --git a/api/server.zig b/api/server.zig index e93cae8..247978d 100644 --- a/api/server.zig +++ b/api/server.zig @@ -45,6 +45,7 @@ pub const UnequipResult = @import("./schemas/equip_request.zig"); pub const EquipResult = @import("./schemas/equip_request.zig"); const DropRate = @import("./schemas/drop_rate.zig"); pub const AcceptTaskResult = @import("./schemas/task_data.zig"); +pub const CompleteTaskResult = @import("./schemas/task_reward_data.zig"); pub const MapContent = @import("./schemas/map_content.zig"); pub const MapTile = @import("./schemas/map.zig"); pub const Item = @import("./schemas/item.zig"); @@ -1028,13 +1029,13 @@ pub fn getMonsters(self: *Server, opts: MonsterOptions) FetchError!std.ArrayList return result; } -pub fn acceptTask(self: *Server, name: []const u8) errors.TaskAcceptError!AcceptTaskResult { +pub fn acceptTask(self: *Server, name: []const u8) errors.AcceptTaskError!AcceptTaskResult { const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/task/new", .{name}); defer self.allocator.free(path); const result = try self.fetchObject( - errors.TaskAcceptError, - errors.parseTaskAcceptError, + errors.AcceptTaskError, + errors.parseAcceptTaskError, AcceptTaskResult, AcceptTaskResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path } @@ -1043,3 +1044,19 @@ pub fn acceptTask(self: *Server, name: []const u8) errors.TaskAcceptError!Accept return result; } + +pub fn completeTask(self: *Server, name: []const u8) errors.TaskCompleteError!CompleteTaskResult { + const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/task/complete", .{name}); + defer self.allocator.free(path); + + const result = try self.fetchObject( + errors.TaskCompleteError, + errors.parseTaskCompleteError, + CompleteTaskResult, + CompleteTaskResult.parse, .{ self.allocator }, + .{ .method = .POST, .path = path } + ); + try self.store.putCharacter(result.character); + + return result; +} diff --git a/api/store.zig b/api/store.zig index 250c03f..e20ada5 100644 --- a/api/store.zig +++ b/api/store.zig @@ -155,7 +155,9 @@ pub fn getMaps(self: *Store, opts: Server.MapOptions) !std.ArrayList(Map) { } if (opts.code) |content_code| { if (map.content == null) continue; - if (!std.mem.eql(u8, map.content.?.code, content_code)) continue; + + const map_content_code = self.getCode(map.content.?.code_id).?; + if (!std.mem.eql(u8, map_content_code, content_code)) continue; } try found.append(map.*); diff --git a/cli/main.zig b/cli/main.zig index 990193e..e363579 100644 --- a/cli/main.zig +++ b/cli/main.zig @@ -38,7 +38,7 @@ pub fn main() !void { std.log.info("Starting main loop", .{}); while (true) { const waitUntil = artificer.nextStepAt(); - const duration = waitUntil - std.time.timestamp(); + const duration = waitUntil - std.time.milliTimestamp(); if (duration > 0) { std.time.sleep(@intCast(duration)); } diff --git a/gui/main.zig b/gui/main.zig index 3db53e5..5d7e2cf 100644 --- a/gui/main.zig +++ b/gui/main.zig @@ -25,12 +25,34 @@ fn getAPITokenFromArgs(allocator: Allocator) !?[]u8 { return try allocator.dupe(u8, std.mem.trim(u8,token,"\n\t ")); } -fn drawCharacterInfo(ui: *UI, rect: rl.Rectangle, brain: Artificer.Brain) void { +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 { @@ -53,7 +75,7 @@ pub fn main() anyerror!void { defer ui.deinit(); while (!rl.windowShouldClose()) { - if (std.time.timestamp() > artificer.nextStepAt()) { + if (std.time.milliTimestamp() > artificer.nextStepAt()) { try artificer.step(); } @@ -68,9 +90,9 @@ pub fn main() anyerror!void { 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| { + for (artificer.characters.items) |*brain| { const info_width = screen_size.x / @as(f32, @floatFromInt(artificer.characters.items.len)); - drawCharacterInfo(&ui, info_stack.next(info_width), brain); + try drawCharacterInfo(&ui, info_stack.next(info_width), &artificer, brain); } } } diff --git a/lib/action.zig b/lib/action.zig index ea78f94..710a3ab 100644 --- a/lib/action.zig +++ b/lib/action.zig @@ -12,6 +12,7 @@ pub const Action = union(enum) { deposit_item: Api.ItemQuantity, withdraw_item: Api.ItemQuantity, craft_item: Api.ItemQuantity, + accept_task, pub fn perform(self: Action, api: *Server, name: []const u8) !ActionResult { const log = std.log.default; @@ -61,6 +62,12 @@ pub const Action = union(enum) { return .{ .craft_item = api.actionCraft(name, code, item.quantity) }; + }, + .accept_task => { + log.debug("[{s}] accept task", .{name}); + return .{ + .accept_task = api.acceptTask(name) + }; } } } @@ -88,6 +95,7 @@ pub const ActionResult = union(enum) { deposit_item: Api.BankDepositItemError!Server.ItemTransactionResult, withdraw_item: Api.BankWithdrawItemError!Server.ItemTransactionResult, craft_item: Api.CraftError!Server.CraftResult, + accept_task: Api.AcceptTaskError!Server.AcceptTaskResult, const AnyError = Server.MoveError; @@ -127,6 +135,9 @@ pub const ActionResult = union(enum) { }, .craft_item => |result| { _ = try result; + }, + .accept_task => |result| { + _ = try result; } } } @@ -149,6 +160,8 @@ pub const ActionResult = union(enum) { error.ItemNotFound, error.NotEnoughItems, error.RecipeNotFound, + error.AlreadyHasTask, + error.TaskMasterNotFound, error.WorkshopNotFound => return ErrorResponse.restart, error.CharacterNotFound, diff --git a/lib/brain.zig b/lib/brain.zig index ad73d3f..5d3d106 100644 --- a/lib/brain.zig +++ b/lib/brain.zig @@ -14,7 +14,7 @@ const Brain = @This(); name: []const u8, action_queue: std.ArrayList(QueuedAction), task: ?CharacterTask = null, -paused_until: ?i64 = null, +paused_until: ?i64 = null, // ms pub fn init(allocator: Allocator, name: []const u8) !Brain { return Brain{ @@ -33,7 +33,7 @@ pub fn performNextAction(self: *Brain, api: *Server) !void { const log = std.log.default; assert(self.action_queue.items.len > 0); - const retry_delay = std.time.ns_per_ms * 500; // 500ms + const retry_delay = 500; // 500ms const next_action = self.action_queue.items[0]; const action_result = try next_action.perform(api, self.name); @@ -41,7 +41,7 @@ pub fn performNextAction(self: *Brain, api: *Server) !void { if (action_result.getErrorResponse()) |error_response| { switch (error_response) { .retry => { - self.paused_until = std.time.timestamp() + retry_delay; + self.paused_until = std.time.milliTimestamp() + retry_delay; log.warn("[{s}] retry action", .{self.name}); return; }, @@ -51,6 +51,7 @@ pub fn performNextAction(self: *Brain, api: *Server) !void { return; }, .abort => { + log.warn("[{s}] abort action {s}", .{ self.name, @tagName(next_action) }); try action_result.getError(); // The error above should always return @@ -69,7 +70,7 @@ pub fn performNextAction(self: *Brain, api: *Server) !void { pub fn step(self: *Brain, api: *Api.Server) !void { if (self.paused_until) |paused_until| { - if (std.time.timestamp() < paused_until) { + if (std.time.milliTimestamp() < paused_until) { return; } self.paused_until = null; @@ -93,7 +94,7 @@ pub fn step(self: *Brain, api: *Api.Server) !void { pub fn cooldown(self: *Brain, api: *Server) i64 { const character = api.store.getCharacter(self.name).?; - const cooldown_expiration: i64 = @intFromFloat(character.cooldown_expiration * std.time.ns_per_s); + const cooldown_expiration: i64 = @intFromFloat(character.cooldown_expiration * std.time.ms_per_s); if (self.paused_until) |pause_until| { return @max(cooldown_expiration, pause_until); diff --git a/lib/root.zig b/lib/root.zig index b637680..5007948 100644 --- a/lib/root.zig +++ b/lib/root.zig @@ -14,7 +14,7 @@ server: Api.Server, characters: std.ArrayList(Brain), task_graph: TaskGraph, -paused_until: ?i64 = null, +paused_until: ?i64 = null, // ms pub fn init(allocator: Allocator, token: []const u8) !Artificer { var server = try Api.Server.init(allocator); @@ -49,7 +49,7 @@ pub fn deinit(self: *Artificer) void { pub fn step(self: *Artificer) !void { if (self.paused_until) |paused_until| { - if (std.time.timestamp() < paused_until) { + if (std.time.milliTimestamp() < paused_until) { return; } self.paused_until = null; @@ -57,7 +57,7 @@ pub fn step(self: *Artificer) !void { runNextActions(self.characters.items, &self.server) catch |err| switch (err) { Api.FetchError.ServerUnavailable => { - self.paused_until = std.time.timestamp() + std.time.ns_per_min * server_down_retry_interval; + self.paused_until = std.time.milliTimestamp() + std.time.ms_per_min * server_down_retry_interval; std.log.warn("Server is down, retrying in {}min", .{ server_down_retry_interval }); return; }, @@ -65,14 +65,40 @@ pub fn step(self: *Artificer) !void { }; for (self.characters.items) |*brain| { - if (brain.task == null) { - const character = self.server.store.getCharacter(brain.name).?; - if (character.task == null) { - brain.task = .{ .accept_task = .{} }; - } + if (brain.task != null) { + try brain.step(&self.server); + continue; } - try brain.step(&self.server); + const character = self.server.store.getCharacter(brain.name).?; + if (character.task) |taskmaster_task| { + if (taskmaster_task.total > taskmaster_task.progress) { + switch (taskmaster_task.type) { + .monsters => { + const monster_code = self.server.store.getCode(taskmaster_task.target_id).?; + + const maps = try self.server.getMaps(.{ .code = monster_code }); + defer maps.deinit(); + + if (maps.items.len > 0) { + const resource_map: Api.Map = maps.items[0]; + std.debug.print("fight at {}\n", .{resource_map.position}); + + brain.task = .{ + .fight = .{ + .at = resource_map.position, + .until = .{ .quantity = taskmaster_task.total - taskmaster_task.progress }, + } + }; + } + }, + .crafts => {}, + .resources => {}, + } + } + } else { + brain.task = .{ .accept_task = .{} }; + } } } @@ -99,17 +125,11 @@ fn earliestCooldown(characters: []Brain, api: *Api.Server) ?i64 { } fn runNextActions(characters: []Brain, api: *Api.Server) !void { - const maybe_earliest_cooldown = earliestCooldown(characters, api); - if (maybe_earliest_cooldown == null) return; - - const earliest_cooldown = maybe_earliest_cooldown.?; - if (earliest_cooldown < std.time.timestamp()) return; - for (characters) |*brain| { if (brain.action_queue.items.len == 0) continue; const cooldown = brain.cooldown(api); - if (earliest_cooldown > cooldown) { + if (std.time.milliTimestamp() >= cooldown) { try brain.performNextAction(api); } } diff --git a/lib/task.zig b/lib/task.zig index fd730c0..53ad338 100644 --- a/lib/task.zig +++ b/lib/task.zig @@ -7,11 +7,22 @@ const ActionResult = @import("./action.zig").ActionResult; const CodeId = Api.CodeId; const ItemQuantity = Api.ItemQuantity; -const bank_position: Position = .{ .x = 4, .y = 1 }; // TODO: Figure this out dynamically +const bank_position = Position{ .x = 4, .y = 1 }; // TODO: Figure this out dynamically + +const task_master_position = Position{ .x = 1, .y = 2 }; // TODO: Figure this out dynamically pub const UntilCondition = union(enum) { xp: u64, item: Api.ItemQuantity, + quantity: u64, + + fn isComplete(self: UntilCondition, progress: u64) bool { + return switch (self) { + .xp => |xp| progress >= xp, + .item => |item| progress >= item.quantity, + .quantity => |quantity| progress >= quantity, + }; + } }; pub const Task = union(enum) { @@ -36,8 +47,8 @@ pub const Task = union(enum) { pub fn isComplete(self: Task) bool { return switch (self) { - .fight => |args| args.progress >= args.until.item.quantity, - .gather => |args| args.progress >= args.until.item.quantity, + .fight => |args| args.until.isComplete(args.progress), + .gather => |args| args.until.isComplete(args.progress), .craft => |args| args.progress >= args.target.quantity, .accept_task => |args| args.done }; @@ -48,17 +59,37 @@ pub const Task = union(enum) { .fight => |*args| { if (result.get(.fight)) |r| { const fight_result: Api.Server.FightResult = r; - const drops = fight_result.fight.drops; - args.progress += drops.getQuantity(args.until.item.id); + switch (args.until) { + .xp => { + args.progress += fight_result.fight.xp; + }, + .item => { + const drops = fight_result.fight.drops; + args.progress += drops.getQuantity(args.until.item.id); + }, + .quantity => { + args.progress += 1; + } + } } }, .gather => |*args| { if (result.get(.gather)) |r| { - const gather_resutl: Api.Server.GatherResult = r; - const items = gather_resutl.details.items; + const gather_result: Api.Server.GatherResult = r; - args.progress += items.getQuantity(args.until.item.id); + switch (args.until) { + .xp => { + args.progress += gather_result.details.xp; + }, + .item => { + const items = gather_result.details.items; + args.progress += items.getQuantity(args.until.item.id); + }, + .quantity => { + args.progress += 1; + } + } } }, .craft => |*args| { @@ -69,8 +100,10 @@ pub const Task = union(enum) { args.progress += items.getQuantity(args.target.id); } }, - .accept_task => { - // TODO: + .accept_task => |*args| { + if (result.get(.accept_task)) |_| { + args.done = true; + } } } } @@ -93,7 +126,11 @@ pub const Task = union(enum) { try ctx.craftRoutine(args.at, args.target.id, args.target.quantity); }, .accept_task => { - // TODO: + if (try ctx.moveIfNeeded(task_master_position)) { + return; + } + + try ctx.action_queue.append(.{ .accept_task = {} }); } } }