From the archive

This commit is contained in:
Nevernown 2025-03-16 15:02:23 +01:00
commit 7ed0202142
21 changed files with 1081 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules/
package-lock.json

64
backbone/cache.js Normal file
View File

@ -0,0 +1,64 @@
const _ = require('underscore');
const state = require('./state.js');
let cache = {};
function i (id) {
return new Promise((resolve, reject) => {
state.create(id);
state.read(id)
.then(data => {
cache[id] = data;
resolve();
})
.catch(err => reject(err));
});
}
function c (id) {
if(_.has(cache, id)) {
state.update(id, cache[id]);
console.info(`[${id}] Committed new state`);
} else {
console.error(`[${id}] Could not commit nonexistant state`);
}
}
function r (id) {
state.read(id)
.then(data => {
cache[id] = data
console.info(`[${id}] Reverted state`);
})
.catch(console.error);
}
function e (id) {
if(_.has(cache, id)) {
delete cache[id];
console.info(`[${id}] Erased state`);
state.delete(id);
} else {
console.error(`[${id}] Could not erase nonexistant state`);
}
}
function s (id) {
return new Promise((resolve, reject) => {
if(_.has(cache, id)) {
resolve(cache[id]);
} else {
state.read(id)
.then(data => {
cache[id] = data;
console.info(`[${id}] read and cached state`);
resolve(cache[id]);
})
.catch(err => reject(err));
}
})
}
module.exports = {
init: i,
commit: c,
revert: r,
erase: e,
state: s
}

26
backbone/setup.js Normal file
View File

@ -0,0 +1,26 @@
const cache = require('./cache.js');
module.exports = client => {
return new Promise((resolve, reject) => {
console.info(`Logged in as ${client.user.tag}!`);
console.info(`[BOOT] Initiating state manager...`);
Promise.all(client.guilds.cache.map(guild => cache.init(guild.id)))
.then(() => resolve())
.catch(err => console.error(err));
console.info(`[BOOT] Locating game channels...`);
client.guilds.cache.each(guild => {
let channel = guild.channels.cache.find(channel => channel.name === 'machine');
if(channel) {
console.info(`[${guild.id}] Found machine channel`);
channel.send(`\`[${guild.id}] NeverSteam Engine v2 startup successful\``)
.then(() => console.info(`[${guild.id}] Sent startup notification`))
.catch(console.error);
} else {
console.info(`[${guild.id}] Could not find machine channel`);
}
});
})
}

90
backbone/state.js Normal file
View File

@ -0,0 +1,90 @@
const fs = require('fs');
const location = './state'
if(!fs.existsSync(location)) {
fs.mkdir(location, err => {
if(err) { console.error(err); }
});
}
function l (id) { return `${location}/${id}.json` }
function c (id) {
let path = l(id);
if(fs.existsSync(path)) {
console.info(`[${id}] State already exists`);
} else {
fs.writeFile(path, JSON.stringify({}), err => {
if(err) {
console.error(err);
} else {
console.info(`[${id}] Created new state`);
}
});
}
}
function r (id) {
return new Promise((resolve, reject) => {
let path = l(id);
if(fs.existsSync(path)) {
console.info(`[${id}] Reading existing state`);
fs.readFile(path, (err, data) => {
if(err) {
console.error(err);
reject(err);
} else {
console.info(`[${id}] Read state`);
try {
let state = JSON.parse(data);
console.info(`[${id}] Deserialized state`);
resolve(state);
} catch(err) {
reject(err);
}
}
});
} else {
console.error(`[${id}] Failed to locate state`);
reject(new Error('State not found.'));
}
});
}
function u (id, state) {
let path = l(id);
if(fs.existsSync(path)) {
fs.writeFile(path, JSON.stringify(state), err => {
if(err) {
console.error(err);
} else {
console.info(`[${id}] Updated state`);
}
});
} else {
console.error(`[${id}] Failed to locate state`);
}
}
function d (id) {
let path = l(id);
if(fs.existsSync(path)) {
fs.unlink(path, err=> {
if(err) {
console.error(err);
} else {
console.info(`[${id}] Removed state`);
}
});
} else {
console.error(`[${id}] Failed to locate state`);
}
}
module.exports = {
create: c,
read: r,
update: u,
delete: d
}

