You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
6.3 KiB
200 lines
6.3 KiB
const sqlite = require('better-sqlite3');
|
|
const db = new sqlite('the_big_db.db', { verbose: console.log });
|
|
|
|
var sockets = new Map();
|
|
|
|
function interpret(context, player, command) {
|
|
// interpret and execute a command entered by a player.
|
|
//
|
|
// context: the location the player has entered the command in from.
|
|
// may be null if it's executing from a script?
|
|
// player: the id of the player object who typed in the command.
|
|
// may? be null if it's executing from a script? used for output.
|
|
// so arguably
|
|
// command: the full string typed in by the player
|
|
|
|
const socket = sockets.get(player)
|
|
const words = command.trim().split(' ');
|
|
|
|
try {
|
|
socket?.send(`interpreting and executing the received command: ${command}`);
|
|
|
|
//for the moment we'll only allow statements of the form "go north" with implicit subject: the player
|
|
const [first, second, third, ...rest] = words;
|
|
|
|
const verb = findVerb(first, context, player);
|
|
|
|
console.log(`executing verb ${verb} towards ${player} on ${player} ${second} ${third}`);
|
|
executeVerb(player, verb, player, second, third, ...rest)
|
|
} catch (error) {
|
|
socket?.send(`got an error! ${error.mesage}`);
|
|
}
|
|
|
|
/*
|
|
// if the second word is a verb, but the first word is also a verb, what do we do?
|
|
if (second in verbs) {
|
|
// for now, assume that if the second word is a verb, it's the verb we want.
|
|
executeVerb(player, verbs.get(second), first, third, ...rest);
|
|
} else {
|
|
// if the second word is not a verb, then the first word is the verb, and the player is the implied subject.
|
|
executeVerb(player, verbs.get(first), player, second, third, ...rest)
|
|
}
|
|
*/
|
|
}
|
|
|
|
function findVerb(word, location, actor) {
|
|
// returns the number of a verb which matches the requested word.
|
|
// check for verbs on actor
|
|
let verb = findVerbOnObject(word, actor);
|
|
if (verb) return verb;
|
|
|
|
const actorContents = getAttribute(actor, "contents");
|
|
for (const obj of (actorContents || [])) {
|
|
verb = findVerbOnObject(word, obj);
|
|
if (verb) return verb;
|
|
}
|
|
|
|
const locationContents = getAttribute(location, "contents");
|
|
for (const obj of (locationContents || [])) {
|
|
verb = findVerbOnObject(word, obj);
|
|
if (verb) return verb;
|
|
}
|
|
|
|
verb = findVerbOnObject(word, location);
|
|
if (verb) return verb;
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function findVerbOnObject(word, object) {
|
|
// check for verbs on a single object, and its chain of parents. return a matching verbId, or undefined
|
|
if (!word) return undefined;
|
|
|
|
let focus = object;
|
|
while (focus) {
|
|
const focusVerbs = getAttribute(focus, "verbs");
|
|
for (const verbId of (focusVerbs || [])) {
|
|
const fullVerb = lookUpObject(verbId);
|
|
|
|
if (!fullVerb) continue;
|
|
|
|
// test our word against
|
|
if (word.toLowerCase() == fullVerb.title.toLowerCase()) {
|
|
return verbId;
|
|
}
|
|
}
|
|
focus = getAttribute(focus, "parent");
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function executeVerb(outputObject, verbId, subject, object, ...rest) {
|
|
const fullVerb = lookUpObject(verbId);
|
|
if (!fullVerb) {
|
|
return sockets.get(outputObject)?.send(`verb not found`);
|
|
}
|
|
sockets.get(outputObject)?.send(`verb found as ${fullVerb.name}`);
|
|
const func = fullVerb.fn;
|
|
if (!func) {
|
|
return sockets.get(outputObject)?.send(`verb was found but didn't have a fn attribute`);
|
|
}
|
|
return fullVerb.fn(outputObject, subject, object, ...rest);
|
|
}
|
|
|
|
const objectQuery = db.prepare('select * from pages where number=? order by time desc');
|
|
function lookUpObject(number) {
|
|
return hardCodedObjects(number) || objectQuery.get(number);
|
|
}
|
|
|
|
const attributeQuery = db.prepare(`select * from attributes where number=?`);
|
|
function lookUpObjectAttributes(number) {
|
|
return JSON.parse(attributeQuery.get(number).contents);
|
|
}
|
|
|
|
|
|
function hardCodedObjects(id) {
|
|
// return objectQuery.get(id);
|
|
if (id == 29) return {
|
|
title: "look",
|
|
verb: true,
|
|
description: "send description of direct object to subject's socket",
|
|
fn: (logReceiver, subject, object, ...rest) => {
|
|
sockets.get(logReceiver)?.send(`you looked around! subject ${subject} object: ${object} args: ${rest}`);
|
|
console.log(`${subject} looked at ${object} with args ${rest}, and output was directed to ${logReceiver}`);
|
|
}
|
|
}
|
|
if (id == 31) return {
|
|
title: "warp",
|
|
verb: true,
|
|
description: "this built-in function changes the player's location, as well as telling their client to move",
|
|
fn: (logReceiver, subject, object, ...rest) => {
|
|
const objectNum = verifyObjectReference(object);
|
|
const subjectNum = verifyObjectReference(subject);
|
|
setAttribute(subjectNum, "location", objectNum);
|
|
|
|
sockets.get(logReceiver)?.send(`location change to: ${object}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
const pullAttribute = db.prepare(`select * from attributes where number=?`);
|
|
function getAttribute(obj, attributeName) {
|
|
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
|
const contents = JSON.parse(attributeStore.contents);
|
|
|
|
if (contents.hasOwnProperty(attributeName)) {
|
|
return contents[attributeName];
|
|
}
|
|
|
|
if (contents.hasOwnProperty("parent")) {
|
|
return getAttribute(contents.parent, attributeName);
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function hasOwnAttribute(obj, attributeName) {
|
|
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
|
const contents = JSON.parse(attributeStore.contents);
|
|
|
|
return contents.hasOwnProperty(attributeName);
|
|
}
|
|
|
|
const insertAttribute = db.prepare(`update or replace attributes set contents=:contents where number=:number`);
|
|
function setAttribute(obj, attributeName, value) {
|
|
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
|
const contents = JSON.parse(attributeStore.contents);
|
|
|
|
contents[attributeName] = value;
|
|
|
|
insertAttribute.run({...attributeStore, contents: JSON.stringify(contents)});
|
|
}
|
|
|
|
function deleteAttribute(obj, attributeName) {
|
|
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
|
const contents = JSON.parse(attributeStore.contents);
|
|
|
|
delete contents["attributeName"];
|
|
|
|
insertAttribute.run({...attributeStore, contents: JSON.stringify(contents)});
|
|
}
|
|
|
|
function verifyObjectReference(obj) {
|
|
try {
|
|
if (typeof(obj) === "string") {
|
|
return Number(obj.replace("#", ""));
|
|
} else if (typeof(obj) === "number") {
|
|
return obj;
|
|
} else if (typeof(obj.number) == "number") {
|
|
return obj.number;
|
|
}
|
|
} catch (error) {
|
|
throw new Error("Tried to get an attribute from something which wasn't an object");
|
|
}
|
|
}
|
|
|
|
module.exports = { interpret, sockets, lookUpObject, getAttribute, setAttribute, deleteAttribute, hasOwnAttribute }; |