diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 0000000..88808c5 --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,5 @@ +{ + "extension": ["test.js"], + "spec": "out/**/*.test.js", + "watch-files": ["out/**/*.test.js"] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..0c07599 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "npm", + "script": "compile:watch", + "isBackground": true, + "group": { + "kind": "build", + "isDefault": true + }, + "runOptions": { + "runOn": "folderOpen" + }, + "presentation": { + "reveal": "never", + "echo": false, + "focus": false, + "panel": "dedicated" + }, + "problemMatcher": { + "base": "$tsc-watch", + "applyTo": "allDocuments" + } + } + ] +} \ No newline at end of file diff --git a/content/core/actions/CoreActions.ts b/content/core/actions/CoreActions.ts new file mode 100644 index 0000000..e60a699 --- /dev/null +++ b/content/core/actions/CoreActions.ts @@ -0,0 +1,23 @@ +import { registerAction } from '@actions'; +import { Game } from '@game'; + +registerAction('Gather Flint', (qty) => { + Game.current.board.addTask({ + taskId: 'core:gather-flint', + options: {} + }) +}); + +registerAction('Gather Slate', (qty) => { + Game.current.board.addTask({ + taskId: 'core:gather-slate', + options: {} + }) +}); + +registerAction('Fetch Sticks', (qty) => { + Game.current.board.addTask({ + taskId: "core:fetch-sticks", + options: {} + }) +}); \ No newline at end of file diff --git a/content/core/actions/FetchSticksAction.ts b/content/core/actions/FetchSticksAction.ts new file mode 100644 index 0000000..fe9ee92 --- /dev/null +++ b/content/core/actions/FetchSticksAction.ts @@ -0,0 +1,2 @@ +import { registerAction } from '@actions'; +import { Game } from '@game'; \ No newline at end of file diff --git a/content/core/actions/GatherRocksAction.ts b/content/core/actions/GatherRocksAction.ts new file mode 100644 index 0000000..0183670 --- /dev/null +++ b/content/core/actions/GatherRocksAction.ts @@ -0,0 +1,2 @@ +import { registerAction } from '@actions'; +import { Game } from '@game'; diff --git a/content/core/items/Items.ts b/content/core/items/Items.ts new file mode 100644 index 0000000..af6aa65 --- /dev/null +++ b/content/core/items/Items.ts @@ -0,0 +1,31 @@ +import { Item } from '@items'; + +export const LOG = new Item().setName("Log").setId('core:resources/log'); +export const STICK = new Item().setName("Stick").setId('core:resources/stick'); +export const PLANT_FIBRES = new Item().setName("Plant Fibres").setId('core:plant-fibres'); + +export const FLINT_NORMAL = new Item().setName("Flint").setId('core:flint'); +export const FLINT_FLAKE = new Item().setName("Flint Flake").setId('core:flint-flake'); +export const SANDSTONE_NORMAL = new Item().setName("Sandstone").setId('core:sandstone'); +export const SANDSTONE_PEBBLE = new Item().setName("Sandstone Pebble").setId('core:sandstone-pebble'); +export const SLATE_NORMAL = new Item().setName("Slate").setId('core:slate'); +export const SLATE_FLAKE = new Item().setName("Slate Flake").setId('core:slate-flake'); +export const LIMESTONE_NORMAL = new Item().setName("Limestone").setId('core:limestone'); +export const LIMESTONE_PEBBLE = new Item().setName("Limestone Pebble").setId('core:limestone-pebble'); +export const SHALE_NORMAL = new Item().setName("Shale").setId('core:shale'); +export const SHALE_PEBBLE = new Item().setName("Shale Pebble").setId('core:shale-pebble'); +export const OBSIDIAN_NORMAL = new Item().setName("Obsidian").setId('core:obsidian') +export const OBSIDIAN_FLAKE = new Item().setName("Obsidian Flake").setId('core:obsidian-flake'); + +export const FLINT_HATCHET = new Item().setName("Flint Hatchet").setId('core:flint-hatchet'); +export const FLINT_ARROWHEAD = new Item().setName("Flint Arrowhead").setId('core:flint-arrowhead'); +export const FLINT_SPEAR = new Item().setName("Flint Spear").setId('core:flint-spear'); +export const SLATE_HATCHET = new Item().setName("Slate Hatchet").setId('core:slate-hatchet'); +export const SLATE_ARROWHEAD = new Item().setName("Slate Arrowhead").setId('core:slate-arrowhead'); +export const SLATE_SPEAR = new Item().setName("Slate Spear").setId('core:slate-spear'); +export const OBSIDIAN_HATCHET = new Item().setName("Obsidian Hatchet").setId('core:obsidian-hatchet'); +export const OBSIDIAN_ARROWHEAD = new Item().setName("Obsidian Arrowhead").setId('core:obsidian-arrowhead'); +export const OBSIDIAN_SPEAR = new Item().setName("Obsidian Spear").setId('core:obsidian-spear'); + +// tools: plant fibres = rope, flint hatchet +// shale - igneous. metamorphasis => slate \ No newline at end of file diff --git a/content/core/tasks/ChopTreeTask.ts b/content/core/tasks/ChopTreeTask.ts new file mode 100644 index 0000000..8e97a5c --- /dev/null +++ b/content/core/tasks/ChopTreeTask.ts @@ -0,0 +1,25 @@ +import chalk from 'chalk'; +import { Game } from '@game'; +import { registerTask, Task } from '@tasks'; +import { LOG } from '../items/Items.js'; + +// TODO cleanup imports, use aliases + +class ChopTreeTask extends Task { + work = 100; + + reward() { + Game.current.inv.add(LOG, 1); + } + + get title() { + return chalk.yellow('Chop Trees'); + } + + get status() { + return chalk.yellow('LOGGING'); + } +} + + +registerTask('core:chop-trees', ChopTreeTask); \ No newline at end of file diff --git a/content/core/tasks/FetchSticksTask.ts b/content/core/tasks/FetchSticksTask.ts new file mode 100644 index 0000000..5440356 --- /dev/null +++ b/content/core/tasks/FetchSticksTask.ts @@ -0,0 +1,21 @@ +import { Task, registerTask } from "@tasks"; +import chalk from 'chalk'; +import { Game } from '@game'; +import { LOG, STICK } from '../items/Items.js'; + +class FetchSticksTask extends Task { + work = 100; + reward(): void { + Game.current.inv.add(STICK, 1); + } + + get title() { + return chalk.yellow('Chop Trees'); + } + + get status() { + return chalk.yellow('LOGGING'); + } +} + +registerTask('core:fetch-sticks', FetchSticksTask); \ No newline at end of file diff --git a/content/core/tasks/GatherRocksTask.ts b/content/core/tasks/GatherRocksTask.ts new file mode 100644 index 0000000..794fdf7 --- /dev/null +++ b/content/core/tasks/GatherRocksTask.ts @@ -0,0 +1,36 @@ +import { Task, registerTask } from "@tasks"; +import chalk from 'chalk'; +import { Game } from '@game'; +import { FLINT_NORMAL, SLATE_NORMAL } from '../items/Items.js'; + +registerTask('core:gather-flint', class GatherFlintTask extends Task { + work = 1000; + + reward(): void { + Game.current.inv.add(FLINT_NORMAL, 1); + } + + get title() { + return 'Gather Flint'; + } + + get status() { + return 'SCAVENGING'; + } +}); + +registerTask('core:gather-slate', class GatherSlateTask extends Task { + work = 1000; + + reward(): void { + Game.current.inv.add(SLATE_NORMAL, 1); + } + + get title() { + return 'Gather Slate'; + } + + get status() { + return 'SCAVENGING'; + } +}); \ No newline at end of file diff --git a/content/core/themes/ashe.ts b/content/core/themes/ashe.ts new file mode 100644 index 0000000..e69de29 diff --git a/content/core/themes/standard.ts b/content/core/themes/standard.ts new file mode 100644 index 0000000..16524ad --- /dev/null +++ b/content/core/themes/standard.ts @@ -0,0 +1,8 @@ +import { registerTheme } from '@themes'; +import chalk from 'chalk' + +registerTheme("default", {}); + +registerTheme("high contrast", { + selected: chalk.ansi256(250).inverse +}); \ No newline at end of file diff --git a/lib/aliases.mjs b/lib/aliases.mjs new file mode 100644 index 0000000..1c7f461 --- /dev/null +++ b/lib/aliases.mjs @@ -0,0 +1,37 @@ +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", + "@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); +} \ No newline at end of file diff --git a/package.json b/package.json index ffa6d61..f1cc8b2 100644 --- a/package.json +++ b/package.json @@ -7,27 +7,35 @@ "dependencies": { "@types/blessed": "^0.1.17", "@types/bonjour": "^3.5.8", + "@types/chai": "^4.2.19", "@types/faker": "^5.5.6", + "@types/mocha": "^8.2.2", "@types/uuid": "^8.3.0", "bonjour": "^3.5.0", + "chai": "^4.3.4", "chalk": "^4.1.1", + "deepmerge": "^4.2.2", "faker": "^5.5.3", - "frigid": "^1.3.8", + "frigid": "^1.3.13", "fs-extra": "^10.0.0", "get-port": "^5.1.1", - "logger": "^0.0.1", + "mocha": "^9.0.1", + "module-alias": "^2.2.2", "neo-blessed": "^0.2.0", "printable-characters": "^1.0.42", "sisteransi": "^1.0.5", "typescript": "^4.3.2", "uuid": "^8.3.2", + "walk-sync": "^3.0.0", "ws": "^7.4.6", "yarn": "^1.22.10" }, "scripts": { "compile:watch": "tsc --watch", - "start": "node --enable-source-maps out/index.js", + "start": "node --no-warnings --loader ./lib/aliases.mjs --enable-source-maps out/src/index.js", "dev": "supervisor -w out -n exit -t -k --exec yarn -- start", - "prod": "git fetch && git pull && yarn && tsc && yarn start" + "prod": "git fetch && git pull && yarn && tsc && yarn start", + "test": "mocha", + "lint": "eslint src/**/*.ts" } } diff --git a/src/Game.ts b/src/Game.ts index 64cf726..25c4472 100644 --- a/src/Game.ts +++ b/src/Game.ts @@ -5,78 +5,77 @@ 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 } from './ui/UI.js'; -import log from './log.js'; -import { ChopTreeTask } from './tasks/ChopTreeTask.js'; -import { Task } from './tasks/Task.js'; +import { render, Renderable, setTitle, start } from './ui/UI.js'; import { ready } from './multiplayer/mDNS.js'; import faker from 'faker'; let game = null; export class Game extends Frigid implements Tickable, Renderable { - pawns: Pawn[] = []; - selected: Pawn; - inventory: Inventory; - board: TaskList; - menu: Menu; - clock: Time; - name: string; + pawns: Pawn[] = []; + selected: Pawn; + inventory: Inventory; + board: TaskList; + menu: Menu; + clock: Time; + name: string; - [DEBUG] = true; + [DEBUG] = true; - static get current(): Game { - if (!game) throw new Error('Somehow called a game before it existed?'); - return game; - } + static get current(): Game { + if (!game) throw new Error('Somehow called a game before it existed?'); + return game; + } - async tick() { - for(const pawn of this.pawns) { - pawn.tick(); - } - render(); - } + async tick() { + for(const pawn of this.pawns) { + pawn.tick(); + } + render(); + } - get inv() { return this.inventory; } + get inv() { return this.inventory; } - removePawn(pawn: Pawn) { - if(pawn === this.selected) { - if(this.pawns.indexOf(this.selected) === this.pawns.length - 1) this.advanceSelection(-1); - else this.advanceSelection(1); - } + removePawn(pawn: Pawn) { + if(pawn === this.selected) { + if(this.pawns.indexOf(this.selected) === this.pawns.length - 1) this.advanceSelection(-1); + else this.advanceSelection(1); + } - this.pawns = this.pawns.filter(testPawn => { - return pawn !== testPawn; - }); - } + this.pawns = this.pawns.filter(testPawn => { + return pawn !== testPawn; + }); + } - advanceSelection(number) { - let index = this.pawns.indexOf(this.selected); - this.selected = this.pawns[Math.min(Math.max(index + number, 0), this.pawns.length - 1)]; - } - - ctor () { - game = this; - this.name ??= faker.address.city(); - setTitle(this.name); - this.pawns ??= []; - this.selected ??= this.pawns[0] || null; - this.menu = new Menu(); - this.board ??= new TaskList(); - this.inventory ??= new Inventory(); - this.clock ??= new Time(); - this.clock.thing = this; - this.clock.start(); - ready(this.name); - render(this); - } + advanceSelection(number) { + let index = this.pawns.indexOf(this.selected); + this.selected = this.pawns[Math.min(Math.max(index + number, 0), this.pawns.length - 1)]; + } + + ctor () { + game = this; + start(); + this.name ??= faker.address.city(); + setTitle(this.name); + this.pawns ??= []; + this.selected ??= this.pawns[0] || null; + this.menu = new Menu(); + this.board ??= new TaskList(); + this.inventory ??= new Inventory(); + this.inventory.validate(); + this.clock ??= new Time(); + this.clock.thing = this; + this.clock.start(); + ready(this.name); + render(this); + } - static serializationDependencies() { - return [Pawn, Inventory, TaskList, Time, ChopTreeTask, Task]; - } + static serializationDependencies() { + return [ Pawn, Inventory, TaskList, Time ]; + } - render() { - this.menu.render(); - this.board.render(); - } + render() { + this.menu.render(); + this.board.render(); + } } \ No newline at end of file diff --git a/src/Inventory.ts b/src/Inventory.ts index 0ee90c5..137ff65 100644 --- a/src/Inventory.ts +++ b/src/Inventory.ts @@ -1,37 +1,59 @@ import { Serializable } from 'frigid'; import { Game } from './Game.js'; -import { Item, ItemState } from './Item.js'; +import { Item, ItemState } from './registries/Items.js'; +import { Popup } from './ui/Popup.js'; import { Renderable } from './ui/UI.js'; export class Inventory extends Serializable implements Renderable { - items: ItemState[]; + items: ItemState[]; - ctor() { - this.items ??= []; - } + ctor() { + this.items ??= []; + } - static serializationDependencies() { - return [ItemState]; - } + validate() { + const invalid: ItemState[] = []; + for(const itemState of this.items) { + try { + itemState.item; + } catch { + invalid.push(itemState); + } + } + if(invalid.length === 0) return; + + for(const itemState of invalid) { + this.remove(itemState); + } + Popup.show('Invalid ItemStates removed:\n\n' + invalid.map(itemState => itemState.qty + 'x ' + itemState.itemId).join('\n')); + } - add(item: Item, qty: number = 1) { - const id = item.id; - const existingArr = this.items.filter(itemState => { - return itemState.itemId === id; - }); - let existing: ItemState = null; - if(existingArr.length === 1) { - existing = existingArr[0]; - } - if(existing) { - existing.qty += qty; - } else { - this.items.push(new ItemState(item, qty, {})); - } - Game.current.sync(); - } + static serializationDependencies() { + return [ItemState]; + } - render() { - return this.items.map(item => item.render()).join('\n'); - } + remove(itemState: ItemState) { + this.items = this.items.filter(test => test !== itemState); + } + + add(item: Item, qty: number = 1) { + const id = item.id; + const existingArr = this.items.filter(itemState => { + return itemState.itemId === id; + }); + let existing: ItemState = null; + if(existingArr.length === 1) { + existing = existingArr[0]; + } + if(existing) { + existing.qty += qty; + } else { + this.items.push(new ItemState(item, qty, {})); + } + Game.current.sync(); + } + + render() { + return this.items.map(item => item.render()).join('\n'); + } } diff --git a/src/Item.ts b/src/Item.ts deleted file mode 100644 index 58859d3..0000000 --- a/src/Item.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Serializable } from 'frigid'; -import { getTheme } from './ui/Theme.js'; -import { Renderable } from './ui/UI.js'; - -export type ItemID = string; - -const items = new Map(); - -// ITEMS SHALL BE SINGULAR -export class Item extends Serializable { - static LOG = new Item().setName("Log").setId('resources:log'); - - name = ''; - id: ItemID = ''; - - setName(name) { - this.name = name; - return this; - } - - setId(id: ItemID) { - this.id = id; - items.set(this.id, this); - return this; - } -} - -export class ItemState extends Serializable implements Renderable { - qty: number; - itemId: ItemID; - data: any; - - get item() { - return items.get(this.itemId); - } - - constructor(item: Item, amount: number, data: any) { - super(); - this.qty = amount; - this.itemId = item.id; - this.data = data; - } - - render() { - return getTheme().normal(` ${this.item.name}{|}${this.qty} `); - } -} \ No newline at end of file diff --git a/src/Pawn.ts b/src/Pawn.ts index 85f6b35..237fc8f 100644 --- a/src/Pawn.ts +++ b/src/Pawn.ts @@ -1,146 +1,136 @@ import { Serializable } from 'frigid'; import faker from 'faker'; -import chalk from 'chalk'; -import log from './log.js'; -import { Task } from './tasks/Task.js'; +import { Task } from './registries/Tasks.js'; import Time, { Tickable } from './Time.js'; -import { ChopTreeTask } from './tasks/ChopTreeTask.js'; import { Game } from './Game.js'; import { render } from './ui/UI.js'; import { Memory } from './Memory.js'; - -const LABORS = { - CUT_TREE: Symbol('CUT_TREE'), - MINING: Symbol('CUT_TREE'), -} - -const SKILLS = { - PICKAXE: Symbol('PICKAXE'), - HATCHET: Symbol('HATCHET') -} +import { getTheme } from './registries/Themes.js'; // const STATUS = { // IDLE: Symbol('IDLE') // } const energyScale = 0.1; +const MAX_ENERGY = 100; + +// TODO add stats getter to return % of all stats export class Pawn extends Serializable implements Tickable { - name: { - first: string, - last: string - }; - job: Task; - awake: boolean; - sex: number; + name: { + first: string, + last: string + }; + job: Task; + awake: boolean; + sex: number; - energy: number; - fun: number; + energy: number; + fun: number; - age: number; + age: number; - memories: Memory[]; + memories: Memory[]; - async tick() { - this.age ++; + async tick() { + this.age ++; - this.energy -= energyScale; + this.energy -= energyScale; - if(this.awake === false) { - this.energy += energyScale * 4; - if(this.energy >= 100) { - this.awake = true; - } - } else { - if(this.job) { - this.job.doWork(1, this); - this.energy -= energyScale; - if(this.job?.completed) { - this.stopWorking(); - } - } else { - const inactive = Game.current.board.tasks.filter(task => { - return task.worker === null; - }); - if(inactive.length > 0) { - const task = inactive[0]; - // const task = inactive[Math.floor(Math.random() * inactive.length)]; - this.assignJob(task); - } - } - if(this.energy <= 0) { - this.stopWorking(); - this.awake = false; - } - } + if(this.awake === false) { + this.energy += energyScale * 4; + if(this.energy >= MAX_ENERGY) { + this.awake = true; + } + } else { + if(this.job) { + this.job.doWork(1, this); + this.energy -= energyScale; + if(this.job?.completed) { + this.stopWorking(); + } + } else { + const inactive = Game.current.board.tasks.filter(task => { + return task.worker === null; + }); + if(inactive.length > 0) { + const task = inactive[0]; + // const task = inactive[Math.floor(Math.random() * inactive.length)]; + this.assignJob(task); + } + } + if(this.energy <= 0) { + this.stopWorking(); + this.awake = false; + } + } - } + } - get idle() { - return !this.job && this.awake; - } + get idle() { + return !this.job && this.awake; + } - ctor() { - log.info('Pawn::ctor') - this.name ??= { - first: faker.name.firstName(), - last: faker.name.lastName() - }; - if(!this.sex) { - this.sex = Math.round(Math.random()); - this.name.first = faker.name.firstName(this.sex); - } - this.awake ??= true; - this.energy ??= 100; - this.memories ??= []; - if(!this.age) { - this.age = Math.floor(525600 * (16 + Math.random() * 9)); - this.memories.push({ - type: "birth", - location: Game.current.name, - time: { - age: 0, - locale: new Time(Game.current.clock.stamp - this.age).toString() - } - }) - } + ctor() { + this.name ??= { + first: faker.name.firstName(), + last: faker.name.lastName() + }; + if(!this.sex) { + this.sex = Math.round(Math.random()); + this.name.first = faker.name.firstName(this.sex); + } + this.awake ??= true; + this.energy ??= MAX_ENERGY; + this.memories ??= []; + if(!this.age) { + this.age = Math.floor(525600 * (16 + Math.random() * 9)); + this.memories.push({ + type: "birth", + location: Game.current.name, + time: { + age: 0, + locale: new Time(Game.current.clock.stamp - this.age).toString() + } + }) + } - if(this.job?.completed) { - this.stopWorking(); - } - } + if(this.job?.completed) { + this.stopWorking(); + } + } - stopWorking() { - if(this.job) { - this.job.stopJob(); - this.job = null; - } - } + stopWorking() { + if(this.job) { + this.job.stopJob(); + this.job = null; + } + } - assignJob(task: Task) { - this.job?.stopJob() - this.job = task; - this.job.claim(this); - } + assignJob(task: Task) { + this.job?.stopJob() + this.job = task; + this.job.claim(this); + } - get status() { - if(this.job) { - return this.job.status; - } else { - return this.awake ? chalk.bold.black('IDLE') : chalk.blue('RESTING') - } - } + get status() { + if(this.job) { + return this.job.status; + } else { + return this.awake ? getTheme().status.idle('IDLE') : getTheme().status.self('RESTING') + } + } - static serializationDependencies() { - return [Task, ChopTreeTask] - } + static serializationDependencies() { + return [Task] + } - toString() { - if(this.name) { - return this.name.first + ' ' + this.name.last; - } else { - return '[Object Pawn]'; - } - } + toString() { + if(this.name) { + return this.name.first + ' ' + this.name.last; + } else { + return '[Object Pawn]'; + } + } } diff --git a/src/Progressbar.ts b/src/Progressbar.ts index ae4d720..c386b5d 100644 --- a/src/Progressbar.ts +++ b/src/Progressbar.ts @@ -1,5 +1,5 @@ import chalk from "chalk"; -import { getTheme } from "./ui/Theme.js"; +import { getTheme } from "./registries/Themes.js"; export enum ProgressbarStyle { indicator = 'indicator', @@ -9,7 +9,7 @@ export enum ProgressbarStyle { export const barCache: Map = new Map(); export function progressbar(completion: number, width: number, style: ProgressbarStyle = ProgressbarStyle.indicator) { - const cacheKey = `${completion}-${width}-${style}`; + const cacheKey = `${Math.round(completion * width * 8)}-${width}-${style}`; if(barCache.has(cacheKey)) { stats.cacheHits ++; return barCache.get(cacheKey); @@ -23,7 +23,19 @@ export function progressbar(completion: number, width: number, style: Progressba } else if(style === ProgressbarStyle.progress) { chalkFn = getTheme().progressBar.normal; } - const chars = [' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█']; + + const chars = [ + '\u0020', + '\u258f', + '\u258e', + '\u258d', + '\u258c', + '\u258b', + '\u258a', + '\u2589', + '\u2588' + ]; + let str = ''; for(let i = 0; i < width; i ++) { const remainder = Math.floor(Math.min(Math.max(0, (completion * width) - i), 1) * 8); diff --git a/src/Task.ts b/src/Task.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/TaskList.ts b/src/TaskList.ts index 1717703..1e27348 100644 --- a/src/TaskList.ts +++ b/src/TaskList.ts @@ -1,8 +1,6 @@ import { Serializable } from 'frigid'; -import { ChopTreeTask } from "./tasks/ChopTreeTask.js"; -import { Game } from './Game.js'; -import { Task } from "./tasks/Task.js"; -import { render, Renderable, tasksPanel } from './ui/UI.js'; +import { Task, taskClasses } from './registries/Tasks.js'; +import { render, Renderable, panels } from './ui/UI.js'; export class TaskList extends Serializable implements Renderable { tasks: Task[] = []; @@ -14,10 +12,21 @@ export class TaskList extends Serializable implements Renderable { } static serializationDependencies() { - return [ChopTreeTask, Task]; + let a = Array.from(taskClasses.values()) + return a as unknown as (typeof Serializable)[]; } - addTask(task) { + addTask({taskId, options}: TaskOptions) { + if(!taskClasses.has(taskId)) + throw new Error('unknown task: ' + taskId); + + // this is any because TS doesnt understand that + // static references and constructors are the + // SAME THING. + // in TS, they're MAGICALLY incompatible... + const taskClass: any = taskClasses.get(taskId); + const task = new taskClass(); + this.tasks = [...this.tasks, task]; } @@ -27,9 +36,19 @@ export class TaskList extends Serializable implements Renderable { render() { // const width = tasksPanel.width; - tasksPanel.setContent(`${this.tasks.map(task => { + panels.left.setContent(`${this.tasks.map(task => { return task.toString(); }).join('\n')}`); // return this.tasks.map(task => task.toString()).join('\n'); } } + +const taskTypes = {}; +export function registerTask(name, clazz) { + taskTypes[name] = clazz; +} + +export type TaskOptions = { + taskId: string, + options: any +} \ No newline at end of file diff --git a/src/Time.test.ts b/src/Time.test.ts new file mode 100644 index 0000000..338f8c9 --- /dev/null +++ b/src/Time.test.ts @@ -0,0 +1,31 @@ +// TODO pull in full test framework and coverage shit from vogue +import { expect } from 'chai'; +import Time from './Time.js'; + +describe('Time', () => { + describe('normalize timestamps correctly', () => { + it('0', () => { + const time = new Time(0); + expect(time.toString()).to.equal("0:00 Jan 1, 0001 CE"); + }) + it('-1', () => { + const time = new Time(-1); + expect(time.toString()).to.equal("23:59 Dec 31, 0001 BCE"); + }) + it('1', () => { + const time = new Time(1); + expect(time.toString()).to.equal("0:01 Jan 1, 0001 CE"); + expect(time) + }) + it('0.25', () => { + const time = new Time(0.25); + expect(time.toString()).to.equal("0:00 Jan 1, 0001 CE"); + expect(time.second).to.equal(15); + }) + it('-0.25', () => { + const time = new Time(-0.25); + expect(time.toString()).to.equal("23:59 Dec 31, 0001 BCE"); + expect(time.second).to.equal(45); + }) + }) +}); \ No newline at end of file diff --git a/src/Time.ts b/src/Time.ts index 38be5bc..63b6534 100644 --- a/src/Time.ts +++ b/src/Time.ts @@ -1,197 +1,193 @@ import chalk from "chalk"; import { Serializable } from "frigid"; -import { isThisTypeNode } from "typescript"; -import log from "./log.js"; -import { getTheme } from "./ui/Theme.js"; +import { getTheme } from "./registries/Themes.js"; import { Renderable } from "./ui/UI.js"; +type AbbreviatedMonthName = string; + const daysInMonth = [ - 31, 28, 31, - 30, 31, 30, - 31, 31, 30, - 31, 30, 31 + 31, 28, 31, + 30, 31, 30, + 31, 31, 30, + 31, 30, 31 ]; -const months = [ - 'Jan', 'Feb', 'Mar', - 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec' +const months: AbbreviatedMonthName[] = [ + 'Jan', 'Feb', 'Mar', + 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec' ] export default class Time extends Serializable implements Renderable { - rate: number; - paused = true; + rate: number; + paused = true; - thing: Tickable; + thing: Tickable; - year: number; - month: number; - day: number; - hour: number; - minute: number; + year: number; + month: number; + day: number; + hour: number; + minute: number; - constructor(timestamp: number = 0) { - super(); - this.minute = timestamp; - this.normalize(); - } + constructor(timestamp: number = 0) { + super(); + this.minute = timestamp; + this.normalize(); + } - asAge() { - if(this.year > 1) { - return this.year + ' years old'; - } else { - if(this.month > 2) { - return this.month + ' months old'; - } else { - if(this.day > 1) { - return this.day + ' days old'; - } else if(this.day === 1) { - return '1 day old'; - } else { - return 'newborn'; - } - } - } - } + asAge() { + if(this.year > 1) { + return this.year + ' years old'; + } else { + if(this.month > 2) { + return this.month + ' months old'; + } else { + if(this.day > 1) { + return this.day + ' days old'; + } else if(this.day === 1) { + return '1 day old'; + } else { + return 'newborn'; + } + } + } + } - render() { - const sym = (this.hour >= 6 && this.hour < 20) ? - chalk.ansi256(226).bgAnsi256(27)(' ⬤ ') : - chalk.ansi256(254).bgAnsi256(17)(' ☾ ') + render() { + const sym = (this.hour >= 6 && this.hour < 20) ? + chalk.ansi256(226).bgAnsi256(27)(' ☼ ') : + chalk.ansi256(254).bgAnsi256(17)(' ☾ ') - return `${sym} ${ - getTheme().normal(`${ - this.hour.toString().padStart(2, ' ') - }:${ - this.minute.toString().padStart(2, '0') - } ${ - months[this.month] - } ${ - this.day + 1 - }, ${ - this.normalizedYear - }`) - }`; + return `${sym} ${ + getTheme().normal(`${ + this.toString() + }`) + }`; + } - // return '☾' || '☼'; - } + toString() { + return `${this.hour}:${Math.floor(this.minute).toString().padStart(2, '0')} ${months[this.month]} ${this.day + 1}, ${this.normalizedYear}` + } - toString() { - return `${this.hour}:${this.minute.toString().padStart(2, '0')} ${months[this.month]} ${this.day + 1}, ${this.normalizedYear}` - } + ctor() { + this.rate = 60; + this.minute ??= 0; + this.hour ??= 0; + this.day ??= 0; + this.month ??= 0; + this.year ??= 0; + } - ctor() { - this.rate = 60; - this.minute ??= 0; - this.hour ??= 0; - this.day ??= 0; - this.month ??= 0; - this.year ??= 0; - } + get second() { + return Math.floor((this.minute % 1) * 60) + } - get stamp() { - let minute = this.minute; - let hour = this.hour; - let day = this.day; - let month = this.month; - let year = this.year; + get stamp() { + let minute = this.minute; + let hour = this.hour; + let day = this.day; + let month = this.month; + let year = this.year; - const daysInYear = daysInMonth.reduce((a, b) => a+b, 0); + const daysInYear = daysInMonth.reduce((a, b) => a+b, 0); - day += daysInYear * year; - year = 0; - while(month > 0) { - day += daysInMonth[month]; - month --; - } + day += daysInYear * year; + year = 0; + while(month > 0) { + day += daysInMonth[month]; + month --; + } - hour += day * 24; - day = 0; + hour += day * 24; + day = 0; - minute += hour * 60; - hour = 0; + minute += hour * 60; + hour = 0; - return minute; - } + return minute; + } - pause() { - this.paused = true; - } + pause() { + this.paused = true; + } - start() { - this.paused = false; - setTimeout(this.doTick.bind(this), 0); - } - - advanceTime(minutes) { - this.minute ++; - this.normalize() - } + start() { + this.paused = false; + setTimeout(this.doTick.bind(this), 0); + } + + advanceTime(seconds) { + this.minute += seconds / 60; + this.normalize() + } - get normalizedYear() { - if(this.year >= 0) { - return (this.year + 1).toString().padStart(4, '0') + ' CE'; - } else { - return Math.abs(this.year).toString().padStart(4, '0') + ' BCE'; - } - } + get normalizedYear() { + if(this.year >= 0) { + return (this.year + 1).toString().padStart(4, '0') + ' CE'; + } else { + return Math.abs(this.year).toString().padStart(4, '0') + ' BCE'; + } + } - normalize() { - while(this.minute >= 60) { - this.minute -= 60; - this.hour ++; - } - while(this.minute < 0) { - this.minute += 60; - this.hour --; - } - while(this.hour >= 24) { - this.hour -= 24; - this.day ++; - } - while(this.hour < 0) { - this.hour += 24; - this.day --; - } - while(this.day < 0) { - this.day += daysInMonth[ - ((this.month % months.length) + months.length) % months.length - ]; - this.month --; - } - while(this.day >= daysInMonth[this.month % months.length]) { - this.day -= daysInMonth[this.month % months.length]; - this.month ++; - } + normalize() { + // while(t) + while(this.minute >= 60) { + this.minute -= 60; + this.hour ++; + } + while(this.minute < 0) { + this.minute += 60; + this.hour --; + } + while(this.hour >= 24) { + this.hour -= 24; + this.day ++; + } + while(this.hour < 0) { + this.hour += 24; + this.day --; + } - while(this.month >= 12) { - this.month -= 12; - this.year ++; - } - while(this.month < 0) { - this.month += 12; - this.year --; - } - } + while(this.day < 0) { + this.day += daysInMonth[ + ((this.month % months.length) + months.length) % months.length + ]; + this.month --; + } + while(this.day >= daysInMonth[this.month % months.length]) { + this.day -= daysInMonth[this.month % months.length]; + this.month ++; + } - async doTick() { - this.advanceTime(1); - const timeout = 1000 / this.rate; - const start = new Date().getTime(); - if(this.thing) { - await this.thing.tick(); - } - const elapsed = new Date().getTime() - start; - const wait = Math.max(timeout - elapsed, 0); - if(this.paused) return; - setTimeout(this.doTick.bind(this), wait) - } + while(this.month >= 12) { + this.month -= 12; + this.year ++; + } + while(this.month < 0) { + this.month += 12; + this.year --; + } + } + + async doTick() { + this.advanceTime(1); + const timeout = 1000 / this.rate; + const start = new Date().getTime(); + if(this.thing) { + await this.thing.tick(); + } + const elapsed = new Date().getTime() - start; + const wait = Math.max(timeout - elapsed, 0); + if(this.paused) return; + setTimeout(this.doTick.bind(this), wait) + } } export interface Tickable { - tick: () => Promise + tick: () => Promise } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 7f8644c..7fe35bd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,36 @@ -import { Game } from './Game.js'; import { render } from './ui/UI.js'; import { ensureDirSync } from 'fs-extra'; -import { parse } from 'path'; +import { lstatSync } from 'fs'; +import { parse, resolve } from 'path'; +import walkSync from 'walk-sync'; +import { fileURLToPath } from 'url'; +import { Game } from '@game'; const saveFile = process.argv[2] || 'data/world01.json'; ensureDirSync(parse(saveFile).dir); -const game = Game.create(saveFile); -render(game); + +// 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 => resolve(extensionsPath, path)) + .filter(path => lstatSync(path).isFile()) + .filter(path => parse(path).ext === '.js'); + // .map(path => import(path)); + +console.log('found', extensions.length, 'extensions'); + +for(const path of extensions) { + console.log('=== [', path, '] ==='); + await import(path); + console.log(); +} + +// 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); \ No newline at end of file diff --git a/src/log.ts b/src/log.ts deleted file mode 100644 index 9641e7b..0000000 --- a/src/log.ts +++ /dev/null @@ -1,15 +0,0 @@ -import logger from 'logger'; - -const log: { - fatal: (...args) => void, - error: (...args) => void, - warn: (...args) => void, - info: (...args) => void, - debug: (...args) => void, -} = logger.createLogger('debug.log'); - -(log as any).format = function(level, date, message) { - return `${date.getTime()} [${level}]${message}`; -}; - -export default log; \ No newline at end of file diff --git a/src/multiplayer/Player.ts b/src/multiplayer/Player.ts index 641e8e1..c1e99fa 100644 --- a/src/multiplayer/Player.ts +++ b/src/multiplayer/Player.ts @@ -1,4 +1,4 @@ -import { ItemState } from '../Item.js'; +import { ItemState } from '../registries/Items.js'; import WebSocket from 'ws'; import { Pawn } from '../Pawn.js'; import { Game } from '../Game.js'; diff --git a/src/multiplayer/mDNS.ts b/src/multiplayer/mDNS.ts index 884cb30..26e9bad 100644 --- a/src/multiplayer/mDNS.ts +++ b/src/multiplayer/mDNS.ts @@ -1,11 +1,10 @@ import bonjour from 'bonjour'; -import log from '../log.js'; import getPort from 'get-port'; import os from 'os' import * as uuid from 'uuid'; import faker from 'faker'; import chalk from 'chalk'; -import { Item } from '../Item.js'; +import { Item } from '../registries/Items.js'; import WebSocket from 'ws'; import { Popup } from '../ui/Popup.js'; import { inspect } from 'util' @@ -30,7 +29,7 @@ export type GiftMessage = { export default network; -export async function ready(name, onThing?) { +export async function ready(name) { const port = await getPort({port: getPort.makeRange(52300, 52399)}); mdns.publish({ type: 'dfi', @@ -71,6 +70,7 @@ mdns.find({ p.name = service.name; p.host = service.host; p.port = service.port; - log.info('Found player', p); devices.push(p); -}); \ No newline at end of file +}).on("down", (service) => { + // TODO remove player from MP +}) \ No newline at end of file diff --git a/src/registries/Actions.ts b/src/registries/Actions.ts new file mode 100644 index 0000000..7f69640 --- /dev/null +++ b/src/registries/Actions.ts @@ -0,0 +1,20 @@ +import { getTheme } from "@themes"; +import { Renderable } from "../ui/UI.js"; + +export const actions: Action[] = []; + +export function registerAction(name: string, invoke: (qty: number) => void) { + console.log('Registered action', name); + actions.push(new Action(name, invoke)) +} + +export class Action { + name: string; + qty: number; + invoke: (qty: number) => void; + + constructor(name: string, done: (qty: number) => void) { + this.name = name; + this.invoke = done; + } +} \ No newline at end of file diff --git a/src/registries/Items.ts b/src/registries/Items.ts new file mode 100644 index 0000000..471515d --- /dev/null +++ b/src/registries/Items.ts @@ -0,0 +1,57 @@ +import { Serializable } from 'frigid'; +import { getTheme } from './Themes.js'; +import { Renderable } from '../ui/UI.js'; + +export type ItemID = string; + +const items = new Map(); + +// ITEMS SHALL BE SINGULAR +export class Item extends Serializable { + + name = ''; + id: ItemID = ''; + + setName(name) { + this.name = name; + this.register(false); + return this; + } + + setId(id: ItemID) { + this.id = id; + this.register(false); + return this; + } + + register(force = true) { + if((!this.id || !this.name) && !force) return; + console.log('Added item', (this.name ?? "[No Name]").padStart(20, ' '), `| (${this.id})`) + items.set(this.id, this); + return this; + } +} + +export class ItemState extends Serializable implements Renderable { + qty: number; + itemId: ItemID; + data: any; + + get item() { + if(!items.has(this.itemId)) + throw new Error('unknown item: ' + this.itemId); + return items.get(this.itemId); + } + + constructor(item: Item, amount: number, data: any) { + super(); + this.qty = amount; + this.itemId = item.id; + this.data = data; + } + + render() { + return getTheme().normal(` ${this.item.name}{|}${this.qty} `); + } +} + diff --git a/src/registries/Tasks.ts b/src/registries/Tasks.ts new file mode 100644 index 0000000..2daf0b4 --- /dev/null +++ b/src/registries/Tasks.ts @@ -0,0 +1,74 @@ +import { Serializable } from 'frigid'; +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'; + +export const taskClasses: Map = new Map(); + +export abstract class Task extends Serializable { + progress = 0; + worker: Pawn; + data: any; + + ctor() { + this.worker ??= null; + this.testCompletion(); + } + + abstract reward(): void; + abstract work: number; + + get completed() { + return this.completion >= 1; + } + + stopJob() { + this.worker = null; + } + + doWork(work = 1, pawn: Pawn) { + this.worker = pawn; + this.progress += work; + this.testCompletion(); + } + + testCompletion() { + if (this.progress >= this.work) { + this.reward(); + Game.current.board.removeTask(this); + } + } + + claim(pawn: Pawn) { + this.worker = pawn; + } + + get completion() { + return Math.min(1, this.progress / this.work); + } + + toString() { + // HACK magic number 2 here, is the border + // of the panel + const width = panels.left.width - 2; + const left = ' ' + this.title + ' ' + (this.worker?.toString() || chalk.bold.black('Queued')); + const bar = width - 2; + return `${left}\n ${progressbar(this.completion, bar, ProgressbarStyle.progress)}\n`; + } + + get title() { + return chalk.bgRedBright.black('[Abstract Task]'); + } + + get status() { + return chalk.bgRedBright.black('DOING A TASK'); + } +} + +export function registerTask(id: string, taskClass: typeof Task) { + console.log('Registered task', id); + taskClasses.set(id, taskClass) +} \ No newline at end of file diff --git a/src/registries/Themes.ts b/src/registries/Themes.ts new file mode 100644 index 0000000..74b1a49 --- /dev/null +++ b/src/registries/Themes.ts @@ -0,0 +1,94 @@ +// blessed doesnt know QUITE how to deal with 16m color modes +// it will always downsample them to 256. which is fine, but +// blessed's algorithm sucks, and comes out with incorrect +// mappings for certain colors. Instead of dealing with that, +// here, we simply tell chalk to always output ansi256 codes +// instead of upsampling them to 16m codes. +import chalk from 'chalk'; +chalk.level = 2; +import merge from 'deepmerge'; + +type StyleFunction = (text: string) => string; + +export type Theme = { + header: StyleFunction, + subheader: StyleFunction, + normal: StyleFunction, + selected: StyleFunction, + hotkey: StyleFunction, + tab: { + normal: StyleFunction, + selected: StyleFunction + }, + border: { + focused: string, + normal: string + }, + progressBar: { + indicator: { + critical: StyleFunction, + warning: StyleFunction, + normal: StyleFunction, + excellent: StyleFunction, + buckets: [number, number, number] + }, + normal: StyleFunction + }, + status: { + idle: StyleFunction, + self: StyleFunction, + work: StyleFunction + } +} + +export const backupTheme: Theme = { + header: chalk.ansi256(255).bold, + subheader: chalk.ansi256(243).bold, + normal: chalk.ansi256(243), + selected: chalk.ansi256(250), + hotkey: chalk.ansi256(40), + tab: { + normal: chalk.ansi256(117), + selected: chalk.ansi256(117).inverse + }, + border: { + focused: '#ffffff', + normal: '#222222' + }, + progressBar: { + indicator: { + critical: chalk.bgAnsi256(235).ansi256(88), + warning: chalk.bgAnsi256(235).ansi256(202), + normal: chalk.bgAnsi256(235).ansi256(70), + excellent: chalk.bgAnsi256(235).ansi256(87), + buckets: [.1, .25, .95] + }, + normal: chalk.bgAnsi256(235).ansi256(243) + }, + status: { + idle: chalk.ansi256(243), + self: chalk.ansi256(45), + work: chalk.ansi256(208) + } +} + +export type ThemeName = string; + +let currentTheme = backupTheme; +const themes: Map = new Map(); + +export function registerTheme(name: ThemeName, theme: Partial) { + console.log('Registered theme', name); + themes.set(name, merge(backupTheme, theme)); +} + +export function setTheme(name: ThemeName): void { + if(!themes.has(name)) return; + currentTheme = themes.get(name); + + // TODO reset borders and other weird shit that wont just update on a re-render +} + +export function getTheme(): Theme { + return currentTheme; +} \ No newline at end of file diff --git a/src/tasks/ChopTreeTask.ts b/src/tasks/ChopTreeTask.ts deleted file mode 100644 index 91f8c63..0000000 --- a/src/tasks/ChopTreeTask.ts +++ /dev/null @@ -1,31 +0,0 @@ -import chalk from 'chalk'; -import { Game } from '../Game.js'; -import { Item } from '../Item.js'; -import { Pawn } from '../Pawn.js'; -import { Task } from './Task.js'; - - -export class ChopTreeTask extends Task { - work = 100; - - constructor(count) { - super(); - - } - - reward() { - Game.current.inv.add(Item.LOG, 1); - } - - static serializationDependencies() { - return [Pawn]; - } - - get title() { - return chalk.yellow('Chop Trees'); - } - - get status() { - return chalk.yellow('LOGGING'); - } -} diff --git a/src/tasks/Task.ts b/src/tasks/Task.ts deleted file mode 100644 index 2c8b1c0..0000000 --- a/src/tasks/Task.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Serializable } from 'frigid'; -import EventEmitter from 'events'; -import chalk from 'chalk'; -import { Pawn } from '../Pawn.js'; -import { render, tasksPanel } from '../ui/UI.js'; -import { Game } from '../Game.js'; -import { progressbar, ProgressbarStyle } from '../Progressbar.js'; - -export class Task extends Serializable { - work = 0; - progress = 0; - worker: Pawn; - - ctor() { - this.worker ??= null; - this.testCompletion(); - } - - reward() {} - - get completed() { - return this.completion >= 1; - } - - stopJob() { - this.worker = null; - } - - doWork(work = 1, pawn: Pawn) { - this.worker = pawn; - this.progress += work; - this.testCompletion(); - } - - testCompletion() { - if (this.progress >= this.work) { - this.reward(); - Game.current.board.removeTask(this); - } - } - - claim(pawn: Pawn) { - this.worker = pawn; - } - - get completion() { - return Math.min(1, this.progress / this.work); - } - - toString() { - // HACK magic number 2 here, is the border - // of the panel - const width = tasksPanel.width - 2; - const left = ' ' + this.title + ' ' + (this.worker?.toString() || chalk.bold.black('Queued')); - const bar = width - 2; - return `${left}\n ${progressbar(this.completion, bar, ProgressbarStyle.progress)}\n`; - } - - get title() { - return chalk.bgRedBright.black('[Abstract Task]'); - } - - get status() { - return chalk.bgRedBright.black('DOING A TASK'); - } -} \ No newline at end of file diff --git a/src/ui/GiftPopup.ts b/src/ui/GiftPopup.ts index 4baeaf0..c5379e6 100644 --- a/src/ui/GiftPopup.ts +++ b/src/ui/GiftPopup.ts @@ -1,11 +1,11 @@ import chalk from 'chalk'; import blessed from 'neo-blessed'; import { Game } from '../Game.js'; -import { ItemState } from '../Item.js'; +import { ItemState } from '../registries/Items.js'; import { Player } from "../multiplayer/Player"; import { Pawn } from '../Pawn.js'; -import { getTheme } from './Theme.js'; -import { boxStyle, screen } from './UI.js'; +import { getTheme } from '../registries/Themes.js'; +import { boxStyle, panels } from './UI.js'; export class GiftPopup { box; @@ -30,7 +30,7 @@ export class GiftPopup { this.send(); } if(key.full === 'escape' || key.full === 'enter') { Game.current.clock.start(); - screen.remove(this.box); + panels.screen.remove(this.box); } else if (key.full === 'up') { this.selected --; } else if (key.full === 'down') { @@ -44,7 +44,7 @@ export class GiftPopup { }); this.render(); for(const pawn of Game.current.pawns) this.pawns.set(pawn, 0); - screen.append(this.box); + panels.screen.append(this.box); this.box.focus(); Game.current.clock.pause(); } @@ -80,6 +80,6 @@ export class GiftPopup { } return pawns.join('\n') })()}\n\n{|}${getTheme().hotkey('escape')}${getTheme().normal(': Cancel ')}\n{|}${getTheme().hotkey('enter')}${getTheme().normal(': Okay ')}`); - screen.render(); + panels.screen.render(); } } \ No newline at end of file diff --git a/src/ui/Menu.ts b/src/ui/Menu.ts index f900fbf..46667a4 100644 --- a/src/ui/Menu.ts +++ b/src/ui/Menu.ts @@ -1,219 +1,111 @@ import { Pawn } from '../Pawn.js'; -import log from '../log.js'; -import { menuPanel, Renderable } from './UI.js'; +import { panels, Renderable } from './UI.js'; import { Game } from '../Game.js'; -import { ChopTreeTask } from '../tasks/ChopTreeTask.js'; -import { progressbar, stats, barCache } from '../Progressbar.js'; +import { progressbar, stats } from '../Progressbar.js'; import { Popup } from './Popup.js'; import mdns from '../multiplayer/mDNS.js'; -import { GiftPopup } from './GiftPopup.js'; -import { PawnDetails } from './PawnDetails.js'; -import { defaultTheme, getTheme } from './Theme.js'; +import { getTheme } from '@themes'; import { inspect } from 'util'; +import PawnsView from './view/PawnsView.js'; +import InventoryView from './view/InventoryView.js'; +import MultiplayerView from './view/MultiplayerView.js'; +import { View } from './View.js'; +import { ActionsView } from './view/ActionsView.js'; -enum SubMenu { - NONE = 'NONE', - TREES = 'TREES' -}; +const clamp = (min, max, value) => Math.min(Math.max(value, min), max); -enum View { - PAWNS = 'Pawns', - INVENTORY = 'Inventory', - MULTIPLAYER = 'Multiplayer', -}; +// TODO move KeypressAcceptor to ui something idk +export interface KeypressAcceptor { + keypress(key: {full: string}): void +} export class Menu implements Renderable { - trees: number = 10; - subMenu: SubMenu = SubMenu.NONE; - view: View = View.PAWNS; + trees: number = 10; + viewIndex: number = 0; + views: View[] = [ + new PawnsView(), + new InventoryView(), + new MultiplayerView(), + new ActionsView() + ] - constructor() { - menuPanel.on('keypress', (evt, key) => { - log.info('keypress', key); - - if (key.full === 'left') { - this.view = View[Object.keys(View)[Math.min(Math.max(Object.values(View).indexOf(this.view) - 1, 0), Object.keys(View).length - 1)]] - } else if (key.full === 'right') { - this.view = View[Object.keys(View)[Math.min(Math.max(Object.values(View).indexOf(this.view) + 1, 0), Object.keys(View).length - 1)]] - } else if (key.full === 'q') { - this.subMenu = SubMenu.TREES; - } else if (key.full === '1') { - Popup.show(inspect(stats)); - } else if (key.full === '2') { - Popup.show('Etiam hendrerit elit sit amet metus congue dictum nec eu lacus. Sed aliquam in justo efficitur faucibus. Duis tellus diam, congue volutpat lorem et, semper consectetur erat. Nunc ac velit dignissim, tincidunt augue eget, tristique orci. Duis lacus sapien, bibendum id pharetra vel, semper et nunc. Vestibulum eu tellus imperdiet, lacinia ante ac, porta nisl. Donec at eleifend risus, ac dictum odio.'); - } else if (key.full === 'escape') { - this.subMenu = SubMenu.NONE; - } + get view() { + return this.views[this.viewIndex]; + } - if(this.view === View.PAWNS) { - if (key.full === 'delete') { - Game.current.removePawn(Game.current.selected); - } else if (key.full === 'up') { - Game.current.advanceSelection(-1); - } else if (key.full === 'down') { - Game.current.advanceSelection(1); - } else if (key.full === 'enter') { - new PawnDetails(Game.current.selected); - } - } + advanceView() { + this.viewIndex ++; + this.viewIndex = clamp(0, this.views.length - 1, this.viewIndex); + } - if(this.view === View.MULTIPLAYER) { - if (key.full === 'enter') { - new GiftPopup(mdns.players[this.multiplayerSelected]); - // mdns.players[this.multiplayerSelected].sendItem(null); - } else if (key.full === 'up') { - this.multiplayerSelected --; - } else if (key.full === 'down') { - this.multiplayerSelected ++; - } - } + regressView() { + this.viewIndex --; + this.viewIndex = clamp(0, this.views.length - 1, this.viewIndex); + } - if(this.subMenu === SubMenu.TREES) { - if (key.full === '=') { - this.trees ++; - } else if (key.full === '-') { - this.trees --; - this.trees = Math.max(this.trees, 1); - } else if (key.full === '+') { - this.trees += 10; - } else if (key.full === '_') { - this.trees -= 10; - this.trees = Math.max(this.trees, 1); - } else if (key.full === 'enter') { - for(let i = 0; i < this.trees; i ++) { - Game.current.board.addTask(new ChopTreeTask(1)); - } - this.subMenu = SubMenu.NONE; - } - } else if(this.subMenu === SubMenu.NONE) { - if (key.full === 'z') { - Game.current.pawns.push(new Pawn()); - } else if (key.full === 'x') { - Game.current.board.clear(); - } - } + constructor() { + panels.right.on('keypress', (evt, key) => { + + if (key.full === 'left') { + this.regressView(); + } else if (key.full === 'right') { + this.advanceView(); + + // debugging hotkeys + } else if (key.full === '1') { + Popup.show(inspect(stats)); + } else if (key.full === 'z') { + Game.current.pawns.push(new Pawn()); + } else if (key.full === 'x') { + Game.current.board.clear(); + } else this.view.keypress(key); - // const pawn = new Pawn(); - // Game.current.pawns.push(pawn); - Game.current.sync(); - }); - } + // if(this.view === View.MULTIPLAYER) { + // if (key.full === 'enter') { + // new GiftPopup(mdns.players[this.multiplayerSelected]); + // // mdns.players[this.multiplayerSelected].sendItem(null); + // } else if (key.full === 'up') { + // this.multiplayerSelected --; + // } else if (key.full === 'down') { + // this.multiplayerSelected ++; + // } + // } - renderJobs() { + // TODO add colortest debug screen! - const colSpace = ((menuPanel.width - 2) / 2); + Game.current.sync(); + }); + } - return (` ${getTheme().header('Menus')}${getTheme().normal(':')}${ - ' '.repeat(colSpace - 8) - }${getTheme().header('Actions')}${getTheme().normal(':')}\n ${ - getTheme().hotkey('q') - }${getTheme().normal(': ')}${ - (this.subMenu !== SubMenu.TREES ? getTheme().normal : getTheme().selected)('Chop Trees') - }${ - ' '.repeat(colSpace - 15) - }${getTheme().hotkey('z')}${getTheme().normal(': ')}${ - (this.subMenu !== SubMenu.NONE ? getTheme().normal : getTheme().selected)('Create Pawn') + renderTopBar() { + const idlers = Game.current.pawns.filter(pawn => pawn.idle); + return ` ${Game.current.clock.render()}{|}${getTheme().normal(`Idle: ${idlers.length}`)} `; + } - }\n${ - ' '.repeat(colSpace) - }${ - getTheme().hotkey('x') - }${getTheme().normal(': ')}${ - (this.subMenu !== SubMenu.NONE ? getTheme().normal : getTheme().selected)('Clear Tasks') - }`); + renderView() { + const colSpace = ((panels.right.width - 2) / 2); + return `{center}${(() => { + return Object.values(this.views).map((view, idx) => { + if(idx === this.viewIndex) { + return getTheme().tab.selected(` ${view.name} `); + } else { + return getTheme().tab.normal(` ${view.name} `); + } + }).join(''); + })()}{/center}\n\n${ + this.view.render() + }`; + } - } - - renderTopBar() { - const idlers = Game.current.pawns.filter(pawn => pawn.idle); - return ` ${Game.current.clock.render()}{|}${getTheme().normal(`Idle: ${idlers.length}`)} `; - } - - renderPawns() { - return `${ - Game.current.pawns.map(pawn => `${(function() { - const selected = pawn === Game.current.selected; - let str = ''; - if(selected) { - str += ` ${getTheme().selected(` ❯ ${pawn.toString()}`)}{|}${pawn.status} \n`; - str += ` ${getTheme().normal('Energy')}{|}${progressbar(pawn.energy / 100, (menuPanel.width - 4) / 2)} \n`; - } else { - str += ` ${getTheme().normal(pawn.toString())}{|}${pawn.status} `; - } - return str; - })()}`).join('\n') - }`; - } - - renderView() { - const colSpace = ((menuPanel.width - 2) / 2); - return `${ - // ' '.repeat(colSpace - 20) - '{center}' - }${(() => { - return Object.values(View).map(view => { - if(view === this.view) { - return getTheme().tab.selected(` ${view} `); - } else { - return getTheme().tab.normal(` ${view} `); - } - }).join(''); - })()}{/center}\n\n${(() => { - switch(this.view) { - case View.PAWNS: return this.renderPawns(); - case View.INVENTORY: return this.renderInv(); - case View.MULTIPLAYER: return this.renderMultiplayer(); - } - })()}` - } - - multiplayerSelected = 0; - - renderMultiplayer() { - if(mdns.players.length === 0) return `{center}${getTheme().normal('No friends online')}{/center}`; - return mdns.players.map((player, i) => { - if(i === this.multiplayerSelected) return ' ' + getTheme().selected(' ❯ ' + player.toString()); - else return ' ' + getTheme().normal(player.toString()); - }).join('\n'); - } - - renderInv() { - return Game.current.inv.render(); - } - - renderSubMenu() { - return `${(() => { - switch(this.subMenu) { - case SubMenu.NONE: - return `{center}${getTheme().normal('* Select a menu above for options *')}{/center}`; - case SubMenu.TREES: - return this.renderTreesSubMenu(); - } - })()}`; - } - - renderTreesSubMenu() { - return [ - `{center}Chop Trees`, - `{left} ${getTheme().hotkey('-=_+')}: ${this.trees}`, - `{left} ${getTheme().hotkey('enter')}: Create Task`, - `{left} ${getTheme().hotkey('escape')}: Cancel` - ].join('\n'); - } - - render() { - const width = menuPanel.width - 2; - const hr = getTheme().normal('━'.repeat(width)); - const content = [ - this.renderTopBar(), - hr, - this.renderView(), - hr, - this.renderJobs(), - hr, - this.renderSubMenu() - ].join('\n'); - menuPanel.setContent(content); - } + render() { + const width = panels.right.width - 2; + const hr = getTheme().normal('━'.repeat(width)); + const content = [ + this.renderTopBar(), + hr, + this.renderView(), + ].join('\n'); + panels.right.setContent(content); + } } \ No newline at end of file diff --git a/src/ui/PawnDetails.ts b/src/ui/PawnDetails.ts index 9f21e6a..69a63f3 100644 --- a/src/ui/PawnDetails.ts +++ b/src/ui/PawnDetails.ts @@ -4,7 +4,7 @@ import { Game } from '../Game.js'; import { stringify } from '../Memory.js'; import { Pawn } from '../Pawn.js'; import Time from '../Time.js'; -import { boxStyle, screen } from './UI.js'; +import { boxStyle, panels } from './UI.js'; export class PawnDetails { box; @@ -23,7 +23,7 @@ export class PawnDetails { this.box.on('keypress', (evt, key) => { if(key.full === 'escape' || key.full === 'enter') { Game.current.clock.start(); - screen.remove(this.box); + panels.screen.remove(this.box); } else if (key.full === 'up') { // this.selected --; } else if (key.full === 'down') { @@ -36,7 +36,7 @@ export class PawnDetails { this.render(); }); this.render(); - screen.append(this.box); + panels.screen.append(this.box); this.box.focus(); Game.current.clock.pause(); } @@ -55,6 +55,6 @@ export class PawnDetails { }: Cancel \n{|}${ chalk.green('enter') }: Okay `); - screen.render(); + panels.screen.render(); } } \ No newline at end of file diff --git a/src/ui/Popup.ts b/src/ui/Popup.ts index 2e2c4d0..824567a 100644 --- a/src/ui/Popup.ts +++ b/src/ui/Popup.ts @@ -1,34 +1,34 @@ import chalk from 'chalk'; import blessed from 'neo-blessed'; import { Game } from '../Game.js'; -import { getTheme } from './Theme.js'; -import { boxStyle, screen } from './UI.js'; +import { getTheme } from '@themes'; +import { boxStyle, panels } from './UI.js'; export class Popup { - box; + box; - static show(content) { - new Popup(content) - } + static show(content) { + new Popup(content) + } - private constructor(content) { - this.box = blessed.box({ - top: 'center', - left: 'center', - width: 'shrink', - height: 'shrink', - content: getTheme().normal(content) + `\n\n{|}` + getTheme().hotkey('enter') + getTheme().normal(`: Okay `), - tags: true, - ...boxStyle(), - }); - this.box.on('keypress', (evt, key) => { - if(key.full === 'escape' || key.full === 'enter') { - Game.current.clock.start(); - screen.remove(this.box); - } - }); - screen.append(this.box); - this.box.focus(); - Game.current.clock.pause(); - } + private constructor(content) { + this.box = blessed.box({ + top: 'center', + left: 'center', + width: '100%', + height: 'shrink', + content: getTheme().normal(content) + `\n\n{right}` + getTheme().hotkey('enter') + getTheme().normal(`: Okay `) + '{/right}', + tags: true, + ...boxStyle(), + }); + this.box.on('keypress', (evt, key) => { + if(key.full === 'escape' || key.full === 'enter') { + Game.current.clock.start(); + panels.screen.remove(this.box); + } + }); + panels.screen.append(this.box); + this.box.focus(); + Game.current.clock.pause(); + } } \ No newline at end of file diff --git a/src/ui/Theme.ts b/src/ui/Theme.ts deleted file mode 100644 index 6252cfc..0000000 --- a/src/ui/Theme.ts +++ /dev/null @@ -1,77 +0,0 @@ -import chalk from "chalk"; - -type StyleFunction = (text: string) => string; - -export type Theme = { - header: StyleFunction, - subheader: StyleFunction, - normal: StyleFunction, - selected: StyleFunction, - hotkey: StyleFunction, - tab: { - normal: StyleFunction, - selected: StyleFunction - }, - border: { - focused: string, - normal: string - }, - progressBar: { - indicator: { - critical: StyleFunction, - warning: StyleFunction, - normal: StyleFunction, - excellent: StyleFunction, - buckets: [number, number, number] - }, - normal: StyleFunction - } -} - -export const defaultTheme: Theme = { - header: chalk.ansi256(255).bold, - subheader: chalk.ansi256(243).bold, - normal: chalk.ansi256(243), - selected: chalk.ansi256(250), - hotkey: chalk.ansi256(40), - tab: { - normal: chalk.ansi256(117).bgAnsi256(232), - selected: chalk.ansi256(232).bgAnsi256(117) - }, - border: { - focused: '#ffffff', - normal: '#222222' - }, - progressBar: { - indicator: { - critical: chalk.bgAnsi256(235).ansi256(88), - warning: chalk.bgAnsi256(235).ansi256(202), - normal: chalk.bgAnsi256(235).ansi256(70), - excellent: chalk.bgAnsi256(235).ansi256(87), - buckets: [.1, .25, .95] - }, - normal: chalk.bgAnsi256(235).ansi256(243) - } -} - -const debugStyle = chalk.ansi256(213); -export const debugTheme: Theme = { - header: debugStyle.inverse, - subheader: debugStyle, - normal: debugStyle, - selected: debugStyle.inverse, - hotkey: debugStyle, - tab: { - normal: debugStyle, - selected: debugStyle.inverse, - }, - border: { - focused: '#ff88ff', - normal: '#ff00ff' - }, - progressBar: defaultTheme.progressBar -} - -export function getTheme(): Theme { - return defaultTheme; -} \ No newline at end of file diff --git a/src/ui/UI.ts b/src/ui/UI.ts index d7d374b..03fa1f1 100644 --- a/src/ui/UI.ts +++ b/src/ui/UI.ts @@ -1,17 +1,14 @@ +import chalk from 'chalk'; import blessed from 'neo-blessed'; import ansi from 'sisteransi'; -import { getTheme } from './Theme.js'; - -export const screen = blessed.screen({ - smartCSR: true, - terminal: 'xterm-256color' -}); +import { getTheme } from '../registries/Themes.js'; export interface Renderable { - render: () => void + render(): void } +// TODO move this to theme export const boxStyle = () => { return { style: { @@ -30,64 +27,130 @@ export const boxStyle = () => { }; }; -let currentRenderable = null; +let leftPanel: any; +let rightPanel: any; +let titleBar: any; +let screen: any; +let currentRenderable: Renderable = null; +let started = false; + +function assertStarted() { + if(!started) throw new Error('Attempted accessing UI before starting it!'); +} + +function assertNotStarted() { + if(started) throw new Error('Attempted starting UI when already started!'); +} + +export function isStarted() { + return started; +} + +export const panels = { + get left() { + assertStarted() + return leftPanel; + }, + get right() { + assertStarted() + return rightPanel; + }, + get screen() { + assertStarted() + return screen; + } +} + +export function setTitle(title: string) { + assertStarted(); + titleBar.setContent(` ${getTheme().header(title)}{|}${getTheme().subheader('v0.1.0')} {/}`); +} + export function render(thing?: Renderable) { - if(!!thing) currentRenderable = thing; - currentRenderable.render(); - screen.render(); + assertStarted(); + if(!!thing) currentRenderable = thing; + + currentRenderable.render(); + screen.render(); } -export const tasksPanel = blessed.box({ - top: 1, - left: 0, - width: '50%+1', - height: '100%-1', - ...boxStyle(), - tags: true -}); +export function start() { + assertNotStarted(); + screen = blessed.screen({ + smartCSR: true, + terminal: 'xterm-256color' + }); -export const menuPanel = blessed.box({ - top: 1, - left: '50%+1', - width: '50%', - height: '100%-1', - ...boxStyle(), - tags: true -}); + leftPanel = blessed.box({ + top: 1, + left: 0, + width: '50%', + height: '100%-1', + ...boxStyle(), + tags: true + }); -const titleBar = blessed.box({ - top: 0, - left: 0, - width: '100%', - height: 1, - tags: true, -}); + rightPanel = blessed.box({ + top: 1, + right: 0, + width: '50%', + height: '100%-1', + ...boxStyle(), + tags: true + }); -export function setTitle(title) { - titleBar.setContent(` ${getTheme().header(title)}{|}${getTheme().subheader('v0.1.0')} {/}`); + titleBar = blessed.box({ + top: 0, + left: 0, + width: '100%', + height: 1, + tags: true, + }); + + screen.append(leftPanel); + screen.append(rightPanel); + screen.append(titleBar); + rightPanel.focus(); + + process.stdout.write(ansi.cursor.hide); + + screen.key(['C-c'], function(ch, key) { + process.stdout.write(ansi.cursor.show); + setTimeout(_ => { + process.exit(0); + }) + }); + + screen.key('f2', () => { + rightPanel.focus(); + }); + + screen.key('f1', () => { + leftPanel.focus(); + }); + + started = true; + setTitle(''); } -setTitle(''); +let ansiTestCard = '{center}'; +for(let i = 16; i < 34; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 52; i < 70; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 88; i < 106; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 124; i < 142; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 160; i < 178; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 196; i < 214; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +ansiTestCard += '\n'; +for(let i = 34; i < 52; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 70; i < 88; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 106; i < 124; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 142; i < 160; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 178; i < 196; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +for(let i = 214; i < 232; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} ${(i-15)%6===0?chalk.reset(' '):''}`); ansiTestCard += '\n'; +ansiTestCard += '\n'; +for(let i = 232; i < 256; i ++) ansiTestCard += chalk.bgAnsi256(i).black(` ${i.toString().padStart(3, ' ')} `) +ansiTestCard += '{/center}'; -menuPanel.focus(); - -screen.append(tasksPanel); -screen.append(menuPanel); -screen.append(titleBar); - -process.stdout.write(ansi.cursor.hide); - -screen.key(['C-c'], function(ch, key) { - process.stdout.write(ansi.cursor.show); - setTimeout(_ => { - process.exit(0); - }) -}); - -tasksPanel.key('f2', () => { - menuPanel.focus(); -}); - -menuPanel.key('f1', () => { - tasksPanel.focus(); -}); \ No newline at end of file +export { + ansiTestCard +}; \ No newline at end of file diff --git a/src/ui/View.ts b/src/ui/View.ts new file mode 100644 index 0000000..d7b278e --- /dev/null +++ b/src/ui/View.ts @@ -0,0 +1,9 @@ +import { Renderable } from './UI.js'; +import { KeypressAcceptor } from './Menu.js'; + +export abstract class View implements Renderable, KeypressAcceptor { + abstract render(): void; + abstract keypress(key: { full: string; }): void; + + name: string; +} diff --git a/src/ui/view/ActionsView.ts b/src/ui/view/ActionsView.ts new file mode 100644 index 0000000..896657b --- /dev/null +++ b/src/ui/view/ActionsView.ts @@ -0,0 +1,33 @@ +import { getTheme } from "../../registries/Themes.js"; +import { Game } from "../../Game.js"; +import { Renderable } from "../UI.js"; +import { View } from "../View.js"; +import { actions } from "@actions"; + +export class ActionsView extends View { + actionIdx: number = 0; + + constructor() { + super(); + this.name = 'Actions'; + } + + keypress(key) { + if(key.full === 'up') { + this.actionIdx --; + } else if (key.full === 'down') { + this.actionIdx ++; + } else if (key.full === 'enter') { + actions[this.actionIdx].invoke(1); + } + } + render() { + return actions.map((action, idx) => `${(() => { + if(this.actionIdx === idx) { + return getTheme().selected(' ❯ ' + action.name); + } else { + return getTheme().normal(' ' + action.name); + } + })()}`).join('\n'); + } +} \ No newline at end of file diff --git a/src/ui/view/InventoryView.ts b/src/ui/view/InventoryView.ts new file mode 100644 index 0000000..d58f6b4 --- /dev/null +++ b/src/ui/view/InventoryView.ts @@ -0,0 +1,14 @@ +import { Game } from "../../Game.js"; +import { View } from "../View.js"; + +export default class InventoryView extends View { + constructor() { + super(); + this.name = 'Inventory'; + } + keypress(key: { full: string; }) {} + + render() { + return Game.current.inv.render(); + }; +} \ No newline at end of file diff --git a/src/ui/view/MultiplayerView.ts b/src/ui/view/MultiplayerView.ts new file mode 100644 index 0000000..2bce850 --- /dev/null +++ b/src/ui/view/MultiplayerView.ts @@ -0,0 +1,32 @@ +import { GiftPopup } from "../GiftPopup.js"; +import { View } from "../View.js" +import mdns from '../../multiplayer/mDNS.js'; +import { getTheme } from "@themes"; + +export default class MultiplayerView extends View { + + selected: number = 0; + + constructor() { + super(); + this.name = 'Multiplayer'; + } + + keypress(key: { full: string; }) { + if (key.full === 'enter') { + new GiftPopup(mdns.players[this.selected]); + } else if (key.full === 'up') { + this.selected --; + } else if (key.full === 'down') { + this.selected ++; + } + } + + render() { + if(mdns.players.length === 0) return `{center}${getTheme().normal('No friends online')}{/center}`; + return mdns.players.map((player, i) => { + if(i === this.selected) return ' ' + getTheme().selected(' ❯ ' + player.toString()); + else return ' ' + getTheme().normal(player.toString()); + }).join('\n'); + }; +} \ No newline at end of file diff --git a/src/ui/view/PawnsView.ts b/src/ui/view/PawnsView.ts new file mode 100644 index 0000000..c9b7c9b --- /dev/null +++ b/src/ui/view/PawnsView.ts @@ -0,0 +1,41 @@ +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 { View } from "../View.js"; + +export default class PawnsView extends View { + constructor() { + super(); + this.name = 'Pawns'; + } + + keypress(key: { full: string; }) { + if (key.full === 'delete') { + Game.current.removePawn(Game.current.selected); + } else if (key.full === 'up') { + Game.current.advanceSelection(-1); + } else if (key.full === 'down') { + Game.current.advanceSelection(1); + } else if (key.full === 'enter') { + new PawnDetails(Game.current.selected); + } + } + + render() { + return `${ + Game.current.pawns.map(pawn => `${(function() { + const selected = pawn === Game.current.selected; + let str = ''; + if(selected) { + str += ` ${getTheme().selected(` ❯ ${pawn.toString()}`)}{|}${pawn.status} \n`; + str += ` ${getTheme().normal('Energy')}{|}${progressbar(pawn.energy / 100, (panels.right.width - 4) / 2)} \n`; + } else { + str += ` ${getTheme().normal(pawn.toString())}{|}${pawn.status} `; + } + return str; + })()}`).join('\n') + }`; + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 64a23ae..4977fa7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,22 @@ { "compilerOptions": { "target": "ES2020", - "module": "ES2020", + "module": "ESNext", "moduleResolution": "Node", "allowSyntheticDefaultImports": true, "outDir": "out", "declaration": true, - "sourceMap": true + "sourceMap": true, + "paths": { + "@themes": ["./src/registries/Themes"], + "@actions": ["./src/registries/Actions"], + "@tasks": ["./src/registries/Tasks"], + "@items": ["./src/registries/Items"], + "@game": ["./src/Game"] + } }, "include": [ - "src/**/*.ts" + "src/**/*.ts", + "content/**/*.ts" ] } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index da32f0f..7f38f58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,10 +14,25 @@ dependencies: "@types/node" "*" +"@types/chai@^4.2.19": + version "4.2.19" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.19.tgz#80f286b515897413c7a35bdda069cc80f2344233" + integrity sha512-jRJgpRBuY+7izT7/WNXP/LsMO9YonsstuL+xuvycDyESpoDoIAsMd7suwpB4h9oEWB+ZlPTqJJ8EHomzNhwTPQ== + "@types/faker@^5.5.6": version "5.5.6" resolved "https://registry.yarnpkg.com/@types/faker/-/faker-5.5.6.tgz#039b700a9d8ad9150ecc842bf5e717e2027b6f75" +"@types/minimatch@^3.0.3", "@types/minimatch@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" + integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== + +"@types/mocha@^8.2.2": + version "8.2.2" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.2.tgz#91daa226eb8c2ff261e6a8cbf8c7304641e095e0" + integrity sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw== + "@types/node@*": version "15.12.2" resolved "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz" @@ -26,16 +41,64 @@ version "8.3.0" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" -ansi-styles@^4.1.0: +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" dependencies: color-convert "^2.0.1" +anymatch@~3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + array-flatten@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + bonjour@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" @@ -47,6 +110,26 @@ bonjour@^3.5.0: multicast-dns "^6.0.1" multicast-dns-service-types "^1.1.0" +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + buffer-indexof@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" @@ -58,13 +141,59 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -chalk@^4.1.1: +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +chai@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@^4.1.0, chalk@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz" dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +chokidar@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" @@ -75,6 +204,30 @@ color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +debug@4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -86,12 +239,22 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" dependencies: object-keys "^1.0.12" +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -109,14 +272,54 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +ensure-posix-path@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz#3c62bdb19fa4681544289edb2b382adc029179ce" + integrity sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + faker@^5.5.3: version "5.5.3" resolved "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz" -frigid@^1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/frigid/-/frigid-1.3.8.tgz#a16919821e5426344bc98d301099f7631d2bae8a" - integrity sha512-i3HgB/5hQsALyumWoRlBvEpAXfTmM3Xw+Ica6E9mTASUVYtqZQ8mzUX8/3zscTUM4bCKhSa7MSvXl9L7pt5ICg== +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +frigid@^1.3.13: + version "1.3.13" + resolved "https://registry.yarnpkg.com/frigid/-/frigid-1.3.13.tgz#27d8592ec1aeb72964fbc4014e38e6c366c5e3b7" + integrity sha512-suwEa7JuMvNqIlVpc848wIptC8o3Ztvadv8a1HojbaNcYdowOlqWi/ieSgQZkBVCpmrBOq7IeuaB6p4BvCUdHQ== fs-extra@^10.0.0: version "10.0.0" @@ -127,10 +330,30 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + get-intrinsic@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" @@ -143,11 +366,35 @@ get-port@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" +glob-parent@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.7: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" @@ -162,6 +409,24 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + ip@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -172,10 +437,49 @@ is-arguments@^1.0.4: dependencies: call-bind "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-date-object@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + is-regex@^1.0.4: version "1.1.3" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" @@ -183,6 +487,23 @@ is-regex@^1.0.4: call-bind "^1.0.2" has-symbols "^1.0.2" +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -192,9 +513,81 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -logger@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/logger/-/logger-0.0.1.tgz#cb08171f8a6f6f674b8499dadf50bed4befb72c4" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +matcher-collection@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/matcher-collection/-/matcher-collection-2.0.1.tgz#90be1a4cf58d6f2949864f65bb3b0f3e41303b29" + integrity sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ== + dependencies: + "@types/minimatch" "^3.0.3" + minimatch "^3.0.2" + +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +mocha@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.0.1.tgz#01e66b7af0012330c0a38c4b6eaa6d92b8a81bf9" + integrity sha512-9zwsavlRO+5csZu6iRtl3GHImAbhERoDsZwdRkdJ/bE+eVplmoxNKE901ZJ9LdSchYBjSCPbjKc5XvcAri2ylw== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.1" + debug "4.3.1" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.7" + growl "1.10.5" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "3.0.4" + ms "2.1.3" + nanoid "3.1.23" + serialize-javascript "5.0.1" + strip-json-comments "3.1.1" + supports-color "8.1.1" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.1.4" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +module-alias@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" + integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== multicast-dns-service-types@^1.1.0: version "1.1.0" @@ -207,9 +600,20 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" +nanoid@3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== + neo-blessed@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/neo-blessed/-/neo-blessed-0.2.0.tgz#30f9495fdd104494402b62c6273a9c9b82de4f2b" + integrity sha512-C2kC4K+G2QnNQFXUIxTQvqmrdSIzGTX1ZRKeDW6ChmvPRw8rTkTEJzbEQHiHy06d36PCl/yMOCjquCRV8SpSQw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== object-is@^1.0.1: version "1.1.5" @@ -222,10 +626,65 @@ object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + printable-characters@^1.0.42: version "1.0.42" resolved "https://registry.yarnpkg.com/printable-characters/-/printable-characters-1.0.42.tgz#3f18e977a9bd8eb37fcc4ff5659d7be90868b3d8" +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + regexp.prototype.flags@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" @@ -233,14 +692,69 @@ regexp.prototype.flags@^1.2.0: call-bind "^1.0.2" define-properties "^1.1.3" -safe-buffer@^5.0.1: +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +safe-buffer@^5.0.1, safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" +serialize-javascript@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" @@ -251,6 +765,18 @@ thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + typescript@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" @@ -264,11 +790,97 @@ uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" +walk-sync@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-3.0.0.tgz#67f882925021e20569a1edd560b8da31da8d171c" + integrity sha512-41TvKmDGVpm2iuH7o+DAOt06yyu/cSHpX3uzAwetzASvlNtVddgIjXIb2DfB/Wa20B1Jo86+1Dv1CraSU7hWdw== + dependencies: + "@types/minimatch" "^3.0.4" + ensure-posix-path "^1.1.0" + matcher-collection "^2.0.1" + minimatch "^3.0.4" + +which@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +workerpool@6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.4.tgz#6a972b6df82e38d50248ee2820aa98e2d0ad3090" + integrity sha512-jGWPzsUqzkow8HoAvqaPWTUPCrlPJaJ5tY8Iz7n1uCz3tTp6s3CDG0FF1NsX42WNlkRSW6Mr+CDZGnNoSsKa7g== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + ws@^7.4.6: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yarn@^1.22.10: version "1.22.10" resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.10.tgz#c99daa06257c80f8fa2c3f1490724e394c26b18c" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==