7
backbone/util.js Normal file
View File

@ -0,0 +1,7 @@
function c(o) {
return JSON.parse(JSON.stringify(o));
}
module.exports = {
clone: c
}

10
game.js Normal file
View File

@ -0,0 +1,10 @@
const Discord = require('discord.js');
const setup = require('./backbone/setup.js');
const process = require('./game/process.js');
const client = new Discord.Client();
client.login('NjcyNTI5MDE2MjI5NzI0MTkw.XjMz0g.XhdRL-wSCFkYmsNBC7j5HE3XUSI');
client.on('ready', () => setup(client).then(() => {
client.on('message', msg => process(msg, client))
}));

39
game/alias.json Normal file
View File

@ -0,0 +1,39 @@
{
"scn": {
"desc": "Alternate to `!scenario`",
"command": "scenario"
},
"h": {
"desc": "Alternate to `!help`",
"command": "help"
},
"up": {
"desc": "Alternate to `!ping`",
"command": "ping"
},
"name": {
"desc": "Alternate to `!me --name`, sets the name of your character (use `!help me` for more information)",
"command": "me",
"arg": "-n "
},
"age": {
"desc": "Alternate to `!me --age`, sets the age of your character (use `!help me` for more information)",
"command": "me",
"arg": "-a "
},
"title": {
"desc": "Alternate to `!me --title`, sets the title of your character (use `!help me` for more information)",
"command": "me",
"arg": "-t "
},
"story": {
"desc": "Alternate to `!me --story`, sets the background story of your character (use `!help me` for more information)",
"command": "me",
"arg": "-s "
},
"done": {
"desc": "Alternate to `!me --done`, set your character to ready-to-play (use `!help me` for more information)",
"command": "me",
"arg": "-d"
}
}

29
game/commands/alias.js Normal file
View File

@ -0,0 +1,29 @@
const scenarios = require('../scenario/scenario.js');
const _ = require('underscore');
const helptext = `The \`!alias\` command can be used to find alternative commands that cannot be found with \`!help\``;
function alias(context) {
return new Promise((resolve, reject) => {
if(context.isHelpRequest) {
context.message.reply(helptext)
.then(() => {
console.info(`[${context.id}] ? alias`);
resolve();
}).catch(err => reject(err));
} else {
let text = 'Listing aliases\n```';
_.each(_.keys(context.alias), key => text += `\n${key}`);
text += '\n```'
context.message.reply(text)
.then(() => {
console.info(`[${context.id}] List alias -> [${context.message.author.id}]`)
resolve();
}).catch(err => reject(err));
}
});
}
module.exports = commands => {
commands.alias = alias;
};

49
game/commands/help.js Normal file
View File

