import { random } from "../random_utils.js";
import { tagFilter } from "./database.js";
import { toTag } from "../string_utils.js";
import Item from "../models/item.js";
import RarityDatabase from "./rarity_database.js";
function parser(line) {
let parts = line.split("!");
let names = parts[0].trim().split(/\s*;\s*/);
let [all, value, freq, enc] = parts[1].match(/(.*)([ruc])(.*)/);
let tagArray = parts[2]
.trim()
.split(/\s+/)
.filter((s) => s !== "");
let frequency = freq === "c" ? "common" : freq === "u" ? "uncommon" : "rare";
let tags = new Set(tagArray);
var consumes;
if (tags.has("cons")) {
consumes = tagArray[tagArray.indexOf("cons") + 1];
}
let results = [];
let params = {
value: parseFloat(value),
consumes,
enc,
frequency,
tags,
};
names.forEach((name) => {
params.name = name;
add(results, params);
});
return results;
}
function add(array, params) {
let item = new Item(params);
item.tags.add(toTag(params.name)); // add name in tag form
array.push(item);
}
/**
* Specialized database for storing and querying game items with rarity-based selection.
* Extends RarityDatabase to provide item-specific filtering and search capabilities.
*
* This class uses a specific parser when configuration data is added via the `add` method.
* The format is as follows:
*
* `name1; name2!valueRarityEnc!tag1 tag2 tag3 ...`
*
* One or more names in the first position should be separated with semicolons. The
* valueRarityEnc value should be in the format `#r#`, e.g. `50c5` for a value of 50, common
* rarity, and encumbrance of 5. Finally there can be one or more tags in the final
* position, that must follow the `item.schema.data`.
*
* @class ItemDatabase
* @extends RarityDatabase
*/
export default class ItemDatabase extends RarityDatabase {
/**
* Creates a new ItemDatabase instance.
* @constructor
*/
constructor() {
super({ parser });
}
/**
* Finds all items matching the specified criteria.
* @param {Object} [options={}] - Search criteria
* @param {string} [options.name] - Name substring to match
* @param {string} [options.tags] - Tag filter string
* @param {number} [options.minValue=0] - Minimum item value
* @param {number} [options.maxValue=Number.MAX_VALUE] - Maximum item value
* @param {number} [options.minEnc=0] - Minimum encumbrance
* @param {number} [options.maxEnc=Number.MAX_VALUE] - Maximum encumbrance
* @returns {Array<Item>} Array of matching Item objects
*
* @example
* itemDb.findAll({ tags: "weapon sword", minValue: 10, maxValue: 100 });
*/
findAll({
name,
tags,
minValue = 0,
maxValue = Number.MAX_VALUE,
minEnc = 0,
maxEnc = Number.MAX_VALUE,
} = {}) {
return this.table.rows
.filter(
(model) =>
model.name.indexOf(name) > -1 ||
(model.value >= minValue &&
model.value <= maxValue &&
model.enc >= minEnc &&
model.enc <= maxEnc &&
tagFilter(model, tags))
)
.map((item) => new Item(item));
}
/**
* Finds a single random item matching the specified criteria,
* weighted by rarity (rare items are less likely to be selected).
* @param {Object} [options={}] - Search criteria
* @param {string} [options.name] - Name substring to match
* @param {string} [options.tags] - Tag filter string
* @param {number} [options.minValue=0] - Minimum item value
* @param {number} [options.maxValue=Number.MAX_VALUE] - Maximum item value
* @param {number} [options.minEnc=0] - Minimum encumbrance
* @param {number} [options.maxEnc=Number.MAX_VALUE] - Maximum encumbrance
* @returns {Item|null} A random matching Item object, or null if none found
*
* @example
* itemDb.findOne({ tags: "weapon", minValue: 5 });
*/
findOne({
name,
tags,
minValue = 0,
maxValue = Number.MAX_VALUE,
minEnc = 0,
maxEnc = Number.MAX_VALUE,
} = {}) {
var freqs = this.table.weightedSort();
while (freqs.length) {
var freq = freqs.shift();
var rows = this.table[freq].filter(
(model) =>
model.name.indexOf(name) > -1 ||
(model.value >= minValue &&
model.value <= maxValue &&
model.enc >= minEnc &&
model.enc <= maxEnc &&
tagFilter(model, tags))
);
if (rows.length) {
return new Item(random(rows));
}
}
return null;
}
}