artificer/api/store.zig

303 lines
8.6 KiB
Zig

const std = @import("std");
const json_utils = @import("json_utils.zig");
const Server = @import("./server.zig");
const json = std.json;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const Store = @This();
const Character = @import("./schemas/character.zig");
const Item = @import("./schemas/item.zig");
const Position = @import("./position.zig");
const Map = @import("./schemas/map.zig");
const Resource = @import("./schemas/resource.zig");
const Monster = @import("./schemas/monster.zig");
const DropRate = @import("./schemas/drop_rate.zig");
pub const CodeId = u16;
allocator: Allocator,
codes: std.ArrayList([]u8),
characters: std.ArrayList(Character),
items: std.StringHashMap(Item),
maps: std.AutoHashMap(Position, Map),
resources: std.StringHashMap(Resource),
monsters: std.StringHashMap(Monster),
// TODO: bank
pub fn init(allocator: Allocator) Store {
return Store{
.allocator = allocator,
.codes = std.ArrayList([]u8).init(allocator),
.characters = std.ArrayList(Character).init(allocator),
.items = std.StringHashMap(Item).init(allocator),
.maps = std.AutoHashMap(Position, Map).init(allocator),
.resources = std.StringHashMap(Resource).init(allocator),
.monsters = std.StringHashMap(Monster).init(allocator),
};
}
pub fn deinit(self: *Store) void {
for (self.codes.items) |code| {
self.allocator.free(code);
}
self.codes.deinit();
for (self.characters.items) |*char| {
char.deinit();
}
self.characters.deinit();
var itemsIter = self.items.valueIterator();
while (itemsIter.next()) |item| {
item.deinit();
}
self.items.deinit();
var mapsIter = self.maps.valueIterator();
while (mapsIter.next()) |map| {
map.deinit(self.allocator);
}
self.maps.deinit();
var resourcesIter = self.resources.valueIterator();
while (resourcesIter.next()) |resource| {
resource.deinit(self.allocator);
}
self.resources.deinit();
var monstersIter = self.monsters.valueIterator();
while (monstersIter.next()) |monster| {
monster.deinit(self.allocator);
}
self.monsters.deinit();
}
pub fn getCodeId(self: *Store, code: []const u8) !CodeId {
assert(code.len != 0);
for (0.., self.codes.items) |i, item_code| {
if (std.mem.eql(u8, code, item_code)) {
return @intCast(i);
}
}
const code_dupe = try self.allocator.dupe(u8, code);
errdefer self.allocator.free(code_dupe);
try self.codes.append(code_dupe);
return @intCast(self.codes.items.len - 1);
}
pub fn getCode(self: *const Store, id: CodeId) ?[]const u8 {
if (id >= self.codes.items.len) {
return null;
}
return self.codes.items[id];
}
pub fn getCodeIdJson(self: *Store, object: json.ObjectMap, name: []const u8) !?CodeId {
const code = try json_utils.getStringRequired(object, name);
if (code.len == 0) {
return null;
}
return try self.getCodeId(code);
}
// ----------------------- Character ------------------------------
fn getCharacterIndex(self: *const Store, name: []const u8) ?usize {
for (0.., self.characters.items) |i, character| {
if (std.mem.eql(u8, character.name, name)) {
return i;
}
}
return null;
}
pub fn getCharacter(self: *Store, name: []const u8) ?Character {
if (self.getCharacterIndex(name)) |index| {
return self.characters.items[index];
}
return null;
}
pub fn putCharacter(self: *Store, character: Character) !void {
if (self.getCharacterIndex(character.name)) |index| {
self.characters.items[index].deinit();
self.characters.items[index] = character;
} else {
try self.characters.append(character);
}
}
// ----------------------- Map ------------------------------
pub fn getMap(self: *Store, x: i64, y: i64) ?Map {
const pos = Position.init(x, y);
return self.maps.get(pos);
}
pub fn getMaps(self: *Store, opts: Server.MapOptions) !std.ArrayList(Map) {
var found = std.ArrayList(Map).init(self.allocator);
errdefer found.deinit();
var mapIter = self.maps.valueIterator();
while (mapIter.next()) |map| {
if (opts.type) |content_type| {
if (map.content == null) continue;
if (map.content.?.type != content_type) continue;
}
if (opts.code) |content_code| {
if (map.content == null) continue;
if (!std.mem.eql(u8, map.content.?.code, content_code)) continue;
}
try found.append(map.*);
}
return found;
}
pub fn putMap(self: *Store, map: Map) !void {
var entry = try self.maps.getOrPut(map.position);
if (entry.found_existing) {
entry.value_ptr.deinit(self.allocator);
}
entry.value_ptr.* = map;
}
// ----------------------- Item ------------------------------
pub fn getItem(self: *Store, code: []const u8) ?Item {
return self.items.get(code);
}
pub fn getItems(self: *Store, opts: Server.ItemOptions) !std.ArrayList(Item) {
var found = std.ArrayList(Item).init(self.allocator);
errdefer found.deinit();
var itemIter = self.items.valueIterator();
while (itemIter.next()) |item| {
if (opts.craft_skill) |craft_skill| {
if (item.craft == null) continue;
if (item.craft.?.skill != craft_skill) continue;
}
if (opts.craft_material) |craft_material| {
if (item.craft == null) continue;
const recipe = item.craft.?;
const craft_material_id = try self.getItemId(craft_material);
const material_quantity = recipe.items.getQuantity(craft_material_id);
if (material_quantity == 0) continue;
}
if (opts.min_level) |min_level| {
if (item.level < min_level) continue;
}
if (opts.max_level) |max_level| {
if (item.level > max_level) continue;
}
if (opts.type) |item_type| {
if (item.type != item_type) continue;
}
if (opts.name) |name| {
if (std.mem.indexOf(u8, item.name, name) == null) continue;
}
try found.append(item.*);
}
return found;
}
pub fn putItem(self: *Store, item: Item) !void {
var entry = try self.items.getOrPut(item.code);
if (entry.found_existing) {
entry.value_ptr.deinit();
}
entry.value_ptr.* = item;
}
// ----------------------- Monster ------------------------------
pub fn getMonster(self: *Store, code: []const u8) ?Monster {
return self.monsters.get(code);
}
pub fn getMonsters(self: *Store, opts: Server.MonsterOptions) !std.ArrayList(Monster) {
var found = std.ArrayList(Monster).init(self.allocator);
errdefer found.deinit();
var monsterIter = self.monsters.valueIterator();
while (monsterIter.next()) |monster| {
if (opts.min_level) |min_level| {
if (monster.level < min_level) continue;
}
if (opts.max_level) |max_level| {
if (monster.level > max_level) continue;
}
if (opts.drop) |drop| {
const item_id = try self.getItemId(drop);
if (!DropRate.doesListContain(&monster.drops, item_id)) {
continue;
}
}
try found.append(monster.*);
}
return found;
}
pub fn putMonster(self: *Store, monster: Monster) !void {
var entry = try self.resources.getOrPut(monster.code_id);
if (entry.found_existing) {
entry.value_ptr.deinit(self.allocator);
}
entry.value_ptr.* = monster;
}
// ----------------------- Resource ------------------------------
pub fn getResource(self: *Store, code: []const u8) ?Resource {
return self.resources.get(code);
}
pub fn getResources(self: *Store, opts: Server.ResourceOptions) !std.ArrayList(Resource) {
var found = std.ArrayList(Resource).init(self.allocator);
errdefer found.deinit();
var resourceIter = self.resources.valueIterator();
while (resourceIter.next()) |resource| {
if (opts.min_level) |min_level| {
if (resource.level < min_level) continue;
}
if (opts.max_level) |max_level| {
if (resource.level > max_level) continue;
}
if (opts.drop) |drop| {
const item_id = try self.getItemId(drop);
if (!DropRate.doesListContain(&resource.drops, item_id)) {
continue;
}
}
try found.append(resource.*);
}
return found;
}
pub fn putResource(self: *Store, resource: Resource) !void {
var entry = try self.resources.getOrPut(resource.code);
if (entry.found_existing) {
entry.value_ptr.deinit(self.allocator);
}
entry.value_ptr.* = resource;
}