@ -0,0 +1,49 @@
const _ = require('underscore');
const helptext = `The \`!help\` command can be used to get a list of commands, or to get specific help text using \`!help <command>\`
Also see the \`!alias\` command for extra simplifications.`
const ex = /^[\w\d]+$/
function help(context) {
return new Promise((resolve, reject) => {
if(context.isHelpRequest) {
context.message.reply(helptext)
.then(() => {
console.info(`[${context.id}] ? help`);
resolve();
}).catch(err => reject(err));
} else if(!context.args) {
let text = 'Listing commands, use \`!help <command>\` for detailed information. Use \`!alias\` to find easy shortcut commands\n```';
_.each(_.keys(context.commands), key => text += `\n${key}`);
text += '\n```'
context.message.reply(text)
.then(() => {
console.info(`[${context.id}] List commands -> [${context.pid}]`)
resolve();
}).catch(err => reject(err));
} else {
let match = ex.exec(context.args);
if(match && _.has(context.commands, context.args)) {
console.info(`[${context.id}] Passing help request to <${context.args}>`);
context.isHelpRequest = true;
context.commands[context.args](context);
} else if (match && _.has(context.alias, context.args)) {
console.info(`[${context.id}] Alias help request to <${context.command}>`);
context.message.reply(`\`!${context.args}\`: ${context.alias[context.args].desc}`)
.then(() => {
console.info(`[${context.id}] Alias -> [${context.pid}]`);
resolve();
}).catch(err => reject(err));
} else {
context.message.reply(`No such command <${context.args}>`)
.then(() => {
console.info(`[${context.id}] No command -> [${context.pid}]`)
resolve();
}).catch(err => reject(err));
}
}
});
}
module.exports = commands => {
commands.help = help;
};

110
game/commands/me.js Normal file
View File

@ -0,0 +1,110 @@
const _ = require('underscore');
const helptext = `The \`!me\` command can be used to begin setting up your player character. \`!me\` shows your character, \`!me --done\` finishes.
\`!me\` shows current information
\`!me --done\` sets your character as ready to play (but you can still adjust it until game start) {\`!me -d \`}
\`!me --name <...>\` set name (Max. 48 characters) {\`!me -n <...>\`}
\`!me --age N\` set age (0 < N < 1000) {\`!me -a N\`}
\`!me --title <...>\` set title name (Max. 48 characters, e.g. Major General, Doctor, Lady) {\`!me -t <...>\`}
\`!me --story <...>\` give a background story (32-320 characters) {\`!me -s <...>\`}
You can combine these for speed, examples:
\`!me -n Johnny Smooth -a 23 -t Presentator\`
\`!me --name Scotty Love --age 24 --title Village Idiot\`
Mix and match long form and shorthand:
\`!me --name Mr. Eaten --age 271 -s <...>\` (<...> omitted for brevity)
Finalize on-the-go (be sure to include all options):
\`!me -n Speedy Gonzales -a 15 -t Insurgent -s <...> -d\` (<...> omitted for brevity)
`
let expressions = {};
expressions.name = {};
expressions.name.pt = /((?:--name)|(?:-n))/;
expressions.name.ex = /(?:(?:--name)|(?:-n)\s)(.{1,48}?)\s?(?:-|$)/;
expressions.age = {};
expressions.age.pt = /((?:--age)|(?:-a))/;
expressions.age.ex = /(?:(?:--age)|(?:-a)\s)(\d{1,3})\s?(?:-|$)/;
expressions.title = {};
expressions.title.pt = /((?:--title)|(?:-t))/;
expressions.title.ex = /(?:(?:--title)|(?:-t)\s)(.{1,48}?)\s?(?:-|$)/;
expressions.story = {};
expressions.story.pt = /((?:--story)|(?:-s))/;
expressions.story.ex = /(?:(?:--story)|(?:-s)\s)(.{32,320}?)\s?(?:-|$)/;
const exDone = /(?:(?:--done)|(?:-d))/
function p(flag, context) {
return new Promise((resolve, reject) => {
let flagged = expressions[flag].pt.exec(context.args);
if(flagged) {
let match = expressions[flag].ex.exec(context.args);
if(match) {
context.state.player[context.pid][flag] = match[1];
console.info(`[${context.id}] set ${flag} for [${context.pid}]`);
resolve(`Set \`${flag}\``);
} else {
console.warn(`[${context.id}] could not parse arguments for flag <${flagged[1]}>, use \`!help me\` to check requirements`);
resolve(`Could not parse arguments for flag \`${flag}\``);
}
} else {
resolve(`Flag for \`${flag}\` not present.`);
}
});
}
function me(context) {
return new Promise((resolve, reject) => {
if(!context.state.player) { context.state.player = {}; }
if(context.isHelpRequest) {
context.message.reply(helptext)
.then(() => {
console.info(`[${context.id}] ? me`);
resolve();
}).catch(err => reject(err));
} else if(!context.args) {
if(!context.state.player[context.pid]) {
context.message.reply(`[${context.pid}] You do not have a character yet, use the \`!help me\` command to find out how to create one.`)
.then(() => resolve())
.catch(err => reject(err));
} else {
let player = context.state.player[context.pid];
let text = '```\n';
text += player.title ? `${player.title} ` : '[missing title] ';
text += player.name ? `${player.name}, ` : '[missing name], ';
text += player.age ? `${player.age} years old.\n` : '[missing age]\n';
text += player.story ? player.story : '[missing background story]';
text += '\n```';
context.message.reply(text)
.then(() => resolve())
.catch(err => reject(err));
}
} else {
if(!context.state.player[context.pid]) { context.state.player[context.pid] = {}}
Promise.all([
p("name", context),
p("age", context),
p("title", context),
p("story", context)
]).then(values => {
let result = _.reduce(values, (c, v) => { return `${c}\n${v}`});
if(exDone.test(context.args)) {
let player = context.state.player[context.pid];
if(player.name && player.age && player.title && player.story) {
player.complete = true;
console.info(`[${context.id}] completed [${context.pid}]`);
result += `\nCompleted basic player setup for [${context.pid}]`;
} else {
console.info(`[${context.id}] incomplete [${context.pid}]`);
result += `\nMissing information for [${context.pid}]. \nUse \`!me\` and \`!help me\` for more information`;
}
}
context.message.reply(result)
.then(() => {
context.stateChanged = true;
resolve(context);
}).catch(err => reject(err));
}).catch(err => reject(err));
}
});
}
module.exports = commands => {
commands.me = me;
};

