262 lines
7.5 KiB
Zig
262 lines
7.5 KiB
Zig
const std = @import("std");
|
|
const Api = @import("artifacts-api");
|
|
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) {
|
|
xp: u64,
|
|
item: Api.ItemIdQuantity,
|
|
};
|
|
|
|
pub const Task = union(enum) {
|
|
fight: struct {
|
|
at: Position,
|
|
until: UntilCondition,
|
|
progress: u64 = 0,
|
|
},
|
|
gather: struct {
|
|
at: Position,
|
|
until: UntilCondition,
|
|
progress: u64 = 0,
|
|
},
|
|
craft: struct {
|
|
at: Position,
|
|
target: Api.ItemIdQuantity,
|
|
progress: u64 = 0,
|
|
},
|
|
accept_task: struct {
|
|
done: bool = false
|
|
},
|
|
|
|
pub fn isComplete(self: Task) bool {
|
|
return switch (self) {
|
|
.fight => |args| args.progress >= args.until.item.quantity,
|
|
.gather => |args| args.progress >= args.until.item.quantity,
|
|
.craft => |args| args.progress >= args.target.quantity,
|
|
.accept_task => |args| args.done
|
|
};
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
};
|
|
|