/src/gamestate.ts
import GameMap from './map'; import CellEditor, { CellEditorCallback } from './celleditor'; import ObjectEditor from './objecteditor'; import MapView from './mapview'; import TextView from './textview'; import Actions from './actions'; import { ID } from './id'; import { GameEvent, GameEventType } from './event'; import { GameObject, GameObjectProperties } from './object'; import { GameAgent } from './agent'; /* GameState This is the root object for all things the game needs to know about. It tracks Maps, Objects, Events, and Agents. Unlike all other objects, GameState instantiated and made available as a default export for convenient access from anywhere. Because of this, it does not have a normal constructor and it should be initialized after page load with the init() method. */ interface Globals { objects: GameObjectProperties[] inventory: object } class GameState { map: GameMap; events: Map<ID, GameEvent>; objects: Map<ID, GameObject>; agents: Map<ID, GameAgent>; playerInventory: Map<ID, number>; textView: TextView; cellEditor: CellEditor; objectEditor: ObjectEditor; mapView: MapView; actions: Actions; editMode: boolean = true; init() { this.events = new Map(); this.objects = new Map(); this.agents = new Map(); this.map = new GameMap(); this.playerInventory = new Map(); document.body.classList.toggle('edit-mode', this.editMode); this.textView = new TextView(); this.cellEditor = new CellEditor(); this.objectEditor = new ObjectEditor(); this.mapView = new MapView(); this.mapView.updateAll(); this.actions = new Actions(); if (localStorage['map']) { this.map.load(JSON.parse(localStorage.map)); } if (localStorage['globals']) { this.loadGlobals(JSON.parse(localStorage.globals)); } this.enterCell(); } print(...args: string[]) { this.textView.print(...args); } movePlayerRel(dx: number, dy: number) { const [ x, y ] = this.map.getPlayerPosition(); this.movePlayer(x + dx, y + dy); } movePlayer(x: number, y: number) { this.map.movePlayer(x, y); this.enterCell(); } printCellText() { const cell = this.map.getCurrentCell(); gs.textView.clear(); gs.textView.print("You have entered the", cell.title); gs.textView.print(); gs.textView.print(cell.description); } showCellActions() { const cell = this.map.getCurrentCell(); this.actions.clear(); for (let [k, e] of cell.events) { if (e.type == GameEventType.Action) { this.actions.addAction(e.title, e.getCallback()); } } if (this.editMode) { this.actions.addAction('Toggle Navigable', () => this.map.toggleNavigable()); this.actions.addAction('Edit Cell', () => this.map.editDescription()); this.actions.addAction('Object Editor', () => this.objectEditor.open()); } } enterCell() { this.printCellText(); this.showCellActions(); } addObject(o: GameObject) { this.objects.set(o.id, o); } removeObject(o: GameObject | ID) { if (o instanceof GameObject) { this.objects.delete(o.id); } else { this.objects.delete(o); } } addInventory(o: ID, count=1) { if (this.playerInventory.has(o)) { const currentCount = this.playerInventory.get(o); this.playerInventory.set(o, currentCount + count); } else { this.playerInventory.set(o, count); } } storeGlobals(): Globals { const objects: GameObjectProperties[] = []; for (let [k, o] of this.objects) { objects.push(o.store()); } const inventory = Object.fromEntries(this.playerInventory); return { objects, inventory }; } loadGlobals(globals: Globals) { if (globals.objects) { for (let o of globals.objects) { const obj = new GameObject(); obj.load(o); this.addObject(obj); } } if (globals.inventory) { this.playerInventory = new Map(Object.entries(globals.inventory)); } } } // Create a default GameState object and export it const gs = new GameState(); export default gs;