171 lines
5.6 KiB
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);
|
|
}
|