const std = @import("std"); const Api = @import("artifacts-api"); const Position = Api.Position; const Server = Api.Server; const assert = std.debug.assert; pub const Action = union(enum) { move: Position, fight, gather, deposit_gold: u64, deposit_item: Api.ItemQuantity, withdraw_item: Api.ItemQuantity, craft_item: Api.ItemQuantity, pub fn perform(self: Action, api: *Server, name: []const u8) !ActionResult { const log = std.log.default; switch (self) { .fight => { log.debug("[{s}] attack", .{name}); return .{ .fight = api.actionFight(name) }; }, .move => |pos| { log.debug("[{s}] move to ({}, {})", .{name, pos.x, pos.y}); return .{ .move = api.actionMove(name, pos.x, pos.y) }; }, .deposit_gold => |quantity| { log.debug("[{s}] deposit {} gold", .{name, quantity}); return .{ .deposit_gold = api.actionBankDepositGold(name, quantity) }; }, .deposit_item => |item| { const code = api.store.getCode(item.id) orelse return error.ItemNotFound; log.debug("[{s}] deposit {s} (x{})", .{name, code, item.quantity}); return .{ .deposit_item = api.actionBankDepositItem(name, code, item.quantity) }; }, .withdraw_item => |item| { const code = api.store.getCode(item.id) orelse return error.ItemNotFound; log.debug("[{s}] withdraw {s} (x{})", .{name, code, item.quantity}); return .{ .withdraw_item = api.actionBankWithdrawItem(name, code, item.quantity) }; }, .gather => { log.debug("[{s}] gather", .{name}); return .{ .gather = api.actionGather(name) }; }, .craft_item => |item| { const code = api.store.getCode(item.id) orelse return error.ItemNotFound; log.debug("[{s}] craft {s} (x{})", .{name, code, item.quantity}); return .{ .craft_item = api.actionCraft(name, code, item.quantity) }; } } } }; pub const ErrorResponse = enum { /// Something went wrong, and you probably can't reasonbly recover from it. Bail, bail! abort, /// You probably were trying to an action a bit too early, just try again a bit later. retry, /// Something in your logic went wrong, re-evaluate your state and do something different. restart, /// The error can be safe ignored, continue doing the next action that you wanted. ignore }; pub const ActionResult = union(enum) { move: Api.MoveError!Server.MoveResult, fight: Api.FightError!Server.FightResult, gather: Api.GatherError!Server.GatherResult, deposit_gold: Api.BankDepositGoldError!Server.GoldTransactionResult, deposit_item: Api.BankDepositItemError!Server.ItemTransactionResult, withdraw_item: Api.BankWithdrawItemError!Server.ItemTransactionResult, craft_item: Api.CraftError!Server.CraftResult, const AnyError = Server.MoveError; const Tag = @typeInfo(ActionResult).Union.tag_type.?; fn fieldType(comptime kind: Tag) type { const field_type = std.meta.fields(ActionResult)[@intFromEnum(kind)].type; return @typeInfo(field_type).ErrorUnion.payload; } pub fn get(self: ActionResult, comptime kind: Tag) ?fieldType(kind) { return switch (self) { kind => |v| v catch null, else => null }; } pub fn getError(self: ActionResult) !void { switch (self) { .fight => |result| { _ = try result; }, .move => |result| { _ = try result; }, .deposit_gold => |result| { _ = try result; }, .deposit_item => |result| { _ = try result; }, .withdraw_item => |result| { _ = try result; }, .gather => |result| { _ = try result; }, .craft_item => |result| { _ = try result; } } } pub fn getErrorResponse(self: ActionResult) ?ErrorResponse { self.getError() catch |err| switch (err) { error.CharacterIsBusy, error.CharacterInCooldown, error.BankIsBusy => return ErrorResponse.retry, error.CharacterAtDestination => return ErrorResponse.ignore, error.MapNotFound, error.CharacterIsFull, error.MonsterNotFound, error.NotEnoughSkill, error.ResourceNotFound, error.NotEnoughGold, error.BankNotFound, error.ItemNotFound, error.NotEnoughItems, error.RecipeNotFound, error.WorkshopNotFound => return ErrorResponse.restart, error.CharacterNotFound, error.ServerUnavailable, error.RequestFailed, error.ParseFailed, error.OutOfMemory => return ErrorResponse.abort }; return null; } }; comptime { const ActionTag = @typeInfo(Action).Union.tag_type.?; const ResultTag = @typeInfo(ActionResult).Union.tag_type.?; assert(std.meta.fields(ActionTag).len == std.meta.fields(ResultTag).len); }