add equip goal
This commit is contained in:
parent
87c09f1e27
commit
cdd11147d3
@ -222,6 +222,7 @@ const EquipErrorDef = ErrorDefinitionList(&[_]ErrorDefinition{
|
|||||||
NotFound,
|
NotFound,
|
||||||
CharacterItemAlreadyEquiped,
|
CharacterItemAlreadyEquiped,
|
||||||
CharacterLocked,
|
CharacterLocked,
|
||||||
|
CharacterSlotEquipmentError,
|
||||||
CharacterNotSkillLevelRequired,
|
CharacterNotSkillLevelRequired,
|
||||||
CharacterNotFound,
|
CharacterNotFound,
|
||||||
CharacterInCooldown,
|
CharacterInCooldown,
|
||||||
|
@ -24,6 +24,8 @@ pub const Craft = @import("./schemas/craft.zig");
|
|||||||
pub const Resource = @import("./schemas/resource.zig");
|
pub const Resource = @import("./schemas/resource.zig");
|
||||||
pub const MoveResult = @import("./schemas/move_result.zig");
|
pub const MoveResult = @import("./schemas/move_result.zig");
|
||||||
pub const SimpleItem = @import("./schemas/simple_item.zig");
|
pub const SimpleItem = @import("./schemas/simple_item.zig");
|
||||||
|
pub const EquipResult = @import("./schemas/equip_result.zig");
|
||||||
|
pub const UnequipResult = EquipResult;
|
||||||
const SkillUsageResult = @import("./schemas/skill_usage_result.zig");
|
const SkillUsageResult = @import("./schemas/skill_usage_result.zig");
|
||||||
pub const GatherResult = SkillUsageResult;
|
pub const GatherResult = SkillUsageResult;
|
||||||
pub const CraftResult = SkillUsageResult;
|
pub const CraftResult = SkillUsageResult;
|
||||||
|
37
api/schemas/equip_result.zig
Normal file
37
api/schemas/equip_result.zig
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// zig fmt: off
|
||||||
|
const std = @import("std");
|
||||||
|
const json_utils = @import("../json_utils.zig");
|
||||||
|
const Store = @import("../store.zig");
|
||||||
|
const Character = @import("./character.zig");
|
||||||
|
const Cooldown = @import("./cooldown.zig");
|
||||||
|
const Item = @import("./item.zig");
|
||||||
|
|
||||||
|
const EquipResult = @This();
|
||||||
|
|
||||||
|
pub const Slot = Character.Equipment.SlotId;
|
||||||
|
|
||||||
|
cooldown: Cooldown,
|
||||||
|
slot: Slot,
|
||||||
|
item: Item,
|
||||||
|
character: Character,
|
||||||
|
|
||||||
|
fn parse(store: *Store, obj: std.json.ObjectMap) !EquipResult {
|
||||||
|
const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty;
|
||||||
|
const item = json_utils.getObject(obj, "item") orelse return error.MissingProperty;
|
||||||
|
const character = json_utils.getObject(obj, "character") orelse return error.MissingProperty;
|
||||||
|
const slot = try json_utils.getStringRequired(obj, "slot");
|
||||||
|
|
||||||
|
return EquipResult{
|
||||||
|
.character = try Character.parse(store, character),
|
||||||
|
.cooldown = try Cooldown.parse(store, cooldown),
|
||||||
|
.item = try Item.parse(store, item),
|
||||||
|
.slot = Slot.fromString(slot) orelse return error.InvalidSlot
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parseAndUpdate(store: *Store, obj: std.json.ObjectMap) !EquipResult {
|
||||||
|
const result = try parse(store, obj);
|
||||||
|
_ = try store.characters.appendOrUpdate(result.character);
|
||||||
|
_ = try store.items.appendOrUpdate(result.item);
|
||||||
|
return result;
|
||||||
|
}
|
@ -3,27 +3,37 @@ const std = @import("std");
|
|||||||
const json_utils = @import("../json_utils.zig");
|
const json_utils = @import("../json_utils.zig");
|
||||||
const Store = @import("../store.zig");
|
const Store = @import("../store.zig");
|
||||||
const Item = @import("./item.zig");
|
const Item = @import("./item.zig");
|
||||||
|
const EnumStringUtils = @import("../enum_string_utils.zig").EnumStringUtils;
|
||||||
|
|
||||||
const Equipment = @This();
|
const Equipment = @This();
|
||||||
|
|
||||||
pub const UtilitySlot = struct {
|
pub const Slot = struct {
|
||||||
id: Store.Id,
|
item: ?Store.Id = null,
|
||||||
quantity: u64,
|
quantity: u64 = 0,
|
||||||
|
|
||||||
fn parse(store: *Store, obj: std.json.ObjectMap, name: []const u8, quantity: []const u8) !?UtilitySlot {
|
fn parse(store: *Store, obj: std.json.ObjectMap, name: []const u8) !Slot {
|
||||||
const item_code = try json_utils.getStringRequired(obj, name);
|
const item_code = try json_utils.getStringRequired(obj, name);
|
||||||
if (item_code.len == 0) {
|
if (item_code.len == 0) {
|
||||||
return null;
|
return Slot{};
|
||||||
}
|
}
|
||||||
|
|
||||||
return UtilitySlot{
|
return Slot{
|
||||||
.id = try store.items.getOrReserveId(item_code),
|
.item = try store.items.getOrReserveId(item_code),
|
||||||
.quantity = try json_utils.getPositiveIntegerRequired(obj, quantity),
|
.quantity = 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parseWithQuantity(store: *Store, obj: std.json.ObjectMap, name: []const u8, quantity: []const u8) !Slot {
|
||||||
|
var slot = try Slot.parse(store, obj, name);
|
||||||
|
if (slot.item != null) {
|
||||||
|
slot.quantity = try json_utils.getPositiveIntegerRequired(obj, quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Slot = enum {
|
pub const SlotId = enum {
|
||||||
weapon,
|
weapon,
|
||||||
shield,
|
shield,
|
||||||
helmet,
|
helmet,
|
||||||
@ -35,92 +45,56 @@ pub const Slot = enum {
|
|||||||
amulet,
|
amulet,
|
||||||
artifact1,
|
artifact1,
|
||||||
artifact2,
|
artifact2,
|
||||||
|
artifact3,
|
||||||
utility1,
|
utility1,
|
||||||
utility2,
|
utility2,
|
||||||
|
|
||||||
fn name(self: Slot) []const u8 {
|
const Utils = EnumStringUtils(SlotId, .{
|
||||||
return switch (self) {
|
.{ "weapon" , SlotId.weapon },
|
||||||
.weapon => "weapon",
|
.{ "shield" , SlotId.shield },
|
||||||
.shield => "shield",
|
.{ "helmet" , SlotId.helmet },
|
||||||
.helmet => "helmet",
|
.{ "body_armor", SlotId.body_armor },
|
||||||
.body_armor => "body_armor",
|
.{ "leg_armor" , SlotId.leg_armor },
|
||||||
.leg_armor => "leg_armor",
|
.{ "boots" , SlotId.boots },
|
||||||
.boots => "boots",
|
.{ "ring1" , SlotId.ring1 },
|
||||||
.ring1 => "ring1",
|
.{ "ring2" , SlotId.ring2 },
|
||||||
.ring2 => "ring2",
|
.{ "amulet" , SlotId.amulet },
|
||||||
.amulet => "amulet",
|
.{ "artifact1" , SlotId.artifact1 },
|
||||||
.artifact1 => "artifact1",
|
.{ "artifact2" , SlotId.artifact2 },
|
||||||
.artifact2 => "artifact2",
|
.{ "artifact3" , SlotId.artifact3 },
|
||||||
.utility1 => "utility1",
|
.{ "utility1" , SlotId.utility1 },
|
||||||
.utility2 => "utility2",
|
.{ "utility2" , SlotId.utility2 },
|
||||||
};
|
});
|
||||||
|
|
||||||
|
pub const toString = Utils.toString;
|
||||||
|
pub const fromString = Utils.fromString;
|
||||||
|
|
||||||
|
pub fn canHoldManyItems(self: SlotId) bool {
|
||||||
|
return self == .utility1 or self == .utility2;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
weapon: ?Store.Id,
|
pub const Slots = std.EnumArray(SlotId, Slot);
|
||||||
shield: ?Store.Id,
|
|
||||||
helmet: ?Store.Id,
|
|
||||||
body_armor: ?Store.Id,
|
|
||||||
leg_armor: ?Store.Id,
|
|
||||||
boots: ?Store.Id,
|
|
||||||
|
|
||||||
ring1: ?Store.Id,
|
slots: Slots,
|
||||||
ring2: ?Store.Id,
|
|
||||||
amulet: ?Store.Id,
|
|
||||||
|
|
||||||
artifact1: ?Store.Id,
|
|
||||||
artifact2: ?Store.Id,
|
|
||||||
artifact3: ?Store.Id,
|
|
||||||
|
|
||||||
utility1: ?UtilitySlot,
|
|
||||||
utility2: ?UtilitySlot,
|
|
||||||
|
|
||||||
pub fn parse(store: *Store, obj: std.json.ObjectMap) !Equipment {
|
pub fn parse(store: *Store, obj: std.json.ObjectMap) !Equipment {
|
||||||
const weapon = try json_utils.getStringRequired(obj, "weapon_slot");
|
|
||||||
const shield = try json_utils.getStringRequired(obj, "shield_slot");
|
|
||||||
const helmet = try json_utils.getStringRequired(obj, "helmet_slot");
|
|
||||||
const body_armor = try json_utils.getStringRequired(obj, "body_armor_slot");
|
|
||||||
const leg_armor = try json_utils.getStringRequired(obj, "leg_armor_slot");
|
|
||||||
const boots = try json_utils.getStringRequired(obj, "boots_slot");
|
|
||||||
const ring1 = try json_utils.getStringRequired(obj, "ring1_slot");
|
|
||||||
const ring2 = try json_utils.getStringRequired(obj, "ring2_slot");
|
|
||||||
const amulet = try json_utils.getStringRequired(obj, "amulet_slot");
|
|
||||||
const artifact1 = try json_utils.getStringRequired(obj, "artifact1_slot");
|
|
||||||
const artifact2 = try json_utils.getStringRequired(obj, "artifact2_slot");
|
|
||||||
const artifact3 = try json_utils.getStringRequired(obj, "artifact3_slot");
|
|
||||||
|
|
||||||
return Equipment{
|
return Equipment{
|
||||||
.weapon = try store.items.getOrReserveId(weapon),
|
.slots = Slots.init(.{
|
||||||
.shield = try store.items.getOrReserveId(shield),
|
.weapon = try Slot.parse(store, obj, "weapon_slot"),
|
||||||
.helmet = try store.items.getOrReserveId(helmet),
|
.shield = try Slot.parse(store, obj, "shield_slot"),
|
||||||
.body_armor = try store.items.getOrReserveId(body_armor),
|
.helmet = try Slot.parse(store, obj, "helmet_slot"),
|
||||||
.leg_armor = try store.items.getOrReserveId(leg_armor),
|
.body_armor = try Slot.parse(store, obj, "body_armor_slot"),
|
||||||
.boots = try store.items.getOrReserveId(boots),
|
.leg_armor = try Slot.parse(store, obj, "leg_armor_slot"),
|
||||||
.ring1 = try store.items.getOrReserveId(ring1),
|
.boots = try Slot.parse(store, obj, "boots_slot"),
|
||||||
.ring2 = try store.items.getOrReserveId(ring2),
|
.ring1 = try Slot.parse(store, obj, "ring1_slot"),
|
||||||
.amulet = try store.items.getOrReserveId(amulet),
|
.ring2 = try Slot.parse(store, obj, "ring2_slot"),
|
||||||
.artifact1 = try store.items.getOrReserveId(artifact1),
|
.amulet = try Slot.parse(store, obj, "amulet_slot"),
|
||||||
.artifact2 = try store.items.getOrReserveId(artifact2),
|
.artifact1 = try Slot.parse(store, obj, "artifact1_slot"),
|
||||||
.artifact3 = try store.items.getOrReserveId(artifact3),
|
.artifact2 = try Slot.parse(store, obj, "artifact2_slot"),
|
||||||
.utility1 = try UtilitySlot.parse(store, obj, "utility1_slot", "utility1_slot_quantity"),
|
.artifact3 = try Slot.parse(store, obj, "artifact3_slot"),
|
||||||
.utility2 = try UtilitySlot.parse(store, obj, "utility2_slot", "utility2_slot_quantity"),
|
.utility1 = try Slot.parseWithQuantity(store, obj, "utility1_slot", "utility1_slot_quantity"),
|
||||||
};
|
.utility2 = try Slot.parseWithQuantity(store, obj, "utility2_slot", "utility2_slot_quantity"),
|
||||||
}
|
})
|
||||||
|
|
||||||
pub fn getSlot(self: Equipment, slot: Slot) ?Store.Id {
|
|
||||||
return switch (slot) {
|
|
||||||
.weapon => self.weapon,
|
|
||||||
.shield => self.shield,
|
|
||||||
.helmet => self.helmet,
|
|
||||||
.body_armor => self.body_armor,
|
|
||||||
.leg_armor => self.leg_armor,
|
|
||||||
.boots => self.boots,
|
|
||||||
.ring1 => self.ring1,
|
|
||||||
.ring2 => self.ring2,
|
|
||||||
.amulet => self.amulet,
|
|
||||||
.artifact1 => self.artifact1,
|
|
||||||
.artifact2 => self.artifact2,
|
|
||||||
.utility1 => if (self.utility1) |util| util.id else null,
|
|
||||||
.utility2 => if (self.utility2) |util| util.id else null,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
111
api/server.zig
111
api/server.zig
@ -14,6 +14,7 @@ const AuthToken = Root.AuthToken;
|
|||||||
const log = std.log.scoped(.api);
|
const log = std.log.scoped(.api);
|
||||||
const ServerURL = std.BoundedArray(u8, 256);
|
const ServerURL = std.BoundedArray(u8, 256);
|
||||||
|
|
||||||
|
const Equipment = @import("./schemas/equipment.zig");
|
||||||
const Item = @import("./schemas/item.zig");
|
const Item = @import("./schemas/item.zig");
|
||||||
const Craft = @import("./schemas/craft.zig");
|
const Craft = @import("./schemas/craft.zig");
|
||||||
const Status = @import("./schemas/status.zig");
|
const Status = @import("./schemas/status.zig");
|
||||||
@ -25,6 +26,8 @@ const Map = @import("./schemas/map.zig");
|
|||||||
const GEOrder = @import("./schemas/ge_order.zig");
|
const GEOrder = @import("./schemas/ge_order.zig");
|
||||||
const MoveResult = @import("./schemas/move_result.zig");
|
const MoveResult = @import("./schemas/move_result.zig");
|
||||||
const SkillUsageResult = @import("./schemas/skill_usage_result.zig");
|
const SkillUsageResult = @import("./schemas/skill_usage_result.zig");
|
||||||
|
const EquipResult = @import("./schemas/equip_result.zig");
|
||||||
|
const UnequipResult = EquipResult;
|
||||||
pub const GatherResult = SkillUsageResult;
|
pub const GatherResult = SkillUsageResult;
|
||||||
pub const CraftResult = SkillUsageResult;
|
pub const CraftResult = SkillUsageResult;
|
||||||
const Image = Store.Image;
|
const Image = Store.Image;
|
||||||
@ -502,32 +505,50 @@ fn fetchArray(
|
|||||||
return result orelse return FetchError.RequestFailed;
|
return result orelse return FetchError.RequestFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prefetch(self: *Server, allocator: std.mem.Allocator) !void {
|
pub const PrefetchOptions = struct {
|
||||||
|
resources: bool = true,
|
||||||
|
maps: bool = true,
|
||||||
|
monsters: bool = true,
|
||||||
|
items: bool = true,
|
||||||
|
images: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn prefetch(self: *Server, allocator: std.mem.Allocator, opts: PrefetchOptions) !void {
|
||||||
// TODO: Create a version of `getResources`, `getMonsters`, `getItems`, etc..
|
// TODO: Create a version of `getResources`, `getMonsters`, `getItems`, etc..
|
||||||
// which don't need an allocator to be passed.
|
// which don't need an allocator to be passed.
|
||||||
// This is for cases when you only care that everything will be saved into the store.
|
// This is for cases when you only care that everything will be saved into the store.
|
||||||
|
|
||||||
const resources = try self.getResources(allocator, .{});
|
if (opts.resources) {
|
||||||
defer resources.deinit();
|
const resources = try self.getResources(allocator, .{});
|
||||||
|
defer resources.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
const maps: std.ArrayList(Map) = try self.getMaps(allocator, .{});
|
if (opts.maps) {
|
||||||
defer maps.deinit();
|
const maps: std.ArrayList(Map) = try self.getMaps(allocator, .{});
|
||||||
|
defer maps.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
const monsters = try self.getMonsters(allocator, .{});
|
if (opts.monsters) {
|
||||||
defer monsters.deinit();
|
const monsters = try self.getMonsters(allocator, .{});
|
||||||
|
defer monsters.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
const items = try self.getItems(allocator, .{});
|
if (opts.items) {
|
||||||
defer items.deinit();
|
const items = try self.getItems(allocator, .{});
|
||||||
|
defer items.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
for (maps.items) |map| {
|
if (opts.images) {
|
||||||
const skin: []const u8 = map.skin.slice();
|
for (self.store.maps.items) |map| {
|
||||||
if (self.store.images.getId(.map, skin) == null) {
|
const skin: []const u8 = map.skin.slice();
|
||||||
_ = try self.getImage(.map, skin);
|
if (self.store.images.getId(.map, skin) == null) {
|
||||||
|
_ = try self.getImage(.map, skin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prefetchCached(self: *Server, allocator: std.mem.Allocator, absolute_cache_path: []const u8) !void {
|
pub fn prefetchCached(self: *Server, allocator: std.mem.Allocator, absolute_cache_path: []const u8, opts: PrefetchOptions) !void {
|
||||||
const status: Status = try self.getStatus();
|
const status: Status = try self.getStatus();
|
||||||
const version = status.version.slice();
|
const version = status.version.slice();
|
||||||
|
|
||||||
@ -539,7 +560,7 @@ pub fn prefetchCached(self: *Server, allocator: std.mem.Allocator, absolute_cach
|
|||||||
} else |_| {}
|
} else |_| {}
|
||||||
} else |_| {}
|
} else |_| {}
|
||||||
|
|
||||||
try self.prefetch(allocator);
|
try self.prefetch(allocator, opts);
|
||||||
|
|
||||||
const file = try std.fs.createFileAbsolute(absolute_cache_path, .{});
|
const file = try std.fs.createFileAbsolute(absolute_cache_path, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
@ -1046,6 +1067,66 @@ pub fn craft(self: *Server, character: []const u8, item: []const u8, quantity: u
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn equip(self: *Server, character: []const u8, slot: Equipment.SlotId, item: []const u8, quantity: u64) errors.EquipError!EquipResult {
|
||||||
|
const path_buff_size = comptime blk: {
|
||||||
|
var count = 0;
|
||||||
|
count += 4; // "/my/"
|
||||||
|
count += Character.max_name_size;
|
||||||
|
count += 13; // "/action/equip"
|
||||||
|
break :blk count;
|
||||||
|
};
|
||||||
|
|
||||||
|
var path_buff: [path_buff_size]u8 = undefined;
|
||||||
|
const path = std.fmt.bufPrint(
|
||||||
|
&path_buff,
|
||||||
|
"/my/{s}/action/equip",.{ character }
|
||||||
|
) catch return FetchError.InvalidPayload;
|
||||||
|
|
||||||
|
var payload_buff: [256]u8 = undefined;
|
||||||
|
const payload = std.fmt.bufPrint(
|
||||||
|
&payload_buff,
|
||||||
|
"{{ \"slot\":\"{s}\", \"code\":\"{s}\", \"quantity\":{} }}", .{ slot.toString(), item, quantity }
|
||||||
|
) catch return FetchError.InvalidPayload;
|
||||||
|
|
||||||
|
return try self.fetchObject(
|
||||||
|
errors.EquipError,
|
||||||
|
errors.parseEquipError,
|
||||||
|
EquipResult,
|
||||||
|
EquipResult.parseAndUpdate,
|
||||||
|
.{ .method = .POST, .path = path, .payload = payload, .ratelimit = .actions }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unequip(self: *Server, character: []const u8, slot: Equipment.SlotId, quantity: u64) errors.UnequipError!UnequipResult {
|
||||||
|
const path_buff_size = comptime blk: {
|
||||||
|
var count = 0;
|
||||||
|
count += 4; // "/my/"
|
||||||
|
count += Character.max_name_size;
|
||||||
|
count += 15; // "/action/unequip"
|
||||||
|
break :blk count;
|
||||||
|
};
|
||||||
|
|
||||||
|
var path_buff: [path_buff_size]u8 = undefined;
|
||||||
|
const path = std.fmt.bufPrint(
|
||||||
|
&path_buff,
|
||||||
|
"/my/{s}/action/unequip",.{ character }
|
||||||
|
) catch return FetchError.InvalidPayload;
|
||||||
|
|
||||||
|
var payload_buff: [256]u8 = undefined;
|
||||||
|
const payload = std.fmt.bufPrint(
|
||||||
|
&payload_buff,
|
||||||
|
"{{ \"slot\":\"{s}\", \"quantity\":{} }}", .{ slot.toString(), quantity }
|
||||||
|
) catch return FetchError.InvalidPayload;
|
||||||
|
|
||||||
|
return try self.fetchObject(
|
||||||
|
errors.UnequipError,
|
||||||
|
errors.parseUnequipError,
|
||||||
|
UnequipResult,
|
||||||
|
UnequipResult.parseAndUpdate,
|
||||||
|
.{ .method = .POST, .path = path, .payload = payload, .ratelimit = .actions }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// https://api.artifactsmmo.com/docs/#/operations/generate_token_token_post
|
// https://api.artifactsmmo.com/docs/#/operations/generate_token_token_post
|
||||||
pub fn generateToken(self: *Server, username: []const u8, password: []const u8) !AuthToken {
|
pub fn generateToken(self: *Server, username: []const u8, password: []const u8) !AuthToken {
|
||||||
const base64_encoder = std.base64.standard.Encoder;
|
const base64_encoder = std.base64.standard.Encoder;
|
||||||
|
18
cli/main.zig
18
cli/main.zig
@ -50,11 +50,10 @@ pub fn main() !void {
|
|||||||
const cwd_path = try std.fs.cwd().realpathAlloc(allocator, ".");
|
const cwd_path = try std.fs.cwd().realpathAlloc(allocator, ".");
|
||||||
defer allocator.free(cwd_path);
|
defer allocator.free(cwd_path);
|
||||||
|
|
||||||
const cache_path = try std.fs.path.resolve(allocator, &.{ cwd_path, "./api-store.bin" });
|
const cache_path = try std.fs.path.resolve(allocator, &.{ cwd_path, "./api-store-cli.bin" });
|
||||||
defer allocator.free(cache_path);
|
defer allocator.free(cache_path);
|
||||||
|
|
||||||
// TODO: Don't prefetch images
|
try server.prefetchCached(allocator, cache_path, .{ .images = false });
|
||||||
try server.prefetchCached(allocator, cache_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const character_id = (try server.getCharacter("Blondie")).?;
|
const character_id = (try server.getCharacter("Blondie")).?;
|
||||||
@ -62,17 +61,10 @@ pub fn main() !void {
|
|||||||
var artificer = try Artificer.init(allocator, &server, character_id);
|
var artificer = try Artificer.init(allocator, &server, character_id);
|
||||||
defer artificer.deinit(allocator);
|
defer artificer.deinit(allocator);
|
||||||
|
|
||||||
// _ = try artificer.appendGoal(Artificer.Goal{
|
|
||||||
// .gather = .{
|
|
||||||
// .item = store.items.getId("sap").?,
|
|
||||||
// .quantity = 1
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
_ = try artificer.appendGoal(Artificer.Goal{
|
_ = try artificer.appendGoal(Artificer.Goal{
|
||||||
.craft = .{
|
.equip = .{
|
||||||
.item = store.items.getId("copper").?,
|
.slot = .weapon,
|
||||||
.quantity = 2
|
.item = store.items.getId("copper_dagger").?
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -102,10 +102,10 @@ pub fn main() anyerror!void {
|
|||||||
const cwd_path = try std.fs.cwd().realpathAlloc(allocator, ".");
|
const cwd_path = try std.fs.cwd().realpathAlloc(allocator, ".");
|
||||||
defer allocator.free(cwd_path);
|
defer allocator.free(cwd_path);
|
||||||
|
|
||||||
const cache_path = try std.fs.path.resolve(allocator, &.{ cwd_path, "./api-store.bin" });
|
const cache_path = try std.fs.path.resolve(allocator, &.{ cwd_path, "./api-store-gui.bin" });
|
||||||
defer allocator.free(cache_path);
|
defer allocator.free(cache_path);
|
||||||
|
|
||||||
try server.prefetchCached(allocator, cache_path);
|
try server.prefetchCached(allocator, cache_path, .{ .images = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
rl.initWindow(800, 450, "Artificer");
|
rl.initWindow(800, 450, "Artificer");
|
||||||
|
61
lib/equip_goal.zig
Normal file
61
lib/equip_goal.zig
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// zig fmt: off
|
||||||
|
const std = @import("std");
|
||||||
|
const Api = @import("artifacts-api");
|
||||||
|
const Artificer = @import("./root.zig");
|
||||||
|
|
||||||
|
const Goal = @This();
|
||||||
|
|
||||||
|
slot: Api.Character.Equipment.SlotId,
|
||||||
|
item: Api.Store.Id,
|
||||||
|
quantity: u64 = 1,
|
||||||
|
|
||||||
|
pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) void {
|
||||||
|
const store = artificer.server.store;
|
||||||
|
const character = store.characters.get(artificer.character).?;
|
||||||
|
|
||||||
|
const equipment_slot = character.equipment.slots.get(self.slot);
|
||||||
|
if (equipment_slot.item) |equiped_item|{
|
||||||
|
if (equiped_item == self.item and !self.slot.canHoldManyItems()) {
|
||||||
|
artificer.removeGoal(goal_id);
|
||||||
|
} else {
|
||||||
|
artificer.queued_actions.appendAssumeCapacity(.{
|
||||||
|
.goal = goal_id,
|
||||||
|
.action = .{
|
||||||
|
.unequip = .{
|
||||||
|
.slot = self.slot,
|
||||||
|
.quantity = self.quantity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
artificer.queued_actions.appendAssumeCapacity(.{
|
||||||
|
.goal = goal_id,
|
||||||
|
.action = .{
|
||||||
|
.equip = .{
|
||||||
|
.slot = self.slot,
|
||||||
|
.item = self.item,
|
||||||
|
.quantity = self.quantity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn requirements(self: Goal, artificer: *Artificer) Artificer.Requirements {
|
||||||
|
_ = artificer;
|
||||||
|
|
||||||
|
var reqs: Artificer.Requirements = .{};
|
||||||
|
reqs.items.addAssumeCapacity(self.item, self.quantity);
|
||||||
|
|
||||||
|
return reqs;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onActionCompleted(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer, result: Artificer.ActionResult) void {
|
||||||
|
_ = self;
|
||||||
|
|
||||||
|
if (result == .equip) {
|
||||||
|
artificer.removeGoal(goal_id);
|
||||||
|
}
|
||||||
|
}
|
56
lib/root.zig
56
lib/root.zig
@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
|
|
||||||
const GatherGoal = @import("gather_goal.zig");
|
const GatherGoal = @import("gather_goal.zig");
|
||||||
const CraftGoal = @import("craft_goal.zig");
|
const CraftGoal = @import("craft_goal.zig");
|
||||||
|
const EquipGoal = @import("equip_goal.zig");
|
||||||
|
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const log = std.log.scoped(.artificer);
|
const log = std.log.scoped(.artificer);
|
||||||
@ -31,11 +32,13 @@ const server_down_retry_interval = 5; // minutes
|
|||||||
pub const Goal = union(enum) {
|
pub const Goal = union(enum) {
|
||||||
gather: GatherGoal,
|
gather: GatherGoal,
|
||||||
craft: CraftGoal,
|
craft: CraftGoal,
|
||||||
|
equip: EquipGoal,
|
||||||
|
|
||||||
pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) !void {
|
pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) !void {
|
||||||
switch (self.*) {
|
switch (self.*) {
|
||||||
.gather => |*gather| gather.tick(goal_id, artificer),
|
.gather => |*gather| gather.tick(goal_id, artificer),
|
||||||
.craft => |*craft| try craft.tick(goal_id, artificer),
|
.craft => |*craft| try craft.tick(goal_id, artificer),
|
||||||
|
.equip => |*equip| equip.tick(goal_id, artificer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,13 +46,15 @@ pub const Goal = union(enum) {
|
|||||||
return switch (self) {
|
return switch (self) {
|
||||||
.gather => |gather| gather.requirements(artificer),
|
.gather => |gather| gather.requirements(artificer),
|
||||||
.craft => |craft| craft.requirements(artificer),
|
.craft => |craft| craft.requirements(artificer),
|
||||||
|
.equip => |equip| equip.requirements(artificer),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn onActionCompleted(self: *Goal, goal_id: Artificer.GoalId, result: Artificer.ActionResult) void {
|
pub fn onActionCompleted(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer, result: Artificer.ActionResult) void {
|
||||||
switch (self.*) {
|
switch (self.*) {
|
||||||
.gather => |*gather| gather.onActionCompleted(goal_id, result),
|
.gather => |*gather| gather.onActionCompleted(goal_id, result),
|
||||||
.craft => |*craft| craft.onActionCompleted(goal_id, result),
|
.craft => |*craft| craft.onActionCompleted(goal_id, result),
|
||||||
|
.equip => |*equip| equip.onActionCompleted(goal_id, artificer, result),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -66,13 +71,24 @@ pub const Action = union(enum) {
|
|||||||
craft: struct {
|
craft: struct {
|
||||||
item: Api.Store.Id,
|
item: Api.Store.Id,
|
||||||
quantity: u64
|
quantity: u64
|
||||||
}
|
},
|
||||||
|
unequip: struct {
|
||||||
|
slot: Api.Equipment.SlotId,
|
||||||
|
quantity: u64
|
||||||
|
},
|
||||||
|
equip: struct {
|
||||||
|
slot: Api.Equipment.SlotId,
|
||||||
|
item: Api.Store.Id,
|
||||||
|
quantity: u64
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ActionResult = union(enum) {
|
pub const ActionResult = union(enum) {
|
||||||
move: Api.MoveResult,
|
move: Api.MoveResult,
|
||||||
gather: Api.GatherResult,
|
gather: Api.GatherResult,
|
||||||
craft: Api.CraftResult,
|
craft: Api.CraftResult,
|
||||||
|
equip: Api.EquipResult,
|
||||||
|
unequip: Api.UnequipResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActionSlot = struct {
|
const ActionSlot = struct {
|
||||||
@ -81,11 +97,7 @@ const ActionSlot = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Requirements = struct {
|
pub const Requirements = struct {
|
||||||
pub const Items = Api.SimpleItem.BoundedArray(blk: {
|
pub const Items = Api.SimpleItem.BoundedArray(Api.Craft.max_items);
|
||||||
var max: usize = 0;
|
|
||||||
max = @max(max, Api.Craft.max_items);
|
|
||||||
break :blk max;
|
|
||||||
});
|
|
||||||
|
|
||||||
items: Items = .{}
|
items: Items = .{}
|
||||||
};
|
};
|
||||||
@ -299,21 +311,28 @@ pub fn tick(self: *Artificer) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const action_slot = self.queued_actions.orderedRemove(0);
|
const action_slot = self.queued_actions.orderedRemove(0);
|
||||||
|
const character_name = character.name.slice();
|
||||||
const action_result = switch (action_slot.action) {
|
const action_result = switch (action_slot.action) {
|
||||||
.move => |position| ActionResult{
|
.move => |position| ActionResult{
|
||||||
.move = try self.server.move(character.name.slice(), position)
|
.move = try self.server.move(character_name, position)
|
||||||
},
|
},
|
||||||
.gather => ActionResult{
|
.gather => ActionResult{
|
||||||
.gather = try self.server.gather(character.name.slice())
|
.gather = try self.server.gather(character_name)
|
||||||
},
|
},
|
||||||
.craft => |craft| ActionResult{
|
.craft => |craft| ActionResult{
|
||||||
.craft = try self.server.craft(character.name.slice(), store.items.getName(craft.item).?, craft.quantity)
|
.craft = try self.server.craft(character_name, store.items.getName(craft.item).?, craft.quantity)
|
||||||
|
},
|
||||||
|
.equip => |equip| ActionResult{
|
||||||
|
.equip = try self.server.equip(character_name, equip.slot, store.items.getName(equip.item).?, equip.quantity)
|
||||||
|
},
|
||||||
|
.unequip => |unequip| ActionResult{
|
||||||
|
.unequip = try self.server.unequip(character_name, unequip.slot, unequip.quantity)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (self.getGoal(action_slot.goal)) |goal_slot| {
|
if (self.getGoal(action_slot.goal)) |goal_slot| {
|
||||||
const goal = &goal_slot.goal.?;
|
const goal = &goal_slot.goal.?;
|
||||||
goal.onActionCompleted(action_slot.goal, action_result);
|
goal.onActionCompleted(action_slot.goal, self, action_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -337,11 +356,24 @@ pub fn tick(self: *Artificer) !void {
|
|||||||
for (reqs.items.slice()) |req_item| {
|
for (reqs.items.slice()) |req_item| {
|
||||||
const inventory_quantity = character.inventory.getQuantity(req_item.id);
|
const inventory_quantity = character.inventory.getQuantity(req_item.id);
|
||||||
if (inventory_quantity < req_item.quantity) {
|
if (inventory_quantity < req_item.quantity) {
|
||||||
|
const missing_quantity = req_item.quantity - inventory_quantity;
|
||||||
|
const item = store.items.get(req_item.id).?;
|
||||||
|
|
||||||
if (self.findBestResourceWithItem(req_item.id) != null) {
|
if (self.findBestResourceWithItem(req_item.id) != null) {
|
||||||
const subgoal_id = try self.appendGoal(.{
|
const subgoal_id = try self.appendGoal(.{
|
||||||
.gather = .{
|
.gather = .{
|
||||||
.item = req_item.id,
|
.item = req_item.id,
|
||||||
.quantity = req_item.quantity - inventory_quantity,
|
.quantity = missing_quantity
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const subgoal = self.getGoal(subgoal_id).?;
|
||||||
|
subgoal.parent_goal = goal_id;
|
||||||
|
} else if (item.craft != null) {
|
||||||
|
const subgoal_id = try self.appendGoal(.{
|
||||||
|
.craft = .{
|
||||||
|
.item = req_item.id,
|
||||||
|
.quantity = missing_quantity
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user