import { bagSpecParser } from "./string_utils.js";
import { createContainer, newCreateBag } from "./create_bag.js";
import { createLocationNameInSequence } from "./create_location_name.js";
import { Location, Store } from "./models/location.js";
import { locationsLoader } from "./data_loaders.js";
import { logger } from "./utils.js";
import { random, selectElements } from "./random_utils.js";
import LocationDatabase from "./db/location_database.js";
const locations = await locationsLoader("locations.data");
export var database = new LocationDatabase();
database.add.apply(database, locations);
database.verify();
const LOCATION_TYPES = database.models
.filter((m) => !m.room)
.map((m) => m.type)
.sort();
export function getLocationTypes() {
return LOCATION_TYPES;
}
/**
* Create a location. You must supply the type of a location template and an instance tree
* will be created from that point in the template hierarchy, working downward throw all
* child nodes templates, returning the resulting instance tree. Tags are not currently used
* in selection of templates, but may be in the future.
*/
export function createLocation({ type = random(LOCATION_TYPES), tags } = {}) {
logger.start("createLocation", { type, tags });
let template = database.findOne({ type, tags });
if (template) {
let root = createInstance(new Location({}), 1, template);
walkTemplate(root, root.seqValue, template);
return logger.end(root);
}
return logger.end(null);
}
function walkTemplate(parent, parentSeqValue, template) {
let elements = selectElements(template.ch);
elements.forEach((type) => {
let childTemplate = database.findOne({ type });
if (!childTemplate) {
console.error("Could not find template for " + type);
return;
}
if (childTemplate.abstract) {
// Abstract templates are not instantiated, but their children are processed
walkTemplate(parent, parentSeqValue, childTemplate);
} else {
let childLocation = createInstance(parent, parentSeqValue, childTemplate);
walkTemplate(childLocation, childLocation.seqValue, childTemplate);
}
});
}
function createInstance(parent, parentSeqValue, childTemplate) {
let child = childTemplate.owner ? new Store(childTemplate) : new Location(childTemplate);
// contents is what exists in a room or place, including containers that contain things
child.contents = determineContents(childTemplate, "contents");
// inventory is specifically for stores
child.inventory = determineContents(childTemplate, "inventory");
if (parent) {
parent.addChild(child);
}
// childIndex is not the right value since the child could be in an unusual position. For example,
// the main floor of a Radio Station could have the rooms Lobby, Break Room, Office and Office.
// In this case the offices would be labeled #103 and #104, though you'd expect them to be
// #101 and #102 since they are the first and second OFFICE on the floor. We get the offset,
// which is based on index of the first occurrence of the type in the parent’s children.
let offset = getOffset(child);
let [childSeqValue, name] = createLocationNameInSequence({
parent,
child,
childIndex: offset,
parentSeqValue,
});
child.seqValue = childSeqValue;
child.name = name;
return child;
}
function getOffset(child) {
let parent = child.parent;
let childTypes = ((parent && parent.children) || []).map((ch) => ch.type);
let first = childTypes.indexOf(child.type);
let last = childTypes.lastIndexOf(child.type);
return last - first;
}
function determineContents(template, propName) {
return selectElements(template[propName])
.map((child) => {
if (child.startsWith("Bag")) {
return newCreateBag(bagSpecParser(child));
} else if (child.startsWith("Stockpile")) {
return newCreateBag(bagSpecParser(child));
// return createStockpile(bagSpecParser(child)); output of this sucks
}
return createContainer({ type: child });
})
.filter((el) => el !== null);
}