artificer/lib/action.zig

171 lines
5.6 KiB
Zig

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);
}