add item crating
This commit is contained in:
parent
7a1b7971a9
commit
b33754efb8
@ -160,6 +160,38 @@ pub const EquipmentSlot = enum {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Skill = enum {
|
||||||
|
weaponcrafting,
|
||||||
|
gearcrafting,
|
||||||
|
jewelrycrafting,
|
||||||
|
cooking,
|
||||||
|
woodcutting,
|
||||||
|
mining,
|
||||||
|
|
||||||
|
fn parse(str: []const u8) ?Skill {
|
||||||
|
const eql = std.mem.eql;
|
||||||
|
const mapping = .{
|
||||||
|
.{ "weaponcrafting" , .weaponcrafting },
|
||||||
|
.{ "gearcrafting" , .gearcrafting },
|
||||||
|
.{ "jewelrycrafting", .jewelrycrafting },
|
||||||
|
.{ "cooking" , .cooking },
|
||||||
|
.{ "woodcutting" , .woodcutting },
|
||||||
|
.{ "mining" , .mining },
|
||||||
|
};
|
||||||
|
if (mapping.len != @typeInfo(Skill).Enum.fields.len) {
|
||||||
|
@compileLog("Mapping is not exhaustive");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline for (mapping) |mapping_entry| {
|
||||||
|
if (eql(u8, str, mapping_entry[0])) {
|
||||||
|
return mapping_entry[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const ServerStatus = struct {
|
const ServerStatus = struct {
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
status: []const u8,
|
status: []const u8,
|
||||||
@ -250,35 +282,32 @@ pub const Cooldown = struct {
|
|||||||
|
|
||||||
fn parse(str: []const u8) ?Reason {
|
fn parse(str: []const u8) ?Reason {
|
||||||
const eql = std.mem.eql;
|
const eql = std.mem.eql;
|
||||||
if (eql(u8, str, "movement")) {
|
const mapping = .{
|
||||||
return .movement;
|
.{ "movement" , .movement },
|
||||||
} else if (eql(u8, str, "fight")) {
|
.{ "fight" , .fight },
|
||||||
return .fight;
|
.{ "crafting" , .crafting },
|
||||||
} else if (eql(u8, str, "crafting")) {
|
.{ "gathering" , .gathering },
|
||||||
return .crafting;
|
.{ "buy_ge" , .buy_ge },
|
||||||
} else if (eql(u8, str, "gathering")) {
|
.{ "sell_ge" , .sell_ge },
|
||||||
return .gathering;
|
.{ "delete_item" , .delete_item },
|
||||||
} else if (eql(u8, str, "buy_ge")) {
|
.{ "deposit_bank" , .deposit_bank },
|
||||||
return .buy_ge;
|
.{ "withdraw_bank", .withdraw_bank },
|
||||||
} else if (eql(u8, str, "sell_ge")) {
|
.{ "equip" , .equip },
|
||||||
return .sell_ge;
|
.{ "unequip" , .unequip },
|
||||||
} else if (eql(u8, str, "delete_item")) {
|
.{ "task" , .task },
|
||||||
return .delete_item;
|
.{ "recycling" , .recycling },
|
||||||
} else if (eql(u8, str, "deposit_bank")) {
|
};
|
||||||
return .deposit_bank;
|
if (mapping.len != @typeInfo(Reason).Enum.fields.len) {
|
||||||
} else if (eql(u8, str, "withdraw_bank")) {
|
@compileLog("Mapping is not exhaustive");
|
||||||
return .withdraw_bank;
|
|
||||||
} else if (eql(u8, str, "equip")) {
|
|
||||||
return .equip;
|
|
||||||
} else if (eql(u8, str, "unequip")) {
|
|
||||||
return .unequip;
|
|
||||||
} else if (eql(u8, str, "task")) {
|
|
||||||
return .task;
|
|
||||||
} else if (eql(u8, str, "recycling")) {
|
|
||||||
return .recycling;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline for (mapping) |mapping_entry| {
|
||||||
|
if (eql(u8, str, mapping_entry[0])) {
|
||||||
|
return mapping_entry[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -365,44 +394,52 @@ pub const FightResult = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ItemQuantity = struct {
|
pub const ItemIdQuantity = struct {
|
||||||
id: ItemId,
|
id: ItemId,
|
||||||
quantity: u64,
|
quantity: u64,
|
||||||
|
|
||||||
pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap) !ItemQuantity {
|
pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap) !ItemIdQuantity {
|
||||||
const code = try json_utils.getStringRequired(obj, "code");
|
const code = try json_utils.getStringRequired(obj, "code");
|
||||||
const quantity = try json_utils.getIntegerRequired(obj, "quantity");
|
const quantity = try json_utils.getIntegerRequired(obj, "quantity");
|
||||||
|
if (quantity < 1) return error.InvalidQuantity;
|
||||||
|
|
||||||
return ItemQuantity{
|
return ItemIdQuantity{
|
||||||
.id = try api.getItemId(code),
|
.id = try api.getItemId(code),
|
||||||
.quantity = @intCast(quantity)
|
.quantity = @intCast(quantity)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SkillDetails = struct {
|
const BoundedItems = std.BoundedArray(ItemIdQuantity, 8);
|
||||||
|
fn parseSimpleItemList(api: *ArtifactsAPI, array: json.Array) !BoundedItems {
|
||||||
|
var items = BoundedItems.init(0) catch unreachable;
|
||||||
|
|
||||||
|
for (array.items) |item_value| {
|
||||||
|
const item_obj = json_utils.asObject(item_value) orelse return error.MissingProperty;
|
||||||
|
|
||||||
|
try items.append(try ItemIdQuantity.parse(api, item_obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SkillResultDetails = struct {
|
||||||
xp: i64,
|
xp: i64,
|
||||||
items: std.BoundedArray(ItemQuantity, 8),
|
items: BoundedItems,
|
||||||
|
|
||||||
fn parse(api: *ArtifactsAPI, obj: json.ObjectMap) !SkillDetails {
|
fn parse(api: *ArtifactsAPI, obj: json.ObjectMap) !SkillResultDetails {
|
||||||
var items = std.BoundedArray(ItemQuantity, 8).init(0) catch unreachable;
|
const items = json_utils.getArray(obj, "items") orelse return error.MissingProperty;
|
||||||
const items_obj = json_utils.getArray(obj, "items") orelse return error.MissingProperty;
|
|
||||||
for (items_obj.items) |item_value| {
|
|
||||||
const item_obj = json_utils.asObject(item_value) orelse return error.MissingProperty;
|
|
||||||
|
|
||||||
try items.append(ItemQuantity.parse(api, item_obj));
|
return SkillResultDetails{
|
||||||
}
|
|
||||||
|
|
||||||
return SkillDetails{
|
|
||||||
.xp = try json_utils.getIntegerRequired(obj, "xp"),
|
.xp = try json_utils.getIntegerRequired(obj, "xp"),
|
||||||
.items = items,
|
.items = try parseSimpleItemList(api, items),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const GatherResult = struct {
|
pub const GatherResult = struct {
|
||||||
cooldown: Cooldown,
|
cooldown: Cooldown,
|
||||||
details: SkillDetails,
|
details: SkillResultDetails,
|
||||||
|
|
||||||
pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap) !GatherResult {
|
pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap) !GatherResult {
|
||||||
const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty;
|
const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty;
|
||||||
@ -410,7 +447,7 @@ pub const GatherResult = struct {
|
|||||||
|
|
||||||
return GatherResult{
|
return GatherResult{
|
||||||
.cooldown = try Cooldown.parse(cooldown),
|
.cooldown = try Cooldown.parse(cooldown),
|
||||||
.details = try SkillDetails.parse(api, details)
|
.details = try SkillResultDetails.parse(api, details)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -531,15 +568,11 @@ pub const ItemTransactionResult = struct {
|
|||||||
else => null
|
else => null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: ItemTransactionResult) void {
|
|
||||||
_ = self;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CraftResult = struct {
|
pub const CraftResult = struct {
|
||||||
cooldown: Cooldown,
|
cooldown: Cooldown,
|
||||||
details: SkillDetails,
|
details: SkillResultDetails,
|
||||||
|
|
||||||
pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap) !CraftResult {
|
pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap) !CraftResult {
|
||||||
const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty;
|
const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty;
|
||||||
@ -547,7 +580,7 @@ pub const CraftResult = struct {
|
|||||||
|
|
||||||
return CraftResult{
|
return CraftResult{
|
||||||
.cooldown = try Cooldown.parse(cooldown),
|
.cooldown = try Cooldown.parse(cooldown),
|
||||||
.details = try SkillDetails.parse(api, details)
|
.details = try SkillResultDetails.parse(api, details)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,6 +703,72 @@ pub const MapResult = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Item = struct {
|
||||||
|
pub const Recipe = struct {
|
||||||
|
skill: Skill,
|
||||||
|
level: u64,
|
||||||
|
quantity: u64,
|
||||||
|
items: BoundedItems,
|
||||||
|
|
||||||
|
pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap) !Recipe {
|
||||||
|
const skill = json_utils.getString(obj, "skill") orelse return error.MissingProperty;
|
||||||
|
const level = json_utils.getInteger(obj, "level") orelse return error.MissingProperty;
|
||||||
|
if (level < 1) return error.InvalidLevel;
|
||||||
|
|
||||||
|
const quantity = json_utils.getInteger(obj, "quantity") orelse return error.MissingProperty;
|
||||||
|
if (quantity < 1) return error.InvalidQuantity;
|
||||||
|
|
||||||
|
const items = json_utils.getArray(obj, "items") orelse return error.MissingProperty;
|
||||||
|
|
||||||
|
return Recipe{
|
||||||
|
.skill = Skill.parse(skill) orelse return error.InvalidSkill,
|
||||||
|
.level = @intCast(level),
|
||||||
|
.quantity = @intCast(quantity),
|
||||||
|
.items = try parseSimpleItemList(api, items)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
allocator: Allocator,
|
||||||
|
name: []u8,
|
||||||
|
code: []u8,
|
||||||
|
level: u64,
|
||||||
|
type: []u8,
|
||||||
|
subtype: []u8,
|
||||||
|
description: []u8,
|
||||||
|
craft: ?Recipe,
|
||||||
|
// TODO: effects
|
||||||
|
// TODO: Grand exchange
|
||||||
|
|
||||||
|
pub fn parse(api: *ArtifactsAPI, obj: json.ObjectMap, allocator: Allocator) !Item {
|
||||||
|
const item_obj = json_utils.getObject(obj, "item") orelse return error.MissingProperty;
|
||||||
|
|
||||||
|
const level = json_utils.getInteger(item_obj, "level") orelse return error.MissingProperty;
|
||||||
|
if (level < 1) return error.InvalidLevel;
|
||||||
|
|
||||||
|
const craft = json_utils.getObject(item_obj, "craft");
|
||||||
|
|
||||||
|
return Item{
|
||||||
|
.allocator = allocator,
|
||||||
|
.name = (try json_utils.dupeString(allocator, item_obj, "name")) orelse return error.MissingProperty,
|
||||||
|
.code = (try json_utils.dupeString(allocator, item_obj, "code")) orelse return error.MissingProperty,
|
||||||
|
.level = @intCast(level),
|
||||||
|
.type = (try json_utils.dupeString(allocator, item_obj, "type")) orelse return error.MissingProperty,
|
||||||
|
.subtype = (try json_utils.dupeString(allocator, item_obj, "subtype")) orelse return error.MissingProperty,
|
||||||
|
.description = (try json_utils.dupeString(allocator, item_obj, "description")) orelse return error.MissingProperty,
|
||||||
|
.craft = if (craft != null) try Recipe.parse(api, craft.?) else null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Item) void {
|
||||||
|
self.allocator.free(self.name);
|
||||||
|
self.allocator.free(self.code);
|
||||||
|
self.allocator.free(self.type);
|
||||||
|
self.allocator.free(self.subtype);
|
||||||
|
self.allocator.free(self.description);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const ArtifactsFetchResult = struct {
|
pub const ArtifactsFetchResult = struct {
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
status: std.http.Status,
|
status: std.http.Status,
|
||||||
@ -711,6 +810,8 @@ const FetchOptions = struct {
|
|||||||
path: []const u8,
|
path: []const u8,
|
||||||
payload: ?[]const u8 = null,
|
payload: ?[]const u8 = null,
|
||||||
|
|
||||||
|
query: ?[]const u8 = null,
|
||||||
|
|
||||||
page: ?u64 = null,
|
page: ?u64 = null,
|
||||||
page_size: ?u64 = null,
|
page_size: ?u64 = null,
|
||||||
paginated: bool = false
|
paginated: bool = false
|
||||||
@ -743,12 +844,23 @@ fn fetch(self: *ArtifactsAPI, options: FetchOptions) APIError!ArtifactsFetchResu
|
|||||||
var fetch_results = std.ArrayList(json.Value).init(arena.allocator());
|
var fetch_results = std.ArrayList(json.Value).init(arena.allocator());
|
||||||
|
|
||||||
while (true) : (current_page += 1) {
|
while (true) : (current_page += 1) {
|
||||||
var query_params: ?[]u8 = null;
|
var pagination_params: ?[]u8 = null;
|
||||||
defer if (query_params) |str| self.allocator.free(str);
|
defer if (pagination_params) |str| self.allocator.free(str);
|
||||||
|
|
||||||
if (options.paginated) {
|
if (options.paginated) {
|
||||||
query_params = try allocPaginationParams(self.allocator, current_page, options.page_size);
|
pagination_params = try allocPaginationParams(self.allocator, current_page, options.page_size);
|
||||||
uri.query = .{ .raw = query_params.? };
|
}
|
||||||
|
|
||||||
|
if (options.query != null and pagination_params != null) {
|
||||||
|
const combined = try std.mem.join(self.allocator, "&", &.{ options.query.?, pagination_params.? });
|
||||||
|
self.allocator.free(pagination_params.?);
|
||||||
|
pagination_params = combined;
|
||||||
|
|
||||||
|
uri.query = .{ .raw = combined };
|
||||||
|
} else if (pagination_params != null) {
|
||||||
|
uri.query = .{ .raw = pagination_params.? };
|
||||||
|
} else if (options.query != null) {
|
||||||
|
uri.query = .{ .raw = options.query.? };
|
||||||
}
|
}
|
||||||
|
|
||||||
var response_storage = std.ArrayList(u8).init(arena.allocator());
|
var response_storage = std.ArrayList(u8).init(arena.allocator());
|
||||||
@ -791,6 +903,7 @@ fn fetch(self: *ArtifactsAPI, options: FetchOptions) APIError!ArtifactsFetchResu
|
|||||||
|
|
||||||
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 APIError.ParseFailed;
|
||||||
|
if (total_pages_i64 < 1) return APIError.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 APIError.ParseFailed;
|
||||||
@ -835,13 +948,6 @@ fn handleFetchError(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == .not_found) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (status != .ok) {
|
|
||||||
return APIError.RequestFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -864,6 +970,12 @@ fn fetchOptionalObject(
|
|||||||
if (handleFetchError(result.status, Error, parseError)) |error_value| {
|
if (handleFetchError(result.status, Error, parseError)) |error_value| {
|
||||||
return error_value;
|
return error_value;
|
||||||
}
|
}
|
||||||
|
if (result.status == .not_found) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (result.status != .ok) {
|
||||||
|
return APIError.RequestFailed;
|
||||||
|
}
|
||||||
|
|
||||||
if (result.body == null) {
|
if (result.body == null) {
|
||||||
return APIError.ParseFailed;
|
return APIError.ParseFailed;
|
||||||
@ -886,7 +998,26 @@ fn fetchObject(
|
|||||||
return result orelse return APIError.RequestFailed;
|
return result orelse return APIError.RequestFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetchArray(
|
fn ObjectList(Object: type) type {
|
||||||
|
return struct {
|
||||||
|
list: std.ArrayList(Object),
|
||||||
|
|
||||||
|
pub fn deinit(self: @This()) void {
|
||||||
|
for (self.list.items) |*item| {
|
||||||
|
if (std.meta.hasMethod(@TypeOf(item), "deinit")) {
|
||||||
|
item.deinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.deinitList();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinitList(self: @This()) void {
|
||||||
|
self.list.deinit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetchOptionalArray(
|
||||||
self: *ArtifactsAPI,
|
self: *ArtifactsAPI,
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
Error: type,
|
Error: type,
|
||||||
@ -895,7 +1026,7 @@ fn fetchArray(
|
|||||||
parseObject: anytype,
|
parseObject: anytype,
|
||||||
parseObjectArgs: anytype,
|
parseObjectArgs: anytype,
|
||||||
fetchOptions: FetchOptions
|
fetchOptions: FetchOptions
|
||||||
) Error!std.ArrayList(Object) {
|
) Error!?ObjectList(Object) {
|
||||||
if (@typeInfo(@TypeOf(parseObject)) != .Fn) {
|
if (@typeInfo(@TypeOf(parseObject)) != .Fn) {
|
||||||
@compileError("`parseObject` must be a function");
|
@compileError("`parseObject` must be a function");
|
||||||
}
|
}
|
||||||
@ -906,23 +1037,56 @@ fn fetchArray(
|
|||||||
if (handleFetchError(result.status, Error, parseError)) |error_value| {
|
if (handleFetchError(result.status, Error, parseError)) |error_value| {
|
||||||
return error_value;
|
return error_value;
|
||||||
}
|
}
|
||||||
|
if (result.status == .not_found) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (result.status != .ok) {
|
||||||
|
return APIError.RequestFailed;
|
||||||
|
}
|
||||||
|
|
||||||
if (result.body == null) {
|
if (result.body == null) {
|
||||||
return APIError.ParseFailed;
|
return APIError.ParseFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
var array = std.ArrayList(Object).init(allocator);
|
var object_array = ObjectList(Object){
|
||||||
errdefer array.deinit();
|
.list = std.ArrayList(Object).init(allocator)
|
||||||
|
};
|
||||||
|
errdefer object_array.deinit();
|
||||||
|
// var array = std.ArrayList(Object).init(allocator);
|
||||||
|
// errdefer {
|
||||||
|
// if (std.meta.hasFn(Object, "deinit")) {
|
||||||
|
// for (array.items) |item| {
|
||||||
|
// if (@typeInfo(Object.deinit).Fn.args.len == 1) {
|
||||||
|
// item.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 APIError.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 APIError.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 APIError.ParseFailed;
|
||||||
array.append(parsed_item) catch return APIError.OutOfMemory;
|
object_array.list.append(parsed_item) catch return APIError.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array;
|
return object_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetchArray(
|
||||||
|
self: *ArtifactsAPI,
|
||||||
|
allocator: Allocator,
|
||||||
|
Error: type,
|
||||||
|
parseError: ?fn (status: std.http.Status) ?Error,
|
||||||
|
Object: type,
|
||||||
|
parseObject: anytype,
|
||||||
|
parseObjectArgs: anytype,
|
||||||
|
fetchOptions: FetchOptions
|
||||||
|
) Error!ObjectList(Object) {
|
||||||
|
const result = try self.fetchOptionalArray(allocator, Error, parseError, Object, parseObject, parseObjectArgs, fetchOptions);
|
||||||
|
return result orelse return APIError.RequestFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setServer(self: *ArtifactsAPI, url: []const u8) !void {
|
pub fn setServer(self: *ArtifactsAPI, url: []const u8) !void {
|
||||||
@ -995,7 +1159,7 @@ pub fn getCharacter(self: *ArtifactsAPI, allocator: Allocator, name: []const u8)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listMyCharacters(self: *ArtifactsAPI, allocator: Allocator) APIError!std.ArrayList(Character) {
|
pub fn listMyCharacters(self: *ArtifactsAPI, allocator: Allocator) APIError!ObjectList(Character) {
|
||||||
return self.fetchArray(
|
return self.fetchArray(
|
||||||
allocator,
|
allocator,
|
||||||
APIError,
|
APIError,
|
||||||
@ -1205,20 +1369,48 @@ pub fn getBankGold(self: *ArtifactsAPI) APIError!u64 {
|
|||||||
|
|
||||||
const data = json_utils.asObject(result.body.?) orelse return APIError.RequestFailed;
|
const data = json_utils.asObject(result.body.?) orelse return APIError.RequestFailed;
|
||||||
const quantity = json_utils.getInteger(data, "quantity") orelse return APIError.ParseFailed;
|
const quantity = json_utils.getInteger(data, "quantity") orelse return APIError.ParseFailed;
|
||||||
|
if (quantity < 0) return APIError.ParseFailed;
|
||||||
|
|
||||||
return @intCast(quantity);
|
return @intCast(quantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getBankItems(self: *ArtifactsAPI, allocator: Allocator) APIError!std.ArrayList(ItemQuantity) {
|
pub fn getBankItems(self: *ArtifactsAPI, allocator: Allocator) APIError!ObjectList(ItemIdQuantity) {
|
||||||
return self.fetchArray(
|
return self.fetchArray(
|
||||||
allocator,
|
allocator,
|
||||||
APIError,
|
APIError,
|
||||||
null,
|
null,
|
||||||
ItemQuantity,
|
ItemIdQuantity,
|
||||||
ItemQuantity.parse, .{},
|
ItemIdQuantity.parse, .{},
|
||||||
.{ .method = .GET, .path = "/my/bank/items", .paginated = true }
|
.{ .method = .GET, .path = "/my/bank/items", .paginated = true }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getBankItemQuantity(self: *ArtifactsAPI, code: []const u8) APIError!?u64 {
|
||||||
|
const query = try std.fmt.allocPrint(self.allocator, "item_code={s}", .{code});
|
||||||
|
defer self.allocator.free(query);
|
||||||
|
|
||||||
|
const maybe_items = try self.fetchOptionalArray(
|
||||||
|
self.allocator,
|
||||||
|
APIError,
|
||||||
|
null,
|
||||||
|
ItemIdQuantity,
|
||||||
|
ItemIdQuantity.parse, .{},
|
||||||
|
.{ .method = .GET, .path = "/my/bank/items", .query = query, .paginated = true }
|
||||||
|
);
|
||||||
|
if (maybe_items == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = maybe_items.?;
|
||||||
|
defer items.deinit();
|
||||||
|
|
||||||
|
const list_items = items.list.items;
|
||||||
|
assert(list_items.len == 1);
|
||||||
|
assert(list_items[0].id == try self.getItemId(code));
|
||||||
|
|
||||||
|
return list_items[0].quantity;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getMap(self: *ArtifactsAPI, allocator: Allocator, x: i64, y: i64) APIError!?MapResult {
|
pub fn getMap(self: *ArtifactsAPI, allocator: Allocator, x: i64, y: i64) APIError!?MapResult {
|
||||||
const path = try std.fmt.allocPrint(self.allocator, "/maps/{}/{}", .{x, y});
|
const path = try std.fmt.allocPrint(self.allocator, "/maps/{}/{}", .{x, y});
|
||||||
defer self.allocator.free(path);
|
defer self.allocator.free(path);
|
||||||
@ -1243,6 +1435,19 @@ pub fn getMaps(self: *ArtifactsAPI, allocator: Allocator) APIError!ObjectList(Ma
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getItem(self: *ArtifactsAPI, allocator: Allocator, code: []const u8) APIError!?Item {
|
||||||
|
const path = try std.fmt.allocPrint(self.allocator, "/items/{s}", .{code});
|
||||||
|
defer self.allocator.free(path);
|
||||||
|
|
||||||
|
return self.fetchOptionalObject(
|
||||||
|
APIError,
|
||||||
|
null,
|
||||||
|
Item,
|
||||||
|
Item.parse, .{ allocator },
|
||||||
|
.{ .method = .GET, .path = path }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
test "parse date time" {
|
test "parse date time" {
|
||||||
try std.testing.expectEqual(1723069394.105, parseDateTime("2024-08-07T22:23:14.105Z").?);
|
try std.testing.expectEqual(1723069394.105, parseDateTime("2024-08-07T22:23:14.105Z").?);
|
||||||
}
|
}
|
||||||
|
@ -102,12 +102,15 @@ pub const Inventory = struct {
|
|||||||
|
|
||||||
pub const Slot = struct {
|
pub const Slot = struct {
|
||||||
id: ?ItemId,
|
id: ?ItemId,
|
||||||
quantity: i64,
|
quantity: u64,
|
||||||
|
|
||||||
fn parse(api: *ArtifactsAPI, slot_obj: json.ObjectMap) !Slot {
|
fn parse(api: *ArtifactsAPI, slot_obj: json.ObjectMap) !Slot {
|
||||||
|
const quantity = try json_utils.getIntegerRequired(slot_obj, "quantity");
|
||||||
|
if (quantity < 0) return error.InvalidQuantity;
|
||||||
|
|
||||||
return Slot{
|
return Slot{
|
||||||
.id = try getItemId(api, slot_obj, "code"),
|
.id = try getItemId(api, slot_obj, "code"),
|
||||||
.quantity = try json_utils.getIntegerRequired(slot_obj, "quantity"),
|
.quantity = @intCast(quantity),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -140,7 +143,7 @@ pub const Inventory = struct {
|
|||||||
const slot = self.findSlot(id) orelse unreachable;
|
const slot = self.findSlot(id) orelse unreachable;
|
||||||
assert(slot.quantity >= quantity);
|
assert(slot.quantity >= quantity);
|
||||||
|
|
||||||
slot.quantity -= @intCast(quantity);
|
slot.quantity -= quantity;
|
||||||
if (slot.quantity == 0) {
|
if (slot.quantity == 0) {
|
||||||
slot.id = null;
|
slot.id = null;
|
||||||
}
|
}
|
||||||
@ -148,7 +151,7 @@ pub const Inventory = struct {
|
|||||||
|
|
||||||
pub fn addItem(self: *Inventory, id: ItemId, quantity: u64) void {
|
pub fn addItem(self: *Inventory, id: ItemId, quantity: u64) void {
|
||||||
if (self.findSlot(id)) |slot| {
|
if (self.findSlot(id)) |slot| {
|
||||||
slot.quantity += @intCast(quantity);
|
slot.quantity += quantity;
|
||||||
} else {
|
} else {
|
||||||
var empty_slot: ?*Slot = null;
|
var empty_slot: ?*Slot = null;
|
||||||
for (&self.slots) |*slot| {
|
for (&self.slots) |*slot| {
|
||||||
@ -159,9 +162,29 @@ pub const Inventory = struct {
|
|||||||
|
|
||||||
assert(empty_slot != null);
|
assert(empty_slot != null);
|
||||||
empty_slot.?.id = id;
|
empty_slot.?.id = id;
|
||||||
empty_slot.?.quantity = @intCast(quantity);
|
empty_slot.?.quantity = quantity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn addItems(self: *Inventory, items: []const ArtifactsAPI.ItemIdQuantity) void {
|
||||||
|
for (items) |item| {
|
||||||
|
self.addItem(item.id, item.quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeItems(self: *Inventory, items: []const ArtifactsAPI.ItemIdQuantity) void {
|
||||||
|
for (items) |item| {
|
||||||
|
self.removeItem(item.id, item.quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getItem(self: *Inventory, id: ItemId) u64 {
|
||||||
|
if (self.findSlot(id)) |slot| {
|
||||||
|
return slot.quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
arena: *std.heap.ArenaAllocator,
|
arena: *std.heap.ArenaAllocator,
|
||||||
@ -249,10 +272,10 @@ pub fn deinit(self: *Character) void {
|
|||||||
child_allocator.destroy(self.arena);
|
child_allocator.destroy(self.arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getItemCount(self: *const Character) u32 {
|
pub fn getItemCount(self: *const Character) u64 {
|
||||||
var count: u32 = 0;
|
var count: u64 = 0;
|
||||||
for (self.inventory.slots) |slot| {
|
for (self.inventory.slots) |slot| {
|
||||||
count += @intCast(slot.quantity);
|
count += slot.quantity;
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
269
src/main.zig
269
src/main.zig
@ -25,8 +25,10 @@ const QueuedAction = union(enum) {
|
|||||||
move: Position,
|
move: Position,
|
||||||
attack,
|
attack,
|
||||||
gather,
|
gather,
|
||||||
depositGold: u64,
|
deposit_gold: u64,
|
||||||
depositItem: struct { id: ArtifactsAPI.ItemId, quantity: u64 },
|
deposit_item: ArtifactsAPI.ItemIdQuantity,
|
||||||
|
withdraw_item: ArtifactsAPI.ItemIdQuantity,
|
||||||
|
craft_item: ArtifactsAPI.ItemIdQuantity,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActionQueue = std.ArrayList(QueuedAction);
|
const ActionQueue = std.ArrayList(QueuedAction);
|
||||||
@ -35,6 +37,10 @@ const ManagedCharacter = struct {
|
|||||||
character: ArtifactsAPI.Character,
|
character: ArtifactsAPI.Character,
|
||||||
action_queue: ActionQueue,
|
action_queue: ActionQueue,
|
||||||
cooldown_expires_at: f64,
|
cooldown_expires_at: f64,
|
||||||
|
|
||||||
|
pub fn position(self: *const ManagedCharacter) Position {
|
||||||
|
return Position{ .x = self.character.x, .y = self.character.y };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn currentTime() f64 {
|
fn currentTime() f64 {
|
||||||
@ -46,11 +52,15 @@ const Manager = struct {
|
|||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
characters: std.ArrayList(ManagedCharacter),
|
characters: std.ArrayList(ManagedCharacter),
|
||||||
api: *ArtifactsAPI,
|
api: *ArtifactsAPI,
|
||||||
|
known_items: std.StringHashMap(ArtifactsAPI.Item),
|
||||||
|
|
||||||
|
bank_position: Position = .{ .x = 4, .y = 1 },
|
||||||
|
|
||||||
fn init(allocator: Allocator, api: *ArtifactsAPI) Manager {
|
fn init(allocator: Allocator, api: *ArtifactsAPI) Manager {
|
||||||
return Manager{
|
return Manager{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.api = api,
|
.api = api,
|
||||||
|
.known_items = std.StringHashMap(ArtifactsAPI.Item).init(allocator),
|
||||||
.characters = std.ArrayList(ManagedCharacter).init(allocator),
|
.characters = std.ArrayList(ManagedCharacter).init(allocator),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -92,28 +102,74 @@ const Manager = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: Manager) void {
|
fn getItem(self: *Manager, code: []const u8) !?ArtifactsAPI.Item {
|
||||||
|
if (self.known_items.get(code)) |item| {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maybe_item = try self.api.getItem(self.allocator, code);
|
||||||
|
if (maybe_item == null) {
|
||||||
|
std.log.warn("attempt to get item '{s}' which does not exist", .{code});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = maybe_item.?;
|
||||||
|
try self.known_items.putNoClobber(item.code, item);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: *Manager) void {
|
||||||
for (self.characters.items) |managed_character| {
|
for (self.characters.items) |managed_character| {
|
||||||
managed_character.action_queue.deinit();
|
managed_character.action_queue.deinit();
|
||||||
}
|
}
|
||||||
self.characters.deinit();
|
self.characters.deinit();
|
||||||
|
|
||||||
|
var known_items_iter = self.known_items.valueIterator();
|
||||||
|
while (known_items_iter.next()) |item| {
|
||||||
|
item.deinit();
|
||||||
|
}
|
||||||
|
self.known_items.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getWorkstation(self: *const Manager, skill: ArtifactsAPI.Skill) Position {
|
||||||
|
_ = self;
|
||||||
|
// TODO: Find workstation using map endpoint
|
||||||
|
|
||||||
|
return switch (skill) {
|
||||||
|
.weaponcrafting => Position{ .x = 2, .y = 1 },
|
||||||
|
.gearcrafting => Position{ .x = 3, .y = 1 },
|
||||||
|
.jewelrycrafting => Position{ .x = 1, .y = 3 },
|
||||||
|
.cooking => Position{ .x = 1, .y = 1 },
|
||||||
|
.woodcutting => Position{ .x = -2, .y = -3 },
|
||||||
|
.mining => Position{ .x = 1, .y = 5 },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn depositIfFull(managed_char: *ManagedCharacter) !bool {
|
fn moveIfNeeded(char: *ManagedCharacter, pos: Position) !bool {
|
||||||
|
if (char.position().eql(pos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try char.action_queue.append(.{ .move = pos });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn depositItemsToBank(manager: *Manager, managed_char: *ManagedCharacter) !bool {
|
||||||
const character = managed_char.character;
|
const character = managed_char.character;
|
||||||
const action_queue = &managed_char.action_queue;
|
const action_queue = &managed_char.action_queue;
|
||||||
|
|
||||||
// Deposit items and gold to bank if full
|
// Deposit items and gold to bank if full
|
||||||
if (character.getItemCount() < character.inventory_max_items) {
|
if (character.getItemCount() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var character_pos = Position.init(character.x, character.y);
|
var character_pos = managed_char.position();
|
||||||
const bank_pos = Position{ .x = 4, .y = 1 };
|
|
||||||
|
|
||||||
if (!character_pos.eql(bank_pos)) {
|
if (!character_pos.eql(manager.bank_position)) {
|
||||||
try action_queue.append(.{ .move = bank_pos });
|
try action_queue.append(.{ .move = manager.bank_position });
|
||||||
}
|
}
|
||||||
|
|
||||||
for (character.inventory.slots) |slot| {
|
for (character.inventory.slots) |slot| {
|
||||||
@ -121,58 +177,140 @@ fn depositIfFull(managed_char: *ManagedCharacter) !bool {
|
|||||||
|
|
||||||
if (slot.id) |item_id| {
|
if (slot.id) |item_id| {
|
||||||
try action_queue.append(.{
|
try action_queue.append(.{
|
||||||
.depositItem = .{ .id = item_id, .quantity = @intCast(slot.quantity) }
|
.deposit_item = .{ .id = item_id, .quantity = @intCast(slot.quantity) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (character.gold > 0) {
|
|
||||||
try action_queue.append(.{ .depositGold = @intCast(character.gold) });
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attackChickenRoutine(managed_char: *ManagedCharacter) !void {
|
fn depositIfFull(manager: *Manager, char: *ManagedCharacter) !bool {
|
||||||
const character = managed_char.character;
|
const character = char.character;
|
||||||
const action_queue = &managed_char.action_queue;
|
if (character.getItemCount() < character.inventory_max_items) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try depositItemsToBank(manager, char);
|
||||||
|
|
||||||
|
if (character.gold > 0) {
|
||||||
|
try char.action_queue.append(.{ .deposit_gold = @intCast(character.gold) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attackChickenRoutine(manager: *Manager, managed_char: *ManagedCharacter) !void {
|
||||||
const chicken_pos = Position{ .x = 0, .y = 1 };
|
const chicken_pos = Position{ .x = 0, .y = 1 };
|
||||||
|
|
||||||
var character_pos = Position.init(character.x, character.y);
|
|
||||||
|
|
||||||
// Deposit items and gold to bank if full
|
// Deposit items and gold to bank if full
|
||||||
if (try depositIfFull(managed_char)) {
|
if (try depositIfFull(manager, managed_char)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go to chickens
|
// Go to chickens
|
||||||
if (!character_pos.eql(chicken_pos)) {
|
if (try moveIfNeeded(managed_char, chicken_pos)) {
|
||||||
try action_queue.append(.{ .move = chicken_pos });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attack chickens
|
// Attack chickens
|
||||||
try action_queue.append(.{ .attack = {} });
|
try managed_char.action_queue.append(.{ .attack = {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gatherResourceRoutine(managed_char: *ManagedCharacter, resource_pos: Position) !void {
|
fn gatherResourceRoutine(manager: *Manager, managed_char: *ManagedCharacter, resource_pos: Position) !void {
|
||||||
const character = managed_char.character;
|
if (try depositIfFull(manager, managed_char)) {
|
||||||
const action_queue = &managed_char.action_queue;
|
|
||||||
|
|
||||||
var character_pos = Position.init(character.x, character.y);
|
|
||||||
|
|
||||||
// Deposit items and gold to bank if full
|
|
||||||
if (try depositIfFull(managed_char)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!character_pos.eql(resource_pos)) {
|
if (try moveIfNeeded(managed_char, resource_pos)) {
|
||||||
try action_queue.append(.{ .move = resource_pos });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try action_queue.append(.{ .gather = {} });
|
try managed_char.action_queue;.append(.{ .gather = {} });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn withdrawFromBank(manager: *Manager, char: *ManagedCharacter, items: []const ArtifactsAPI.ItemIdQuantity) !bool {
|
||||||
|
var has_all_items = true;
|
||||||
|
for (items) |item_quantity| {
|
||||||
|
const inventory_quantity = char.character.inventory.getItem(item_quantity.id);
|
||||||
|
if(inventory_quantity < item_quantity.quantity) {
|
||||||
|
has_all_items = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_all_items) return false;
|
||||||
|
|
||||||
|
if (try moveIfNeeded(char, manager.bank_position)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (items) |item_quantity| {
|
||||||
|
const inventory_quantity = char.character.inventory.getItem(item_quantity.id);
|
||||||
|
if(inventory_quantity < item_quantity.quantity) {
|
||||||
|
try char.action_queue.append(.{ .withdraw_item = .{
|
||||||
|
.id = item_quantity.id,
|
||||||
|
.quantity = item_quantity.quantity - inventory_quantity,
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn craftItem(manager: *Manager, char: *ManagedCharacter, id: ArtifactsAPI.ItemId, quantity: u64) !bool {
|
||||||
|
const inventory_quantity = char.character.inventory.getItem(id);
|
||||||
|
if (inventory_quantity >= quantity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = manager.api.getItemCode(id) orelse return error.InvalidItemId;
|
||||||
|
const item = try manager.getItem(code) orelse return error.ItemNotFound;
|
||||||
|
if (item.craft == null) {
|
||||||
|
return error.NotCraftable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipe = item.craft.?;
|
||||||
|
const workstation = manager.getWorkstation(recipe.skill);
|
||||||
|
|
||||||
|
if (try moveIfNeeded(char, workstation)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try char.action_queue.append(.{ .craft_item = .{
|
||||||
|
.id = id,
|
||||||
|
.quantity = quantity - inventory_quantity
|
||||||
|
}});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn craftItemFromBank(manager: *Manager, char: *ManagedCharacter, id: ArtifactsAPI.ItemId, quantity: u64) !bool {
|
||||||
|
const inventory_quantity = char.character.inventory.getItem(id);
|
||||||
|
if (inventory_quantity >= quantity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = manager.api.getItemCode(id) orelse return error.InvalidItemId;
|
||||||
|
const target_item = try manager.getItem(code) orelse return error.ItemNotFound;
|
||||||
|
if (target_item.craft == null) {
|
||||||
|
return error.NotCraftable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipe = target_item.craft.?;
|
||||||
|
assert(recipe.quantity == 1); // TODO: Add support for recipe which produce multiple items
|
||||||
|
|
||||||
|
var needed_items = recipe.items;
|
||||||
|
for (needed_items.slice()) |*needed_item| {
|
||||||
|
needed_item.quantity *= quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try withdrawFromBank(manager, char, needed_items.constSlice())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try craftItem(manager, char, id, quantity)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
@ -208,14 +346,13 @@ pub fn main() !void {
|
|||||||
std.log.info("Server status: {s} v{s}", .{ status.status, status.version });
|
std.log.info("Server status: {s} v{s}", .{ status.status, status.version });
|
||||||
std.log.info("Characters online: {}", .{ status.characters_online });
|
std.log.info("Characters online: {}", .{ status.characters_online });
|
||||||
|
|
||||||
const characters = try api.listMyCharacters();
|
const characters = try api.listMyCharacters(allocator);
|
||||||
defer characters.deinit();
|
defer characters.deinit();
|
||||||
|
|
||||||
for (characters.items) |character| {
|
// for (characters.list.items) |character| {
|
||||||
try manager.addCharacter(character);
|
// try manager.addCharacter(character);
|
||||||
}
|
// }
|
||||||
|
try manager.addCharacter(characters.list.items[0]);
|
||||||
|
|
||||||
|
|
||||||
std.log.info("Starting main loop", .{});
|
std.log.info("Starting main loop", .{});
|
||||||
while (manager.poll()) |char| {
|
while (manager.poll()) |char| {
|
||||||
@ -243,32 +380,56 @@ pub fn main() !void {
|
|||||||
char.character.x = pos.x;
|
char.character.x = pos.x;
|
||||||
char.character.y = pos.y;
|
char.character.y = pos.y;
|
||||||
},
|
},
|
||||||
.depositGold => |quantity| {
|
.deposit_gold => |quantity| {
|
||||||
std.log.debug("deposit {} gold from {s}", .{quantity, char.character.name});
|
std.log.debug("deposit {} gold from {s}", .{quantity, char.character.name});
|
||||||
var result = try api.actionBankDepositGold(char.character.name, quantity);
|
const result = try api.actionBankDepositGold(char.character.name, quantity);
|
||||||
defer result.deinit();
|
|
||||||
|
|
||||||
cooldown = result.cooldown;
|
cooldown = result.cooldown;
|
||||||
char.character.gold -= @intCast(quantity);
|
char.character.gold -= @intCast(quantity);
|
||||||
assert(char.character.gold >= 0);
|
assert(char.character.gold >= 0);
|
||||||
},
|
},
|
||||||
.depositItem => |item| {
|
.deposit_item => |item| {
|
||||||
std.log.debug("deposit {s}(x{}) from {s}", .{api.getItemCode(item.id).?, item.quantity, char.character.name});
|
|
||||||
const code = api.getItemCode(item.id) orelse return error.ItemNotFound;
|
const code = api.getItemCode(item.id) orelse return error.ItemNotFound;
|
||||||
var result = try api.actionBankDepositItem(char.character.name, code, item.quantity);
|
std.log.debug("deposit {s} (x{}) from {s}", .{code, item.quantity, char.character.name});
|
||||||
defer result.deinit();
|
|
||||||
|
const result = try api.actionBankDepositItem(char.character.name, code, item.quantity);
|
||||||
|
|
||||||
cooldown = result.cooldown;
|
cooldown = result.cooldown;
|
||||||
char.character.inventory.removeItem(item.id, item.quantity);
|
char.character.inventory.removeItem(item.id, item.quantity);
|
||||||
},
|
},
|
||||||
|
.withdraw_item => |item| {
|
||||||
|
const code = api.getItemCode(item.id) orelse return error.ItemNotFound;
|
||||||
|
std.log.debug("withdraw {s} (x{}) from {s}", .{code, item.quantity, char.character.name});
|
||||||
|
|
||||||
|
const result = try api.actionBankWithdrawItem(char.character.name, code, item.quantity);
|
||||||
|
|
||||||
|
cooldown = result.cooldown;
|
||||||
|
char.character.inventory.addItem(item.id, item.quantity);
|
||||||
|
},
|
||||||
.gather => {
|
.gather => {
|
||||||
std.log.debug("{s} gathers", .{char.character.name});
|
std.log.debug("{s} gathers", .{char.character.name});
|
||||||
var result = try api.actionGather(char.character.name);
|
var result = try api.actionGather(char.character.name);
|
||||||
|
|
||||||
cooldown = result.cooldown;
|
cooldown = result.cooldown;
|
||||||
for (result.details.items.slice()) |item| {
|
char.character.inventory.addItems(result.details.items.slice());
|
||||||
char.character.inventory.addItem(item.id, @intCast(item.quantity));
|
},
|
||||||
|
.craft_item => |item| {
|
||||||
|
const code = api.getItemCode(item.id) orelse return error.ItemNotFound;
|
||||||
|
std.log.debug("craft {s} (x{}) from {s}", .{code, item.quantity, char.character.name});
|
||||||
|
|
||||||
|
var result = try api.actionCraft(char.character.name, code, item.quantity);
|
||||||
|
|
||||||
|
cooldown = result.cooldown;
|
||||||
|
|
||||||
|
var inventory = &char.character.inventory;
|
||||||
|
|
||||||
|
const item_details = (try manager.getItem(code)) orelse return error.ItemNotFound;
|
||||||
|
const recipe = item_details.craft orelse return error.RecipeNotFound;
|
||||||
|
for (recipe.items.slice()) |recipe_item| {
|
||||||
|
inventory.removeItem(recipe_item.id, recipe_item.quantity * item.quantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inventory.addItems(result.details.items.slice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,6 +439,16 @@ pub fn main() !void {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add checking if character state is in sync. Debug mode only
|
||||||
|
|
||||||
|
// if (try craftItemFromBank(&manager, char, try api.getItemId("copper"), 10)) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (try depositItemsToBank(&manager, char)) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
if (std.mem.eql(u8, char.character.name, "Devin")) {
|
if (std.mem.eql(u8, char.character.name, "Devin")) {
|
||||||
try gatherResourceRoutine(char, .{ .x = -1, .y = 0 }); // Ash trees
|
try gatherResourceRoutine(char, .{ .x = -1, .y = 0 }); // Ash trees
|
||||||
} else if (std.mem.eql(u8, char.character.name, "Dawn")) {
|
} else if (std.mem.eql(u8, char.character.name, "Dawn")) {
|
||||||
|
Loading…
Reference in New Issue
Block a user