From d4c008a5e178bdce035421db0a8030aac06e920e Mon Sep 17 00:00:00 2001 From: Valerie Date: Fri, 2 Jul 2021 23:01:45 -0400 Subject: [PATCH] stuff, broken --- content/core/actions/CoreActions.ts | 13 ++---- content/core/items/CoreItems.ts | 34 +++++++++++--- content/core/themes/standard.ts | 2 +- lib/aliases.mjs | 1 + src/Inventory.ts | 38 +++++++--------- src/registries/Actions.ts | 2 +- src/registries/Items.ts | 10 +++-- src/registries/Themes.ts | 14 +++--- src/ui/GiftPopup.ts | 2 +- src/ui/SelectItem.ts | 69 +++++++++++++++++++++++++++++ src/ui/view/ActionsView.ts | 2 +- src/ui/view/MultiplayerView.ts | 2 +- src/ui/view/PawnsView.ts | 60 ++++++++++++------------- tsconfig.json | 1 + 14 files changed, 170 insertions(+), 80 deletions(-) create mode 100644 src/ui/SelectItem.ts diff --git a/content/core/actions/CoreActions.ts b/content/core/actions/CoreActions.ts index 020d6c0..f45111e 100644 --- a/content/core/actions/CoreActions.ts +++ b/content/core/actions/CoreActions.ts @@ -2,16 +2,10 @@ import { registerAction } from '@actions'; import { Game } from '@game'; import { ItemState } from '@items'; import { TaskState } from '@tasks'; +import { SelectItem } from '../../../src/ui/SelectItem.js'; import { FLINT_NORMAL } from '../items/CoreItems.js'; import { GATHER_FLINT, MAKE_ARROWHEAD } from '../tasks/CoreTasks.js'; -// 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', @@ -24,8 +18,9 @@ registerAction('Gather Flint', () => { Game.current.board.addTask(taskState); }); -registerAction('Create Arrowhead', (qty) => { - const rock = new ItemState(FLINT_NORMAL, 1, null); +registerAction('Create Arrowhead', () => { + // const rock = new ItemState(FLINT_NORMAL, 1, null); + const item = SelectItem.show() const task = new TaskState(MAKE_ARROWHEAD, { baseMaterial: rock }); diff --git a/content/core/items/CoreItems.ts b/content/core/items/CoreItems.ts index 2dfb41a..483d277 100644 --- a/content/core/items/CoreItems.ts +++ b/content/core/items/CoreItems.ts @@ -1,8 +1,23 @@ import { Item, ItemFilter, ItemProperty, ItemState } from '@items' +class Material { + name: string; + hardness: number; + + setName(name: string) { + this.name = name; + return this; + } + + setHardness(n: number) { + this.hardness = n; + } +} + // #region properties! export const ROCK = new ItemProperty('core:rock') -export const ROCK_HARDNESS = new ItemProperty('core:mohs-hardness') +export const MATERIAL = new ItemProperty('core:material') +export const ROCK_SIZE = new ItemProperty('core:rock-size') export const SEDIMENTARY = new ItemProperty('core:sedimentary') export const IGNEOUS = new ItemProperty('core:igneous') export const METAMORPHIC = new ItemProperty('core:metamorphic') @@ -26,7 +41,12 @@ export const PLANT_FIBRES = new Item() export const FLINT_NORMAL = new Item() .setName("Flint") .setId('core:flint') - .setProperty(ROCK_HARDNESS, 7) + .setProperty(MATERIAL, new Material() + .setName('Flint') + .setHardness(7) + ) + .setProperty(ROCK, true) + .setProperty(IGNEOUS, true) export const SANDSTONE_NORMAL = new Item() .setName("Sandstone") @@ -104,9 +124,13 @@ export const OBSIDIAN_SPEAR = new Item() // #endregion -// export function FILTER_CRAFTABLE_ROCK(item: ItemState) { -// return -// } +export function FILTER_CRAFTABLE_ROCK(itemState: ItemState) { + if(!itemState.item.getProperty(MATERIAL)) return false; + const mat: Material = itemState.item.getProperty(MATERIAL) as Material; + return itemState.item.getProperty(ROCK) + && mat.hardness >= 6 + && mat.hardness < 10 +} // tools: plant fibres = rope, flint hatchet // shale - igneous. metamorphasis => slate \ No newline at end of file diff --git a/content/core/themes/standard.ts b/content/core/themes/standard.ts index 16524ad..b5e7844 100644 --- a/content/core/themes/standard.ts +++ b/content/core/themes/standard.ts @@ -4,5 +4,5 @@ import chalk from 'chalk' registerTheme("default", {}); registerTheme("high contrast", { - selected: chalk.ansi256(250).inverse + bright: chalk.ansi256(250).inverse }); \ No newline at end of file diff --git a/lib/aliases.mjs b/lib/aliases.mjs index 1c7f461..1a1ad90 100644 --- a/lib/aliases.mjs +++ b/lib/aliases.mjs @@ -5,6 +5,7 @@ const moduleAliases = { "@actions": "./out/src/registries/Actions.js", "@tasks": "./out/src/registries/Tasks.js", "@items": "./out/src/registries/Items.js", + "@ui": "./out/src/ui/UI.js", "@game": "./out/src/Game.js" }; diff --git a/src/Inventory.ts b/src/Inventory.ts index c488101..9c505b3 100644 --- a/src/Inventory.ts +++ b/src/Inventory.ts @@ -42,29 +42,23 @@ export class Inventory extends Serializable implements Renderable { } private reduceInv() { - // TODO deduplicate itemstates... - // use a reduce to reconstruct the array. - // REMEMBER TO MAINTAIN THE OBJECTs! - // dont do immutability to it, as the objects - // may have crossreferences! (maybe) - } + this.items = this.items.reduce((items, 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(); - // } + // TODO at some point, be able to merge data items? + + const existing = items.find(testItemState => { + return itemState.itemId === testItemState.itemId + && itemState.data === testItemState.data; + }); + + if(existing) { + existing.qty += itemState.qty; + } else { + items.push(itemState); + } + return items + }, [] as ItemState[]) + } render() { return this.items.map(item => item.render()).join('\n'); diff --git a/src/registries/Actions.ts b/src/registries/Actions.ts index 7f69640..1707d26 100644 --- a/src/registries/Actions.ts +++ b/src/registries/Actions.ts @@ -3,7 +3,7 @@ import { Renderable } from "../ui/UI.js"; export const actions: Action[] = []; -export function registerAction(name: string, invoke: (qty: number) => void) { +export function registerAction(name: string, invoke: () => void) { console.log('Registered action', name); actions.push(new Action(name, invoke)) } diff --git a/src/registries/Items.ts b/src/registries/Items.ts index a901bbc..d4c8a3a 100644 --- a/src/registries/Items.ts +++ b/src/registries/Items.ts @@ -6,14 +6,12 @@ export type ItemID = string; const items = new Map>(); -export type PropertyValue = number | boolean; - // ITEMS SHALL BE SINGULAR export class Item extends Serializable { name = ''; id: ItemID = ''; - props: Map = new Map(); + props: Map = new Map(); setName(name: string) { this.name = name; @@ -50,6 +48,12 @@ export class ItemState extends Serializable implements Renderable { itemId: ItemID; data: Data; + take(qty: number) { + if(this.qty < qty) throw new Error('cant split more than stack from stack...'); + this.qty -= qty; + return new ItemState(this.item, qty, this.data); + } + get item() { if(!items.has(this.itemId)) throw new Error('unknown item: ' + this.itemId); diff --git a/src/registries/Themes.ts b/src/registries/Themes.ts index a1b17d7..2b08525 100644 --- a/src/registries/Themes.ts +++ b/src/registries/Themes.ts @@ -13,8 +13,9 @@ type StyleFunction = (text: string) => string; export type Theme = { header: StyleFunction, subheader: StyleFunction, + bright: StyleFunction, normal: StyleFunction, - selected: StyleFunction, + dimmed: StyleFunction, hotkey: StyleFunction, tab: { normal: StyleFunction, @@ -42,10 +43,11 @@ export type Theme = { } export const backupTheme: Theme = { - header: chalk.ansi256(255).bold, - subheader: chalk.ansi256(243).bold, - normal: chalk.ansi256(243), - selected: chalk.ansi256(250), + header: chalk.ansi256(255), + subheader: chalk.ansi256(250), + bright: chalk.ansi256(255), + normal: chalk.ansi256(250), + dimmed: chalk.ansi256(245), hotkey: chalk.ansi256(40), tab: { normal: chalk.ansi256(117), @@ -53,7 +55,7 @@ export const backupTheme: Theme = { }, border: { focused: '#ffffff', - normal: '#222222' + normal: '#888888' }, progressBar: { indicator: { diff --git a/src/ui/GiftPopup.ts b/src/ui/GiftPopup.ts index 53e7663..f8cf5d6 100644 --- a/src/ui/GiftPopup.ts +++ b/src/ui/GiftPopup.ts @@ -69,7 +69,7 @@ export class GiftPopup { this.box.setContent(`${(() => { let pawns = []; for (const [pawn, qty] of this.pawns.entries()) { - const style = i === this.selected ? getTheme().selected : getTheme().normal; + const style = i === this.selected ? getTheme().bright : getTheme().normal; if(qty > 0) { pawns.push(style(`{|}${pawn.toString()} `)) } else { diff --git a/src/ui/SelectItem.ts b/src/ui/SelectItem.ts new file mode 100644 index 0000000..2e4c912 --- /dev/null +++ b/src/ui/SelectItem.ts @@ -0,0 +1,69 @@ +import { Game } from "@game"; +import { ItemState } from "@items"; +import { boxStyle, getTheme } from "@themes"; +import { panels } from "@ui"; +import EventEmitter from "events"; +import blessed from 'neo-blessed'; + +type ItemFilterFunction = (itemState: ItemState) => boolean; + +export class SelectItem { + box: any; + emitter: EventEmitter; + qty: number; + items: ItemState[]; + selectedIdx: number; + + static show(filter: ItemFilterFunction): Promise> { + const si = new SelectItem(filter, 1); + return new Promise(res => { + si.emitter.on('selected', (itemState: ItemState) => { + res(itemState); + }); + si.emitter.on('cancel', () => { + res(null); + }) + }); + } + + private open() { + panels.screen.append(this.box); + this.box.focus(); + Game.current.clock.pause(); + } + + private close() { + Game.current.clock.start(); + panels.screen.remove(this.box); + } + + get selectedItem(): ItemState { + return null; + } + + private constructor(filter: ItemFilterFunction, qty: number) { + this.emitter = new EventEmitter(); + this.qty = qty; + this.box = blessed.box({ + top: 'center', + left: 'center', + width: 'shrink', + height: 'shrink', + tags: true, + ...boxStyle(), + }); + this.box.on('keypress', (evt: {}, key: {full: string}) => { + if(key.full === 'escape') { + this.emitter.emit('cancel'); + this.close(); + } else if(key.full === 'enter') { + this.emitter.emit('selected', this.selectedItem.take(this.qty)); + this.close(); + } else if(key.full === 'down') { + + } + }); + this.items = Game.current.inv.items.filter(filter); + this.open(); + } +} \ No newline at end of file diff --git a/src/ui/view/ActionsView.ts b/src/ui/view/ActionsView.ts index 1876846..fb0c868 100644 --- a/src/ui/view/ActionsView.ts +++ b/src/ui/view/ActionsView.ts @@ -24,7 +24,7 @@ export class ActionsView extends View { render() { return actions.map((action, idx) => `${(() => { if(this.actionIdx === idx) { - return getTheme().selected(' ❯ ' + action.name); + return getTheme().bright(' ❯ ' + action.name); } else { return getTheme().normal(' ' + action.name); } diff --git a/src/ui/view/MultiplayerView.ts b/src/ui/view/MultiplayerView.ts index 2bce850..bf62cd6 100644 --- a/src/ui/view/MultiplayerView.ts +++ b/src/ui/view/MultiplayerView.ts @@ -25,7 +25,7 @@ export default class MultiplayerView extends View { 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()); + if(i === this.selected) return ' ' + getTheme().bright(' ❯ ' + player.toString()); else return ' ' + getTheme().normal(player.toString()); }).join('\n'); }; diff --git a/src/ui/view/PawnsView.ts b/src/ui/view/PawnsView.ts index c9b7c9b..9d8574c 100644 --- a/src/ui/view/PawnsView.ts +++ b/src/ui/view/PawnsView.ts @@ -6,36 +6,36 @@ import { panels } from "../UI.js"; import { View } from "../View.js"; export default class PawnsView extends View { - constructor() { - super(); - this.name = 'Pawns'; - } + 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); - } - } + 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') - }`; - } + render() { + return `${ + Game.current.pawns.map(pawn => `${(function() { + const selected = pawn === Game.current.selected; + let str = ''; + if(selected) { + str += ` ${getTheme().bright(` ❯ ${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 bcb37a1..e907747 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "@actions": ["./src/registries/Actions"], "@tasks": ["./src/registries/Tasks"], "@items": ["./src/registries/Items"], + "@ui": ["./src/ui/UI"], "@game": ["./src/Game"] }, "noImplicitAny": true