23
game/commands/ping.js Normal file
View File

@ -0,0 +1,23 @@
const helptext = `The \`!ping\` command can be used to check if NeverSteam v2 is running, it ignores any arguments given.`;
function ping(context) {
return new Promise((resolve, reject) => {
if(context.isHelpRequest) {
context.message.reply(helptext)
.then(() => {
console.info(`[${context.id}] ? ping`)
resolve();
}).catch(err => reject(err));
} else {
context.message.reply(`[${context.id}] ECHO`)
.then(() => {
console.info(`[${context.id}] PING -> ECHO [${context.pid}]`)
resolve();
}).catch(err => reject(err));
}
});
}
module.exports = commands => {
commands.ping = ping;
};

38
game/commands/scene.js Normal file
View File

@ -0,0 +1,38 @@
const scenarios = require('../scenario/scenario.js');
const _ = require('underscore');
const helptext = `The \`!scenario\` command can be used to select a scenario to play. use \`!scenario\` to list all options, and \`!scenario <name>\` to select a particular scenario.`;
function s(context) {
return new Promise((resolve, reject) => {
if(context.isHelpRequest) {
context.message.reply(helptext)
.then(() => {
console.info(`[${context.id}] ? scene`);
resolve();
}).catch(err => reject(err));
} else if(context.args) {
context.storyline = context.args;
scenarios.init(context)
.then(context => {
console.info(`[${context.id}] Initiated scenario ${context.args}`);
resolve();
})
.catch(err => reject(err));
} else {
let text = '```\n';
_.each(scenarios.list(context), scenario => text += `${scenario}\n`);
text += '```';
context.message.reply(text)
.then(() => {
console.info(`[${context.id}] Listed scenarios`);
resolve();
})
.catch(err => reject(err));
}
});
}
module.exports = commands => {
commands.scenario = s;
};

78
game/context.js Normal file
View File

