submenus, progressbar stuff inventory
parent
83821b4668
commit
f2023c7af3
|
|
@ -8,7 +8,6 @@
|
|||
"@types/blessed": "^0.1.17",
|
||||
"chalk": "^4.1.1",
|
||||
"faker": "^5.5.3",
|
||||
"frigid": "^1.3.3",
|
||||
"logger": "^0.0.1",
|
||||
"neo-blessed": "^0.2.0",
|
||||
"printable-characters": "^1.0.42",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ import { Task } from './Task.js';
|
|||
export class ChopTreeTask extends Task {
|
||||
work = 100;
|
||||
|
||||
constructor(count) {
|
||||
super();
|
||||
|
||||
}
|
||||
|
||||
reward() {
|
||||
Game.current.inv.add(Item.LOG, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,16 +53,15 @@ export class Game extends Frigid implements Tickable, Renderable {
|
|||
}
|
||||
|
||||
ctor () {
|
||||
game = this;
|
||||
this.pawns ??= [];
|
||||
this.selected ??= this.pawns[0] || null;
|
||||
this.menu = new Menu();
|
||||
this.board ??= new TaskList();
|
||||
this.board.game = this;
|
||||
this.inventory ??= new Inventory();
|
||||
this.clock ??= new Time();
|
||||
this.clock.thing = this;
|
||||
this.clock.start();
|
||||
game = this;
|
||||
render(this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,37 @@
|
|||
import { Serializable } from 'frigid';
|
||||
import { Item } from './Item.js';
|
||||
import { SMap } from './SMap.js';
|
||||
import { ItemID } from './index.js';
|
||||
import { Game } from './Game.js';
|
||||
import { Item, ItemState } from './Item.js';
|
||||
import { Renderable } from './UI.js';
|
||||
|
||||
export class Inventory extends Serializable {
|
||||
items = new SMap<ItemID, number>();
|
||||
export class Inventory extends Serializable implements Renderable {
|
||||
items: ItemState[];
|
||||
|
||||
ctor() {
|
||||
this.items ??= [];
|
||||
}
|
||||
|
||||
static serializationDependencies() {
|
||||
return [SMap];
|
||||
return [ItemState];
|
||||
}
|
||||
|
||||
add(item: Item, qty: number = 1) {
|
||||
const id = item.id;
|
||||
this.ditem(id, qty);
|
||||
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();
|
||||
}
|
||||
|
||||
remove(item: Item, qty: number = 1) {
|
||||
const id = item.id;
|
||||
this.ditem(id, -qty);
|
||||
}
|
||||
|
||||
ditem(id, n) {
|
||||
if (this.items.has(id))
|
||||
this.items.set(id, this.items.get(id) + n);
|
||||
else
|
||||
this.items.set(id, n);
|
||||
render() {
|
||||
return this.items.map(item => item.render()).join('\n');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
30
src/Item.ts
30
src/Item.ts
|
|
@ -1,5 +1,9 @@
|
|||
import { Serializable } from 'frigid';
|
||||
import { ItemID } from './index.js';
|
||||
import { Renderable } from './UI';
|
||||
|
||||
export type ItemID = string;
|
||||
|
||||
const items = new Map<ItemID, Item>();
|
||||
|
||||
// ITEMS SHALL BE SINGULAR
|
||||
export class Item extends Serializable {
|
||||
|
|
@ -13,8 +17,30 @@ export class Item extends Serializable {
|
|||
return this;
|
||||
}
|
||||
|
||||
setId(id) {
|
||||
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 ` ${this.item.name}{|}${this.qty} `;
|
||||
}
|
||||
}
|
||||
147
src/Menu.ts
147
src/Menu.ts
|
|
@ -6,9 +6,25 @@ import { Game } from './Game.js';
|
|||
import { Task } from './Task.js';
|
||||
import { ChopTreeTask } from './ChopTreeTask.js';
|
||||
import { progressbar } from './Progressbar.js';
|
||||
import { inspect } from 'util';
|
||||
|
||||
enum SubMenu {
|
||||
NONE = 'NONE',
|
||||
TREES = 'TREES'
|
||||
};
|
||||
|
||||
enum View {
|
||||
PAWNS = 'Pawns',
|
||||
INVENTORY = 'Inventory',
|
||||
BUILDINGS = 'Buildings',
|
||||
};
|
||||
|
||||
export class Menu implements Renderable {
|
||||
|
||||
trees: number = 10;
|
||||
subMenu: SubMenu = SubMenu.NONE;
|
||||
view: View = View.PAWNS;
|
||||
|
||||
constructor() {
|
||||
screen.on('keypress', (evt, key) => {
|
||||
log.info('keypress', key);
|
||||
|
|
@ -18,22 +34,41 @@ export class Menu implements Renderable {
|
|||
Game.current.advanceSelection(-1);
|
||||
} else if (key.full === 'down') {
|
||||
Game.current.advanceSelection(1);
|
||||
} else if (key.full === 'c') {
|
||||
Game.current.pawns.push(new Pawn());
|
||||
Game.current.sync();
|
||||
} else if (key.full === 'x') {
|
||||
let i = 0;
|
||||
for(const task of Game.current.board.tasks) {
|
||||
setTimeout(_ => {
|
||||
Game.current.board.removeTask(task);
|
||||
}, i * 100);
|
||||
i ++;
|
||||
}
|
||||
Game.current.sync();
|
||||
} else if (key.full === 't') {
|
||||
const job: Task = new ChopTreeTask();
|
||||
Game.current.board.addTask(job);
|
||||
} else 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 === 'escape') {
|
||||
this.subMenu = SubMenu.NONE;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// const pawn = new Pawn();
|
||||
// Game.current.pawns.push(pawn);
|
||||
Game.current.sync();
|
||||
|
|
@ -41,15 +76,30 @@ export class Menu implements Renderable {
|
|||
}
|
||||
|
||||
renderJobs() {
|
||||
return (`\
|
||||
${chalk.greenBright('t')}: Chop Trees
|
||||
${chalk.greenBright('c')}: Create Pawn
|
||||
${chalk.greenBright('x')}: Clear Tasks
|
||||
|
||||
const colSpace = ((menuPanel.width - 2) / 2);
|
||||
|
||||
return (` Menus:${' '.repeat(colSpace - 8)}Actions:\n ${
|
||||
chalk.greenBright('q')
|
||||
}: ${
|
||||
(this.subMenu !== SubMenu.TREES ? chalk.bold.black : _ => _)('Chop Trees')
|
||||
}${
|
||||
' '.repeat(colSpace - 15)
|
||||
}${chalk.greenBright('z')}: ${
|
||||
(this.subMenu !== SubMenu.NONE ? chalk.bold.black : _ => _)('Create Pawn')
|
||||
|
||||
}\n${
|
||||
' '.repeat(colSpace)
|
||||
}${
|
||||
chalk.greenBright('x')
|
||||
}: ${
|
||||
(this.subMenu !== SubMenu.NONE ? chalk.bold.black : _ => _)('Clear Tasks')
|
||||
}\
|
||||
`);
|
||||
|
||||
}
|
||||
|
||||
topBar() {
|
||||
renderTopBar() {
|
||||
const idlers = Game.current.pawns.filter(pawn => pawn.idle);
|
||||
return ` ${Game.current.clock.toString()}{|}Idle: ${idlers.length} `;
|
||||
}
|
||||
|
|
@ -70,10 +120,63 @@ export class Menu implements Renderable {
|
|||
}`;
|
||||
}
|
||||
|
||||
renderView() {
|
||||
const colSpace = ((menuPanel.width - 2) / 2);
|
||||
return `${
|
||||
// ' '.repeat(colSpace - 20)
|
||||
'{center}'
|
||||
}${(() => {
|
||||
return Object.values(View).map(view => {
|
||||
if(view === this.view) {
|
||||
return chalk.cyan.inverse(` ${view} `);
|
||||
} else {
|
||||
return chalk.cyan(` ${view} `);
|
||||
}
|
||||
}).join('');
|
||||
})()}{/center}\n\n${(() => {
|
||||
switch(this.view) {
|
||||
case View.PAWNS: return this.renderPawns();
|
||||
case View.INVENTORY: return this.renderInv();
|
||||
}
|
||||
})()}`
|
||||
}
|
||||
|
||||
renderInv() {
|
||||
return Game.current.inv.render();
|
||||
}
|
||||
|
||||
renderSubMenu() {
|
||||
return `${(() => {
|
||||
switch(this.subMenu) {
|
||||
case SubMenu.NONE:
|
||||
return `{center}${tags.bright}${tags.black.fg}* Select a menu above for options *`;
|
||||
case SubMenu.TREES:
|
||||
return this.renderTreesSubMenu();
|
||||
}
|
||||
})()}`;
|
||||
}
|
||||
|
||||
renderTreesSubMenu() {
|
||||
return [
|
||||
`{center}Chop Trees`,
|
||||
`{left} ${chalk.greenBright('-=_+')}: ${this.trees}`,
|
||||
`{left} ${chalk.greenBright('enter')}: Create Task`,
|
||||
`{left} ${chalk.greenBright('escape')}: Cancel`
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
render() {
|
||||
const width = menuPanel.width - 2;
|
||||
const hr = chalk.bold.black('━'.repeat(width));
|
||||
const content = [this.topBar(), hr, this.renderPawns(), hr, this.renderJobs()].join('\n');
|
||||
const content = [
|
||||
this.renderTopBar(),
|
||||
hr,
|
||||
this.renderView(),
|
||||
hr,
|
||||
this.renderJobs(),
|
||||
hr,
|
||||
this.renderSubMenu()
|
||||
].join('\n');
|
||||
menuPanel.setContent(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,26 @@
|
|||
import chalk from "chalk";
|
||||
|
||||
export function progressbar(completion, width, style = chalk.bold.bgRed.green) {
|
||||
export enum ProgressbarStyle {
|
||||
indicator = 'indicator',
|
||||
progress = 'progress'
|
||||
}
|
||||
|
||||
export function progressbar(completion, width, style: ProgressbarStyle = ProgressbarStyle.indicator) {
|
||||
let chalkFn
|
||||
if(style === ProgressbarStyle.indicator) {
|
||||
if(completion > 0.8) chalkFn = chalk.bgBlue.cyan;
|
||||
else if(completion > 0.5) chalkFn = chalk.bgBlue.green;
|
||||
else if(completion > 0.2) chalkFn = chalk.bgBlue.yellow;
|
||||
else chalkFn = chalk.bgBlue.red;
|
||||
} else if(style === ProgressbarStyle.progress) {
|
||||
chalkFn = chalk.bgBlue.cyan;
|
||||
}
|
||||
const chars = [' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█'];
|
||||
let str = '';
|
||||
for(let i = 0; i < width; i ++) {
|
||||
const remainder = Math.floor(Math.min(Math.max(0, (completion * width) - i), 1) * 8);
|
||||
const char = chars[remainder];
|
||||
str += style(char);
|
||||
str += chalkFn(char);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import chalk from 'chalk';
|
|||
import { Pawn } from './Pawn.js';
|
||||
import { render, tasksPanel } from './UI.js';
|
||||
import { Game } from './Game.js';
|
||||
import { progressbar } from './Progressbar.js';
|
||||
import { progressbar, ProgressbarStyle } from './Progressbar.js';
|
||||
|
||||
export class Task extends Serializable {
|
||||
work = 0;
|
||||
|
|
@ -53,7 +53,7 @@ export class Task extends Serializable {
|
|||
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)}\n`;
|
||||
return `${left}\n ${progressbar(this.completion, bar, ProgressbarStyle.progress)}\n`;
|
||||
}
|
||||
|
||||
get title() {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,12 @@ import { render, Renderable, tasksPanel } from './UI.js';
|
|||
|
||||
export class TaskList extends Serializable implements Renderable {
|
||||
tasks: Task[] = [];
|
||||
game: Game;
|
||||
|
||||
clear() {
|
||||
for(const task of this.tasks) {
|
||||
this.removeTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
static serializationDependencies() {
|
||||
return [ChopTreeTask, Task];
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { Game } from './Game.js';
|
||||
import { render } from './UI.js';
|
||||
|
||||
export type ItemID = string;
|
||||
|
||||
const game = Game.create('data/world01.json');
|
||||
render(game);
|
||||
|
|
|
|||
|
|
@ -39,10 +39,6 @@ faker@^5.5.3:
|
|||
version "5.5.3"
|
||||
resolved "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz"
|
||||
|
||||
frigid@^1.3.3:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/frigid/-/frigid-1.3.5.tgz#8712a349061b3f816758b45bc317d5f7c0b8aef0"
|
||||
|
||||
has-flag@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
|
||||
|
|
|
|||
Loading…
Reference in New Issue