add creation of sub goals through requirements

This commit is contained in:
Rokas Puzonas 2025-01-03 18:17:18 +02:00
parent df56eceab6
commit 87c09f1e27
6 changed files with 109 additions and 18 deletions

View File

@ -23,6 +23,7 @@ pub const Equipment = @import("./schemas/equipment.zig");
pub const Craft = @import("./schemas/craft.zig");
pub const Resource = @import("./schemas/resource.zig");
pub const MoveResult = @import("./schemas/move_result.zig");
pub const SimpleItem = @import("./schemas/simple_item.zig");
const SkillUsageResult = @import("./schemas/skill_usage_result.zig");
pub const GatherResult = SkillUsageResult;
pub const CraftResult = SkillUsageResult;

View File

@ -30,10 +30,10 @@ pub fn BoundedArray(comptime slot_count: u32) type {
const Items = std.BoundedArray(SimpleItem, slot_count);
return struct {
items: Items,
items: Items = .{ .len = 0 },
pub fn init() @This() {
return @This(){ .items = Items.init(0) catch unreachable };
return @This(){};
}
pub fn parse(store: *Store, slots_array: std.json.Array) !@This() {
@ -89,6 +89,16 @@ pub fn BoundedArray(comptime slot_count: u32) type {
}
}
pub fn addAssumeCapacity(self: *@This(), id: Store.Id, quantity: u64) void {
if (quantity == 0) return;
if (self.findSlot(id)) |slot| {
slot.quantity += quantity;
} else {
self.items.appendAssumeCapacity(SimpleItem.init(id, quantity));
}
}
pub fn addSlice(self: *@This(), items: []const SimpleItem) void {
for (items) |item| {
self.add(item.id, item.quantity);
@ -117,7 +127,7 @@ pub fn BoundedArray(comptime slot_count: u32) type {
return count;
}
pub fn slice(self: *@This()) []SimpleItem {
pub fn slice(self: @This()) []const SimpleItem {
return self.items.slice();
}

View File

@ -72,7 +72,7 @@ pub fn main() !void {
_ = try artificer.appendGoal(Artificer.Goal{
.craft = .{
.item = store.items.getId("copper").?,
.quantity = 3
.quantity = 2
}
});

View File

@ -1,12 +1,20 @@
// zig fmt: off
const Api = @import("artifacts-api");
const Artificer = @import("./root.zig");
const Requirements = Artificer.Requirements;
const Goal = @This();
item: Api.Store.Id,
quantity: u64,
fn getCraftMultiples(self: Goal, craft: Api.Craft) u64 {
return @intFromFloat(@ceil(
@as(f32, @floatFromInt(self.quantity)) /
@as(f32, @floatFromInt(craft.quantity))
));
}
pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) !void {
const store = artificer.server.store;
const character = store.characters.get(artificer.character).?;
@ -24,18 +32,6 @@ pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) !void
return error.SkillTooLow;
}
const craft_multiples: u64 = @intFromFloat(@ceil(
@as(f32, @floatFromInt(self.quantity)) /
@as(f32, @floatFromInt(craft.quantity))
));
for (craft.items.items.slice()) |craft_item| {
const inventory_item_quantity = character.inventory.getQuantity(craft_item.id);
if (inventory_item_quantity < craft_item.quantity * craft_multiples) {
return error.NotEnoughItems;
}
}
const workshop_position = artificer.findNearestWorkstation(craft.skill).?;
if (!workshop_position.eql(character.position)) {
artificer.queued_actions.appendAssumeCapacity(.{
@ -51,6 +47,21 @@ pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) !void
});
}
pub fn requirements(self: Goal, artificer: *Artificer) Artificer.Requirements {
var reqs: Artificer.Requirements = .{};
const store = artificer.server.store;
const item = store.items.get(self.item).?;
const craft = item.craft.?;
const craft_multiples = self.getCraftMultiples(craft);
for (craft.items.slice()) |craft_item| {
reqs.items.addAssumeCapacity(craft_item.id, craft_item.quantity * craft_multiples);
}
return reqs;
}
pub fn onActionCompleted(self: *Goal, goal_id: Artificer.GoalId, result: Artificer.ActionResult) void {
_ = goal_id;

View File

@ -34,6 +34,15 @@ pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) void
});
}
pub fn requirements(self: Goal, artificer: *Artificer) Artificer.Requirements {
_ = self;
_ = artificer;
const reqs: Artificer.Requirements = .{};
// TODO: add skill requirement
return reqs;
}
pub fn onActionCompleted(self: *Goal, goal_id: Artificer.GoalId, result: Artificer.ActionResult) void {
_ = goal_id;

View File

@ -16,7 +16,11 @@ pub const GoalId = packed struct {
const Index = u11;
generation: Generation,
index: Index
index: Index,
pub fn eql(self: GoalId, other: GoalId) bool {
return self.index == other.index and self.generation == other.generation;
}
};
const max_goals = std.math.maxInt(GoalId.Index);
@ -35,6 +39,13 @@ pub const Goal = union(enum) {
}
}
pub fn requirements(self: Goal, artificer: *Artificer) Requirements {
return switch (self) {
.gather => |gather| gather.requirements(artificer),
.craft => |craft| craft.requirements(artificer),
};
}
pub fn onActionCompleted(self: *Goal, goal_id: Artificer.GoalId, result: Artificer.ActionResult) void {
switch (self.*) {
.gather => |*gather| gather.onActionCompleted(goal_id, result),
@ -45,6 +56,7 @@ pub const Goal = union(enum) {
const GoalSlot = struct {
generation: GoalId.Generation = 0,
parent_goal: ?GoalId = null,
goal: ?Goal = null,
};
@ -68,6 +80,16 @@ const ActionSlot = struct {
action: Action,
};
pub const Requirements = struct {
pub const Items = Api.SimpleItem.BoundedArray(blk: {
var max: usize = 0;
max = @max(max, Api.Craft.max_items);
break :blk max;
});
items: Items = .{}
};
const QueuedActions = std.ArrayListUnmanaged(ActionSlot);
server: *Api.Server,
@ -256,8 +278,19 @@ fn getGoalCount(self: *Artificer) u32 {
return count;
}
fn hasSubGoals(self: *Artificer, goal_id: GoalId) bool {
for (self.goal_slots) |goal_slot| {
if (goal_slot.goal != null and goal_slot.parent_goal != null and goal_slot.parent_goal.?.eql(goal_id)) {
return true;
}
}
return false;
}
pub fn tick(self: *Artificer) !void {
const store = self.server.store;
const character = store.characters.get(self.character).?;
if (self.queued_actions.items.len > 0) {
const expires_in = self.timeUntilCooldownExpires();
@ -265,7 +298,6 @@ pub fn tick(self: *Artificer) !void {
return;
}
const character = store.characters.get(self.character).?;
const action_slot = self.queued_actions.orderedRemove(0);
const action_result = switch (action_slot.action) {
.move => |position| ActionResult{
@ -297,6 +329,34 @@ pub fn tick(self: *Artificer) !void {
.generation = goal_slot.generation
};
if (self.hasSubGoals(goal_id)) {
continue;
}
const reqs = goal.requirements(self);
for (reqs.items.slice()) |req_item| {
const inventory_quantity = character.inventory.getQuantity(req_item.id);
if (inventory_quantity < req_item.quantity) {
if (self.findBestResourceWithItem(req_item.id) != null) {
const subgoal_id = try self.appendGoal(.{
.gather = .{
.item = req_item.id,
.quantity = req_item.quantity - inventory_quantity,
}
});
const subgoal = self.getGoal(subgoal_id).?;
subgoal.parent_goal = goal_id;
} else {
@panic("Not all requirements were handled");
}
}
}
if (self.hasSubGoals(goal_id)) {
continue;
}
try goal.tick(goal_id, self);
}
}