@ -0,0 +1,78 @@
const fs = require('fs');
const _ = require('underscore');
const cache = require('../backbone/cache.js');
const alias = require('./alias.json');
let commands = {};
fs.readdirSync('./game/commands').forEach(file => {
require(`./commands/${file}`)(commands);
});
function a(context) {
try {
let cmd = context.alias[context.command].command;
if(context.alias[context.command].arg) {
let args = alias[context.command].arg + context.args;
context.args = args;
}
context.command = cmd;
} catch {
console.info(`[${context.id}] Not an alias <${context.command}>`);
}
}
const ex = /^!(?:([\w\d]+)|(?:([\w\d]+?)\s(.+)))$/
function c (context) {
return new Promise((resolve, reject) => {
let match = ex.exec(context.message.content);
if (match) {
context.command = match[2] ? match[2] : match[1];
context.args = match[3];
context.alias = alias;
a(context);
context.commands = commands;
context.pid = context.message.author.id;
cache.state(context.id)
.then(data => {
context.state = data;
resolve(context);
}).catch(err => reject(err));
} else { resolve(false); }
});
}
function p(message) {
return new Promise((resolve, reject) => {
let context = { message: message, id: message.guild.id };
c(context)
.then(context => resolve(context))
.catch(err => reject(err));
});
}
function x(context) {
return new Promise((resolve, reject) => {
if(context) {
if(_.has(commands, context.command)) {
console.info(`[${context.id}] Execute command <${context.command}>`);
commands[context.command](context)
.then(() => {
if(context.stateChanged){
cache.commit(context.id);
} else {
console.info(`[${context.id}] No change to commit (Context.stateChanged == false)`);
}
resolve();
})
.catch(err => reject(err));
} else {
console.warn(`[${context.id}] Unknown command <${context.command}>`);
}
}
});
}
module.exports = {
parse: p,
execute: x
}

20
game/process.js Normal file
View File

@ -0,0 +1,20 @@
const Discord = require('discord.js');
const context = require('./context.js')
function process(message) {
context.parse(message)
.then(context.execute)
.catch(console.error)
}
module.exports = (message, client) => {
if(message.author && message.author.id !== client.user.id){
if(message.channel instanceof Discord.TextChannel) {
process(message);
} else {
message.reply(`NeverSteam v2 is not available in DM`)
.then(() => console.warn('[WARN] Sent DM warning'))
.catch(console.error);
}
}
}

80
game/scenario/scenario.js Normal file
View File

@ -0,0 +1,80 @@
const fs = require('fs');
const _ = require('underscore');
let storylines = {};
fs.readdirSync('./game/scenario/storylines').forEach(file => {
require(`./storylines/${file}`)(storylines);
});
function l(context) {
return _.keys(storylines);
}
function v(context) {
return new Promise((resolve, reject) => {
let ok = true;
console.info(`[${context.id}] Verifying map integrity...`);
let uniques = _.chain(context.scenario.map.locations)
.map(location => location.id)
.uniq().value();
if(uniques.length !== context.scenario.map.locations.length) {
console.error(`[${context.id}] ID collision detected`);
reject(new Error("ID Collision in map data"));
}
_.each(context.scenario.map.connections, connection => {
if(!_.contains(uniques, connection.locations[0])) {
console.error(`[${context.id}] Invalid connection detected [${connection.locations[0]}]`);
ok = false;
} else if(!_.contains(uniques, connection.locations[1])) {
console.error(`[${context.id}] Invalid connection detected [${connection.locations[1]}]`);
ok = false;
}
});
if(ok) {
console.info(`[${context.id}] ... Verified map integrity`);
resolve(context);
} else {
reject(new Error("Invalid map data"));
}
});
}
function i(context) {
return new Promise((resolve, reject) => {
if(!context.scenario) {
if(context.storyline) {
if(_.has(storylines, context.storyline)) {
storylines[context.storyline].initialize(context)
.then(context => {
context.state.scenario = context.scenario;
v(context)
.then(context => {
context.message.reply(`Initiated scenario \`[${context.storyline}]\``)
.then(() => {
context.stateChanged = true;
resolve(context);
})
.catch(err => reject(err))
})
.catch(err => reject(err));
})
.catch(err => reject(err));
} else {
console.warn(`[${context.id}] No such storyline named ${context.storyline}.`)
reject(new Error(`No such storyline named ${context.storyline}.`));
}
} else {
console.error(`[${context.id}] Missing storyline context`);
reject(new Error(`Invalid context`));
}
} else {
console.warn(`[${context.id}] Scenario already initiated.`);
reject(new Error(`Scenario already initiated.`));
}
});
}
module.exports = {
init: i,
list: l
}

