Merge branch 'materials' of https://github.com/marcus13345/df-idle into materials
commit
2e670c59fd
|
|
@ -2,3 +2,4 @@ node_modules
|
|||
out
|
||||
data
|
||||
*.log
|
||||
bin
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"label": "tsc",
|
||||
"type": "npm",
|
||||
"script": "compile:watch",
|
||||
"script": "tsc:watch",
|
||||
"isBackground": true,
|
||||
"group": {
|
||||
"kind": "build",
|
||||
|
|
@ -17,12 +17,49 @@
|
|||
"reveal": "never",
|
||||
"echo": false,
|
||||
"focus": false,
|
||||
"panel": "dedicated"
|
||||
"panel": "shared",
|
||||
"group": "main"
|
||||
},
|
||||
"problemMatcher": {
|
||||
"base": "$tsc-watch",
|
||||
"applyTo": "allDocuments"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "dev",
|
||||
"type": "npm",
|
||||
"script": "dev",
|
||||
"isBackground": true,
|
||||
"group": "build",
|
||||
"runOptions": {
|
||||
"runOn": "folderOpen"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"echo": false,
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"group": "main"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "rollup",
|
||||
"type": "npm",
|
||||
"script": "rollup:watch",
|
||||
"isBackground": true,
|
||||
"group": "build",
|
||||
"runOptions": {
|
||||
"runOn": "folderOpen"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"echo": false,
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"group": "main"
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "entry"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import './core/items/CoreItems.js';
|
||||
import './core/tasks/CoreTasks.js';
|
||||
import './core/themes/standard.js';
|
||||
|
|
@ -1,10 +1,7 @@
|
|||
import { Task } from "@tasks";
|
||||
import { Game } from '@game';
|
||||
import { ARROWHEAD, FLINT_NORMAL, SLATE, STICK } from '../items/CoreItems.js';
|
||||
import { ItemState } from "@items";
|
||||
import { Popup } from "../../../src/ui/Popup.js";
|
||||
import { inspect } from 'util';
|
||||
import { Item, ItemState } from "@items";
|
||||
import { Place, ResourceNode } from "@world";
|
||||
// import { STICK } from "../items/CoreItems.js";
|
||||
|
||||
|
||||
// export const GATHER_FLINT = new Task('core:gather-flint')
|
||||
// .setName('Gather Flint')
|
||||
|
|
@ -32,6 +29,12 @@ import { Place, ResourceNode } from "@world";
|
|||
// Game.current.inv.add(itemState);
|
||||
// });
|
||||
|
||||
|
||||
export const STICK = new Item()
|
||||
.setName("Stick")
|
||||
.plural('Sticks')
|
||||
.setId('core:resources/stick')
|
||||
|
||||
export const Forest = new Place()
|
||||
.setName('Forest')
|
||||
.setId('core:forest')
|
||||
|
|
|
|||
12
index.html
12
index.html
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>df-idle</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="./out/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
import path from 'path';
|
||||
|
||||
const moduleAliases = {
|
||||
"@themes": "./out/src/registries/Themes.js",
|
||||
"@actions": "./out/src/registries/Actions.js",
|
||||
"@tasks": "./out/src/registries/Tasks.js",
|
||||
"@items": "./out/src/registries/Items.js",
|
||||
"@world": "./out/src/World.js",
|
||||
"@ui": "./out/src/ui/UI.js",
|
||||
"@game": "./out/src/Game.js"
|
||||
};
|
||||
|
||||
const getAliases = () => {
|
||||
const base = process.cwd();
|
||||
const aliases = moduleAliases || {};
|
||||
const absoluteAliases = Object.keys(aliases).reduce((acc, key) =>
|
||||
aliases[key][0] === '/'
|
||||
? acc
|
||||
: { ...acc, [key]: 'file:///' + path.join(base, aliases[key]) },
|
||||
aliases)
|
||||
return absoluteAliases;
|
||||
}
|
||||
|
||||
const isAliasInSpecifier = (path, alias) => {
|
||||
return path.indexOf(alias) === 0
|
||||
&& (path.length === alias.length || path[alias.length] === '/')
|
||||
}
|
||||
|
||||
const aliases = getAliases();
|
||||
|
||||
export const resolve = (specifier, parentModuleURL, defaultResolve) => {
|
||||
const alias = Object.keys(aliases).find((key) => isAliasInSpecifier(specifier, key));
|
||||
|
||||
const newSpecifier = alias === undefined
|
||||
? specifier
|
||||
: path.join(aliases[alias], specifier.substr(alias.length));
|
||||
|
||||
return defaultResolve(newSpecifier, parentModuleURL);
|
||||
}
|
||||
28
package.json
28
package.json
|
|
@ -1,22 +1,30 @@
|
|||
{
|
||||
"name": "df-idle",
|
||||
"name": "hadean",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.14.8",
|
||||
"@babel/preset-env": "^7.14.8",
|
||||
"@nodegui/nodegui": "^0.34.0",
|
||||
"@rollup/plugin-alias": "^3.1.4",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@types/blessed": "^0.1.17",
|
||||
"@types/bonjour": "^3.5.8",
|
||||
"@types/chai": "^4.2.19",
|
||||
"@types/faker": "^5.5.6",
|
||||
"@types/fs-extra": "^9.0.11",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/node-ipc": "^9.1.5",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"@types/watch": "^1.0.2",
|
||||
"@types/ws": "^7.4.5",
|
||||
"@web/dev-server": "^0.1.18",
|
||||
"bonjour": "^3.5.0",
|
||||
"chai": "^4.3.4",
|
||||
"chalk": "^4.1.1",
|
||||
"chokidar": "^3.5.2",
|
||||
"deepmerge": "^4.2.2",
|
||||
"faker": "^5.5.3",
|
||||
"frigid": "^1.3.13",
|
||||
|
|
@ -24,26 +32,36 @@
|
|||
"get-port": "^5.1.1",
|
||||
"mocha": "^9.0.1",
|
||||
"module-alias": "^2.2.2",
|
||||
"multiview": "^3.0.1",
|
||||
"neo-blessed": "^0.2.0",
|
||||
"node-dev": "^7.0.0",
|
||||
"node-ipc": "^10.0.2",
|
||||
"nodemon": "^2.0.7",
|
||||
"printable-characters": "^1.0.42",
|
||||
"rollup": "^2.53.3",
|
||||
"sisteransi": "^1.0.5",
|
||||
"supervisor": "^0.12.0",
|
||||
"typescript": "^4.3.2",
|
||||
"uuid": "^8.3.2",
|
||||
"walk-sync": "^3.0.0",
|
||||
"watch": "^1.0.2",
|
||||
"ws": "^7.4.6",
|
||||
"yarn": "^1.22.10"
|
||||
},
|
||||
"scripts": {
|
||||
"compile:watch": "tsc --watch",
|
||||
"start": "node --no-warnings --loader ./lib/aliases.mjs --enable-source-maps out/src/index.js",
|
||||
"dev": "nodemon -I --watch out --exec yarn start",
|
||||
"compile": "yarn tsc & yarn rollup",
|
||||
"start": "yarn qode bin/app.bundle.js",
|
||||
"dev": "yarn x bin/ipc-tower.bundle.js",
|
||||
"prod": "git fetch && git pull && yarn && tsc && yarn start",
|
||||
"test": "mocha",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"web": "web-dev-server --open index.html --node-resolve --watch",
|
||||
"profile": "node --inspect-brk --no-warnings --loader ./lib/aliases.mjs --enable-source-maps out/src/index.js"
|
||||
"profile": "yarn qode --inspect-brk bin/app.bundle.js",
|
||||
"qode": "qode",
|
||||
"x": "node",
|
||||
"tsc": "tsc",
|
||||
"tsc:watch": "yarn tsc --watch",
|
||||
"rollup": "rollup --config rollup.config.js",
|
||||
"rollup:watch": "yarn rollup --watch"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
perf.js
15
perf.js
|
|
@ -1,15 +0,0 @@
|
|||
console.clear();
|
||||
|
||||
const n = 10 ** 8;
|
||||
// const map = new Map();
|
||||
|
||||
console.log('sequential insertion');
|
||||
for(let run = 0; run < 10; run ++) {
|
||||
const arr = [];
|
||||
|
||||
console.time('array');
|
||||
for(let i = 0; i < n; i ++) {
|
||||
arr.push(133769420)
|
||||
}
|
||||
console.timeEnd('array')
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import alias from '@rollup/plugin-alias';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import tsconfig from './tsconfig.json';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const aliases = Object.entries(tsconfig.compilerOptions.paths).map(([name, [path]]) => {
|
||||
return {
|
||||
find: name,
|
||||
replacement: resolve(__dirname, 'out', path) + '.js'
|
||||
};
|
||||
});
|
||||
|
||||
const shared = {
|
||||
plugins: [
|
||||
alias({
|
||||
entries: [
|
||||
...aliases,
|
||||
{ find: 'frigid', replacement: resolve(__dirname, 'node_modules/frigid/out/index.js') }
|
||||
]
|
||||
})
|
||||
],
|
||||
watch: {
|
||||
include: 'out/**/*'
|
||||
}
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
...shared,
|
||||
input: './out/src/index.js',
|
||||
output: {
|
||||
file: 'bin/app.bundle.js',
|
||||
format: 'es'
|
||||
}
|
||||
},
|
||||
{
|
||||
...shared,
|
||||
input: './out/src/hot-index.js',
|
||||
output: {
|
||||
file: 'bin/ipc-tower.bundle.js',
|
||||
format: 'es'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
export const IPC_PATH = '/tmp/dfi.dev';
|
||||
export const IPC_CLIENT_APPSAPCE = 'dfi.';
|
||||
export const IPC_CLIENT_CONNECT_NAME = 'dev';
|
||||
export const IPC_QUIT_EVENT = 'app.quit';
|
||||
export const IPC_RESTART_EVENT = 'app.restart';
|
||||
export const IPC_REQUEST_RESTART = 'app.request-restart';
|
||||
|
||||
export const MDNS_TYPE = 'hdn';
|
||||
|
||||
export const APPLICATION_NAME = 'Hadean'
|
||||
20
src/Game.ts
20
src/Game.ts
|
|
@ -1,29 +1,24 @@
|
|||
import { Frigid, Serializable } from 'frigid';
|
||||
import { DEBUG } from 'frigid/out/Serializable.js';
|
||||
import { Pawn } from './Pawn.js';
|
||||
import { TaskList } from './TaskList.js';
|
||||
import { Inventory } from './Inventory.js';
|
||||
import { Menu } from './ui/Menu.js';
|
||||
import Time, { Tickable } from './Time.js';
|
||||
import { render, Renderable, setTitle, start } from './ui/UI.js';
|
||||
import { setTitle, start, update } from '@ui';
|
||||
import { ready } from './multiplayer/mDNS.js';
|
||||
import faker from 'faker';
|
||||
import { World } from '@world';
|
||||
|
||||
let game: Game = null;
|
||||
|
||||
export class Game extends Frigid implements Tickable, Renderable {
|
||||
export class Game extends Frigid implements Tickable {
|
||||
pawns: Pawn[] = [];
|
||||
selected: Pawn;
|
||||
inventory: Inventory;
|
||||
board: TaskList;
|
||||
menu: Menu;
|
||||
clock: Time;
|
||||
name: string;
|
||||
world: World;
|
||||
|
||||
[DEBUG] = true;
|
||||
|
||||
static get current(): Game {
|
||||
if (!game) throw new Error('Somehow called a game before it existed?');
|
||||
return game;
|
||||
|
|
@ -33,7 +28,7 @@ export class Game extends Frigid implements Tickable, Renderable {
|
|||
for(const pawn of this.pawns) {
|
||||
pawn.tick();
|
||||
}
|
||||
render();
|
||||
update();
|
||||
}
|
||||
|
||||
get inv() { return this.inventory; }
|
||||
|
|
@ -62,7 +57,6 @@ export class Game extends Frigid implements Tickable, Renderable {
|
|||
this.world ??= new World();
|
||||
this.pawns ??= [];
|
||||
this.selected ??= this.pawns[0] || null;
|
||||
this.menu = new Menu();
|
||||
this.board ??= new TaskList();
|
||||
this.inventory ??= new Inventory();
|
||||
this.inventory.validate();
|
||||
|
|
@ -70,17 +64,9 @@ export class Game extends Frigid implements Tickable, Renderable {
|
|||
this.clock.thing = this;
|
||||
this.clock.start();
|
||||
ready(this.name);
|
||||
render(this);
|
||||
}
|
||||
|
||||
static serializationDependencies() {
|
||||
return [ Pawn, Inventory, TaskList, Time, World ];
|
||||
}
|
||||
|
||||
render() {
|
||||
this.menu.render();
|
||||
this.board.render();
|
||||
// TODO this logic dont make sense
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
import { Serializable } from 'frigid';
|
||||
import { Game } from './Game.js';
|
||||
import { Item, ItemState } from './registries/Items.js';
|
||||
import { Popup } from './ui/Popup.js';
|
||||
import { Renderable } from './ui/UI.js';
|
||||
import { Popup } from '@ui';
|
||||
|
||||
export class Inventory extends Serializable implements Renderable {
|
||||
export class Inventory extends Serializable {
|
||||
items: ItemState<any>[];
|
||||
|
||||
ctor() {
|
||||
|
|
|
|||
40
src/Pawn.ts
40
src/Pawn.ts
|
|
@ -3,13 +3,13 @@ import faker from 'faker';
|
|||
import { Task, TaskState } from './registries/Tasks.js';
|
||||
import Time, { Tickable } from './Time.js';
|
||||
import { Game } from './Game.js';
|
||||
import { render, Renderable, RenderMode } from './ui/UI.js';
|
||||
// import { render, Renderable, RenderMode } from '@ui';
|
||||
import { Memory } from './Memory.js';
|
||||
import { getTheme } from '@themes';
|
||||
|
||||
// TODO add stats getter to return % of all stats
|
||||
|
||||
export class Pawn extends Serializable implements Tickable, Renderable {
|
||||
export class Pawn extends Serializable implements Tickable {
|
||||
name: {
|
||||
first: string,
|
||||
last: string
|
||||
|
|
@ -80,25 +80,25 @@ export class Pawn extends Serializable implements Tickable, Renderable {
|
|||
return [TaskState]
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.render(RenderMode.ONELINE);
|
||||
}
|
||||
// toString() {
|
||||
// return this.render(RenderMode.ONELINE);
|
||||
// }
|
||||
|
||||
render(mode: RenderMode): string {
|
||||
if(mode === RenderMode.ONELINE) {
|
||||
if(this.name) {
|
||||
return this.name.first + ' ' + this.name.last;
|
||||
} else {
|
||||
return '[Object Pawn]';
|
||||
}
|
||||
} else if (mode === RenderMode.DETAILS) {
|
||||
return `${
|
||||
this.toString()
|
||||
}{|}${
|
||||
this.status
|
||||
}\nDETAILS\nDETAILS`
|
||||
}
|
||||
}
|
||||
// render(mode: RenderMode): string {
|
||||
// if(mode === RenderMode.ONELINE) {
|
||||
// if(this.name) {
|
||||
// return this.name.first + ' ' + this.name.last;
|
||||
// } else {
|
||||
// return '[Object Pawn]';
|
||||
// }
|
||||
// } else if (mode === RenderMode.DETAILS) {
|
||||
// return `${
|
||||
// this.toString()
|
||||
// }{|}${
|
||||
// this.status
|
||||
// }\nDETAILS\nDETAILS`
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// const task =
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import EventEmitter from 'events';
|
||||
import ipc from 'node-ipc';
|
||||
import {
|
||||
IPC_CLIENT_CONNECT_NAME,
|
||||
IPC_CLIENT_APPSAPCE,
|
||||
IPC_QUIT_EVENT,
|
||||
IPC_RESTART_EVENT,
|
||||
IPC_REQUEST_RESTART
|
||||
} from './Constants.js';
|
||||
|
||||
let connected = false;
|
||||
|
||||
class ProcessManagerClass extends EventEmitter {
|
||||
quit() {
|
||||
if (connected) {
|
||||
ipc.of[name].emit(IPC_QUIT_EVENT);
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
restart() {
|
||||
if (connected) {
|
||||
ipc.of[name].emit(IPC_RESTART_EVENT);
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const ProcessManager = new ProcessManagerClass();
|
||||
|
||||
const name = IPC_CLIENT_CONNECT_NAME;
|
||||
ipc.config.appspace = IPC_CLIENT_APPSAPCE;
|
||||
ipc.config.silent = true;
|
||||
|
||||
ipc.connectTo(name, () => {
|
||||
ipc.of[name].on('connect', () => connected = true);
|
||||
ipc.of[name].on('disconnect', () => connected = false);
|
||||
ipc.of[name].on(IPC_REQUEST_RESTART, () => {
|
||||
ProcessManager.emit('reload');
|
||||
})
|
||||
});
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
import { getTheme } from '@themes';
|
||||
import { Serializable } from 'frigid';
|
||||
import { Task, TaskState } from './registries/Tasks.js';
|
||||
import { render, Renderable, panels } from './ui/UI.js';
|
||||
|
||||
export class TaskList extends Serializable implements Renderable {
|
||||
export class TaskList extends Serializable {
|
||||
tasks: TaskState<unknown, unknown>[] = [];
|
||||
|
||||
clear() {
|
||||
|
|
@ -34,13 +33,11 @@ export class TaskList extends Serializable implements Renderable {
|
|||
}
|
||||
|
||||
render() {
|
||||
// const width = tasksPanel.width;
|
||||
panels.left.setContent(`${this.tasks.map(task => `${
|
||||
getTheme().normal(task.toString())
|
||||
} ${
|
||||
getTheme().dimmed(task.worker?.toString() ?? '')
|
||||
}`).join('\n')}`);
|
||||
return '';
|
||||
// return this.tasks.map(task => task.toString()).join('\n');
|
||||
// panels.left.setContent(`${this.tasks.map(task => `${
|
||||
// getTheme().normal(task.toString())
|
||||
// } ${
|
||||
// getTheme().dimmed(task.worker?.toString() ?? '')
|
||||
// }`).join('\n')}`);
|
||||
// return '';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import chalk from "chalk";
|
||||
import { Serializable } from "frigid";
|
||||
import { getTheme } from "@themes";
|
||||
import { Renderable } from "./ui/UI.js";
|
||||
|
||||
type AbbreviatedMonthName = string;
|
||||
|
||||
|
|
@ -19,7 +18,7 @@ const months: AbbreviatedMonthName[] = [
|
|||
'Oct', 'Nov', 'Dec'
|
||||
]
|
||||
|
||||
export default class Time extends Serializable implements Renderable {
|
||||
export default class Time extends Serializable {
|
||||
rate: number;
|
||||
paused = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import { lstatSync } from 'fs';
|
||||
import { parse, resolve } from 'path';
|
||||
import walkSync from 'walk-sync';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { APPLICATION_NAME } from './Constants.js';
|
||||
|
||||
export function osrsNumber(x: number): string {
|
||||
if(x < 10_000) return '' + x;
|
||||
else if (x < 10_000_000) return Math.floor(x / 1000) + 'K';
|
||||
else return Math.floor(x / 1_000_000) + 'M';
|
||||
}
|
||||
|
||||
export async function loadExtensions() {
|
||||
console.log(APPLICATION_NAME + ': Loading extensions');
|
||||
const extensionsPath = resolve(parse(fileURLToPath(import.meta.url)).dir, '../content');
|
||||
|
||||
const extensions = walkSync(extensionsPath)
|
||||
.map(path => [path, resolve(extensionsPath, path)])
|
||||
.filter(path => lstatSync(path[1]).isFile())
|
||||
.filter(path => parse(path[1]).ext === '.js');
|
||||
|
||||
console.log('found', extensions.length, 'extensions');
|
||||
|
||||
for (const path of extensions) {
|
||||
console.log('=== [', path[0], '] ===');
|
||||
await import(path[1]);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log('Setup Complete.');
|
||||
}
|
||||
|
||||
// export function
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
import ipc from 'node-ipc';
|
||||
import {
|
||||
IPC_PATH,
|
||||
IPC_QUIT_EVENT,
|
||||
IPC_RESTART_EVENT,
|
||||
IPC_REQUEST_RESTART
|
||||
} from './Constants.js';
|
||||
import { spawn, ChildProcess } from 'child_process';
|
||||
import watch from 'watch';
|
||||
import chalk from 'chalk';
|
||||
|
||||
ipc.config.silent = true;
|
||||
|
||||
const exec = 'yarn';
|
||||
const args = [
|
||||
'start'
|
||||
]
|
||||
|
||||
ipc.serve(IPC_PATH, () => {
|
||||
ipc.server.on(IPC_QUIT_EVENT, async () => {
|
||||
await killProcess();
|
||||
ipc.server.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
ipc.server.on(IPC_RESTART_EVENT, restart)
|
||||
});
|
||||
|
||||
ipc.server.start();
|
||||
|
||||
let proc: ChildProcess = null;
|
||||
|
||||
function startProcess() {
|
||||
proc = spawn(exec, args, {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
console.log(`[${proc.pid}] ${chalk.grey(`${exec} ${args.join(' ')}`)}`);
|
||||
proc.on('exit', () => {
|
||||
console.log('process died');
|
||||
proc = null;
|
||||
})
|
||||
}
|
||||
|
||||
async function killProcess() {
|
||||
if(proc) {
|
||||
console.log('killing process...');
|
||||
const killedPromise = new Promise((res) => {
|
||||
proc.on('exit', () => {
|
||||
res(void 0);
|
||||
})
|
||||
})
|
||||
proc.kill();
|
||||
await killedPromise;
|
||||
proc = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function restart() {
|
||||
await killProcess();
|
||||
startProcess();
|
||||
}
|
||||
|
||||
startProcess();
|
||||
|
||||
let restartTimer: NodeJS.Timeout = null;
|
||||
|
||||
function fileChange() {
|
||||
console.log('changes detected');
|
||||
// appendFileSync('log.log', evt + ' ' + path + '\n');
|
||||
// console.log(cluster.isMaster, evt, path);
|
||||
if(restartTimer) clearTimeout(restartTimer)
|
||||
restartTimer = setTimeout(() => {
|
||||
ipc.server.broadcast(IPC_REQUEST_RESTART);
|
||||
restartTimer = null;
|
||||
}, 1000);
|
||||
}
|
||||
// chokidar.watch('./out').on('all', fileChange);
|
||||
watch.watchTree('./bin', fileChange);
|
||||
56
src/index.ts
56
src/index.ts
|
|
@ -1,15 +1,22 @@
|
|||
import { render } from './ui/UI.js';
|
||||
import { ensureDirSync } from 'fs-extra';
|
||||
import { lstatSync } from 'fs';
|
||||
import { parse, resolve } from 'path';
|
||||
import walkSync from 'walk-sync';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { parse } from 'path';
|
||||
import { Game } from '@game';
|
||||
import { isStarted, stop } from './ui/UI.js';
|
||||
import { writeFileSync } from 'fs';
|
||||
import {
|
||||
isStarted,
|
||||
stop,
|
||||
update,
|
||||
GameView,
|
||||
setView,
|
||||
start
|
||||
} from '@ui';
|
||||
import ansi from 'sisteransi';
|
||||
|
||||
console.clear();
|
||||
// HACK static extension loading
|
||||
import './../content/content.js';
|
||||
import { loadExtensions } from './Util.js';
|
||||
import { APPLICATION_NAME } from './Constants.js';
|
||||
|
||||
// console.clear();
|
||||
|
||||
function gracefulShutdown() {
|
||||
if (isStarted()) {
|
||||
|
|
@ -22,44 +29,29 @@ function gracefulShutdown() {
|
|||
}
|
||||
console.log('exitting');
|
||||
process.stdout.write(ansi.cursor.show);
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
process.on('exit', gracefulShutdown);
|
||||
|
||||
process.on('exit', gracefulShutdown);
|
||||
|
||||
const saveFile = process.argv[2] || 'data/world01.json';
|
||||
|
||||
ensureDirSync(parse(saveFile).dir);
|
||||
|
||||
|
||||
// TODO extract extension loading into separate file
|
||||
console.log('df-idle: Loading extensions');
|
||||
const extensionsPath = resolve(parse(fileURLToPath(import.meta.url)).dir, '../content');
|
||||
|
||||
const extensions = walkSync(extensionsPath)
|
||||
.map(path => [path, resolve(extensionsPath, path)])
|
||||
.filter(path => lstatSync(path[1]).isFile())
|
||||
.filter(path => parse(path[1]).ext === '.js');
|
||||
|
||||
console.log('found', extensions.length, 'extensions');
|
||||
|
||||
for(const path of extensions) {
|
||||
console.log('=== [', path[0], '] ===');
|
||||
await import(path[1]);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log('Setup Complete.');
|
||||
|
||||
// loadExtensions();
|
||||
|
||||
for (let seconds = 0; seconds > 0; seconds --) {
|
||||
process.stdout.write('Starting DF-Idle in ' + seconds + '\r');
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
process.stdout.write('Starting ' + APPLICATION_NAME + ' in ' + seconds + '\r');
|
||||
}
|
||||
console.clear();
|
||||
process.stdout.write('Starting ' + APPLICATION_NAME + ' in ' + 0 + '\n');
|
||||
// console.clear();
|
||||
|
||||
// TODO move render logic into game, so that the ui doesnt exist until the game does...
|
||||
// maybe, i mean, an argument could be made for not that, because the game
|
||||
// isnt necessarily the entire thing, its just one instance of a save file.
|
||||
// But probably the initial menu screens will be their own thing entirely.
|
||||
const game = Game.create(saveFile);
|
||||
start();
|
||||
const gameView = new GameView(game);
|
||||
setView(gameView);
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ import faker from 'faker';
|
|||
import chalk from 'chalk';
|
||||
import { Item } from '../registries/Items.js';
|
||||
import WebSocket from 'ws';
|
||||
import { Popup } from '../ui/Popup.js';
|
||||
import { Popup } from '@ui';
|
||||
import { inspect } from 'util'
|
||||
import { Pawn } from '../Pawn.js';
|
||||
import { Game } from '../Game.js';
|
||||
import { Player } from './Player.js';
|
||||
import { injectTravelMemory } from '../Memories.js';
|
||||
import { MDNS_TYPE } from '../Constants.js';
|
||||
|
||||
const mdns = bonjour();
|
||||
const ID = uuid.v4();
|
||||
|
|
@ -33,7 +34,7 @@ export default network;
|
|||
export async function ready(name: string) {
|
||||
const port = await getPort({port: getPort.makeRange(52300, 52399)});
|
||||
mdns.publish({
|
||||
type: 'dfi',
|
||||
type: MDNS_TYPE,
|
||||
name,
|
||||
port: port
|
||||
});
|
||||
|
|
@ -58,7 +59,7 @@ export async function ready(name: string) {
|
|||
}
|
||||
|
||||
mdns.find({
|
||||
type: 'dfi'
|
||||
type: MDNS_TYPE
|
||||
}, (service) => {
|
||||
const p = new Player();
|
||||
p.name = service.name;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
import { Game } from '@game';
|
||||
import {
|
||||
QLabel,
|
||||
QTabWidget,
|
||||
QWidget,
|
||||
QIcon
|
||||
} from '@nodegui/nodegui';
|
||||
import { View } from './View.js';
|
||||
|
||||
// 40x30
|
||||
const w = 40;
|
||||
const h = 30;
|
||||
export class GameView extends View {
|
||||
game: Game;
|
||||
title: QLabel;
|
||||
left: QWidget;
|
||||
right: QTabWidget;
|
||||
|
||||
addComponents(): void {
|
||||
this.addLayout();
|
||||
|
||||
this.title = new QLabel();
|
||||
this.left = new QWidget();
|
||||
this.right = new QTabWidget();
|
||||
|
||||
this.title.setText(this.game.name);
|
||||
|
||||
this.left.setInlineStyle('border: 1px solid white;')
|
||||
this.right.setInlineStyle('border: 1px solid white;')
|
||||
|
||||
// this.layout.addWidget(this.left, 1, 0, 29, 20);
|
||||
// this.layout.addWidget(this.right, 1, 21, 29, 20);
|
||||
|
||||
this.layout.addWidget(this.title, 0, 0, 1, w);
|
||||
this.layout.addWidget(this.left, 1, 0, h - 1, w / 2);
|
||||
this.layout.addWidget(this.right, 1, w / 2, h - 1, w / 2);
|
||||
|
||||
const page1 = new QLabel();
|
||||
page1.setText('Page 1');
|
||||
const page2 = new QLabel();
|
||||
page2.setText('Page 2');
|
||||
|
||||
this.right.addTab(page1, new QIcon(), "Page 1");
|
||||
this.right.addTab(page2, new QIcon(), "Page 2");
|
||||
}
|
||||
|
||||
constructor(game: Game) {
|
||||
super();
|
||||
this.game = game;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { QGridLayout, QLabel, QWidget } from "@nodegui/nodegui";
|
||||
import { View } from "./View.js";
|
||||
|
||||
export class LoadingView extends View {
|
||||
label: QLabel;
|
||||
|
||||
addComponents(): void {
|
||||
this.addLayout();
|
||||
|
||||
this.label = new QLabel();
|
||||
this.label.setText('Loading World...');
|
||||
this.layout.addWidget(this.label);
|
||||
}
|
||||
|
||||
destory() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export class Popup {
|
||||
static show(content: string) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { QGridLayout, QLabel, QMainWindow, QPushButton, QWidget } from "@nodegui/nodegui";
|
||||
import { ProcessManager } from "../ProcessManager";
|
||||
|
||||
export class RequestReloadPopup {
|
||||
static show() {
|
||||
const window = new QMainWindow();
|
||||
window.setFixedSize(200, 100);
|
||||
const root = new QWidget();
|
||||
window.setCentralWidget(root);
|
||||
const layout = new QGridLayout();
|
||||
root.setLayout(layout);
|
||||
const label = new QLabel();
|
||||
label.setText('A reload has been requested');
|
||||
layout.addWidget(label, 0, 0, 4, 3);
|
||||
const reloadButton = new QPushButton();
|
||||
reloadButton.setText('Reload');
|
||||
layout.addWidget(reloadButton, 4, 2, 1, 1);
|
||||
window.show();
|
||||
|
||||
reloadButton.addEventListener('clicked', () => {
|
||||
ProcessManager.restart();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { QGridLayout, QMainWindow, QWidget } from "@nodegui/nodegui";
|
||||
import { win } from "@ui";
|
||||
|
||||
export abstract class View {
|
||||
root: QWidget;
|
||||
layout: QGridLayout;
|
||||
|
||||
addLayout() {
|
||||
this.root = new QWidget();
|
||||
this.root.setObjectName("root");
|
||||
win.setCentralWidget(this.root);
|
||||
|
||||
this.layout = new QGridLayout();
|
||||
this.root.setLayout(this.layout);
|
||||
}
|
||||
|
||||
abstract addComponents(): void;
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import {
|
||||
QMainWindow, WidgetEventTypes
|
||||
} from '@nodegui/nodegui';
|
||||
import { APPLICATION_NAME } from '../Constants.js';
|
||||
import { ProcessManager } from '../ProcessManager.js';
|
||||
import { LoadingView } from './LoadingView.js';
|
||||
import { RequestReloadPopup } from './RequestReloadPopup.js';
|
||||
import { View } from './View.js';
|
||||
export { GameView } from './GameView.js';
|
||||
export { Popup } from './Popup.js';
|
||||
|
||||
export const win = new QMainWindow();
|
||||
win.setFixedSize(800, 600);
|
||||
win.setWindowTitle(APPLICATION_NAME);
|
||||
win.setStyleSheet(`
|
||||
#root {
|
||||
background-color: black;
|
||||
height: '100%';
|
||||
}
|
||||
QPushButton {
|
||||
background: #333333;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background: cyan;
|
||||
color: black;
|
||||
}
|
||||
* {
|
||||
font-family: 'MxPlus IBM VGA 8x16';
|
||||
font-size: 16px;
|
||||
}
|
||||
QTabWidget {
|
||||
border: 1px solid white;
|
||||
}
|
||||
QTabWidget::pane {
|
||||
background: black;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
}
|
||||
QTabBar::tab {
|
||||
background: black;
|
||||
color: cyan;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
QTabBar::tab:selected {
|
||||
background: cyan;
|
||||
color: black;
|
||||
}
|
||||
`);
|
||||
win.show();
|
||||
(global as any).win = win;
|
||||
win.addEventListener(WidgetEventTypes.Paint, _ => _);
|
||||
win.addEventListener('customContextMenuRequested', console.log)
|
||||
win.addEventListener('objectNameChanged', console.log)
|
||||
win.addEventListener('windowIconChanged', console.log)
|
||||
win.addEventListener('windowTitleChanged', console.log)
|
||||
|
||||
setView(new LoadingView());
|
||||
|
||||
export function start() {
|
||||
|
||||
}
|
||||
|
||||
export function stop() {
|
||||
win.close();
|
||||
}
|
||||
|
||||
export function setView(view: View) {
|
||||
view.addComponents();
|
||||
}
|
||||
|
||||
export function setTitle(title: string) {
|
||||
|
||||
}
|
||||
|
||||
export function update() {
|
||||
|
||||
}
|
||||
|
||||
export function isStarted() {
|
||||
return win.isVisible();
|
||||
}
|
||||
|
||||
ProcessManager.on('reload', () => {
|
||||
RequestReloadPopup.show();
|
||||
//
|
||||
})
|
||||
|
||||
|
||||
// HACK this is bullshit, :)
|
||||
function f() {
|
||||
win.repaint();
|
||||
win.update();
|
||||
setTimeout(f, 100);
|
||||
}
|
||||
f();
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import { Serializable } from 'frigid';
|
||||
import { getTheme } from '@themes';
|
||||
import { Renderable } from '../ui/UI.js';
|
||||
import { osrsNumber } from '../Util.js';
|
||||
|
||||
export type ItemID = string;
|
||||
|
||||
const items = new Map<ItemID, Item<any>>();
|
||||
|
||||
// ITEMS SHALL BE SINGULAR
|
||||
export class Item<Data = any> extends Serializable {
|
||||
export class Item<Data = any> {
|
||||
|
||||
name = {
|
||||
singular: '',
|
||||
|
|
@ -52,7 +52,7 @@ export class Item<Data = any> extends Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
export class ItemState<Data> extends Serializable implements Renderable {
|
||||
export class ItemState<Data> extends Serializable {
|
||||
qty: number;
|
||||
itemId: ItemID;
|
||||
data: Data;
|
||||
|
|
@ -77,7 +77,10 @@ export class ItemState<Data> extends Serializable implements Renderable {
|
|||
}
|
||||
|
||||
render() {
|
||||
return getTheme().normal(` ${this.item.name}{|}${this.qty} `);
|
||||
return getTheme().normal(` ${osrsNumber(this.qty)} ${(() => {
|
||||
if(this.qty === 1) return this.item.name.singular;
|
||||
else return this.item.name.plural;
|
||||
})()} `);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import EventEmitter from 'events';
|
|||
import chalk from 'chalk';
|
||||
import { Pawn } from '../Pawn.js';
|
||||
import { Game } from '../Game.js';
|
||||
import { panels } from '../ui/UI.js';
|
||||
import { progressbar, ProgressbarStyle } from '../Progressbar.js';
|
||||
|
||||
const tasks: Map<string, Task<unknown, unknown>> = new Map();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { Game } from "@game";
|
||||
import { boxStyle, getTheme } from "@themes";
|
||||
import { panels } from "@ui";
|
||||
import { panels } from "./UI.js";
|
||||
import blessed from 'neo-blessed';
|
||||
import { ProcessManager } from "../ProcessManager.js";
|
||||
|
||||
// TODO convert all these popup-y things to be View based
|
||||
// make them be boxes that have a view
|
||||
|
|
@ -9,6 +10,7 @@ export class EscapeMenu {
|
|||
|
||||
options = [
|
||||
'RESUME',
|
||||
'RELOAD',
|
||||
'QUIT'
|
||||
];
|
||||
selected = 0;
|
||||
|
|
@ -22,7 +24,7 @@ export class EscapeMenu {
|
|||
this.box = blessed.box({
|
||||
top: 3,
|
||||
left: 'center',
|
||||
width: 30,
|
||||
width: 20,
|
||||
height: 'shrink',
|
||||
content: '',
|
||||
tags: true,
|
||||
|
|
@ -43,8 +45,12 @@ export class EscapeMenu {
|
|||
break;
|
||||
}
|
||||
case 1: {
|
||||
ProcessManager.restart();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
Game.current.sync();
|
||||
process.exit(0);
|
||||
ProcessManager.quit();
|
||||
}
|
||||
}
|
||||
} else if(key.full === 'escape') {
|
||||
|
|
@ -60,9 +66,10 @@ export class EscapeMenu {
|
|||
}
|
||||
|
||||
render() {
|
||||
this.box.setContent(' Paused\n\n' + this.options.map((v, i) => {
|
||||
const space = ' '.repeat(this.box.width / 2 - 4);
|
||||
this.box.setContent(space + 'Paused\n\n' + this.options.map((v, i) => {
|
||||
if(i === this.selected) {
|
||||
return ` ${getTheme().bright(v)} `;
|
||||
return ` ❯ ${getTheme().bright(v)} `;
|
||||
} else {
|
||||
return ` ${getTheme().normal(v)} `;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { Game } from "@game";
|
||||
import { ItemState } from "@items";
|
||||
import { boxStyle, getTheme } from "@themes";
|
||||
import { panels } from "@ui";
|
||||
import { panels } from "./UI";
|
||||
import EventEmitter from "events";
|
||||
import blessed from 'neo-blessed';
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Renderable, RenderMode } from './UI.js';
|
||||
import { Renderable, RenderMode } from './UI';
|
||||
import { KeypressAcceptor } from './Menu.js';
|
||||
import { getTheme } from '@themes';
|
||||
|
||||
|
|
@ -3,6 +3,7 @@ import blessed from 'neo-blessed';
|
|||
import ansi from 'sisteransi';
|
||||
import { boxStyle, getTheme } from '@themes';
|
||||
export { Popup } from './Popup.js';
|
||||
export { Menu } from './Menu.js'
|
||||
|
||||
export interface Renderable {
|
||||
render(mode?: RenderMode): string
|
||||
|
|
@ -29,15 +30,15 @@ export function isStarted() {
|
|||
|
||||
export const panels = {
|
||||
get left() {
|
||||
assertStarted()
|
||||
assertStarted();
|
||||
return leftPanel;
|
||||
},
|
||||
get right() {
|
||||
assertStarted()
|
||||
assertStarted();
|
||||
return rightPanel;
|
||||
},
|
||||
get screen() {
|
||||
assertStarted()
|
||||
assertStarted();
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Renderable } from './UI.js';
|
||||
import { Renderable } from './UI';
|
||||
import { KeypressAcceptor } from './Menu.js';
|
||||
|
||||
export abstract class View implements Renderable, KeypressAcceptor {
|
||||
|
|
@ -2,7 +2,7 @@ import { getTheme } from "@themes";
|
|||
import { Game } from "../../Game.js";
|
||||
import { progressbar } from "../../Progressbar.js";
|
||||
import { PawnDetails } from "../PawnDetails.js";
|
||||
import { panels } from "../UI.js";
|
||||
import { panels } from "../UI";
|
||||
import { View } from "../View.js";
|
||||
|
||||
export default class PawnsView extends View {
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
"@tasks": ["./src/registries/Tasks"],
|
||||
"@items": ["./src/registries/Items"],
|
||||
"@world": ["./src/World"],
|
||||
"@ui": ["./src/ui/UI"],
|
||||
"@ui": ["./src/qt/index"],
|
||||
"@game": ["./src/Game"]
|
||||
},
|
||||
"noImplicitAny": true
|
||||
|
|
|
|||
Loading…
Reference in New Issue