diff --git a/src/Game.ts b/src/Game.ts index d06256b..64cf726 100644 --- a/src/Game.ts +++ b/src/Game.ts @@ -3,13 +3,13 @@ import { DEBUG } from 'frigid/out/Serializable.js'; import { Pawn } from './Pawn.js'; import { TaskList } from './TaskList.js'; import { Inventory } from './Inventory.js'; -import { Menu } from './Menu.js'; +import { Menu } from './ui/Menu.js'; import Time, { Tickable } from './Time.js'; -import { render, Renderable, setTitle } from './UI.js'; +import { render, Renderable, setTitle } from './ui/UI.js'; import log from './log.js'; -import { ChopTreeTask } from './ChopTreeTask.js'; -import { Task } from './Task.js'; -import { ready } from './mDNS.js'; +import { ChopTreeTask } from './tasks/ChopTreeTask.js'; +import { Task } from './tasks/Task.js'; +import { ready } from './multiplayer/mDNS.js'; import faker from 'faker'; let game = null; diff --git a/src/Inventory.ts b/src/Inventory.ts index c5d94f4..0ee90c5 100644 --- a/src/Inventory.ts +++ b/src/Inventory.ts @@ -1,7 +1,7 @@ import { Serializable } from 'frigid'; import { Game } from './Game.js'; import { Item, ItemState } from './Item.js'; -import { Renderable } from './UI.js'; +import { Renderable } from './ui/UI.js'; export class Inventory extends Serializable implements Renderable { items: ItemState[]; diff --git a/src/Item.ts b/src/Item.ts index 6d73d87..f68b381 100644 --- a/src/Item.ts +++ b/src/Item.ts @@ -1,5 +1,5 @@ import { Serializable } from 'frigid'; -import { Renderable } from './UI'; +import { Renderable } from './ui/UI'; export type ItemID = string; diff --git a/src/Memory.ts b/src/Memory.ts new file mode 100644 index 0000000..84b7c4c --- /dev/null +++ b/src/Memory.ts @@ -0,0 +1,26 @@ +export type Memory = TravelMemory | BirthMemory; + +type ProtoMemory = { + type: string, + time: { + stamp: number, + locale: string + } +} + +export type TravelMemory = ProtoMemory & { + type: "travel", + location: string, +} + +export type BirthMemory = ProtoMemory & { + type: "birth", + location: string, +} + +export function stringify(memory: Memory): string { + switch(memory.type) { + case "birth": return `I was born at ${memory.time.locale} in ${memory.location}`; + case "travel": return `I traveled to ${memory.location}.`; + } +} \ No newline at end of file diff --git a/src/Pawn.ts b/src/Pawn.ts index 98e7d33..77bc970 100644 --- a/src/Pawn.ts +++ b/src/Pawn.ts @@ -2,11 +2,12 @@ import { Serializable } from 'frigid'; import faker from 'faker'; import chalk from 'chalk'; import log from './log.js'; -import { Task } from './Task.js'; +import { Task } from './tasks/Task.js'; import { Tickable } from './Time.js'; -import { ChopTreeTask } from './ChopTreeTask.js'; +import { ChopTreeTask } from './tasks/ChopTreeTask.js'; import { Game } from './Game.js'; -import { render } from './UI.js'; +import { render } from './ui/UI.js'; +import { Memory } from './Memory.js'; const LABORS = { CUT_TREE: Symbol('CUT_TREE'), @@ -38,7 +39,11 @@ export class Pawn extends Serializable implements Tickable { age: number; + memories: Memory[]; + async tick() { + this.age ++; + this.energy -= energyScale; if(this.awake === false) { @@ -87,6 +92,20 @@ export class Pawn extends Serializable implements Tickable { } this.awake ??= true; this.energy ??= 100; + this.memories ??= []; + if(!this.age) { + this.age = 0; + this.memories.push({ + type: "birth", + location: Game.current.name, + time: { + stamp: Game.current.clock.stamp, + locale: Game.current.clock.toString() + } + }) + } + + if(this.job?.completed) { this.stopWorking(); } diff --git a/src/TaskList.ts b/src/TaskList.ts index 6eca62d..1717703 100644 --- a/src/TaskList.ts +++ b/src/TaskList.ts @@ -1,8 +1,8 @@ import { Serializable } from 'frigid'; -import { ChopTreeTask } from "./ChopTreeTask.js"; +import { ChopTreeTask } from "./tasks/ChopTreeTask.js"; import { Game } from './Game.js'; -import { Task } from "./Task.js"; -import { render, Renderable, tasksPanel } from './UI.js'; +import { Task } from "./tasks/Task.js"; +import { render, Renderable, tasksPanel } from './ui/UI.js'; export class TaskList extends Serializable implements Renderable { tasks: Task[] = []; diff --git a/src/Time.ts b/src/Time.ts index 9e89fff..c68696d 100644 --- a/src/Time.ts +++ b/src/Time.ts @@ -2,6 +2,7 @@ import chalk from "chalk"; import { Serializable } from "frigid"; import { isThisTypeNode } from "typescript"; import log from "./log.js"; +import { Renderable } from "./ui/UI.js"; const daysInMonth = [ 31, 28, 31, @@ -17,7 +18,7 @@ const months = [ 'Oct', 'Nov', 'Dec' ] -export default class Time extends Serializable{ +export default class Time extends Serializable implements Renderable{ rate: number; paused = true; @@ -29,7 +30,7 @@ export default class Time extends Serializable{ hour: number; minute: number; - toString() { + render() { const sym = (this.hour >= 6 && this.hour < 20) ? chalk.yellowBright('☼') : chalk.blue('☾') @@ -39,6 +40,10 @@ export default class Time extends Serializable{ // return '☾' || '☼'; } + toString() { + return `${this.hour}:${this.minute} ${months[this.month]} ${this.day + 1}, ${(this.year + 1).toString().padStart(4, '0')}` + } + ctor() { this.rate = 60; this.minute ??= 0; @@ -48,6 +53,31 @@ export default class Time extends Serializable{ this.year ??= 0; } + 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); + + day += daysInYear * year; + year = 0; + while(month > 0) { + day += daysInMonth[month]; + month --; + } + + hour += day * 24; + day = 0; + + minute += hour * 60; + hour = 0; + + return minute; + } + pause() { this.paused = true; } diff --git a/src/World.ts b/src/World.ts new file mode 100644 index 0000000..0eb8a61 --- /dev/null +++ b/src/World.ts @@ -0,0 +1,5 @@ +import { Serializable } from "frigid"; + +export class World extends Serializable { + +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 352f568..c3cb857 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import { Game } from './Game.js'; -import { render } from './UI.js'; +import { render } from './ui/UI.js'; const saveFile = process.argv[2]; diff --git a/src/multiplayer/Player.ts b/src/multiplayer/Player.ts new file mode 100644 index 0000000..641e8e1 --- /dev/null +++ b/src/multiplayer/Player.ts @@ -0,0 +1,39 @@ +import { ItemState } from '../Item.js'; +import WebSocket from 'ws'; +import { Pawn } from '../Pawn.js'; +import { Game } from '../Game.js'; +import { GiftMessage } from './mDNS'; + + +export class Player { + name: string; + host: string; + port: number; + + toString() { + return this.name; + } + + send(items: (ItemState | Pawn)[]) { + return new Promise((res, rej) => { + const pawnJsons: string[] = []; + for (const item of items) { + Game.current.removePawn(item as Pawn); + pawnJsons.push(item.toJson()); + } + const gift: GiftMessage = { + pawns: pawnJsons, + from: Game.current.name + }; + const socket = new WebSocket(`ws://${this.host}:${this.port}`); + socket.on('open', () => { + socket.send(JSON.stringify(gift)); + socket.close(); + res(undefined); + }); + socket.on('error', () => { + rej(items); + }); + }); + } +} diff --git a/src/mDNS.ts b/src/multiplayer/mDNS.ts similarity index 61% rename from src/mDNS.ts rename to src/multiplayer/mDNS.ts index 8f62d7d..27e4ea8 100644 --- a/src/mDNS.ts +++ b/src/multiplayer/mDNS.ts @@ -1,54 +1,22 @@ import bonjour from 'bonjour'; -import log from './log.js'; +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, ItemState } from './Item.js'; +import { Item } from '../Item.js'; import WebSocket from 'ws'; -import { Popup } from './Popup.js'; +import { Popup } from '../ui/Popup.js'; import { inspect } from 'util' -import { Pawn } from './Pawn.js'; -import { Game } from './Game.js'; +import { Pawn } from '../Pawn.js'; +import { Game } from '../Game.js'; +import { Player } from './Player.js'; const mdns = bonjour(); const ID = uuid.v4(); let devices: Player[] = []; -export class Player { - name: string; - host: string; - port: number; - - toString() { - return this.name; - } - - send(items: (ItemState | Pawn)[]) { - return new Promise((res, rej) => { - const pawnJsons: string[] = []; - for(const item of items) { - Game.current.removePawn(item as Pawn); - pawnJsons.push(item.toJson()); - } - const gift: GiftMessage = { - pawns: pawnJsons, - from: Game.current.name - }; - const socket = new WebSocket(`ws://${this.host}:${this.port}`); - socket.on('open', () => { - socket.send(JSON.stringify(gift)); - socket.close(); - res(undefined); - }); - socket.on('error', () => { - rej(items); - }); - }); - } -} - const network = { get players() { return devices; diff --git a/src/ChopTreeTask.ts b/src/tasks/ChopTreeTask.ts similarity index 73% rename from src/ChopTreeTask.ts rename to src/tasks/ChopTreeTask.ts index 5292965..91f8c63 100644 --- a/src/ChopTreeTask.ts +++ b/src/tasks/ChopTreeTask.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; -import { Game } from './Game.js'; -import { Item } from './Item.js'; -import { Pawn } from './Pawn.js'; +import { Game } from '../Game.js'; +import { Item } from '../Item.js'; +import { Pawn } from '../Pawn.js'; import { Task } from './Task.js'; diff --git a/src/Task.ts b/src/tasks/Task.ts similarity index 82% rename from src/Task.ts rename to src/tasks/Task.ts index d128a83..2c8b1c0 100644 --- a/src/Task.ts +++ b/src/tasks/Task.ts @@ -1,10 +1,10 @@ import { Serializable } from 'frigid'; import EventEmitter from 'events'; import chalk from 'chalk'; -import { Pawn } from './Pawn.js'; -import { render, tasksPanel } from './UI.js'; -import { Game } from './Game.js'; -import { progressbar, ProgressbarStyle } from './Progressbar.js'; +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; diff --git a/src/GiftPopup.ts b/src/ui/GiftPopup.ts similarity index 92% rename from src/GiftPopup.ts rename to src/ui/GiftPopup.ts index 70b21b1..db385cb 100644 --- a/src/GiftPopup.ts +++ b/src/ui/GiftPopup.ts @@ -1,9 +1,9 @@ import chalk from 'chalk'; import blessed from 'neo-blessed'; -import { Game } from './Game.js'; -import { ItemState } from './Item.js'; -import { Player } from './mDNS.js'; -import { Pawn } from './Pawn.js'; +import { Game } from '../Game.js'; +import { ItemState } from '../Item.js'; +import { Player } from "../multiplayer/Player"; +import { Pawn } from '../Pawn.js'; import { boxStyle, screen } from './UI.js'; export class GiftPopup { diff --git a/src/Menu.ts b/src/ui/Menu.ts similarity index 89% rename from src/Menu.ts rename to src/ui/Menu.ts index 0dcb0b0..d5059b1 100644 --- a/src/Menu.ts +++ b/src/ui/Menu.ts @@ -1,15 +1,14 @@ -import { Pawn } from './Pawn.js'; -import log from './log.js'; -import { screen, menuPanel, render, tags, Renderable } from './UI.js'; +import { Pawn } from '../Pawn.js'; +import log from '../log.js'; +import { menuPanel, tags, Renderable } from './UI.js'; import chalk from 'chalk'; -import { Game } from './Game.js'; -import { Task } from './Task.js'; -import { ChopTreeTask } from './ChopTreeTask.js'; -import { progressbar } from './Progressbar.js'; -import { inspect } from 'util'; +import { Game } from '../Game.js'; +import { ChopTreeTask } from '../tasks/ChopTreeTask.js'; +import { progressbar } from '../Progressbar.js'; import { Popup } from './Popup.js'; -import mdns from './mDNS.js'; +import mdns from '../multiplayer/mDNS.js'; import { GiftPopup } from './GiftPopup.js'; +import { PawnDetails } from './PawnDetails.js'; enum SubMenu { NONE = 'NONE', @@ -53,6 +52,8 @@ export class Menu implements Renderable { Game.current.advanceSelection(-1); } else if (key.full === 'down') { Game.current.advanceSelection(1); + } else if (key.full === 'enter') { + new PawnDetails(Game.current.selected); } } @@ -124,7 +125,7 @@ export class Menu implements Renderable { renderTopBar() { const idlers = Game.current.pawns.filter(pawn => pawn.idle); - return ` ${Game.current.clock.toString()}{|}Idle: ${idlers.length} `; + return ` ${Game.current.clock.render()}{|}Idle: ${idlers.length} `; } renderPawns() { diff --git a/src/ui/PawnDetails.ts b/src/ui/PawnDetails.ts new file mode 100644 index 0000000..3a56a7b --- /dev/null +++ b/src/ui/PawnDetails.ts @@ -0,0 +1,59 @@ +import chalk from 'chalk'; +import blessed from 'neo-blessed'; +import { Game } from '../Game.js'; +import { stringify } from '../Memory.js'; +import { Pawn } from '../Pawn.js'; +import { boxStyle, screen } from './UI.js'; + +export class PawnDetails { + box; + pawn: Pawn; + + constructor(pawn: Pawn) { + this.pawn = pawn; + this.box = blessed.box({ + top: 0, + left: 'center', + width: 'shrink', + height: 'shrink', + tags: true, + ...boxStyle(), + }); + this.box.on('keypress', (evt, key) => { + if(key.full === 'escape' || key.full === 'enter') { + Game.current.clock.start(); + screen.remove(this.box); + } else if (key.full === 'up') { + // this.selected --; + } else if (key.full === 'down') { + // this.selected ++; + } else if (key.full === 'right') { + // this.pawns.set(Game.current.pawns[this.selected], 1); + } else if (key.full === 'left') { + // this.pawns.set(Game.current.pawns[this.selected], 0); + } + this.render(); + }); + this.render(); + screen.append(this.box); + this.box.focus(); + Game.current.clock.pause(); + } + + render() { + let i = 0; + this.box.setContent(`${ + this.pawn.toString() + }{|}${ + (this.pawn.sex ? "male" : "female") + + ', ' + this.pawn.age + }\n${(() => { + return this.pawn.memories.map(memory => stringify(memory)).join('\n') + })()}\n\n{|}${ + chalk.green('escape') + }: Cancel \n{|}${ + chalk.green('enter') + }: Okay `); + screen.render(); + } +} \ No newline at end of file diff --git a/src/Popup.ts b/src/ui/Popup.ts similarity index 90% rename from src/Popup.ts rename to src/ui/Popup.ts index 64224e6..cfd2418 100644 --- a/src/Popup.ts +++ b/src/ui/Popup.ts @@ -1,6 +1,6 @@ import chalk from 'chalk'; import blessed from 'neo-blessed'; -import { Game } from './Game.js'; +import { Game } from '../Game.js'; import { boxStyle, screen } from './UI.js'; export class Popup { diff --git a/src/UI.ts b/src/ui/UI.ts similarity index 88% rename from src/UI.ts rename to src/ui/UI.ts index 84bcb66..5384060 100644 --- a/src/UI.ts +++ b/src/ui/UI.ts @@ -95,4 +95,12 @@ process.stdout.write(ansi.cursor.hide); screen.key(['C-c'], function(ch, key) { process.stdout.write(ansi.cursor.show); return process.exit(0); +}); + +tasksPanel.key('f2', () => { + menuPanel.focus(); +}); + +menuPanel.key('f1', () => { + tasksPanel.focus(); }); \ No newline at end of file