add automatic gathering of materials for recipe
This commit is contained in:
parent
7d572e7064
commit
64c4d9ff47
@ -1088,7 +1088,7 @@ fn fetch(self: *Server, options: FetchOptions) APIError!ArtifactsFetchResult {
|
||||
|
||||
if (options.paginated) {
|
||||
const total_pages_i64 = json_utils.getInteger(parsed.object, "pages") orelse return APIError.ParseFailed;
|
||||
if (total_pages_i64 < 1) return APIError.ParseFailed;
|
||||
if (total_pages_i64 < 0) return APIError.ParseFailed;
|
||||
total_pages = @intCast(total_pages_i64);
|
||||
|
||||
const page_results = json_utils.getArray(parsed.object, "data") orelse return APIError.ParseFailed;
|
||||
|
@ -13,7 +13,7 @@ pub fn BoundedSlotsArray(comptime slot_count: u32) type {
|
||||
return struct {
|
||||
slots: Slots,
|
||||
|
||||
fn init() @This() {
|
||||
pub fn init() @This() {
|
||||
return @This(){
|
||||
.slots = Slots.init(0) catch unreachable
|
||||
};
|
||||
@ -62,20 +62,13 @@ pub fn BoundedSlotsArray(comptime slot_count: u32) type {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(self: *@This(), id: ItemId, quantity: u64) void {
|
||||
pub fn add(self: *@This(), id: ItemId, quantity: u64) !void {
|
||||
if (quantity == 0) return;
|
||||
|
||||
if (self.findSlot(id)) |slot| {
|
||||
slot.quantity += quantity;
|
||||
} else {
|
||||
var empty_slot: ?*Slot = null;
|
||||
for (&self.slots) |*slot| {
|
||||
if (slot.id == null) {
|
||||
empty_slot = slot;
|
||||
}
|
||||
}
|
||||
|
||||
assert(empty_slot != null);
|
||||
empty_slot.?.id = id;
|
||||
empty_slot.?.quantity = quantity;
|
||||
try self.slots.append(Slot{ .id = id, .quantity = quantity });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ pub const CharacterTask = union(enum) {
|
||||
|
||||
name: []const u8,
|
||||
action_queue: std.ArrayList(QueuedAction),
|
||||
task: ?CharacterTask = null,
|
||||
task: ?*CharacterTask = null,
|
||||
|
||||
pub fn init(allocator: Allocator, name: []const u8) !CharacterBrain {
|
||||
return CharacterBrain{
|
||||
@ -391,7 +391,7 @@ pub fn performNextAction(self: *CharacterBrain, api: *Server) !void {
|
||||
fn onActionCompleted(self: *CharacterBrain, result: QueuedActionResult) void {
|
||||
if (self.task == null) return;
|
||||
|
||||
switch (self.task.?) {
|
||||
switch (self.task.?.*) {
|
||||
.fight => |*args| {
|
||||
if (result.get(.fight)) |r| {
|
||||
const fight_result: Server.FightResult = r;
|
||||
@ -433,7 +433,7 @@ pub fn performTask(self: *CharacterBrain, api: *Server) !void {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (self.task.?) {
|
||||
switch (self.task.?.*) {
|
||||
.fight => |args| {
|
||||
try self.fightRoutine(api, args.at);
|
||||
},
|
||||
|
152
src/main.zig
152
src/main.zig
@ -5,6 +5,7 @@ const assert = std.debug.assert;
|
||||
const Position = @import("./api/position.zig");
|
||||
const Server = @import("./api/server.zig");
|
||||
const CharacterBrain = @import("./character_brain.zig");
|
||||
const BoundedSlotsArray = @import("./api/slot_array.zig").BoundedSlotsArray;
|
||||
|
||||
// pub const std_options = .{ .log_level = .debug };
|
||||
|
||||
@ -90,6 +91,7 @@ const TaskTree = struct {
|
||||
const TaskTreeNode = struct {
|
||||
parent: ?usize,
|
||||
character_task: CharacterBrain.CharacterTask,
|
||||
additional_items: BoundedSlotsArray(8) = BoundedSlotsArray(8).init(),
|
||||
};
|
||||
|
||||
const Nodes = std.ArrayList(TaskTreeNode);
|
||||
@ -115,8 +117,8 @@ const TaskTree = struct {
|
||||
return self.nodes.items.len-1;
|
||||
}
|
||||
|
||||
fn appendSubTree(self: *TaskTree, code: []const u8, quantity: u64, parent: ?usize) !void {
|
||||
if (quantity == 0) return;
|
||||
fn appendSubTree(self: *TaskTree, code: []const u8, quantity: u64, parent: ?usize) !?usize {
|
||||
if (quantity == 0) return null;
|
||||
|
||||
const item = (try self.api.getItem(code)).?;
|
||||
const item_id = try self.api.getItemId(code);
|
||||
@ -144,11 +146,19 @@ const TaskTree = struct {
|
||||
|
||||
for (recipe.items.slots.constSlice()) |recipe_item| {
|
||||
const recipe_item_code = self.api.getItemCode(recipe_item.id).?;
|
||||
try self.appendSubTree(recipe_item_code, recipe_item.quantity * craft_count, node_id);
|
||||
const needed_quantity = recipe_item.quantity * craft_count;
|
||||
const subtree_id = try self.appendSubTree(recipe_item_code, needed_quantity, node_id);
|
||||
|
||||
// The target item can not be currently produced.
|
||||
if (subtree_id == null) {
|
||||
var node = &self.nodes.items[node_id];
|
||||
try node.additional_items.add(recipe_item.id, needed_quantity);
|
||||
}
|
||||
} else {
|
||||
if (item.type == .resource) {
|
||||
if (eql(u8, item.subtype, "mining")) {
|
||||
}
|
||||
|
||||
return node_id;
|
||||
} else if (item.type == .resource) {
|
||||
if (eql(u8, item.subtype, "mining") or eql(u8, item.subtype, "fishing") or eql(u8, item.subtype, "woodcutting")) {
|
||||
const resources = try self.api.getResources(.{ .drop = code });
|
||||
defer resources.deinit();
|
||||
|
||||
@ -163,7 +173,7 @@ const TaskTree = struct {
|
||||
if (resource_maps.items.len > 1) std.log.warn("Multiple map locations exist for resource", .{});
|
||||
const resource_map = resource_maps.items[0];
|
||||
|
||||
_ = try self.appendNode(TaskTreeNode{
|
||||
return try self.appendNode(TaskTreeNode{
|
||||
.parent = parent,
|
||||
.character_task = .{
|
||||
.gather = .{
|
||||
@ -183,11 +193,13 @@ const TaskTree = struct {
|
||||
const resource_maps = try self.api.getMaps(.{ .code = monster_code });
|
||||
defer resource_maps.deinit();
|
||||
|
||||
if (resource_maps.items.len == 0) return error.MapNotFound;
|
||||
// This monster currently doesn't exist on the map. Probably only spawns in certain situations.
|
||||
if (resource_maps.items.len == 0) return null;
|
||||
|
||||
if (resource_maps.items.len > 1) std.log.warn("Multiple map locations exist for monster", .{});
|
||||
const resource_map = resource_maps.items[0];
|
||||
|
||||
_ = try self.appendNode(TaskTreeNode{
|
||||
return try self.appendNode(TaskTreeNode{
|
||||
.parent = parent,
|
||||
.character_task = .{
|
||||
.fight = .{
|
||||
@ -197,9 +209,9 @@ const TaskTree = struct {
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn listByParent(self: *const TaskTree, parent: ?usize) !std.ArrayList(usize) {
|
||||
@ -212,6 +224,31 @@ const TaskTree = struct {
|
||||
return found_nodes;
|
||||
}
|
||||
|
||||
fn listNextTasks(self: *const TaskTree) !std.ArrayList(*CharacterBrain.CharacterTask) {
|
||||
var next_tasks = std.ArrayList(*CharacterBrain.CharacterTask).init(self.allocator);
|
||||
errdefer next_tasks.deinit();
|
||||
|
||||
for (0.., self.nodes.items) |i, *node| {
|
||||
var child_nodes = try self.listByParent(i);
|
||||
defer child_nodes.deinit();
|
||||
|
||||
var can_be_started = true;
|
||||
for (child_nodes.items) |child_node_id| {
|
||||
var child_node = self.nodes.items[child_node_id];
|
||||
if (!child_node.character_task.isComplete()) {
|
||||
can_be_started = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (can_be_started) {
|
||||
try next_tasks.append(&node.character_task);
|
||||
}
|
||||
}
|
||||
|
||||
return next_tasks;
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
self: TaskTree,
|
||||
comptime fmt: []const u8,
|
||||
@ -253,6 +290,11 @@ const TaskTree = struct {
|
||||
for (child_nodes.items) |child_node| {
|
||||
try self.formatNode(child_node, level + 1, writer);
|
||||
}
|
||||
for (node.additional_items.slots.constSlice()) |slot| {
|
||||
const item_code = self.api.getItemCode(slot.id).?;
|
||||
try writer.writeBytesNTimes(" ", level+1);
|
||||
try writer.print("+ {{ \"{s}\" x {} }}\n", .{item_code, slot.quantity});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -264,13 +306,14 @@ pub fn main() !void {
|
||||
var api = try Server.init(allocator);
|
||||
defer api.deinit();
|
||||
|
||||
try api.prefetch();
|
||||
|
||||
const token = (try getAPITokenFromArgs(allocator)) orelse return error.MissingToken;
|
||||
defer allocator.free(token);
|
||||
|
||||
try api.setToken(token);
|
||||
|
||||
std.log.info("prefetching static data", .{});
|
||||
try api.prefetch();
|
||||
|
||||
var goal_manager = GoalManager.init(&api, allocator);
|
||||
defer goal_manager.deinit();
|
||||
|
||||
@ -281,69 +324,17 @@ pub fn main() !void {
|
||||
try goal_manager.addCharacter(char.name);
|
||||
}
|
||||
|
||||
goal_manager.characters.items[0].task = .{
|
||||
.fight = .{
|
||||
.at = Position.init(0, 1),
|
||||
.target = .{
|
||||
.id = try api.getItemId("egg"),
|
||||
.quantity = 1
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
goal_manager.characters.items[1].task = .{
|
||||
.gather = .{
|
||||
.at = Position.init(-1, 0),
|
||||
.target = .{
|
||||
.id = try api.getItemId("ash_wood"),
|
||||
.quantity = 3
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
goal_manager.characters.items[2].task = .{
|
||||
.gather = .{
|
||||
.at = Position.init(2, 0),
|
||||
.target = .{
|
||||
.id = try api.getItemId("copper_ore"),
|
||||
.quantity = 3
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
goal_manager.characters.items[3].task = .{
|
||||
.gather = .{
|
||||
.at = Position.init(4, 2),
|
||||
.target = .{
|
||||
.id = try api.getItemId("gudgeon"),
|
||||
.quantity = 3
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
goal_manager.characters.items[4].task = .{
|
||||
.fight = .{
|
||||
.at = Position.init(0, 1),
|
||||
.target = .{
|
||||
.id = try api.getItemId("raw_chicken"),
|
||||
.quantity = 1
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var task_tree = TaskTree.init(allocator, &api);
|
||||
defer task_tree.deinit();
|
||||
|
||||
try task_tree.appendSubTree("sticky_dagger", 5, null);
|
||||
// try task_tree.appendSubTree("copper_boots" , 5, null);
|
||||
// try task_tree.appendSubTree("copper_helmet", 5, null);
|
||||
// try task_tree.appendSubTree("copper_legs_armor", 5, null);
|
||||
// try task_tree.appendSubTree("copper_armor", 5, null);
|
||||
// try task_tree.appendSubTree("copper_ring", 5, null);
|
||||
// _ = try task_tree.appendSubTree("wooden_staff", 5, null);
|
||||
_ = try task_tree.appendSubTree("wooden_shield", 5, null);
|
||||
|
||||
std.debug.print("{}", .{task_tree});
|
||||
|
||||
if (false) {
|
||||
var default_prng = std.rand.DefaultPrng.init(@bitCast(std.time.timestamp()));
|
||||
var random = default_prng.random();
|
||||
|
||||
std.log.info("Starting main loop", .{});
|
||||
while (true) {
|
||||
goal_manager.runNextAction() catch |err| switch (err) {
|
||||
@ -360,15 +351,24 @@ pub fn main() !void {
|
||||
for (goal_manager.characters.items) |*brain| {
|
||||
if (brain.action_queue.items.len > 0) continue;
|
||||
|
||||
if (brain.isRoutineFinished()) {
|
||||
if (!try brain.depositItemsToBank(&api)) {
|
||||
brain.routine = null;
|
||||
}
|
||||
if (brain.isTaskFinished()) {
|
||||
if (try brain.depositItemsToBank(&api)) {
|
||||
continue;
|
||||
}
|
||||
brain.task = null;
|
||||
}
|
||||
|
||||
try brain.performRoutine(&api);
|
||||
}
|
||||
if (brain.task == null) {
|
||||
var next_tasks = try task_tree.listNextTasks();
|
||||
defer next_tasks.deinit();
|
||||
|
||||
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(&api);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user