160 lines
5.4 KiB
Zig
160 lines
5.4 KiB
Zig
const std = @import("std");
|
|
const json_utils = @import("../json_utils.zig");
|
|
const Store = @import("../store.zig");
|
|
const Position = @import("../position.zig");
|
|
const parseDateTime = @import("../date_time/parse.zig").parseDateTime;
|
|
const Allocator = std.mem.Allocator;
|
|
const json = std.json;
|
|
const assert = std.debug.assert;
|
|
|
|
const SkillStats = @import("./skill_stats.zig");
|
|
const CombatStats = @import("./combat_stats.zig");
|
|
const Equipment = @import("./equipment.zig");
|
|
const BoundedSlotsArray = @import("./slot_array.zig").BoundedSlotsArray;
|
|
|
|
const Inventory = BoundedSlotsArray(20);
|
|
|
|
const Character = @This();
|
|
|
|
const TaskMasterTask = struct {
|
|
target: []u8,
|
|
type: []u8,
|
|
progress: u64,
|
|
total: u64,
|
|
};
|
|
|
|
allocator: Allocator,
|
|
|
|
name: []u8,
|
|
skin: []u8,
|
|
account: ?[]u8,
|
|
gold: i64,
|
|
hp: i64,
|
|
haste: i64,
|
|
position: Position,
|
|
cooldown_expiration: f64,
|
|
|
|
combat: SkillStats,
|
|
mining: SkillStats,
|
|
woodcutting: SkillStats,
|
|
fishing: SkillStats,
|
|
weaponcrafting: SkillStats,
|
|
gearcrafting: SkillStats,
|
|
jewelrycrafting: SkillStats,
|
|
cooking: SkillStats,
|
|
|
|
water: CombatStats,
|
|
fire: CombatStats,
|
|
earth: CombatStats,
|
|
air: CombatStats,
|
|
|
|
equipment: Equipment,
|
|
|
|
inventory_max_items: u64,
|
|
inventory: Inventory,
|
|
|
|
task: ?TaskMasterTask,
|
|
|
|
pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Character {
|
|
const inventory = json_utils.getArray(obj, "inventory") orelse return error.MissingProperty;
|
|
const cooldown_expiration = json_utils.getString(obj, "cooldown_expiration") orelse return error.MissingProperty;
|
|
|
|
const x = try json_utils.getIntegerRequired(obj, "x");
|
|
const y = try json_utils.getIntegerRequired(obj, "y");
|
|
const name = (try json_utils.dupeString(allocator, obj, "name")) orelse return error.MissingProperty;
|
|
if (name.len == 0) {
|
|
return error.InvalidName;
|
|
}
|
|
|
|
const inventory_max_items = json_utils.getInteger(obj, "inventory_max_items") orelse return error.MissingProperty;
|
|
if (inventory_max_items < 0) {
|
|
return error.InvalidInventoryMaxItems;
|
|
}
|
|
|
|
var task: ?TaskMasterTask = null;
|
|
const task_target = try json_utils.getStringRequired(obj, "task");
|
|
if (task_target.len > 0) {
|
|
const progress = try json_utils.getIntegerRequired(obj, "task_progress");
|
|
if (progress < 0) {
|
|
return error.InvalidTaskProgress;
|
|
}
|
|
|
|
const total = try json_utils.getIntegerRequired(obj, "task_total");
|
|
if (total < 0) {
|
|
return error.InvalidTaskTotal;
|
|
}
|
|
|
|
task = TaskMasterTask{
|
|
.target = try allocator.dupe(u8, task_target),
|
|
.type = try json_utils.dupeStringRequired(allocator, obj, "task_type"),
|
|
.total = @intCast(total),
|
|
.progress = @intCast(progress),
|
|
};
|
|
}
|
|
|
|
return Character{
|
|
.allocator = allocator,
|
|
.account = try json_utils.dupeString(allocator, obj, "account"),
|
|
.name = name,
|
|
.skin = (try json_utils.dupeString(allocator, obj, "skin")) orelse return error.MissingProperty,
|
|
|
|
.gold = try json_utils.getIntegerRequired(obj, "gold"),
|
|
.hp = try json_utils.getIntegerRequired(obj, "hp"),
|
|
.haste = try json_utils.getIntegerRequired(obj, "haste"),
|
|
.position = Position.init(x, y),
|
|
.cooldown_expiration = parseDateTime(cooldown_expiration) orelse return error.InvalidDateTime,
|
|
|
|
.combat = try SkillStats.parse(obj, "level", "xp", "max_xp"),
|
|
.mining = try SkillStats.parse(obj, "mining_level", "mining_xp", "mining_max_xp"),
|
|
.woodcutting = try SkillStats.parse(obj, "woodcutting_level", "woodcutting_xp", "woodcutting_max_xp"),
|
|
.fishing = try SkillStats.parse(obj, "fishing_level", "fishing_xp", "fishing_max_xp"),
|
|
.weaponcrafting = try SkillStats.parse(obj, "weaponcrafting_level", "weaponcrafting_xp", "weaponcrafting_max_xp"),
|
|
.gearcrafting = try SkillStats.parse(obj, "gearcrafting_level", "gearcrafting_xp", "gearcrafting_max_xp"),
|
|
.jewelrycrafting = try SkillStats.parse(obj, "jewelrycrafting_level", "jewelrycrafting_xp", "jewelrycrafting_max_xp"),
|
|
.cooking = try SkillStats.parse(obj, "cooking_level", "cooking_xp", "cooking_max_xp"),
|
|
|
|
.water = try CombatStats.parse(obj, "attack_water", "dmg_water", "res_water"),
|
|
.fire = try CombatStats.parse(obj, "attack_fire", "dmg_fire", "res_fire"),
|
|
.earth = try CombatStats.parse(obj, "attack_earth", "dmg_earth", "res_earth"),
|
|
.air = try CombatStats.parse(obj, "attack_air", "dmg_air", "res_air"),
|
|
|
|
.equipment = try Equipment.parse(store, obj),
|
|
|
|
.inventory_max_items = @intCast(inventory_max_items),
|
|
.inventory = try Inventory.parse(store, inventory),
|
|
|
|
.task = task
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *Character) void {
|
|
if (self.account) |str| self.allocator.free(str);
|
|
self.allocator.free(self.name);
|
|
self.allocator.free(self.skin);
|
|
|
|
if (self.task) |task| {
|
|
self.allocator.free(task.type);
|
|
self.allocator.free(task.target);
|
|
}
|
|
}
|
|
|
|
pub fn getItemCount(self: *const Character) u64 {
|
|
return self.inventory.totalQuantity();
|
|
}
|
|
|
|
pub fn format(
|
|
self: Character,
|
|
comptime fmt: []const u8,
|
|
options: std.fmt.FormatOptions,
|
|
writer: anytype,
|
|
) !void {
|
|
_ = fmt;
|
|
_ = options;
|
|
|
|
try writer.print("{s}{{ .name = \"{s}\", .position = {} ... }}", .{
|
|
@typeName(Character),
|
|
self.name,
|
|
self.position
|
|
});
|
|
}
|