add acceptTask to api
This commit is contained in:
parent
f9dc023b90
commit
dad53513c7
@ -17,6 +17,13 @@ const Inventory = BoundedSlotsArray(20);
|
|||||||
|
|
||||||
const Character = @This();
|
const Character = @This();
|
||||||
|
|
||||||
|
const TaskMasterTask = struct {
|
||||||
|
target: []u8,
|
||||||
|
type: []u8,
|
||||||
|
progress: u64,
|
||||||
|
total: u64,
|
||||||
|
};
|
||||||
|
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
name: []u8,
|
name: []u8,
|
||||||
@ -47,6 +54,8 @@ equipment: Equipment,
|
|||||||
inventory_max_items: u64,
|
inventory_max_items: u64,
|
||||||
inventory: Inventory,
|
inventory: Inventory,
|
||||||
|
|
||||||
|
task: ?TaskMasterTask,
|
||||||
|
|
||||||
pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character {
|
pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character {
|
||||||
const inventory = json_utils.getArray(obj, "inventory") orelse return error.MissingProperty;
|
const inventory = json_utils.getArray(obj, "inventory") orelse return error.MissingProperty;
|
||||||
const cooldown_expiration = json_utils.getString(obj, "cooldown_expiration") orelse return error.MissingProperty;
|
const cooldown_expiration = json_utils.getString(obj, "cooldown_expiration") orelse return error.MissingProperty;
|
||||||
@ -63,6 +72,27 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character
|
|||||||
return error.InvalidInventoryMaxItems;
|
return error.InvalidInventoryMaxItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return Character{
|
return Character{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.account = try json_utils.dupeString(allocator, obj, "account"),
|
.account = try json_utils.dupeString(allocator, obj, "account"),
|
||||||
@ -92,7 +122,9 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character
|
|||||||
.equipment = try Equipment.parse(api, obj),
|
.equipment = try Equipment.parse(api, obj),
|
||||||
|
|
||||||
.inventory_max_items = @intCast(inventory_max_items),
|
.inventory_max_items = @intCast(inventory_max_items),
|
||||||
.inventory = try Inventory.parse(api, inventory)
|
.inventory = try Inventory.parse(api, inventory),
|
||||||
|
|
||||||
|
.task = task
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +132,11 @@ pub fn deinit(self: *Character) void {
|
|||||||
if (self.account) |str| self.allocator.free(str);
|
if (self.account) |str| self.allocator.free(str);
|
||||||
self.allocator.free(self.name);
|
self.allocator.free(self.name);
|
||||||
self.allocator.free(self.skin);
|
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 {
|
pub fn getItemCount(self: *const Character) u64 {
|
||||||
|
194
api/errors.zig
Normal file
194
api/errors.zig
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const ErrorDefinition = struct {
|
||||||
|
name: [:0]const u8,
|
||||||
|
code: ?u10,
|
||||||
|
|
||||||
|
fn init(name: [:0]const u8, code: ?u10) ErrorDefinition {
|
||||||
|
return ErrorDefinition{
|
||||||
|
.name = name,
|
||||||
|
.code = code
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn ErrorDefinitionList(errors: []const ErrorDefinition) type {
|
||||||
|
var errorNames: [errors.len]std.builtin.Type.Error = undefined;
|
||||||
|
for (0.., errors) |i, def| {
|
||||||
|
errorNames[i] = .{ .name = def.name };
|
||||||
|
}
|
||||||
|
|
||||||
|
const error_set = @Type(.{ .ErrorSet = &errorNames });
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
const ErrorSet = error_set;
|
||||||
|
|
||||||
|
fn parse(status: std.http.Status) ?ErrorSet {
|
||||||
|
inline for (errors) |err| {
|
||||||
|
if (err.code == @intFromEnum(status)) {
|
||||||
|
return @field(ErrorSet, err.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ServerUnavailable = ErrorDefinition.init("ServerUnavailable", 503);
|
||||||
|
const RequestFailed = ErrorDefinition.init("RequestFailed", null);
|
||||||
|
const ParseFailed = ErrorDefinition.init("ParseFailed", null);
|
||||||
|
const OutOfMemory = ErrorDefinition.init("OutOfMemory", null);
|
||||||
|
|
||||||
|
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 CharacterAtDestination = ErrorDefinition.init("CharacterAtDestination", 490);
|
||||||
|
const SlotIsEmpty = ErrorDefinition.init("SlotIsEmpty", 491);
|
||||||
|
const NotEnoughGold = ErrorDefinition.init("NotEnoughGold", 492);
|
||||||
|
const NotEnoughSkill = ErrorDefinition.init("NotEnoughSkill", 493);
|
||||||
|
const CharacterIsFull = ErrorDefinition.init("CharacterIsFull", 497);
|
||||||
|
const CharacterNotFound = ErrorDefinition.init("CharacterNotFound", 498);
|
||||||
|
const CharacterInCooldown = ErrorDefinition.init("CharacterInCooldown", 499);
|
||||||
|
|
||||||
|
const BankNotFound = ErrorDefinition.init("BankNotFound", 598);
|
||||||
|
const MonsterNotFound = ErrorDefinition.init("MonsterNotFound", 598);
|
||||||
|
const ResourceNotFound = ErrorDefinition.init("ResourceNotFound", 598);
|
||||||
|
const WorkshopNotFound = ErrorDefinition.init("WorkshopNotFound", 598);
|
||||||
|
const TaskMasterNotFound = ErrorDefinition.init("TaskMasterNotFound", 598);
|
||||||
|
|
||||||
|
pub const FetchError = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
ServerUnavailable,
|
||||||
|
RequestFailed,
|
||||||
|
ParseFailed,
|
||||||
|
OutOfMemory,
|
||||||
|
}).ErrorSet;
|
||||||
|
|
||||||
|
const MoveErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
MapNotFound,
|
||||||
|
CharacterIsBusy,
|
||||||
|
CharacterAtDestination,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown
|
||||||
|
});
|
||||||
|
pub const MoveError = FetchError || MoveErrorDef.ErrorSet;
|
||||||
|
pub const parseMoveError = MoveErrorDef.parse;
|
||||||
|
|
||||||
|
const FightErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
CharacterIsBusy,
|
||||||
|
CharacterIsFull,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
MonsterNotFound,
|
||||||
|
});
|
||||||
|
pub const FightError = FetchError || FightErrorDef.ErrorSet;
|
||||||
|
pub const parseFightError = FightErrorDef.parse;
|
||||||
|
|
||||||
|
const GatherErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
CharacterIsBusy,
|
||||||
|
NotEnoughSkill,
|
||||||
|
CharacterIsFull,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
ResourceNotFound
|
||||||
|
});
|
||||||
|
pub const GatherError = FetchError || GatherErrorDef.ErrorSet;
|
||||||
|
pub const parseGatherError = GatherErrorDef.parse;
|
||||||
|
|
||||||
|
const BankDepositItemErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
ItemNotFound,
|
||||||
|
BankIsBusy,
|
||||||
|
NotEnoughItems,
|
||||||
|
CharacterIsBusy,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
BankNotFound
|
||||||
|
});
|
||||||
|
pub const BankDepositItemError = FetchError || BankDepositItemErrorDef.ErrorSet;
|
||||||
|
pub const parseBankDepositItemError = BankDepositItemErrorDef.parse;
|
||||||
|
|
||||||
|
const BankDepositGoldErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
BankIsBusy,
|
||||||
|
NotEnoughGold,
|
||||||
|
CharacterIsBusy,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
BankNotFound
|
||||||
|
});
|
||||||
|
pub const BankDepositGoldError = FetchError || BankDepositGoldErrorDef.ErrorSet;
|
||||||
|
pub const parseBankDepositGoldError = BankDepositGoldErrorDef.parse;
|
||||||
|
|
||||||
|
const BankWithdrawGoldErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
BankIsBusy,
|
||||||
|
NotEnoughGold,
|
||||||
|
CharacterIsBusy,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
BankNotFound
|
||||||
|
});
|
||||||
|
pub const BankWithdrawGoldError = FetchError || BankWithdrawGoldErrorDef.ErrorSet;
|
||||||
|
pub const parseBankWithdrawGoldError = BankWithdrawGoldErrorDef.parse;
|
||||||
|
|
||||||
|
const BankWithdrawItemErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
ItemNotFound,
|
||||||
|
BankIsBusy,
|
||||||
|
NotEnoughItems,
|
||||||
|
CharacterIsBusy,
|
||||||
|
CharacterIsFull,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
BankNotFound
|
||||||
|
});
|
||||||
|
pub const BankWithdrawItemError = FetchError || BankWithdrawItemErrorDef.ErrorSet;
|
||||||
|
pub const parseBankWithdrawItemError = BankWithdrawItemErrorDef.parse;
|
||||||
|
|
||||||
|
const CraftErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
RecipeNotFound,
|
||||||
|
NotEnoughItems,
|
||||||
|
CharacterIsBusy,
|
||||||
|
NotEnoughSkill,
|
||||||
|
CharacterIsFull,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
WorkshopNotFound
|
||||||
|
});
|
||||||
|
pub const CraftError = FetchError || CraftErrorDef.ErrorSet;
|
||||||
|
pub const parseCraftError = CraftErrorDef.parse;
|
||||||
|
|
||||||
|
const UnequipErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
ItemNotFound, // TODO: Can this really occur? maybe a bug in docs
|
||||||
|
CharacterIsBusy,
|
||||||
|
SlotIsEmpty,
|
||||||
|
CharacterIsFull,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
});
|
||||||
|
pub const UnequipError = FetchError || UnequipErrorDef.ErrorSet;
|
||||||
|
pub const parseUnequipError = UnequipErrorDef.parse;
|
||||||
|
|
||||||
|
const EquipErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
ItemNotFound,
|
||||||
|
SlotIsFull,
|
||||||
|
CharacterIsBusy,
|
||||||
|
NotEnoughSkill,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
});
|
||||||
|
pub const EquipError = FetchError || EquipErrorDef.ErrorSet;
|
||||||
|
pub const parseEquipError = EquipErrorDef.parse;
|
||||||
|
|
||||||
|
const TaskAcceptErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
||||||
|
CharacterIsBusy,
|
||||||
|
AlreadyHasTask,
|
||||||
|
CharacterNotFound,
|
||||||
|
CharacterInCooldown,
|
||||||
|
TaskMasterNotFound
|
||||||
|
});
|
||||||
|
pub const TaskAcceptError = FetchError || TaskAcceptErrorDef.ErrorSet;
|
||||||
|
pub const parseTaskAcceptError = TaskAcceptErrorDef.parse;
|
12
api/root.zig
12
api/root.zig
@ -3,6 +3,16 @@ pub const Server = @import("server.zig");
|
|||||||
pub const Position = @import("position.zig");
|
pub const Position = @import("position.zig");
|
||||||
pub const BoundedSlotsArray = @import("slot_array.zig").BoundedSlotsArray;
|
pub const BoundedSlotsArray = @import("slot_array.zig").BoundedSlotsArray;
|
||||||
|
|
||||||
pub const Error = Server.APIError;
|
pub const Slot = Server.Slot;
|
||||||
pub const ItemId = Server.ItemId;
|
pub const ItemId = Server.ItemId;
|
||||||
pub const ItemIdQuantity = Server.ItemIdQuantity;
|
pub const ItemIdQuantity = Server.ItemIdQuantity;
|
||||||
|
|
||||||
|
const errors = @import("errors.zig");
|
||||||
|
pub const FetchError = errors.FetchError;
|
||||||
|
pub const MoveError = errors.MoveError;
|
||||||
|
pub const FightError = errors.FightError;
|
||||||
|
pub const GatherError = errors.GatherError;
|
||||||
|
pub const BankDepositGoldError = errors.BankDepositGoldError;
|
||||||
|
pub const BankDepositItemError = errors.BankDepositItemError;
|
||||||
|
pub const BankWithdrawItemError = errors.BankWithdrawItemError;
|
||||||
|
pub const CraftError = errors.CraftError;
|
||||||
|
473
api/server.zig
473
api/server.zig
@ -13,6 +13,9 @@ const EnumStringUtils = @import("./enum_string_utils.zig").EnumStringUtils;
|
|||||||
pub const Slot = @import("./slot.zig");
|
pub const Slot = @import("./slot.zig");
|
||||||
pub const Position = @import("./position.zig");
|
pub const Position = @import("./position.zig");
|
||||||
|
|
||||||
|
const errors = @import("./errors.zig");
|
||||||
|
const FetchError = errors.FetchError;
|
||||||
|
|
||||||
// Specification: https://api.artifactsmmo.com/docs
|
// Specification: https://api.artifactsmmo.com/docs
|
||||||
|
|
||||||
const Server = @This();
|
const Server = @This();
|
||||||
@ -41,108 +44,6 @@ prefetched_maps: bool = false,
|
|||||||
prefetched_monsters: bool = false,
|
prefetched_monsters: bool = false,
|
||||||
prefetched_items: bool = false,
|
prefetched_items: bool = false,
|
||||||
|
|
||||||
// ------------------------- API errors ------------------------
|
|
||||||
|
|
||||||
pub const APIError = error {
|
|
||||||
ServerUnavailable,
|
|
||||||
RequestFailed,
|
|
||||||
ParseFailed,
|
|
||||||
OutOfMemory
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const MoveError = APIError || error {
|
|
||||||
MapNotFound,
|
|
||||||
CharacterIsBusy,
|
|
||||||
CharacterAtDestination,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FightError = APIError || error {
|
|
||||||
CharacterIsBusy,
|
|
||||||
CharacterIsFull,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown,
|
|
||||||
MonsterNotFound,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const GatherError = APIError || error {
|
|
||||||
CharacterIsBusy,
|
|
||||||
NotEnoughSkill,
|
|
||||||
CharacterIsFull,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown,
|
|
||||||
ResourceNotFound
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BankDepositItemError = APIError || error {
|
|
||||||
ItemNotFound,
|
|
||||||
BankIsBusy,
|
|
||||||
NotEnoughItems,
|
|
||||||
CharacterIsBusy,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown,
|
|
||||||
BankNotFound
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BankDepositGoldError = APIError || error {
|
|
||||||
BankIsBusy,
|
|
||||||
NotEnoughGold,
|
|
||||||
CharacterIsBusy,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown,
|
|
||||||
BankNotFound
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BankWithdrawGoldError = APIError || error {
|
|
||||||
BankIsBusy,
|
|
||||||
NotEnoughGold,
|
|
||||||
CharacterIsBusy,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown,
|
|
||||||
BankNotFound
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BankWithdrawItemError = APIError || error {
|
|
||||||
ItemNotFound,
|
|
||||||
BankIsBusy,
|
|
||||||
NotEnoughItems,
|
|
||||||
CharacterIsBusy,
|
|
||||||
CharacterIsFull,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown,
|
|
||||||
BankNotFound
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const CraftError = APIError || error {
|
|
||||||
RecipeNotFound,
|
|
||||||
NotEnoughItems,
|
|
||||||
CharacterIsBusy,
|
|
||||||
NotEnoughSkill,
|
|
||||||
CharacterIsFull,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown,
|
|
||||||
WorkshopNotFound
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const UnequipError = APIError || error {
|
|
||||||
ItemNotFound, // TODO: Can this really occur? maybe a bug in docs
|
|
||||||
CharacterIsBusy,
|
|
||||||
SlotIsEmpty,
|
|
||||||
CharacterIsFull,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const EquipError = APIError || error {
|
|
||||||
ItemNotFound,
|
|
||||||
SlotIsFull,
|
|
||||||
CharacterIsBusy,
|
|
||||||
NotEnoughSkill,
|
|
||||||
CharacterNotFound,
|
|
||||||
CharacterInCooldown,
|
|
||||||
};
|
|
||||||
|
|
||||||
// ------------------------- API result structs ------------------------
|
// ------------------------- API result structs ------------------------
|
||||||
|
|
||||||
pub const EquipmentSlot = @import("./equipment.zig").Slot;
|
pub const EquipmentSlot = @import("./equipment.zig").Slot;
|
||||||
@ -285,17 +186,6 @@ pub const FightResult = struct {
|
|||||||
.character = try Character.parse(api, character, allocator)
|
.character = try Character.parse(api, character, allocator)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseError(status: std.http.Status) ?FightError {
|
|
||||||
return switch (@intFromEnum(status)) {
|
|
||||||
486 => FightError.CharacterIsBusy,
|
|
||||||
497 => FightError.CharacterIsFull,
|
|
||||||
498 => FightError.CharacterNotFound,
|
|
||||||
499 => FightError.CharacterInCooldown,
|
|
||||||
598 => FightError.MonsterNotFound,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Replace this with ItemSlot struct
|
// TODO: Replace this with ItemSlot struct
|
||||||
@ -354,18 +244,6 @@ pub const GatherResult = struct {
|
|||||||
.character = try Character.parse(api, character, allocator)
|
.character = try Character.parse(api, character, allocator)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseError(status: std.http.Status) ?GatherError {
|
|
||||||
return switch (@intFromEnum(status)) {
|
|
||||||
486 => GatherError.CharacterIsBusy,
|
|
||||||
493 => GatherError.NotEnoughSkill,
|
|
||||||
497 => GatherError.CharacterIsFull,
|
|
||||||
498 => GatherError.CharacterNotFound,
|
|
||||||
499 => GatherError.CharacterInCooldown,
|
|
||||||
598 => GatherError.ResourceNotFound,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const MoveResult = struct {
|
pub const MoveResult = struct {
|
||||||
@ -381,17 +259,6 @@ pub const MoveResult = struct {
|
|||||||
.character = try Character.parse(api, character, allocator)
|
.character = try Character.parse(api, character, allocator)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseError(status: std.http.Status) ?MoveError {
|
|
||||||
return switch (@intFromEnum(status)) {
|
|
||||||
404 => MoveError.MapNotFound,
|
|
||||||
486 => MoveError.CharacterIsBusy,
|
|
||||||
490 => MoveError.CharacterAtDestination,
|
|
||||||
498 => MoveError.CharacterNotFound,
|
|
||||||
499 => MoveError.CharacterInCooldown,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const GoldTransactionResult = struct {
|
pub const GoldTransactionResult = struct {
|
||||||
@ -407,30 +274,6 @@ pub const GoldTransactionResult = struct {
|
|||||||
.character = try Character.parse(api, character, allocator)
|
.character = try Character.parse(api, character, allocator)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseDepositError(status: std.http.Status) ?BankDepositGoldError {
|
|
||||||
return switch (@intFromEnum(status)) {
|
|
||||||
461 => BankDepositGoldError.BankIsBusy,
|
|
||||||
486 => BankDepositGoldError.CharacterIsBusy,
|
|
||||||
492 => BankDepositGoldError.NotEnoughGold,
|
|
||||||
498 => BankDepositGoldError.CharacterNotFound,
|
|
||||||
499 => BankDepositGoldError.CharacterInCooldown,
|
|
||||||
598 => BankDepositGoldError.BankNotFound,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parseWithdrawError(status: std.http.Status) ?BankWithdrawGoldError {
|
|
||||||
return switch (@intFromEnum(status)) {
|
|
||||||
460 => BankWithdrawGoldError.NotEnoughGold,
|
|
||||||
461 => BankWithdrawGoldError.BankIsBusy,
|
|
||||||
486 => BankWithdrawGoldError.CharacterIsBusy,
|
|
||||||
498 => BankWithdrawGoldError.CharacterNotFound,
|
|
||||||
499 => BankWithdrawGoldError.CharacterInCooldown,
|
|
||||||
598 => BankWithdrawGoldError.BankNotFound,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ItemTransactionResult = struct {
|
pub const ItemTransactionResult = struct {
|
||||||
@ -446,33 +289,6 @@ pub const ItemTransactionResult = struct {
|
|||||||
.character = try Character.parse(api, character, allocator)
|
.character = try Character.parse(api, character, allocator)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseDepositError(status: std.http.Status) ?BankDepositItemError {
|
|
||||||
return switch (@intFromEnum(status)) {
|
|
||||||
404 => BankDepositItemError.ItemNotFound,
|
|
||||||
461 => BankDepositItemError.BankIsBusy,
|
|
||||||
478 => BankDepositItemError.NotEnoughItems,
|
|
||||||
486 => BankDepositItemError.CharacterIsBusy,
|
|
||||||
498 => BankDepositItemError.CharacterNotFound,
|
|
||||||
499 => BankDepositItemError.CharacterInCooldown,
|
|
||||||
598 => BankDepositItemError.BankNotFound,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parseWithdrawError(status: std.http.Status) ?BankWithdrawItemError {
|
|
||||||
return switch (@intFromEnum(status)) {
|
|
||||||
404 => BankWithdrawItemError.ItemNotFound,
|
|
||||||
461 => BankWithdrawItemError.BankIsBusy,
|
|
||||||
478 => BankWithdrawItemError.NotEnoughItems,
|
|
||||||
486 => BankWithdrawItemError.CharacterIsBusy,
|
|
||||||
497 => BankWithdrawItemError.CharacterIsFull,
|
|
||||||
498 => BankWithdrawItemError.CharacterNotFound,
|
|
||||||
499 => BankWithdrawItemError.CharacterInCooldown,
|
|
||||||
598 => BankWithdrawItemError.BankNotFound,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CraftResult = struct {
|
pub const CraftResult = struct {
|
||||||
@ -491,20 +307,6 @@ pub const CraftResult = struct {
|
|||||||
.character = try Character.parse(api, character, allocator)
|
.character = try Character.parse(api, character, allocator)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseError(status: std.http.Status) ?CraftError {
|
|
||||||
return switch (@intFromEnum(status)) {
|
|
||||||
404 => CraftError.RecipeNotFound,
|
|
||||||
478 => CraftError.NotEnoughItems,
|
|
||||||
486 => CraftError.CharacterIsBusy,
|
|
||||||
493 => CraftError.NotEnoughSkill,
|
|
||||||
497 => CraftError.CharacterIsFull,
|
|
||||||
498 => CraftError.CharacterNotFound,
|
|
||||||
499 => CraftError.CharacterInCooldown,
|
|
||||||
598 => CraftError.WorkshopNotFound,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const UnequipResult = struct {
|
pub const UnequipResult = struct {
|
||||||
@ -526,18 +328,6 @@ pub const UnequipResult = struct {
|
|||||||
.item = item_id
|
.item = item_id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseError(status: std.http.Status) ?UnequipError {
|
|
||||||
return switch (@intFromEnum(status)) {
|
|
||||||
404 => UnequipError.ItemNotFound,
|
|
||||||
486 => UnequipError.CharacterIsBusy,
|
|
||||||
491 => UnequipError.SlotIsEmpty,
|
|
||||||
497 => UnequipError.CharacterIsFull,
|
|
||||||
498 => UnequipError.CharacterNotFound,
|
|
||||||
499 => UnequipError.CharacterInCooldown,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const EquipResult = struct {
|
pub const EquipResult = struct {
|
||||||
@ -553,18 +343,56 @@ pub const EquipResult = struct {
|
|||||||
.cooldown = try Cooldown.parse(cooldown)
|
.cooldown = try Cooldown.parse(cooldown)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub fn parseError(status: std.http.Status) ?EquipError {
|
pub const TaskType = enum {
|
||||||
return switch (@intFromEnum(status)) {
|
monsters,
|
||||||
404 => EquipError.ItemNotFound,
|
resources,
|
||||||
478 => EquipError.ItemNotFound, // TODO: What is the difference between 404 and 478?
|
crafts
|
||||||
485 => EquipError.SlotIsFull,
|
};
|
||||||
486 => EquipError.CharacterIsBusy,
|
pub const TaskTypeUtils = EnumStringUtils(TaskType, .{
|
||||||
491 => EquipError.SlotIsFull, // TODO: What is the difference between 485 and 491?
|
.{ "monsters" , TaskType.monsters },
|
||||||
496 => EquipError.NotEnoughSkill,
|
.{ "resources", TaskType.resources },
|
||||||
498 => EquipError.CharacterNotFound,
|
.{ "crafts" , TaskType.crafts },
|
||||||
499 => EquipError.CharacterInCooldown,
|
});
|
||||||
else => null
|
|
||||||
|
pub const Task = struct {
|
||||||
|
id: ItemId, // TODO: Refactor `ItemId` to include other object types
|
||||||
|
type: TaskType,
|
||||||
|
total: u64,
|
||||||
|
|
||||||
|
pub fn parse(api: *Server, obj: json.ObjectMap) !Task {
|
||||||
|
const task_type = try json_utils.getStringRequired(obj, "type");
|
||||||
|
const total = try json_utils.getIntegerRequired(obj, "total");
|
||||||
|
if (total < 0) {
|
||||||
|
return error.InvalidTaskTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = json_utils.getStringRequired(obj, "code");
|
||||||
|
const id = try api.getItemId(code);
|
||||||
|
|
||||||
|
return Task{
|
||||||
|
.id = id,
|
||||||
|
.type = TaskTypeUtils.fromString(task_type) orelse return error.InvalidTaskType,
|
||||||
|
.total = @intCast(total)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const AcceptTaskResult = struct {
|
||||||
|
cooldown: Cooldown,
|
||||||
|
character: Character,
|
||||||
|
task: Task,
|
||||||
|
|
||||||
|
pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !AcceptTaskResult {
|
||||||
|
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 EquipResult{
|
||||||
|
.cooldown = try Cooldown.parse(cooldown),
|
||||||
|
.character = try Character.parse(api, character, allocator),
|
||||||
|
.task = try Task.parse(api, task)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -578,12 +406,12 @@ pub const MapContentType = enum {
|
|||||||
tasks_master,
|
tasks_master,
|
||||||
};
|
};
|
||||||
pub const MapContentTypeUtils = EnumStringUtils(MapContentType, .{
|
pub const MapContentTypeUtils = EnumStringUtils(MapContentType, .{
|
||||||
.{ "monster" , .monster },
|
.{ "monster" , MapContentType.monster },
|
||||||
.{ "resource" , .resource },
|
.{ "resource" , MapContentType.resource },
|
||||||
.{ "workshop" , .workshop },
|
.{ "workshop" , MapContentType.workshop },
|
||||||
.{ "bank" , .bank },
|
.{ "bank" , MapContentType.bank },
|
||||||
.{ "grand_exchange", .grand_exchange },
|
.{ "grand_exchange", MapContentType.grand_exchange },
|
||||||
.{ "tasks_master" , .tasks_master },
|
.{ "tasks_master" , MapContentType.tasks_master },
|
||||||
});
|
});
|
||||||
|
|
||||||
pub const MapTile = struct {
|
pub const MapTile = struct {
|
||||||
@ -1010,7 +838,7 @@ fn allocPaginationParams(allocator: Allocator, page: u64, page_size: ?u64) ![]u8
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add retries when hitting a ratelimit
|
// TODO: add retries when hitting a ratelimit
|
||||||
fn fetch(self: *Server, options: FetchOptions) APIError!ArtifactsFetchResult {
|
fn fetch(self: *Server, options: FetchOptions) FetchError!ArtifactsFetchResult {
|
||||||
const method = options.method;
|
const method = options.method;
|
||||||
const path = options.path;
|
const path = options.path;
|
||||||
const payload = options.payload;
|
const payload = options.payload;
|
||||||
@ -1062,7 +890,7 @@ fn fetch(self: *Server, options: FetchOptions) APIError!ArtifactsFetchResult {
|
|||||||
defer if (authorization_header) |str| self.allocator.free(str);
|
defer if (authorization_header) |str| self.allocator.free(str);
|
||||||
|
|
||||||
if (self.token) |token| {
|
if (self.token) |token| {
|
||||||
authorization_header = std.fmt.allocPrint(self.allocator, "Bearer {s}", .{token}) catch return APIError.OutOfMemory;
|
authorization_header = std.fmt.allocPrint(self.allocator, "Bearer {s}", .{token}) catch return FetchError.OutOfMemory;
|
||||||
opts.headers.authorization = .{ .override = authorization_header.? };
|
opts.headers.authorization = .{ .override = authorization_header.? };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1072,13 +900,13 @@ fn fetch(self: *Server, options: FetchOptions) APIError!ArtifactsFetchResult {
|
|||||||
log.debug("fetch {} {s}", .{method, path});
|
log.debug("fetch {} {s}", .{method, path});
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = self.client.fetch(opts) catch return APIError.RequestFailed;
|
const result = self.client.fetch(opts) catch return FetchError.RequestFailed;
|
||||||
const response_body = response_storage.items;
|
const response_body = response_storage.items;
|
||||||
|
|
||||||
log.debug("fetch result {}", .{result.status});
|
log.debug("fetch result {}", .{result.status});
|
||||||
|
|
||||||
if (result.status == .service_unavailable) {
|
if (result.status == .service_unavailable) {
|
||||||
return APIError.ServerUnavailable;
|
return FetchError.ServerUnavailable;
|
||||||
} else if (result.status != .ok) {
|
} else if (result.status != .ok) {
|
||||||
return ArtifactsFetchResult{
|
return ArtifactsFetchResult{
|
||||||
.arena = arena,
|
.arena = arena,
|
||||||
@ -1086,20 +914,20 @@ fn fetch(self: *Server, options: FetchOptions) APIError!ArtifactsFetchResult {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = json.parseFromSliceLeaky(json.Value, arena.allocator(), response_body, .{ .allocate = .alloc_if_needed }) catch return APIError.ParseFailed;
|
const parsed = json.parseFromSliceLeaky(json.Value, arena.allocator(), response_body, .{ .allocate = .alloc_if_needed }) catch return FetchError.ParseFailed;
|
||||||
if (parsed != json.Value.object) {
|
if (parsed != json.Value.object) {
|
||||||
return APIError.ParseFailed;
|
return FetchError.ParseFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
result_status = result.status;
|
result_status = result.status;
|
||||||
|
|
||||||
if (options.paginated) {
|
if (options.paginated) {
|
||||||
const total_pages_i64 = json_utils.getInteger(parsed.object, "pages") orelse return APIError.ParseFailed;
|
const total_pages_i64 = json_utils.getInteger(parsed.object, "pages") orelse return FetchError.ParseFailed;
|
||||||
if (total_pages_i64 < 0) return APIError.ParseFailed;
|
if (total_pages_i64 < 0) return FetchError.ParseFailed;
|
||||||
total_pages = @intCast(total_pages_i64);
|
total_pages = @intCast(total_pages_i64);
|
||||||
|
|
||||||
const page_results = json_utils.getArray(parsed.object, "data") orelse return APIError.ParseFailed;
|
const page_results = json_utils.getArray(parsed.object, "data") orelse return FetchError.ParseFailed;
|
||||||
fetch_results.appendSlice(page_results.items) catch return APIError.OutOfMemory;
|
fetch_results.appendSlice(page_results.items) catch return FetchError.OutOfMemory;
|
||||||
|
|
||||||
if (current_page >= total_pages) break;
|
if (current_page >= total_pages) break;
|
||||||
} else {
|
} else {
|
||||||
@ -1125,7 +953,7 @@ fn handleFetchError(
|
|||||||
parseError: ?fn (status: std.http.Status) ?Error,
|
parseError: ?fn (status: std.http.Status) ?Error,
|
||||||
) ?Error {
|
) ?Error {
|
||||||
if (status != .ok) {
|
if (status != .ok) {
|
||||||
if (Error != APIError) {
|
if (Error != FetchError) {
|
||||||
if (parseError == null) {
|
if (parseError == null) {
|
||||||
@compileError("`parseError` must be defined, if `Error` is not `APIError`");
|
@compileError("`parseError` must be defined, if `Error` is not `APIError`");
|
||||||
}
|
}
|
||||||
@ -1166,15 +994,15 @@ fn fetchOptionalObject(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (result.status != .ok) {
|
if (result.status != .ok) {
|
||||||
return APIError.RequestFailed;
|
return FetchError.RequestFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.body == null) {
|
if (result.body == null) {
|
||||||
return APIError.ParseFailed;
|
return FetchError.ParseFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = json_utils.asObject(result.body.?) orelse return APIError.ParseFailed;
|
const body = json_utils.asObject(result.body.?) orelse return FetchError.ParseFailed;
|
||||||
return @call(.auto, parseObject, .{ self, body } ++ parseObjectArgs) catch return APIError.ParseFailed;
|
return @call(.auto, parseObject, .{ self, body } ++ parseObjectArgs) catch return FetchError.ParseFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetchObject(
|
fn fetchObject(
|
||||||
@ -1187,7 +1015,7 @@ fn fetchObject(
|
|||||||
fetchOptions: FetchOptions
|
fetchOptions: FetchOptions
|
||||||
) Error!Object {
|
) Error!Object {
|
||||||
const result = try self.fetchOptionalObject(Error, parseError, Object, parseObject, parseObjectArgs, fetchOptions);
|
const result = try self.fetchOptionalObject(Error, parseError, Object, parseObject, parseObjectArgs, fetchOptions);
|
||||||
return result orelse return APIError.RequestFailed;
|
return result orelse return FetchError.RequestFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetchOptionalArray(
|
fn fetchOptionalArray(
|
||||||
@ -1214,11 +1042,11 @@ fn fetchOptionalArray(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (result.status != .ok) {
|
if (result.status != .ok) {
|
||||||
return APIError.RequestFailed;
|
return FetchError.RequestFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.body == null) {
|
if (result.body == null) {
|
||||||
return APIError.ParseFailed;
|
return FetchError.ParseFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
var array = std.ArrayList(Object).init(allocator);
|
var array = std.ArrayList(Object).init(allocator);
|
||||||
@ -1233,12 +1061,12 @@ fn fetchOptionalArray(
|
|||||||
array.deinit();
|
array.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
const result_data = json_utils.asArray(result.body.?) orelse return APIError.ParseFailed;
|
const result_data = json_utils.asArray(result.body.?) orelse return FetchError.ParseFailed;
|
||||||
for (result_data.items) |result_item| {
|
for (result_data.items) |result_item| {
|
||||||
const item_obj = json_utils.asObject(result_item) orelse return APIError.ParseFailed;
|
const item_obj = json_utils.asObject(result_item) orelse return FetchError.ParseFailed;
|
||||||
|
|
||||||
const parsed_item = @call(.auto, parseObject, .{ self, item_obj } ++ parseObjectArgs) catch return APIError.ParseFailed;
|
const parsed_item = @call(.auto, parseObject, .{ self, item_obj } ++ parseObjectArgs) catch return FetchError.ParseFailed;
|
||||||
array.append(parsed_item) catch return APIError.OutOfMemory;
|
array.append(parsed_item) catch return FetchError.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
@ -1255,7 +1083,7 @@ fn fetchArray(
|
|||||||
fetchOptions: FetchOptions
|
fetchOptions: FetchOptions
|
||||||
) Error!std.ArrayList(Object) {
|
) Error!std.ArrayList(Object) {
|
||||||
const result = try self.fetchOptionalArray(allocator, Error, parseError, Object, parseObject, parseObjectArgs, fetchOptions);
|
const result = try self.fetchOptionalArray(allocator, Error, parseError, Object, parseObject, parseObjectArgs, fetchOptions);
|
||||||
return result orelse return APIError.RequestFailed;
|
return result orelse return FetchError.RequestFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setURL(self: *Server, url: []const u8) !void {
|
pub fn setURL(self: *Server, url: []const u8) !void {
|
||||||
@ -1431,9 +1259,9 @@ pub fn prefetchItems(self: *Server) !void {
|
|||||||
|
|
||||||
// ------------------------- Endpoints ------------------------
|
// ------------------------- Endpoints ------------------------
|
||||||
|
|
||||||
pub fn getServerStatus(self: *Server) !ServerStatus {
|
pub fn getServerStatus(self: *Server) FetchError!ServerStatus {
|
||||||
return try self.fetchObject(
|
return try self.fetchObject(
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
ServerStatus,
|
ServerStatus,
|
||||||
ServerStatus.parse, .{ self.allocator },
|
ServerStatus.parse, .{ self.allocator },
|
||||||
@ -1441,12 +1269,12 @@ pub fn getServerStatus(self: *Server) !ServerStatus {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getCharacter(self: *Server, name: []const u8) APIError!?Character {
|
pub fn getCharacter(self: *Server, name: []const u8) FetchError!?Character {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/characters/{s}", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/characters/{s}", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
var maybe_character = try self.fetchOptionalObject(
|
var maybe_character = try self.fetchOptionalObject(
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
Character,
|
Character,
|
||||||
Character.parse, .{ self.allocator },
|
Character.parse, .{ self.allocator },
|
||||||
@ -1462,10 +1290,10 @@ pub fn getCharacter(self: *Server, name: []const u8) APIError!?Character {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listMyCharacters(self: *Server) APIError!std.ArrayList(Character) {
|
pub fn listMyCharacters(self: *Server) FetchError!std.ArrayList(Character) {
|
||||||
const characters = try self.fetchArray(
|
const characters = try self.fetchArray(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
Character,
|
Character,
|
||||||
Character.parse, .{ self.allocator },
|
Character.parse, .{ self.allocator },
|
||||||
@ -1479,13 +1307,13 @@ pub fn listMyCharacters(self: *Server) APIError!std.ArrayList(Character) {
|
|||||||
return characters;
|
return characters;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn actionFight(self: *Server, name: []const u8) FightError!FightResult {
|
pub fn actionFight(self: *Server, name: []const u8) errors.FightError!FightResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/fight", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/fight", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
FightError,
|
errors.FightError,
|
||||||
FightResult.parseError,
|
errors.parseFightError,
|
||||||
FightResult,
|
FightResult,
|
||||||
FightResult.parse, .{ self.allocator },
|
FightResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path }
|
.{ .method = .POST, .path = path }
|
||||||
@ -1495,13 +1323,13 @@ pub fn actionFight(self: *Server, name: []const u8) FightError!FightResult {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn actionGather(self: *Server, name: []const u8) GatherError!GatherResult {
|
pub fn actionGather(self: *Server, name: []const u8) errors.GatherError!GatherResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/gathering", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/gathering", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
GatherError,
|
errors.GatherError,
|
||||||
GatherResult.parseError,
|
errors.parseGatherError,
|
||||||
GatherResult,
|
GatherResult,
|
||||||
GatherResult.parse, .{ self.allocator },
|
GatherResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path }
|
.{ .method = .POST, .path = path }
|
||||||
@ -1511,7 +1339,7 @@ pub fn actionGather(self: *Server, name: []const u8) GatherError!GatherResult {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn actionMove(self: *Server, name: []const u8, x: i64, y: i64) MoveError!MoveResult {
|
pub fn actionMove(self: *Server, name: []const u8, x: i64, y: i64) errors.MoveError!MoveResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/move", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/move", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
@ -1519,8 +1347,8 @@ pub fn actionMove(self: *Server, name: []const u8, x: i64, y: i64) MoveError!Mov
|
|||||||
defer self.allocator.free(payload);
|
defer self.allocator.free(payload);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
MoveError,
|
errors.MoveError,
|
||||||
MoveResult.parseError,
|
errors.parseMoveError,
|
||||||
MoveResult,
|
MoveResult,
|
||||||
MoveResult.parse, .{ self.allocator },
|
MoveResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path, .payload = payload }
|
.{ .method = .POST, .path = path, .payload = payload }
|
||||||
@ -1534,7 +1362,7 @@ pub fn actionBankDepositGold(
|
|||||||
self: *Server,
|
self: *Server,
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
quantity: u64
|
quantity: u64
|
||||||
) BankDepositGoldError!GoldTransactionResult {
|
) errors.BankDepositGoldError!GoldTransactionResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/bank/deposit/gold", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/bank/deposit/gold", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
@ -1542,8 +1370,8 @@ pub fn actionBankDepositGold(
|
|||||||
defer self.allocator.free(payload);
|
defer self.allocator.free(payload);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
BankDepositGoldError,
|
errors.BankDepositGoldError,
|
||||||
GoldTransactionResult.parseDepositError,
|
errors.parseBankDepositGoldError,
|
||||||
GoldTransactionResult,
|
GoldTransactionResult,
|
||||||
GoldTransactionResult.parse, .{ self.allocator },
|
GoldTransactionResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path, .payload = payload }
|
.{ .method = .POST, .path = path, .payload = payload }
|
||||||
@ -1558,7 +1386,7 @@ pub fn actionBankDepositItem(
|
|||||||
name: []const u8,
|
name: []const u8,
|
||||||
code: []const u8,
|
code: []const u8,
|
||||||
quantity: u64
|
quantity: u64
|
||||||
) BankDepositItemError!ItemTransactionResult {
|
) errors.BankDepositItemError!ItemTransactionResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/bank/deposit", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/bank/deposit", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
@ -1566,8 +1394,8 @@ pub fn actionBankDepositItem(
|
|||||||
defer self.allocator.free(payload);
|
defer self.allocator.free(payload);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
BankDepositItemError,
|
errors.BankDepositItemError,
|
||||||
ItemTransactionResult.parseDepositError,
|
errors.parseBankDepositItemError,
|
||||||
ItemTransactionResult,
|
ItemTransactionResult,
|
||||||
ItemTransactionResult.parse, .{ self.allocator },
|
ItemTransactionResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path, .payload = payload }
|
.{ .method = .POST, .path = path, .payload = payload }
|
||||||
@ -1581,7 +1409,7 @@ pub fn actionBankWithdrawGold(
|
|||||||
self: *Server,
|
self: *Server,
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
quantity: u64
|
quantity: u64
|
||||||
) BankDepositGoldError!GoldTransactionResult {
|
) errors.BankDepositGoldError!GoldTransactionResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/bank/withdraw/gold", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/bank/withdraw/gold", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
@ -1589,8 +1417,8 @@ pub fn actionBankWithdrawGold(
|
|||||||
defer self.allocator.free(payload);
|
defer self.allocator.free(payload);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
BankWithdrawGoldError,
|
errors.BankWithdrawGoldError,
|
||||||
GoldTransactionResult.parseWithdrawError,
|
errors.parseBankWithdrawGoldError,
|
||||||
GoldTransactionResult,
|
GoldTransactionResult,
|
||||||
GoldTransactionResult.parse, .{ self.allocator },
|
GoldTransactionResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path, .payload = payload }
|
.{ .method = .POST, .path = path, .payload = payload }
|
||||||
@ -1605,7 +1433,7 @@ pub fn actionBankWithdrawItem(
|
|||||||
name: []const u8,
|
name: []const u8,
|
||||||
code: []const u8,
|
code: []const u8,
|
||||||
quantity: u64
|
quantity: u64
|
||||||
) BankWithdrawItemError!ItemTransactionResult {
|
) errors.BankWithdrawItemError!ItemTransactionResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/bank/withdraw", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/bank/withdraw", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
@ -1613,8 +1441,8 @@ pub fn actionBankWithdrawItem(
|
|||||||
defer self.allocator.free(payload);
|
defer self.allocator.free(payload);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
BankWithdrawItemError,
|
errors.BankWithdrawItemError,
|
||||||
ItemTransactionResult.parseWithdrawError,
|
errors.parseBankWithdrawItemError,
|
||||||
ItemTransactionResult,
|
ItemTransactionResult,
|
||||||
ItemTransactionResult.parse, .{ self.allocator },
|
ItemTransactionResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path, .payload = payload }
|
.{ .method = .POST, .path = path, .payload = payload }
|
||||||
@ -1637,8 +1465,8 @@ pub fn actionCraft(
|
|||||||
defer self.allocator.free(payload);
|
defer self.allocator.free(payload);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
CraftError,
|
errors.CraftError,
|
||||||
CraftResult.parseError,
|
errors.parseCraftError,
|
||||||
CraftResult,
|
CraftResult,
|
||||||
CraftResult.parse, .{ self.allocator },
|
CraftResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path, .payload = payload }
|
.{ .method = .POST, .path = path, .payload = payload }
|
||||||
@ -1652,7 +1480,7 @@ pub fn actionUnequip(
|
|||||||
self: *Server,
|
self: *Server,
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
slot: EquipmentSlot
|
slot: EquipmentSlot
|
||||||
) UnequipError!UnequipResult {
|
) errors.UnequipError!UnequipResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/unequip", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/unequip", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
@ -1660,8 +1488,8 @@ pub fn actionUnequip(
|
|||||||
defer self.allocator.free(payload);
|
defer self.allocator.free(payload);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
UnequipError,
|
errors.UnequipError,
|
||||||
UnequipResult.parseError,
|
errors.parseUnequipError,
|
||||||
UnequipResult,
|
UnequipResult,
|
||||||
UnequipResult.parse, .{ self.allocator },
|
UnequipResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path, .payload = payload }
|
.{ .method = .POST, .path = path, .payload = payload }
|
||||||
@ -1676,7 +1504,7 @@ pub fn actionEquip(
|
|||||||
name: []const u8,
|
name: []const u8,
|
||||||
slot: EquipmentSlot,
|
slot: EquipmentSlot,
|
||||||
code: []const u8
|
code: []const u8
|
||||||
) EquipError!EquipResult {
|
) errors.EquipError!EquipResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/equip", .{name});
|
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/equip", .{name});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
@ -1684,8 +1512,8 @@ pub fn actionEquip(
|
|||||||
defer self.allocator.free(payload);
|
defer self.allocator.free(payload);
|
||||||
|
|
||||||
const result = try self.fetchObject(
|
const result = try self.fetchObject(
|
||||||
EquipError,
|
errors.EquipError,
|
||||||
EquipResult.parseError,
|
errors.parseEquipError,
|
||||||
EquipResult,
|
EquipResult,
|
||||||
EquipResult.parse, .{ self.allocator },
|
EquipResult.parse, .{ self.allocator },
|
||||||
.{ .method = .POST, .path = path, .payload = payload }
|
.{ .method = .POST, .path = path, .payload = payload }
|
||||||
@ -1695,28 +1523,28 @@ pub fn actionEquip(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getBankGold(self: *Server) APIError!u64 {
|
pub fn getBankGold(self: *Server) FetchError!u64 {
|
||||||
const result = try self.fetch(.{ .method = .GET, .path = "/my/bank/gold" });
|
const result = try self.fetch(.{ .method = .GET, .path = "/my/bank/gold" });
|
||||||
defer result.deinit();
|
defer result.deinit();
|
||||||
|
|
||||||
if (result.status != .ok) {
|
if (result.status != .ok) {
|
||||||
return APIError.RequestFailed;
|
return FetchError.RequestFailed;
|
||||||
}
|
}
|
||||||
if (result.body == null) {
|
if (result.body == null) {
|
||||||
return APIError.ParseFailed;
|
return FetchError.ParseFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = json_utils.asObject(result.body.?) orelse return APIError.RequestFailed;
|
const data = json_utils.asObject(result.body.?) orelse return FetchError.RequestFailed;
|
||||||
const quantity = json_utils.getInteger(data, "quantity") orelse return APIError.ParseFailed;
|
const quantity = json_utils.getInteger(data, "quantity") orelse return FetchError.ParseFailed;
|
||||||
if (quantity < 0) return APIError.ParseFailed;
|
if (quantity < 0) return FetchError.ParseFailed;
|
||||||
|
|
||||||
return @intCast(quantity);
|
return @intCast(quantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getBankItems(self: *Server, allocator: Allocator) APIError!std.ArrayList(ItemIdQuantity) {
|
pub fn getBankItems(self: *Server, allocator: Allocator) FetchError!std.ArrayList(ItemIdQuantity) {
|
||||||
return self.fetchArray(
|
return self.fetchArray(
|
||||||
allocator,
|
allocator,
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
ItemIdQuantity,
|
ItemIdQuantity,
|
||||||
ItemIdQuantity.parse, .{},
|
ItemIdQuantity.parse, .{},
|
||||||
@ -1724,13 +1552,13 @@ pub fn getBankItems(self: *Server, allocator: Allocator) APIError!std.ArrayList(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getBankItemQuantity(self: *Server, code: []const u8) APIError!?u64 {
|
pub fn getBankItemQuantity(self: *Server, code: []const u8) FetchError!?u64 {
|
||||||
const query = try std.fmt.allocPrint(self.allocator, "item_code={s}", .{code});
|
const query = try std.fmt.allocPrint(self.allocator, "item_code={s}", .{code});
|
||||||
defer self.allocator.free(query);
|
defer self.allocator.free(query);
|
||||||
|
|
||||||
const maybe_items = try self.fetchOptionalArray(
|
const maybe_items = try self.fetchOptionalArray(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
ItemIdQuantity,
|
ItemIdQuantity,
|
||||||
ItemIdQuantity.parse, .{},
|
ItemIdQuantity.parse, .{},
|
||||||
@ -1750,7 +1578,7 @@ pub fn getBankItemQuantity(self: *Server, code: []const u8) APIError!?u64 {
|
|||||||
return list_items[0].quantity;
|
return list_items[0].quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getMap(self: *Server, x: i64, y: i64) APIError!?MapTile {
|
pub fn getMap(self: *Server, x: i64, y: i64) FetchError!?MapTile {
|
||||||
const position = Position.init(x, y);
|
const position = Position.init(x, y);
|
||||||
|
|
||||||
if (self.findMap(position)) |map| {
|
if (self.findMap(position)) |map| {
|
||||||
@ -1765,7 +1593,7 @@ pub fn getMap(self: *Server, x: i64, y: i64) APIError!?MapTile {
|
|||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
const result = self.fetchOptionalObject(
|
const result = self.fetchOptionalObject(
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
MapTile,
|
MapTile,
|
||||||
MapTile.parse, .{ self.allocator },
|
MapTile.parse, .{ self.allocator },
|
||||||
@ -1784,7 +1612,7 @@ pub const MapOptions = struct {
|
|||||||
type: ?MapContentType = null,
|
type: ?MapContentType = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn getMaps(self: *Server, opts: MapOptions) APIError!std.ArrayList(MapTile) {
|
pub fn getMaps(self: *Server, opts: MapOptions) FetchError!std.ArrayList(MapTile) {
|
||||||
if (self.prefetched_maps) {
|
if (self.prefetched_maps) {
|
||||||
var found = std.ArrayList(MapTile).init(self.allocator);
|
var found = std.ArrayList(MapTile).init(self.allocator);
|
||||||
var mapIter = self.maps.valueIterator();
|
var mapIter = self.maps.valueIterator();
|
||||||
@ -1815,7 +1643,7 @@ pub fn getMaps(self: *Server, opts: MapOptions) APIError!std.ArrayList(MapTile)
|
|||||||
|
|
||||||
const result = try self.fetchArray(
|
const result = try self.fetchArray(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
MapTile,
|
MapTile,
|
||||||
MapTile.parse, .{ self.allocator },
|
MapTile.parse, .{ self.allocator },
|
||||||
@ -1829,7 +1657,7 @@ pub fn getMaps(self: *Server, opts: MapOptions) APIError!std.ArrayList(MapTile)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getItem(self: *Server, code: []const u8) APIError!?Item {
|
pub fn getItem(self: *Server, code: []const u8) FetchError!?Item {
|
||||||
if (self.items.get(code)) |item| {
|
if (self.items.get(code)) |item| {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@ -1842,7 +1670,7 @@ pub fn getItem(self: *Server, code: []const u8) APIError!?Item {
|
|||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
const result = try self.fetchOptionalObject(
|
const result = try self.fetchOptionalObject(
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
ItemWithGE,
|
ItemWithGE,
|
||||||
ItemWithGE.parse, .{ self.allocator },
|
ItemWithGE.parse, .{ self.allocator },
|
||||||
@ -1858,7 +1686,7 @@ pub fn getItem(self: *Server, code: []const u8) APIError!?Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getItemById(self: *Server, id: ItemId) APIError!?Item {
|
pub fn getItemById(self: *Server, id: ItemId) FetchError!?Item {
|
||||||
const code = self.getItemCode(id) orelse return null;
|
const code = self.getItemCode(id) orelse return null;
|
||||||
return self.getItem(code);
|
return self.getItem(code);
|
||||||
}
|
}
|
||||||
@ -1872,7 +1700,7 @@ pub const ItemOptions = struct {
|
|||||||
type: ?ItemType = null,
|
type: ?ItemType = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn getItems(self: *Server, opts: ItemOptions) APIError!std.ArrayList(Item) {
|
pub fn getItems(self: *Server, opts: ItemOptions) FetchError!std.ArrayList(Item) {
|
||||||
if (self.prefetched_items) {
|
if (self.prefetched_items) {
|
||||||
var found = std.ArrayList(Item).init(self.allocator);
|
var found = std.ArrayList(Item).init(self.allocator);
|
||||||
var itemIter = self.items.valueIterator();
|
var itemIter = self.items.valueIterator();
|
||||||
@ -1933,7 +1761,7 @@ pub fn getItems(self: *Server, opts: ItemOptions) APIError!std.ArrayList(Item) {
|
|||||||
|
|
||||||
const result = try self.fetchArray(
|
const result = try self.fetchArray(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
Item,
|
Item,
|
||||||
Item.parse, .{ self.allocator },
|
Item.parse, .{ self.allocator },
|
||||||
@ -1948,7 +1776,7 @@ pub fn getItems(self: *Server, opts: ItemOptions) APIError!std.ArrayList(Item) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getResource(self: *Server, code: []const u8) APIError!?Resource {
|
pub fn getResource(self: *Server, code: []const u8) FetchError!?Resource {
|
||||||
if (self.resources.get(code)) |resource| {
|
if (self.resources.get(code)) |resource| {
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
@ -1961,7 +1789,7 @@ pub fn getResource(self: *Server, code: []const u8) APIError!?Resource {
|
|||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
const result = try self.fetchOptionalObject(
|
const result = try self.fetchOptionalObject(
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
Resource,
|
Resource,
|
||||||
Resource.parse, .{ self.allocator },
|
Resource.parse, .{ self.allocator },
|
||||||
@ -1982,7 +1810,7 @@ pub const ResourceOptions = struct {
|
|||||||
skill: ?ResourceSkill = null,
|
skill: ?ResourceSkill = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn getResources(self: *Server, opts: ResourceOptions) APIError!std.ArrayList(Resource) {
|
pub fn getResources(self: *Server, opts: ResourceOptions) FetchError!std.ArrayList(Resource) {
|
||||||
if (self.prefetched_resources) {
|
if (self.prefetched_resources) {
|
||||||
var found = std.ArrayList(Resource).init(self.allocator);
|
var found = std.ArrayList(Resource).init(self.allocator);
|
||||||
var resourceIter = self.resources.valueIterator();
|
var resourceIter = self.resources.valueIterator();
|
||||||
@ -2028,7 +1856,7 @@ pub fn getResources(self: *Server, opts: ResourceOptions) APIError!std.ArrayList
|
|||||||
|
|
||||||
const result = try self.fetchArray(
|
const result = try self.fetchArray(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
Resource,
|
Resource,
|
||||||
Resource.parse, .{ self.allocator },
|
Resource.parse, .{ self.allocator },
|
||||||
@ -2043,7 +1871,7 @@ pub fn getResources(self: *Server, opts: ResourceOptions) APIError!std.ArrayList
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getMonster(self: *Server, code: []const u8) APIError!?Monster {
|
pub fn getMonster(self: *Server, code: []const u8) FetchError!?Monster {
|
||||||
if (self.monsters.get(code)) |monster| {
|
if (self.monsters.get(code)) |monster| {
|
||||||
return monster;
|
return monster;
|
||||||
}
|
}
|
||||||
@ -2056,7 +1884,7 @@ pub fn getMonster(self: *Server, code: []const u8) APIError!?Monster {
|
|||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
const result = try self.fetchOptionalObject(
|
const result = try self.fetchOptionalObject(
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
Monster,
|
Monster,
|
||||||
Monster.parse, .{ self.allocator },
|
Monster.parse, .{ self.allocator },
|
||||||
@ -2076,7 +1904,7 @@ pub const MonsterOptions = struct {
|
|||||||
min_level: ?u64 = null,
|
min_level: ?u64 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn getMonsters(self: *Server, opts: MonsterOptions) APIError!std.ArrayList(Monster) {
|
pub fn getMonsters(self: *Server, opts: MonsterOptions) FetchError!std.ArrayList(Monster) {
|
||||||
if (self.prefetched_monsters) {
|
if (self.prefetched_monsters) {
|
||||||
var found = std.ArrayList(Monster).init(self.allocator);
|
var found = std.ArrayList(Monster).init(self.allocator);
|
||||||
var monsterIter = self.monsters.valueIterator();
|
var monsterIter = self.monsters.valueIterator();
|
||||||
@ -2119,7 +1947,7 @@ pub fn getMonsters(self: *Server, opts: MonsterOptions) APIError!std.ArrayList(M
|
|||||||
|
|
||||||
const result = try self.fetchArray(
|
const result = try self.fetchArray(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
APIError,
|
FetchError,
|
||||||
null,
|
null,
|
||||||
Monster,
|
Monster,
|
||||||
Monster.parse, .{ self.allocator },
|
Monster.parse, .{ self.allocator },
|
||||||
@ -2133,3 +1961,18 @@ pub fn getMonsters(self: *Server, opts: MonsterOptions) APIError!std.ArrayList(M
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn acceptTask(self: *Server, name: []const u8) errors.TaskAcceptError {
|
||||||
|
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,
|
||||||
|
EquipResult,
|
||||||
|
EquipResult.parse, .{ self.allocator },
|
||||||
|
.{ .method = .POST, .path = path }
|
||||||
|
);
|
||||||
|
try self.addOrUpdateCharacter(result.character);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
170
lib/action.zig
Normal file
170
lib/action.zig
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
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: Server.ItemIdQuantity,
|
||||||
|
withdraw_item: Server.ItemIdQuantity,
|
||||||
|
craft_item: Server.ItemIdQuantity,
|
||||||
|
|
||||||
|
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.getItemCode(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.getItemCode(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.getItemCode(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);
|
||||||
|
}
|
562
lib/brain.zig
562
lib/brain.zig
@ -1,118 +1,28 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const ArtifactsAPI = @import("artifacts-api");
|
const Api = @import("artifacts-api");
|
||||||
const Server = ArtifactsAPI.Server;
|
const Server = Api.Server;
|
||||||
|
const Position = Api.Position;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Position = Server.Position;
|
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
const CharacterTask = @import("./task.zig").Task;
|
const CharacterTask = @import("./task.zig").Task;
|
||||||
|
const QueuedAction = @import("./action.zig").Action;
|
||||||
|
const QueuedActionResult = @import("./action.zig").ActionResult;
|
||||||
|
|
||||||
const CharacterBrain = @This();
|
const Brain = @This();
|
||||||
|
|
||||||
const bank_position: Position = .{ .x = 4, .y = 1 }; // TODO: Figure this out dynamically
|
|
||||||
|
|
||||||
pub const QueuedAction = union(enum) {
|
|
||||||
move: Position,
|
|
||||||
fight,
|
|
||||||
gather,
|
|
||||||
deposit_gold: u64,
|
|
||||||
deposit_item: Server.ItemIdQuantity,
|
|
||||||
withdraw_item: Server.ItemIdQuantity,
|
|
||||||
craft_item: Server.ItemIdQuantity,
|
|
||||||
|
|
||||||
pub fn perform(self: QueuedAction, api: *Server, name: []const u8, ) !QueuedActionResult {
|
|
||||||
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.getItemCode(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.getItemCode(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.getItemCode(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)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const QueuedActionResult = union(enum) {
|
|
||||||
move: Server.MoveError!Server.MoveResult,
|
|
||||||
fight: Server.FightError!Server.FightResult,
|
|
||||||
gather: Server.GatherError!Server.GatherResult,
|
|
||||||
deposit_gold: Server.BankDepositGoldError!Server.GoldTransactionResult,
|
|
||||||
deposit_item: Server.BankDepositItemError!Server.ItemTransactionResult,
|
|
||||||
withdraw_item: Server.BankWithdrawItemError!Server.ItemTransactionResult,
|
|
||||||
craft_item: Server.CraftError!Server.CraftResult,
|
|
||||||
|
|
||||||
const Tag = @typeInfo(QueuedActionResult).Union.tag_type.?;
|
|
||||||
|
|
||||||
fn fieldType(comptime kind: Tag) type {
|
|
||||||
const field_type = std.meta.fields(QueuedActionResult)[@intFromEnum(kind)].type;
|
|
||||||
return @typeInfo(field_type).ErrorUnion.payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(self: QueuedActionResult, comptime kind: Tag) ?fieldType(kind) {
|
|
||||||
return switch (self) {
|
|
||||||
kind => |v| v catch null,
|
|
||||||
else => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
comptime {
|
|
||||||
assert(@typeInfo(QueuedAction).Union.fields.len == @typeInfo(QueuedActionResult).Union.fields.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
action_queue: std.ArrayList(QueuedAction),
|
action_queue: std.ArrayList(QueuedAction),
|
||||||
task: ?*CharacterTask = null,
|
task: ?CharacterTask = null,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, name: []const u8) !CharacterBrain {
|
pub fn init(allocator: Allocator, name: []const u8) !Brain {
|
||||||
return CharacterBrain{
|
return Brain{
|
||||||
.name = try allocator.dupe(u8, name),
|
.name = try allocator.dupe(u8, name),
|
||||||
.action_queue = std.ArrayList(QueuedAction).init(allocator),
|
.action_queue = std.ArrayList(QueuedAction).init(allocator),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: CharacterBrain) void {
|
pub fn deinit(self: Brain) void {
|
||||||
const allocator = self.action_queue.allocator;
|
const allocator = self.action_queue.allocator;
|
||||||
allocator.free(self.name);
|
allocator.free(self.name);
|
||||||
self.action_queue.deinit();
|
self.action_queue.deinit();
|
||||||
@ -123,450 +33,56 @@ fn currentTime() f64 {
|
|||||||
return timestamp / std.time.ms_per_s;
|
return timestamp / std.time.ms_per_s;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn performNextAction(self: *CharacterBrain, api: *Server) !void {
|
pub fn performNextAction(self: *Brain, api: *Server) !void {
|
||||||
const log = std.log.default;
|
const log = std.log.default;
|
||||||
assert(self.action_queue.items.len > 0);
|
assert(self.action_queue.items.len > 0);
|
||||||
|
|
||||||
const APIError = Server.APIError;
|
|
||||||
|
|
||||||
const retry_delay = 0.5; // 500ms
|
const retry_delay = 0.5; // 500ms
|
||||||
var character = api.findCharacterPtr(self.name).?;
|
var character = api.findCharacterPtr(self.name).?;
|
||||||
|
|
||||||
const next_action = self.action_queue.items[0];
|
const next_action = self.action_queue.items[0];
|
||||||
const action_result = try next_action.perform(api, self.name);
|
const action_result = try next_action.perform(api, self.name);
|
||||||
switch (action_result) {
|
|
||||||
.fight => |result| {
|
|
||||||
const FightError = Server.FightError;
|
|
||||||
_ = result catch |err| switch (err) {
|
|
||||||
FightError.CharacterInCooldown,
|
|
||||||
FightError.CharacterIsBusy => {
|
|
||||||
// A bit too eager, retry action
|
|
||||||
character.cooldown_expiration = currentTime() + retry_delay;
|
|
||||||
log.warn("[{s}] retry fighting", .{self.name});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
FightError.CharacterIsFull,
|
if (action_result.getErrorResponse()) |error_response| {
|
||||||
FightError.MonsterNotFound => {
|
switch (error_response) {
|
||||||
// Re-evaluate what the character should do, something is not right.
|
.retry => {
|
||||||
log.warn("[{s}] clear action queue", .{self.name});
|
character.cooldown_expiration = currentTime() + retry_delay;
|
||||||
self.action_queue.clearAndFree();
|
log.warn("[{s}] retry withdrawing item", .{self.name});
|
||||||
return;
|
},
|
||||||
},
|
.restart => {
|
||||||
|
log.warn("[{s}] clear action queue", .{self.name});
|
||||||
|
self.action_queue.clearAndFree();
|
||||||
|
},
|
||||||
|
.abort => {
|
||||||
|
try action_result.getError();
|
||||||
|
|
||||||
FightError.CharacterNotFound,
|
// The error above should always return
|
||||||
APIError.ServerUnavailable,
|
unreachable;
|
||||||
APIError.RequestFailed,
|
},
|
||||||
APIError.ParseFailed,
|
.ignore => { },
|
||||||
APIError.OutOfMemory => {
|
|
||||||
// Welp... Abondon ship. Bail. Bail
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.move => |result| {
|
|
||||||
const MoveError = Server.MoveError;
|
|
||||||
_ = result catch |err| switch (err) {
|
|
||||||
MoveError.CharacterIsBusy,
|
|
||||||
MoveError.CharacterInCooldown => {
|
|
||||||
// A bit too eager, retry action
|
|
||||||
character.cooldown_expiration = currentTime() + retry_delay;
|
|
||||||
log.warn("[{s}] retry moving", .{self.name});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
MoveError.CharacterAtDestination => {
|
|
||||||
// Not great, but I guess the goal achieved? The character is at the desired location.
|
|
||||||
log.warn("[{s}] tried to move, but already at destination", .{self.name});
|
|
||||||
},
|
|
||||||
|
|
||||||
MoveError.MapNotFound => {
|
|
||||||
// Re-evaluate what the character should do, something is not right.
|
|
||||||
log.warn("[{s}] clear action queue", .{self.name});
|
|
||||||
self.action_queue.clearAndFree();
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
MoveError.CharacterNotFound,
|
|
||||||
APIError.ServerUnavailable,
|
|
||||||
APIError.RequestFailed,
|
|
||||||
APIError.ParseFailed,
|
|
||||||
APIError.OutOfMemory => {
|
|
||||||
// Welp... Abondon ship. Bail. Bail
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.deposit_gold => |result| {
|
|
||||||
const BankDepositGoldError = Server.BankDepositGoldError;
|
|
||||||
_ = result catch |err| switch (err) {
|
|
||||||
BankDepositGoldError.BankIsBusy,
|
|
||||||
BankDepositGoldError.CharacterIsBusy,
|
|
||||||
BankDepositGoldError.CharacterInCooldown => {
|
|
||||||
// A bit too eager, retry action
|
|
||||||
character.cooldown_expiration = currentTime() + retry_delay;
|
|
||||||
log.warn("[{s}] retry depositing gold", .{self.name});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
BankDepositGoldError.NotEnoughGold,
|
|
||||||
BankDepositGoldError.BankNotFound => {
|
|
||||||
// Re-evaluate what the character should do, something is not right.
|
|
||||||
log.warn("[{s}] clear action queue", .{self.name});
|
|
||||||
self.action_queue.clearAndFree();
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
BankDepositGoldError.CharacterNotFound,
|
|
||||||
APIError.ServerUnavailable,
|
|
||||||
APIError.RequestFailed,
|
|
||||||
APIError.ParseFailed,
|
|
||||||
APIError.OutOfMemory => {
|
|
||||||
// Welp... Abondon ship. Bail. Bail
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.deposit_item => |result| {
|
|
||||||
const BankDepositItemError = Server.BankDepositItemError;
|
|
||||||
_ = result catch |err| switch (err) {
|
|
||||||
BankDepositItemError.BankIsBusy,
|
|
||||||
BankDepositItemError.CharacterIsBusy,
|
|
||||||
BankDepositItemError.CharacterInCooldown => {
|
|
||||||
// A bit too eager, retry action
|
|
||||||
character.cooldown_expiration = currentTime() + retry_delay;
|
|
||||||
log.warn("[{s}] retry depositing item", .{self.name});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
BankDepositItemError.ItemNotFound,
|
|
||||||
BankDepositItemError.NotEnoughItems,
|
|
||||||
BankDepositItemError.BankNotFound => {
|
|
||||||
// Re-evaluate what the character should do, something is not right.
|
|
||||||
log.warn("[{s}] clear action queue", .{self.name});
|
|
||||||
self.action_queue.clearAndFree();
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
BankDepositItemError.CharacterNotFound,
|
|
||||||
APIError.ServerUnavailable,
|
|
||||||
APIError.RequestFailed,
|
|
||||||
APIError.ParseFailed,
|
|
||||||
APIError.OutOfMemory => {
|
|
||||||
// Welp... Abondon ship. Bail. Bail
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.withdraw_item => |result| {
|
|
||||||
const BankWithdrawItemError = Server.BankWithdrawItemError;
|
|
||||||
_ = result catch |err| switch (err) {
|
|
||||||
BankWithdrawItemError.CharacterIsBusy,
|
|
||||||
BankWithdrawItemError.CharacterInCooldown,
|
|
||||||
BankWithdrawItemError.BankIsBusy => {
|
|
||||||
// A bit too eager, retry action
|
|
||||||
character.cooldown_expiration = currentTime() + retry_delay;
|
|
||||||
log.warn("[{s}] retry withdrawing item", .{self.name});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
BankWithdrawItemError.ItemNotFound,
|
|
||||||
BankWithdrawItemError.NotEnoughItems,
|
|
||||||
BankWithdrawItemError.CharacterIsFull,
|
|
||||||
BankWithdrawItemError.BankNotFound => {
|
|
||||||
// Re-evaluate what the character should do, something is not right.
|
|
||||||
log.warn("[{s}] clear action queue", .{self.name});
|
|
||||||
self.action_queue.clearAndFree();
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
BankWithdrawItemError.CharacterNotFound,
|
|
||||||
APIError.ServerUnavailable,
|
|
||||||
APIError.RequestFailed,
|
|
||||||
APIError.ParseFailed,
|
|
||||||
APIError.OutOfMemory => {
|
|
||||||
// Welp... Abondon ship. Bail. Bail
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.gather => |result| {
|
|
||||||
const GatherError = Server.GatherError;
|
|
||||||
_ = result catch |err| switch (err) {
|
|
||||||
GatherError.CharacterInCooldown,
|
|
||||||
GatherError.CharacterIsBusy => {
|
|
||||||
// A bit too eager, retry action
|
|
||||||
character.cooldown_expiration = currentTime() + retry_delay;
|
|
||||||
log.warn("[{s}] retry withdrawing item", .{self.name});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
GatherError.NotEnoughSkill,
|
|
||||||
GatherError.CharacterIsFull,
|
|
||||||
GatherError.ResourceNotFound => {
|
|
||||||
// Re-evaluate what the character should do, something is not right.
|
|
||||||
log.warn("[{s}] clear action queue", .{self.name});
|
|
||||||
self.action_queue.clearAndFree();
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
GatherError.CharacterNotFound,
|
|
||||||
APIError.ServerUnavailable,
|
|
||||||
APIError.RequestFailed,
|
|
||||||
APIError.ParseFailed,
|
|
||||||
APIError.OutOfMemory => {
|
|
||||||
// Welp... Abondon ship. Bail. Bail
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.craft_item => |result| {
|
|
||||||
const CraftError = Server.CraftError;
|
|
||||||
_ = result catch |err| switch (err) {
|
|
||||||
CraftError.CharacterInCooldown,
|
|
||||||
CraftError.CharacterIsBusy => {
|
|
||||||
// A bit too eager, retry action
|
|
||||||
character.cooldown_expiration = currentTime() + retry_delay;
|
|
||||||
log.warn("[{s}] retry withdrawing item", .{self.name});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
CraftError.RecipeNotFound,
|
|
||||||
CraftError.NotEnoughItems,
|
|
||||||
CraftError.NotEnoughSkill,
|
|
||||||
CraftError.CharacterIsFull,
|
|
||||||
CraftError.WorkshopNotFound => {
|
|
||||||
// Re-evaluate what the character should do, something is not right.
|
|
||||||
log.warn("[{s}] clear action queue", .{self.name});
|
|
||||||
self.action_queue.clearAndFree();
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
CraftError.CharacterNotFound,
|
|
||||||
APIError.ServerUnavailable,
|
|
||||||
APIError.RequestFailed,
|
|
||||||
APIError.ParseFailed,
|
|
||||||
APIError.OutOfMemory => {
|
|
||||||
// Welp... Abondon ship. Bail. Bail
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = self.action_queue.orderedRemove(0);
|
_ = self.action_queue.orderedRemove(0);
|
||||||
|
|
||||||
self.onActionCompleted(action_result);
|
if (self.task) |*task| {
|
||||||
}
|
task.onActionCompleted(action_result);
|
||||||
|
|
||||||
fn onActionCompleted(self: *CharacterBrain, result: QueuedActionResult) void {
|
|
||||||
if (self.task == null) return;
|
|
||||||
|
|
||||||
switch (self.task.?.*) {
|
|
||||||
.fight => |*args| {
|
|
||||||
if (result.get(.fight)) |r| {
|
|
||||||
const fight_result: Server.FightResult = r;
|
|
||||||
const drops = fight_result.fight.drops;
|
|
||||||
|
|
||||||
args.progress += drops.getQuantity(args.until.item.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.gather => |*args| {
|
|
||||||
if (result.get(.gather)) |r| {
|
|
||||||
const gather_resutl: Server.GatherResult = r;
|
|
||||||
const items = gather_resutl.details.items;
|
|
||||||
|
|
||||||
args.progress += items.getQuantity(args.until.item.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.craft => |*args| {
|
|
||||||
if (result.get(.craft_item)) |r| {
|
|
||||||
const craft_result: Server.CraftResult = r;
|
|
||||||
const items = craft_result.details.items;
|
|
||||||
|
|
||||||
args.progress += items.getQuantity(args.target.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isTaskFinished(self: *CharacterBrain) bool {
|
pub fn step(self: *Brain, api: *Api.Server) !void {
|
||||||
if (self.task == null) {
|
if (self.action_queue.items.len > 0) return;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.task.?.isComplete();
|
if (self.task) |task| {
|
||||||
}
|
if (task.isComplete()) {
|
||||||
|
// if (try brain.depositItemsToBank(&self.server)) {
|
||||||
pub fn performTask(self: *CharacterBrain, api: *Server) !void {
|
// continue;
|
||||||
if (self.task == null) {
|
// }
|
||||||
// TODO: std.log.debug("[{s}] idle", .{self.name});
|
self.task = null;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (self.task.?.*) {
|
|
||||||
.fight => |args| {
|
|
||||||
try self.fightRoutine(api, args.at);
|
|
||||||
},
|
|
||||||
.gather => |args| {
|
|
||||||
try self.gatherRoutine(api, args.at);
|
|
||||||
},
|
|
||||||
.craft => |args| {
|
|
||||||
try self.craftRoutine(api, args.at, args.target.id, args.target.quantity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn moveIfNeeded(self: *CharacterBrain, api: *Server, pos: Position) !bool {
|
|
||||||
const character = api.findCharacter(self.name).?;
|
|
||||||
|
|
||||||
if (character.position.eql(pos)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.action_queue.append(.{ .move = pos });
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn depositItemsToBank(self: *CharacterBrain, api: *Server) !bool {
|
|
||||||
var character = api.findCharacter(self.name).?;
|
|
||||||
const action_queue = &self.action_queue;
|
|
||||||
|
|
||||||
// Deposit items and gold to bank if full
|
|
||||||
if (character.getItemCount() == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!character.position.eql(bank_position)) {
|
|
||||||
try action_queue.append(.{ .move = bank_position });
|
|
||||||
}
|
|
||||||
|
|
||||||
for (character.inventory.slice()) |slot| {
|
|
||||||
try action_queue.append(.{
|
|
||||||
.deposit_item = .{ .id = slot.id, .quantity = slot.quantity }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn depositIfFull(self: *CharacterBrain, api: *Server) !bool {
|
|
||||||
const character = api.findCharacter(self.name).?;
|
|
||||||
if (character.getItemCount() < character.inventory_max_items) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = try self.depositItemsToBank(api);
|
|
||||||
|
|
||||||
if (character.gold > 0) {
|
|
||||||
try self.action_queue.append(.{ .deposit_gold = @intCast(character.gold) });
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fightRoutine(self: *CharacterBrain, api: *Server, enemy_position: Position) !void {
|
|
||||||
if (try self.depositIfFull(api)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try self.moveIfNeeded(api, enemy_position)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.action_queue.append(.{ .fight = {} });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gatherRoutine(self: *CharacterBrain, api: *Server, resource_position: Position) !void {
|
|
||||||
if (try self.depositIfFull(api)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try self.moveIfNeeded(api, resource_position)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.action_queue.append(.{ .gather = {} });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn withdrawFromBank(self: *CharacterBrain, api: *Server, items: []const Server.Slot) !bool {
|
|
||||||
var character = api.findCharacter(self.name).?;
|
|
||||||
|
|
||||||
var has_all_items = true;
|
|
||||||
for (items) |item_quantity| {
|
|
||||||
const inventory_quantity = character.inventory.getQuantity(item_quantity.id);
|
|
||||||
if(inventory_quantity < item_quantity.quantity) {
|
|
||||||
has_all_items = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (has_all_items) return false;
|
|
||||||
|
|
||||||
if (try self.moveIfNeeded(api, bank_position)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (items) |item_quantity| {
|
|
||||||
const inventory_quantity = character.inventory.getQuantity(item_quantity.id);
|
|
||||||
if(inventory_quantity < item_quantity.quantity) {
|
|
||||||
try self.action_queue.append(.{ .withdraw_item = .{
|
|
||||||
.id = item_quantity.id,
|
|
||||||
.quantity = item_quantity.quantity - inventory_quantity,
|
|
||||||
}});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
if (self.task) |task| {
|
||||||
}
|
try task.queueActions(api, self.name, &self.action_queue);
|
||||||
|
|
||||||
fn craftItem(self: *CharacterBrain, api: *Server, workstation: Position, id: Server.ItemId, quantity: u64) !bool {
|
|
||||||
var character = api.findCharacter(self.name).?;
|
|
||||||
|
|
||||||
const inventory_quantity = character.inventory.getQuantity(id);
|
|
||||||
if (inventory_quantity >= quantity) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try self.moveIfNeeded(api, workstation)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.action_queue.append(.{ .craft_item = .{
|
|
||||||
.id = id,
|
|
||||||
.quantity = quantity - inventory_quantity
|
|
||||||
}});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn craftRoutine(self: *CharacterBrain, api: *Server, workstation: Position, id: Server.ItemId, quantity: u64) !void {
|
|
||||||
var character = api.findCharacter(self.name).?;
|
|
||||||
const inventory_quantity = character.inventory.getQuantity(id);
|
|
||||||
if (inventory_quantity >= quantity) {
|
|
||||||
if (try self.depositItemsToBank(api)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const code = api.getItemCode(id) orelse return error.InvalidItemId;
|
|
||||||
const target_item = try api.getItem(code) orelse return error.ItemNotFound;
|
|
||||||
if (target_item.craft == null) {
|
|
||||||
return error.NotCraftable;
|
|
||||||
}
|
|
||||||
|
|
||||||
const recipe = target_item.craft.?;
|
|
||||||
|
|
||||||
var needed_items = recipe.items;
|
|
||||||
for (needed_items.slice()) |*needed_item| {
|
|
||||||
needed_item.quantity *= quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try self.withdrawFromBank(api, needed_items.slice())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try self.craftItem(api, workstation, id, quantity)) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
lib/root.zig
30
lib/root.zig
@ -3,6 +3,7 @@ const Api = @import("artifacts-api");
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub const Brain = @import("./brain.zig");
|
pub const Brain = @import("./brain.zig");
|
||||||
|
pub const TaskGraph = @import("./task_graph.zig");
|
||||||
|
|
||||||
const Artificer = @This();
|
const Artificer = @This();
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ const server_down_retry_interval = 5; // minutes
|
|||||||
|
|
||||||
server: Api.Server,
|
server: Api.Server,
|
||||||
characters: std.ArrayList(Brain),
|
characters: std.ArrayList(Brain),
|
||||||
|
task_graph: TaskGraph,
|
||||||
|
|
||||||
paused_until: ?i64 = null,
|
paused_until: ?i64 = null,
|
||||||
|
|
||||||
@ -32,7 +34,8 @@ pub fn init(allocator: Allocator, token: []const u8) !Artificer {
|
|||||||
|
|
||||||
return Artificer{
|
return Artificer{
|
||||||
.server = server,
|
.server = server,
|
||||||
.characters = characters
|
.characters = characters,
|
||||||
|
.task_graph = TaskGraph.init(allocator)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +56,7 @@ pub fn step(self: *Artificer) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runNextActions(self.characters.items, &self.server) catch |err| switch (err) {
|
runNextActions(self.characters.items, &self.server) catch |err| switch (err) {
|
||||||
Api.Error.ServerUnavailable => {
|
Api.FetchError.ServerUnavailable => {
|
||||||
self.paused_until = std.time.timestamp() + std.time.ns_per_min * server_down_retry_interval;
|
self.paused_until = std.time.timestamp() + std.time.ns_per_min * server_down_retry_interval;
|
||||||
std.log.warn("Server is down, retrying in {}min", .{ server_down_retry_interval });
|
std.log.warn("Server is down, retrying in {}min", .{ server_down_retry_interval });
|
||||||
return;
|
return;
|
||||||
@ -62,26 +65,15 @@ pub fn step(self: *Artificer) !void {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (self.characters.items) |*brain| {
|
for (self.characters.items) |*brain| {
|
||||||
if (brain.action_queue.items.len > 0) continue;
|
|
||||||
|
|
||||||
if (brain.isTaskFinished()) {
|
|
||||||
if (try brain.depositItemsToBank(&self.server)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
brain.task = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (brain.task == null) {
|
if (brain.task == null) {
|
||||||
// var next_tasks = try task_tree.listNextTasks();
|
const character = self.server.findCharacter(brain.name).?;
|
||||||
// defer next_tasks.deinit();
|
if (character.task == null) {
|
||||||
//
|
brain.task = .{ .accept_task = .{} };
|
||||||
// if (next_tasks.items.len > 0) {
|
}
|
||||||
// const next_task_index = random.intRangeLessThan(usize, 0, next_tasks.items.len);
|
|
||||||
// brain.task = next_tasks.items[next_task_index];
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try brain.performTask(&self.server);
|
|
||||||
|
try brain.step(&self.server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
240
lib/task.zig
240
lib/task.zig
@ -1,6 +1,14 @@
|
|||||||
|
const std = @import("std");
|
||||||
const Api = @import("artifacts-api");
|
const Api = @import("artifacts-api");
|
||||||
const Position = Api.Position;
|
const Position = Api.Position;
|
||||||
|
|
||||||
|
const Action = @import("./action.zig").Action;
|
||||||
|
const ActionResult = @import("./action.zig").ActionResult;
|
||||||
|
const ItemId = Api.ItemId;
|
||||||
|
const Slot = Api.Slot;
|
||||||
|
|
||||||
|
const bank_position: Position = .{ .x = 4, .y = 1 }; // TODO: Figure this out dynamically
|
||||||
|
|
||||||
pub const UntilCondition = union(enum) {
|
pub const UntilCondition = union(enum) {
|
||||||
xp: u64,
|
xp: u64,
|
||||||
item: Api.ItemIdQuantity,
|
item: Api.ItemIdQuantity,
|
||||||
@ -22,18 +30,232 @@ pub const Task = union(enum) {
|
|||||||
target: Api.ItemIdQuantity,
|
target: Api.ItemIdQuantity,
|
||||||
progress: u64 = 0,
|
progress: u64 = 0,
|
||||||
},
|
},
|
||||||
|
accept_task: struct {
|
||||||
|
done: bool = false
|
||||||
|
},
|
||||||
|
|
||||||
pub fn isComplete(self: Task) bool {
|
pub fn isComplete(self: Task) bool {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.fight => |args| {
|
.fight => |args| args.progress >= args.until.item.quantity,
|
||||||
return args.progress >= args.until.item.quantity;
|
.gather => |args| args.progress >= args.until.item.quantity,
|
||||||
},
|
.craft => |args| args.progress >= args.target.quantity,
|
||||||
.gather => |args| {
|
.accept_task => |args| args.done
|
||||||
return args.progress >= args.until.item.quantity;
|
|
||||||
},
|
|
||||||
.craft => |args| {
|
|
||||||
return args.progress >= args.target.quantity;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn onActionCompleted(self: *Task, result: ActionResult) void {
|
||||||
|
switch (self.*) {
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.gather => |*args| {
|
||||||
|
if (result.get(.gather)) |r| {
|
||||||
|
const gather_resutl: Api.Server.GatherResult = r;
|
||||||
|
const items = gather_resutl.details.items;
|
||||||
|
|
||||||
|
args.progress += items.getQuantity(args.until.item.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.craft => |*args| {
|
||||||
|
if (result.get(.craft_item)) |r| {
|
||||||
|
const craft_result: Api.Server.CraftResult = r;
|
||||||
|
const items = craft_result.details.items;
|
||||||
|
|
||||||
|
args.progress += items.getQuantity(args.target.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.accept_task => {
|
||||||
|
// TODO:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queueActions(self: Task, api: *Api.Server, name: []const u8, action_queue: *std.ArrayList(Action)) !void {
|
||||||
|
const ctx = TaskContext{
|
||||||
|
.api = api,
|
||||||
|
.name = name,
|
||||||
|
.action_queue = action_queue
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (self) {
|
||||||
|
.fight => |args| {
|
||||||
|
try ctx.fightRoutine(args.at);
|
||||||
|
},
|
||||||
|
.gather => |args| {
|
||||||
|
try ctx.gatherRoutine(args.at);
|
||||||
|
},
|
||||||
|
.craft => |args| {
|
||||||
|
try ctx.craftRoutine(args.at, args.target.id, args.target.quantity);
|
||||||
|
},
|
||||||
|
.accept_task => {
|
||||||
|
// TODO:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TaskContext = struct {
|
||||||
|
api: *Api.Server,
|
||||||
|
name: []const u8,
|
||||||
|
action_queue: *std.ArrayList(Action),
|
||||||
|
|
||||||
|
fn moveIfNeeded(self: TaskContext, pos: Position) !bool {
|
||||||
|
const character = self.api.findCharacter(self.name).?;
|
||||||
|
|
||||||
|
if (character.position.eql(pos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.action_queue.append(.{ .move = pos });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn depositItemsToBank(self: TaskContext) !bool {
|
||||||
|
var character = self.api.findCharacter(self.name).?;
|
||||||
|
const action_queue = self.action_queue;
|
||||||
|
|
||||||
|
// Deposit items and gold to bank if full
|
||||||
|
if (character.getItemCount() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!character.position.eql(bank_position)) {
|
||||||
|
try action_queue.append(.{ .move = bank_position });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (character.inventory.slice()) |slot| {
|
||||||
|
try action_queue.append(.{
|
||||||
|
.deposit_item = .{ .id = slot.id, .quantity = slot.quantity }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn depositIfFull(self: TaskContext) !bool {
|
||||||
|
const character = self.api.findCharacter(self.name).?;
|
||||||
|
if (character.getItemCount() < character.inventory_max_items) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try depositItemsToBank(self);
|
||||||
|
|
||||||
|
if (character.gold > 0) {
|
||||||
|
try self.action_queue.append(.{ .deposit_gold = @intCast(character.gold) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fightRoutine(self: TaskContext, enemy_position: Position) !void {
|
||||||
|
if (try self.depositIfFull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try self.moveIfNeeded(enemy_position)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.action_queue.append(.{ .fight = {} });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gatherRoutine(self: TaskContext, resource_position: Position) !void {
|
||||||
|
if (try self.depositIfFull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try self.moveIfNeeded(resource_position)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.action_queue.append(.{ .gather = {} });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn withdrawFromBank(self: TaskContext, items: []const Slot) !bool {
|
||||||
|
var character = self.api.findCharacter(self.name).?;
|
||||||
|
|
||||||
|
var has_all_items = true;
|
||||||
|
for (items) |item_quantity| {
|
||||||
|
const inventory_quantity = character.inventory.getQuantity(item_quantity.id);
|
||||||
|
if(inventory_quantity < item_quantity.quantity) {
|
||||||
|
has_all_items = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_all_items) return false;
|
||||||
|
|
||||||
|
if (try self.moveIfNeeded(bank_position)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (items) |item_quantity| {
|
||||||
|
const inventory_quantity = character.inventory.getQuantity(item_quantity.id);
|
||||||
|
if(inventory_quantity < item_quantity.quantity) {
|
||||||
|
try self.action_queue.append(.{ .withdraw_item = .{
|
||||||
|
.id = item_quantity.id,
|
||||||
|
.quantity = item_quantity.quantity - inventory_quantity,
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn craftItem(self: TaskContext, workstation: Position, id: ItemId, quantity: u64) !bool {
|
||||||
|
var character = self.api.findCharacter(self.name).?;
|
||||||
|
|
||||||
|
const inventory_quantity = character.inventory.getQuantity(id);
|
||||||
|
if (inventory_quantity >= quantity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try self.moveIfNeeded(workstation)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.action_queue.append(.{ .craft_item = .{
|
||||||
|
.id = id,
|
||||||
|
.quantity = quantity - inventory_quantity
|
||||||
|
}});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn craftRoutine(self: TaskContext, workstation: Position, id: ItemId, quantity: u64) !void {
|
||||||
|
var character = self.api.findCharacter(self.name).?;
|
||||||
|
const inventory_quantity = character.inventory.getQuantity(id);
|
||||||
|
if (inventory_quantity >= quantity) {
|
||||||
|
if (try self.depositItemsToBank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = self.api.getItemCode(id) orelse return error.InvalidItemId;
|
||||||
|
const target_item = try self.api.getItem(code) orelse return error.ItemNotFound;
|
||||||
|
if (target_item.craft == null) {
|
||||||
|
return error.NotCraftable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipe = target_item.craft.?;
|
||||||
|
|
||||||
|
var needed_items = recipe.items;
|
||||||
|
for (needed_items.slice()) |*needed_item| {
|
||||||
|
needed_item.quantity *= quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try self.withdrawFromBank(needed_items.slice())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try self.craftItem(workstation, id, quantity)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user