View File

@ -0,0 +1,28 @@
let map = require(`./zeppelin/map.json`);
const _ = require('underscore');
function i(context) {
return new Promise((resolve, reject) => {
//Build from here.
let scenario = {};
console.info(`[${context.id}] Loading zeppelin map information`);
scenario.map = map;
console.info(`[${context.id}] Setting auxiliary context`);
scenario.timer = 0;
console.info(`[${context.id}] Succesfully loaded zeppelin scenario`);
context.scenario = scenario;
resolve(context);
});
}
function t(context) {
//populate with triggers.js
//later, calm down.
}
module.exports = storylines => {
storylines['zeppelin'] = {
initialize: i,
trigger: t
}
}

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1,265 @@
{
"locations": [
{
"id": "FRP",
"name": "The freight platform",
"objects": [
{
"id": "CRATE",
"count": 5
},
{
"id": "CRATE_2",
"count": 17
},
{
"id": "CANNISTER",
"count": 3
}
]
},
{
"id": "WTR",
"name": "The waiting room",
"objects": [
{
"id": "BENCH",
"count": 3
}
]
},
{
"id": "PSP",
"name": "The passenger platform",
"objects": [
{
"id": "BENCH",
"count": 1
}
]
},
{
"id": "ENG_L",
"name": "The lower engine room",
"objects": [
{
"id": "ENGINE",
"count": 1
},
{
"id": "RACK",
"count": 2
}
]
},
{
"id": "ENT",
"name": "The entrance hallway",
"objects": [
{
"id": "WARDROBE",
"count": 2
}
]
},
{
"id": "DNH",
"name": "The dining hall",
"objects": [
{
"id": "TABLE",
"count": 3
},
{
"id": "CHAIR",
"count": 24
},
{
"id": "LAMP",
"count": 6
}
]
},
{
"id": "SLN",
"name": "The outlook salon"
},
{
"id": "GLY",
"name": "The galley"
},
{
"id": "FST",
"name": "The food storage"
},
{
"id": "WHL",
"name": "The wheelhouse"
},
{
"id": "ENG_U",
"name": "The upper engine room"
},
{
"id": "GHL",
"name": "The left balloon gangway"
},
{
"id": "GHR",
"name": "The right balloon gangway"
},
{
"id": "CBG",
"name": "The cabin gangway"
},
{
"id": "FRC",
"name": "The freight compartiment"
},
{
"id": "RYL",
"name": "The royal suite"
},
{
"id": "FCL_1",
"name": "First class suite 1"
},
{
"id": "FCL_2",
"name": "First class suite 2"
},
{
"id": "BNK_1",
"name": "Bunkbed cabin 1"
},
{
"id": "BNK_2",
"name": "Bunkbed cabin 2"
},
{
"id": "CRW_1",
"name": "Crew compartiments 1"
},
{
"id": "CRW_2",
"name": "Crew compartiments 2"
},
{
"id": "STR",
"name": "Storage closet"
}
],
"connections": [
{
"locations": ["PSP", "ENT"],
"type": "Steel sealed rolling door",
"blocked": {
"reason": "The rolling door is closed and sealed shut."
}
},
{
"locations": ["FRP", "FRC"],
"type": "Steel sealed rolling door",
"blocked": {
"reason": "The rolling door is closed and sealed shut."
}
},
{
"locations": ["PSP", "FRP"]
},
{
"locations": ["WTR", "PSP"]
},
{
"locations": ["CBG", "ENT"]
},
{
"locations": ["FST", "ENT"],
"type": "Reinforced door",
"blocked": {
"reason": "It's locked.",
"key": ""
}
},
{
"locations": ["GLY", "ENT"],
"type": "Reinforced door"
},
{
"locations": ["DNH", "ENT"],
"type": "Reinforced door"
},
{
"locations": ["DNH", "SLN"],
"type": "Stairs"
},
{
"locations": ["WHL", "SLN"],
"type": "Stairs"
},
{
"locations": ["WHL", "GHR"],
"type": "Reinforced door"
},
{
"locations": ["WHL", "GHL"],
"type": "Reinforced door"
},
{
"locations": ["ENG_U", "GHR"]
},
{
"locations": ["ENG_U", "GHL"]
},
{
"locations": ["ENG_U", "ENG_L"],
"type": "welded steel ladder"
},
{
"locations": ["ENG_L", "FRC"]
},
{
"locations": ["FRC", "CBG"]
},
{
"locations": ["RYL", "CBG"],
"type": "Reinforced door",
"blocked": {
"reason": "It's locked.",
"key": ""
}
},
{
"locations": ["FCL_1", "CBG"],
"type": "Reinforced door"
},
{
"locations": ["FCL_2", "CBG"],
"type": "Reinforced door"
},
{
"locations": ["BNK_1", "CBG"],
"type": "Reinforced door"
},
{
"locations": ["BNK_2", "CBG"],
"type": "Reinforced door"
},
{
"locations": ["CRW_1", "CBG"],
"type": "Reinforced door"
},
{
"locations": ["CRW_2", "CBG"],
"type": "Reinforced door"
},
{
"locations": ["STR", "CBG"],
"type": "Reinforced door",
"blocked": {
"reason": "It's locked.",
"key": ""
}
}
]
}

