";
});
return content;
});
logFancy(`(v${self.version}) initialized.`);
}
}
class FlatMMOPlus {
constructor() {
this.version = VERSION;
this.plugins = {};
this.panels = {};
this.debug = false;
this.nextUniqueId = 1;
this.customChatCommands = {
help: (command, data) => {
console.log("help", command, data);
}
}
this.customChatHelp = {};
this.customDialogOptions = {};
this.currentPanel = "inventory";
if(localStorage.getItem(LOCAL_STORAGE_KEY_DEBUG) == "1") {
this.debug = true;
}
}
registerCustomChatCommand(command, f, help) {
if (Array.isArray(command)) {
command.forEach(cmd => this.registerCustomChatCommand(cmd, f, help))
return;
}
if(typeof command !== "string" || typeof f !== "function") {
throw new TypeError("FlatMMOPlus.registerCustomChatCommand takes the following arguments: (command:string, f:function)");
}
if(CHAT_COMMAND_NO_OVERRIDE.includes(command)) {
throw new Error(`Cannot override the following chat commands: ${CHAT_COMMAND_NO_OVERRIDE.join(", ")}`);
}
if(command in this.customChatCommands) {
console.warn(`FlatMMOPlus: re-registering custom chat command "${command}" which already exists.`);
}
this.customChatCommands[command] = f;
if(help && typeof help === "string") {
this.customChatHelp[command] = help.replace(/%COMMAND%/g, command);
} else {
delete this.customChatHelp[command];
}
}
handleCustomChatCommand(command, message) {
// return true if command handler exists, false otherwise
const f = this.customChatCommands[command];
if(typeof f === "function") {
try {
f(command, message);
}
catch(err) {
console.error(`Error executing custom command "${command}"`, err);
}
return true;
}
return false;
}
uniqueId() {
return this.nextUniqueId++;
}
setDebug(debug) {
if(debug) {
this.debug = true;
localStorage.setItem(LOCAL_STORAGE_KEY_DEBUG, "1");
}
else {
this.debug = false;
localStorage.removeItem(LOCAL_STORAGE_KEY_DEBUG);
}
}
setPluginConfigUIDirty(id, dirty) {
if(typeof id !== "string" || typeof dirty !== "boolean") {
throw new TypeError("FlatMMOPlus.setPluginConfigUIDirty takes the following arguments: (id:string, dirty:boolean)");
}
const plugin = this.plugins[id];
const button = document.getElementById(`flatmmoplus-configbutton-${plugin.id}-apply`);
if(button) {
button.disabled = !dirty;
}
}
loadPluginConfigs(id) {
if (typeof id !== "string") {
throw new TypeError("FlatMMOPlus.reloadPluginConfigs takes the following arguments: (id:string)");
}
const plugin = this.plugins[id];
const config = {};
let stored;
try {
stored = JSON.parse(localStorage.getItem(`flatmmoplus.${id}.config`) || "{}");
} catch(err) {
console.error(`Failed to load configs for plugin with id "${id} - will use defaults instead."`);
stored = {};
}
if (plugin.opts.config && Array.isArray(plugin.opts.config)) {
plugin.opts.config.forEach(cfg => {
const el = document.getElementById(`flatmmoplus-config-${plugin.id}-${cfg.id}`);
let value = stored[cfg.id];
if (value == null || typeof value === "undefined") {
value = cfg.default;
}
config[cfg.id] = value;
if (el) {
if (CONFIG_TYPES_BOOLEAN.includes(cfg.type) && typeof value === "boolean") {
el.checked = value;
} else if (CONFIG_TYPES_INTEGER.includes(cfg.type) && typeof value === "number") {
el.value = value;
} else if (CONFIG_TYPES_FLOAT.includes(cfg.type) && typeof value === "number") {
el.value = value;
} else if (CONFIG_TYPES_STRING.includes(cfg.type) && typeof value === "string") {
el.value = value;
} else if (CONFIG_TYPES_SELECT.includes(cfg.type) && typeof value === "string") {
el.value = value;
} else if (CONFIG_TYPES_COLOR.includes(cfg.type) && typeof value === "string") {
el.value = value;
}
}
});
}
plugin.config = config;
this.setPluginConfigUIDirty(id, false);
if (typeof plugin.onConfigsChanged === "function") {
plugin.onConfigsChanged();
}
}
savePluginConfigs(id) {
if (typeof id !== "string") {
throw new TypeError("FlatMMOPlus.savePluginConfigs takes the following arguments: (id:string)");
}
const plugin = this.plugins[id];
const config = {};
if (plugin.opts.config && Array.isArray(plugin.opts.config)) {
plugin.opts.config.forEach(cfg => {
const el = document.getElementById(`flatmmoplus-config-${plugin.id}-${cfg.id}`);
if (CONFIG_TYPES_BOOLEAN.includes(cfg.type)) {
config[cfg.id] = el.checked;
} else if (CONFIG_TYPES_INTEGER.includes(cfg.type)) {
config[cfg.id] = parseInt(el.value);
} else if (CONFIG_TYPES_FLOAT.includes(cfg.type)) {
config[cfg.id] = parseFloat(el.value);
} else if (CONFIG_TYPES_STRING.includes(cfg.type)) {
config[cfg.id] = el.value;
} else if (CONFIG_TYPES_SELECT.includes(cfg.type)) {
config[cfg.id] = el.value;
} else if (CONFIG_TYPES_COLOR.includes(cfg.type)) {
config[cfg.id] = el.value;
}
});
}
plugin.config = config;
localStorage.setItem(`flatmmoplus.${id}.config`, JSON.stringify(config));
this.setPluginConfigUIDirty(id, false);
if (typeof plugin.onConfigsChanged === "function") {
plugin.onConfigsChanged();
}
}
addPanel(id, title, content) {
if(typeof id !== "string" || typeof title !== "string" || (typeof content !== "string" && typeof content !== "function") ) {
throw new TypeError("FlatMMOPlus.addPanel takes the following arguments: (id:string, title:string, content:string|function)");
}
const lastPanel = document.querySelector("#ui-panel-worship");
lastPanel.insertAdjacentHTML("afterend",`
${title}
`);
this.panels[id] = {
id: id,
title: title,
content: content
};
this.refreshPanel(id);
}
refreshPanel(id) {
if(typeof id !== "string") {
throw new TypeError("FlatMMOPlus.refreshPanel takes the following arguments: (id:string)");
}
const panel = this.panels[id];
if(!panel) {
throw new TypeError(`Error rendering panel with id="${id}" - panel has not be added.`);
}
let content = panel.content;
if(!["string", "function"].includes(typeof content)) {
throw new TypeError(`Error rendering panel with id="${id}" - panel.content must be a string or a function returning a string.`);
}
if(typeof content === "function") {
content = content();
if(typeof content !== "string") {
throw new TypeError(`Error rendering panel with id="${id}" - panel.content must be a string or a function returning a string.`);
}
}
const panelContent = document.getElementById(`ui-panel-${id}-content`);
panelContent.innerHTML = content;
if(id === "flatmmoplus") {
this.forEachPlugin(plugin => {
this.loadPluginConfigs(plugin.id);
});
}
}
registerPlugin(plugin) {
if(!(plugin instanceof FlatMMOPlusPlugin)) {
throw new TypeError("FlatMMOPlus.registerPlugin takes the following arguments: (plugin:FlatMMOPlusPlugin)");
}
if(plugin.id in this.plugins) {
throw new Error(`FlatMMOPlusPlugin with id "${plugin.id}" is already registered. Make sure your plugin id is unique!`);
}
this.plugins[plugin.id] = plugin;
this.loadPluginConfigs(plugin.id);
let versionString = plugin.opts&&plugin.opts.about&&plugin.opts.about.version ? ` (v${plugin.opts.about.version})` : "";
logFancy(`registered plugin "${plugin.id}"${versionString}`);
}
forEachPlugin(f) {
if(typeof f !== "function") {
throw new TypeError("FlatMMOPlus.forEachPlugin takes the following arguments: (f:function)");
}
Object.values(this.plugins).forEach(plugin => {
try {
f(plugin);
}
catch(err) {
console.error(`Error occurred while executing function for plugin "${plugin.id}."`);
console.error(err);
}
});
}
setPanel(panel) {
if(typeof panel !== "string") {
throw new TypeError("FlatMMOPlus.setPanel takes the following arguments: (panel:string)");
}
window.switch_panels(panel);
}
sendMessage(message) {
if(typeof message !== "string") {
throw new TypeError("FlatMMOPlus.sendMessage takes the following arguments: (message:string)");
}
if(Globals.websocket && Globals.websocket.readyState == 1) {
Globals.websocket.send(message);
}
}
hideCustomPanels() {
Object.values(this.panels).forEach((panel) => {
const el = document.getElementById(`ui-panel-${panel.id}`);
if(el) {
el.style.display = "none";
}
});
}
onMessageReceived(data) {
if(this.debug) {
console.log(`FM+ onMessageReceived: ${data}`);
}
if(data) {
this.forEachPlugin((plugin) => {
if(typeof plugin.onMessageReceived === "function") {
plugin.onMessageReceived(data);
}
});
if(data.startsWith("LOGGED_IN")) {
this.onLogin();
} else if (data.startsWith("CHAT_LOCAL_MESSAGE=")) {
const split = data.substring("CHAT_LOCAL_MESSAGE=".length).split("~");
let [sender, message] = split[1].split(" yelled: ");
//Server messages don't have the "yelled"
if (!message) {
message = sender;
sender = "";
}
const chatData = {
username: sender,
tag: "none",
sigil: "none",
color: split[0],
message: message,
yell: true
}
this.onChat(chatData);
} else if (data.startsWith("CHAT=")) {
const split = data.substring("CHAT=".length).split("~");
const chatData = {
username: split[0],
tag: split[1],
sigil: split[2],
color: split[3],
message: split[4],
yell: false
}
this.onChat(chatData);
}
}
}
onLogin() {
if(this.debug) {
console.log(`FM+ onLogin`);
}
logFancy("login detected");
this.forEachPlugin((plugin) => {
if(typeof plugin.onLogin === "function") {
plugin.onLogin();
}
});
document.getElementById("chat").insertAdjacentHTML("beforeend",`
FYI: Use the /help command to see information on available chat commands.
`)
//Chat auto scroll is always true for now
chat_div_element.scrollTop = chat_div_element.scrollHeight;
}
onChat(data) {
if(this.debug) {
console.log(`FM+ onChat`, data);
}
this.forEachPlugin((plugin) => {
if(typeof plugin.onChat === "function") {
plugin.onChat(data);
}
});
}
onPanelChanged(panelBefore, panelAfter) {
if(this.debug) {
console.log(`FM+ onPanelChanged "${panelBefore}" -> "${panelAfter}"`);
}
if(panelAfter === "flatmmoplus") {
this.refreshPanel("flatmmoplus");
}
this.forEachPlugin((plugin) => {
if(typeof plugin.onPanelChanged === "function") {
plugin.onPanelChanged(panelBefore, panelAfter);
}
});
}
onMapChanged(mapBefore, mapAfter) {
if(this.debug) {
console.log(`FMMO+ onMapChanged "${mapBefore}" -> "${mapAfter}"`);
}
this.forEachPlugin((plugin) => {
if(typeof plugin.onMapChanged === "function") {
plugin.onMapChanged(mapBefore, mapAfter);
}
});
}
onInventoryChanged(inventoryBefore, inventoryAfter) {
if(this.debug) {
console.log(`FMMO+ onInventoryChanged "${inventoryBefore}" -> "${inventoryAfter}"`);
}
this.forEachPlugin((plugin) => {
if(typeof plugin.onInventoryChanged === "function") {
plugin.onInventoryChanged(inventoryBefore, inventoryAfter);
}
});
}
}
// Add to window and init
window.FlatMMOPlusPlugin = FlatMMOPlusPlugin;
window.FlatMMOPlus = new FlatMMOPlus();
window.FlatMMOPlus.customChatCommands["help"] = (command, data='') => {
let help;
if(data && data!="help") {
let helpContent = window.FlatMMOPlus.customChatHelp[data.trim()] || "No help content was found for this command.";
help = `
Command Help: /${data}:${helpContent} `
}
else {
help = `
Command Help: Available Commands:${Object.keys(window.FlatMMOPlus.customChatCommands).sort().map(s => "/"+s).join(" ")} Use the /help command for more information about a specific command: /help <command>
`
}
document.getElementById("chat").insertAdjacentHTML("beforeend",help)
//Chat auto scroll is always true for now
chat_div_element.scrollTop = chat_div_element.scrollHeight;
};
//flatChat overrides this
window.FlatMMOPlus.registerCustomChatCommand("clear", (command, data='') => {
document.getElementById("chat").innerHTML = "";
}, `Clears all messages in chat.`);
window.FlatMMOPlus.registerCustomChatCommand("yell", (command, data='') => {
Globals.websocket.send('CHAT=/yell ' + data);
}, `Chat to everyone on server. Usage: /%COMMAND% [message]`);
window.FlatMMOPlus.registerCustomChatCommand("stuck", (command, data='') => {
Globals.websocket.send('CHAT=/stuck');
}, `Use if your character is stuck and cannot move.`);
internal.init.call(window.FlatMMOPlus);
})();