From ccd4974266bc0e7a9f7593f9ade89b336771fa91 Mon Sep 17 00:00:00 2001 From: nolwn Date: Tue, 25 May 2021 16:02:06 -0700 Subject: [PATCH] Refactor player and room to support adding effects. --- Container.ts | 29 --------------------- Game.ts | 18 ++++++++++++++ Interpreter.ts | 4 +-- Player.ts | 27 ++++++++++++-------- Scene.ts | 48 ++++++++++++++++++++++------------- State.ts | 13 ++++++++++ Vessel.ts | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ data/data.ts | 10 -------- data/rooms.ts | 26 ++++++++++++++----- main.ts | 2 +- terms/actions.ts | 4 +-- terms/terms.ts | 2 +- types.ts | 29 +++++++++++++++++++++ 13 files changed, 198 insertions(+), 79 deletions(-) delete mode 100644 Container.ts create mode 100644 Game.ts create mode 100644 State.ts create mode 100644 Vessel.ts delete mode 100644 data/data.ts create mode 100644 types.ts diff --git a/Container.ts b/Container.ts deleted file mode 100644 index d5ad8f3..0000000 --- a/Container.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { Item } from "./data/data.ts"; - -export default class Container { - protected items: Item[]; - - constructor(items: Item[]) { - this.items = items; - } - - description(items: Item[]): string | null { - if (items.length === 0) return null; - - const vowels = ["a", "e", "i", "o", "u"]; - const description = items - .map(({ name }, i) => { - let anItem = `${vowels.includes(name[0]) ? "an" : "a"} ${name}`; - - // if we have more than one item, and this is the last item... - if (i > 1 && i + 1 === items.length) { - anItem = `and ${anItem}`; - } - - return anItem; - }) - .join(", "); - - return description; - } -} diff --git a/Game.ts b/Game.ts new file mode 100644 index 0000000..aeef051 --- /dev/null +++ b/Game.ts @@ -0,0 +1,18 @@ +import type Player from "./Player.ts"; +import type Scene from "./Scene.ts"; + +export default class Game { + #player: Player; + #scene: Scene; + + constructor(player: Player, scene: Scene) { + this.#player = player; + this.#scene = scene; + } + + lookAtScene(): string { + const { effects } = this.#player; + + return this.#scene.look(effects); + } +} diff --git a/Interpreter.ts b/Interpreter.ts index e724b81..b56a0da 100644 --- a/Interpreter.ts +++ b/Interpreter.ts @@ -1,5 +1,5 @@ -import Player from "./Player.ts"; -import Scene from "./Scene.ts"; +import type Player from "./Player.ts"; +import type Scene from "./Scene.ts"; import { terms } from "./terms/terms.ts"; import type { Action, diff --git a/Player.ts b/Player.ts index f9814c3..827a17e 100644 --- a/Player.ts +++ b/Player.ts @@ -1,21 +1,21 @@ -import { Item } from "./data/data.ts"; -import Container from "./Container.ts"; -import User from "./User.ts"; +import { Effect, Item, VesselProperties } from "./types.ts"; +import Vessel from "./Vessel.ts"; -export default class Player extends Container { - #user: User; +export default class Player extends Vessel { + #effects: Effect[]; - constructor(items?: Item[]) { - super(items || []); - this.#user = new User(); + constructor(items: Item[] = [], effects: Effect[] = []) { + super({ items }, { effects: [] }); + + this.#effects = effects; } - put(item: Item) { - this.items.push(item); + get effects() { + return this.#effects; } look(): string { - const description = super.description(this.items); + const description = super.description(this._properties.items || []); if (description) { return `You have ${description}`; @@ -23,4 +23,9 @@ export default class Player extends Container { return "You have nothing."; } } + + put(item: Item) { + if (!this._properties.items) this._properties.items = []; + this._properties.items.push(item); + } } diff --git a/Scene.ts b/Scene.ts index 8ab7974..2d28a48 100644 --- a/Scene.ts +++ b/Scene.ts @@ -1,32 +1,46 @@ -import { GameData, Item } from "./data/data.ts"; -import Container from "./Container.ts"; +import { GameData, Item, SceneProperties } from "./types.ts"; +import Vessel from "./Vessel.ts"; -export default class Scene extends Container { - #roomDescription: string; +export default class Scene extends Vessel { + #isViewed: boolean; - constructor(gameData: GameData) { - super(gameData.items); - - this.#roomDescription = gameData.description; + constructor(gameData: GameData) { + // TODO: [] is a placeholder for scene effects. + super(gameData.properties, gameData.conditions); + this.#isViewed = false; } - look(): string { - const itemsDescription = super.description(this.items); - let description = this.#roomDescription; + look(activePlayerEffects: string[], activeSceneEffects: string[]): string { + const properties = this.applyEffects( + activePlayerEffects, + activeSceneEffects + ); + const itemsDescription = super.description(properties.items || []); + const { description, shortDescription } = properties; - if (itemsDescription) { - description += `\n\nThere is ${itemsDescription}`; + let fullDescription = description || "Nothing to see here..."; + + if (this.#isViewed && shortDescription) { + fullDescription = shortDescription; + } else if (description) { + this.#isViewed = true; + fullDescription = description; } - return description; + if (itemsDescription) { + fullDescription += `\n\nThere is ${itemsDescription}`; + } + + return fullDescription; } get(target: string): Item | null { - const idx = this.items.findIndex(({ name }) => name === target); + const { items = [] } = this._properties; + const idx = items.findIndex(({ name }) => name === target); if (idx >= 0) { - const item = this.items[idx]; - this.items.splice(idx, 1); + const item = items[idx]; + items.splice(idx, 1); return item; } diff --git a/State.ts b/State.ts new file mode 100644 index 0000000..1f8b757 --- /dev/null +++ b/State.ts @@ -0,0 +1,13 @@ +import { GameData, SceneProperties } from "./types.ts"; +import Player from "./Player.ts"; +import Scene from "./Scene.ts"; + +export class State { + #player: Player; + #scene: Scene; + + constructor(gameData: GameData) { + this.#player = new Player(); + this.#scene = new Scene(gameData); + } +} diff --git a/Vessel.ts b/Vessel.ts new file mode 100644 index 0000000..d35ad0a --- /dev/null +++ b/Vessel.ts @@ -0,0 +1,65 @@ +import type { Conditions, Effect, Item, VesselProperties } from "./types.ts"; + +export default class Vessel { + protected _conditions: Conditions; + protected _properties: T; + protected _activeEffects: string[]; + + constructor( + properties: T, + conditions: Conditions, + activeEffects: string[] = [] + ) { + this._conditions = conditions; + this._properties = properties; + this._activeEffects = activeEffects; + } + + get activeEffects(): string[] { + return this._activeEffects; + } + + description(items: Item[]): string | null { + if (items.length === 0) return null; + + const vowels = ["a", "e", "i", "o", "u"]; + const description = items + .map(({ name }, i) => { + let anItem = `${vowels.includes(name[0]) ? "an" : "a"} ${name}`; + + // if we have more than one item, and this is the last item... + if (i > 1 && i + 1 === items.length) { + anItem = `and ${anItem}`; + } + + return anItem; + }) + .join(", "); + + return description; + } + + // Player effects should be applied first, then scene effects should be applied. + // This will mean that scene effects will take precedence over player effects. + applyEffects( + activePlayerEffects: string[], + activeSceneEffects: string[] + ): T { + const activeEffects = [...activePlayerEffects, ...activeSceneEffects]; + const map = this._conditions.effects.reduce( + (map: { [name: string]: Effect }, effect: Effect) => { + map[effect.name] = effect; + return map; + }, + {} + ); + const effects = activeEffects.map((e) => map[e]); + const appliedProperties = { ...this._properties }; + + for (const effect of effects) { + Object.assign(appliedProperties, effect.properties); + } + + return appliedProperties; + } +} diff --git a/data/data.ts b/data/data.ts deleted file mode 100644 index f6fd938..0000000 --- a/data/data.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from "./rooms.ts"; - -export interface Item { - name: string; -} - -export interface GameData { - description: string; - items: Item[]; -} diff --git a/data/rooms.ts b/data/rooms.ts index d118e1b..eae4140 100644 --- a/data/rooms.ts +++ b/data/rooms.ts @@ -1,8 +1,22 @@ -import { GameData } from "./data.ts"; +import { GameData, SceneProperties } from "../types.ts"; -export const hall: GameData = { - description: - "You are standing in a big hall. There's lots of nooks, crannies, and" + - "room for general testing. Aw yeah... sweet testing!", - items: [{ name: "flashlight" }], +export const hall: GameData = { + properties: { + description: "It's very dark", + items: [{ name: "flashlight" }], + }, + conditions: { + effects: [ + { + name: "lights-on", + properties: { + description: + "You are standing in a big hall. There's lots of nooks, " + + "crannies, and room for general testing. Aw yeah... sweet testing!", + shortDescription: "You are in a big hall.", + }, + source: "player", + }, + ], + }, }; diff --git a/main.ts b/main.ts index 0ef157c..12bebf5 100644 --- a/main.ts +++ b/main.ts @@ -2,7 +2,7 @@ import Interpreter from "./Interpreter.ts"; import Player from "./Player.ts"; import Scene from "./Scene.ts"; import User from "./User.ts"; -import { hall } from "./data/data.ts"; +import { hall } from "./types.ts"; async function main() { const user = new User(); // for communication with the user diff --git a/terms/actions.ts b/terms/actions.ts index 00ccb49..2caa9d9 100644 --- a/terms/actions.ts +++ b/terms/actions.ts @@ -1,8 +1,8 @@ import Scene from "../Scene.ts"; import Player from "../Player.ts"; -export function look(_player: Player, scene: Scene): string { - return scene.look(); +export function look(player: Player, scene: Scene): string { + return scene.look(player.activeEffects, scene.activeEffects); } export function pickUpItem( diff --git a/terms/terms.ts b/terms/terms.ts index 694e6f9..fef83da 100644 --- a/terms/terms.ts +++ b/terms/terms.ts @@ -21,8 +21,8 @@ const look: Action = { const take: Action = { action: actions.pickUpItem, - category: "action", canPrecedeVariable: true, + category: "action", constant: "take", precedesCategories: [], precedesConstants: [], diff --git a/types.ts b/types.ts new file mode 100644 index 0000000..a9d095b --- /dev/null +++ b/types.ts @@ -0,0 +1,29 @@ +export * from "./data/rooms.ts"; + +export interface Item { + name: string; +} + +export interface Effect { + name: string; + properties: T; + source: "player" | "scene"; +} + +export interface VesselProperties { + items?: Item[]; +} + +export interface SceneProperties extends VesselProperties { + description?: string; + shortDescription?: string; +} + +export interface Conditions { + effects: Effect[]; +} + +export interface GameData { + conditions: Conditions; + properties: SceneProperties; +}