View File

@ -0,0 +1,106 @@
[
{
"id": "TABLE",
"name": "table",
"desc": "a regular table"
},
{
"id": "TABLE_2",
"name": "coffee table",
"desc": "a low table typically used to place drinks on"
},
{
"id": "CHAIR",
"name": "chair",
"desc": "a wooden chair"
},
{
"id": "SOFA",
"name": "sofa",
"desc": "a soft, fancy two-person chair"
},
{
"id": "CHAIR_2",
"name": "chair",
"desc": "a richly colored, soft chair"
},
{
"id": "BED",
"name": "bed",
"desc": "a place to sleep for one person"
},
{
"id": "BED_2",
"name": "bed",
"desc": "a place to sleep for two people"
},
{
"id": "BUNKBED",
"name": "bunkbed",
"desc": "a place to sleep for two single people"
},
{
"id": "ENGINE",
"name": "engine",
"desc": "without this, the zeppelin can only float"
},
{
"id": "CANNISTER",
"name": "spare cannister",
"desc": "holds extra gas to replenish the zeppelin"
},
{
"id": "CRATE",
"name": "crate",
"desc": "a big box"
},
{
"id": "CRATE_2",
"name": "small crate",
"desc": "a small box"
},
{
"id": "VASE",
"name": "vase",
"desc": "Glued in place to survive turbulence"
},
{
"id": "SAFE",
"name": "safe",
"desc": "a place to sleep for two people",
"inv": {
"locked": true,
"key": "SAFE_KEY_01"
}
},
{
"id": "WHEEL",
"name": "steering wheel",
"desc": "controls the rudder at the back of the zeppelin"
},
{
"id": "WINDOW",
"name": "window",
"desc": "the reinforced glass is not quite reassuring"
},
{
"id": "BENCH",
"name": "wooden bench",
"desc": "not quite first-class"
},
{
"id": "RACK",
"name": "tool rack",
"desc": "holds tools for quick use"
},
{
"id": "WARDROBE",
"name": "reinforced wardrobe",
"desc": "keeps clothing safe during the flight"
},
{
"id": "LAMP",
"name": "oil lamp",
"desc": "as safe as it gets, gives off some light"
}
]

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "neversteam2",
"version": "1.0.0",
"description": "NeverSteam Game manager",
"main": "game.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Nevernown",
"license": "ISC",
"dependencies": {
"discord.js": "^12.1.1",
"underscore": "^1.10.2"
}
}

View File

@ -0,0 +1 @@
{"scenario":{"map":{"locations":[{"id":"FRP","name":"The freight platform"},{"id":"WTR","name":"The waiting room"},{"id":"PSP","name":"The passenger platform"},{"id":"ENG_L","name":"The lower engine room"},{"id":"ENT","name":"The entrance hallway"},{"id":"DNH","name":"The dining hall"},{"id":"SLN","name":"The outlook salon"},{"id":"GLY","name":"The galley"},{"id":"FST","name":"The food storage"},{"id":"WHL","name":"The wheelhouse"},{"id":"ENG_U","name":"The upper engine room"},{"id":"GHL","name":"The left balloon gangway"},{"id":"GHR","name":"The right balloon gangway"},{"id":"CBG","name":"The cabin gangway"},{"id":"FRC","name":"The freight compartiment"},{"id":"RYL","name":"The royal suite"},{"id":"FCL_1","name":"First class suite 1"},{"id":"FCL_2","name":"First class suite 2"},{"id":"BNK_1","name":"Bunkbed cabin 1"},{"id":"BNK_2","name":"Bunkbed cabin 2"},{"id":"CRW_1","name":"Crew compartiments 1"},{"id":"CRW_2","name":"Crew compartiments 2"},{"id":"STR","name":"Storage closet"}],"connections":[{"locations":["PSP","ENT"],"type":"Steel sealed rolling door","blocked":{"reason":"The rolling door is closed and sealed shut."}},{"locations":["FRP","FRC"],"type":"Steel sealed rolling door","blocked":{"reason":"The rolling door is closed and sealed shut."}},{"locations":["PSP","FRP"]},{"locations":["WTR","PSP"]},{"locations":["CBG","ENT"]},{"locations":["FST","ENT"],"type":"Reinforced door","blocked":{"reason":"It's locked.","key":""}},{"locations":["GLY","ENT"],"type":"Reinforced door"},{"locations":["DNH","ENT"],"type":"Reinforced door"},{"locations":["DNH","SLN"],"type":"Stairs"},{"locations":["WHL","SLN"],"type":"Stairs"},{"locations":["WHL","GHR"],"type":"Reinforced door"},{"locations":["WHL","GHL"],"type":"Reinforced door"},{"locations":["ENG_U","GHR"]},{"locations":["ENG_U","GHL"]},{"locations":["ENG_U","ENG_L"],"type":"welded steel ladder"},{"locations":["ENG_L","FRC"]},{"locations":["FRC","CBG"]},{"locations":["RYL","CBG"],"type":"Reinforced door","blocked":{"reason":"It's locked.","key":""}},{"locations":["FCL_1","CBG"],"type":"Reinforced door"},{"locations":["FCL_2","CBG"],"type":"Reinforced door"},{"locations":["BNK_1","CBG"],"type":"Reinforced door"},{"locations":["BNK_2","CBG"],"type":"Reinforced door"},{"locations":["CRW_1","CBG"],"type":"Reinforced door"},{"locations":["CRW_2","CBG"],"type":"Reinforced door"},{"locations":["STR","CBG"],"type":"Reinforced door","blocked":{"reason":"It's locked.","key":""}}]},"timer":0},"player":{"261931520292421634":{"name":"Baron Black","title":"Detective","age":"17","story":"Baron is an old raven, who picked up a trick or two in his time, nowadays he spends most time going after hackers and other digital scum","complete":true},"197071757021020160":{"name":"stom","age":"1","title":"1"}}}