Refactor player and room to support adding effects.
This commit is contained in:
29
Container.ts
29
Container.ts
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
18
Game.ts
Normal file
18
Game.ts
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import Player from "./Player.ts";
|
import type Player from "./Player.ts";
|
||||||
import Scene from "./Scene.ts";
|
import type Scene from "./Scene.ts";
|
||||||
import { terms } from "./terms/terms.ts";
|
import { terms } from "./terms/terms.ts";
|
||||||
import type {
|
import type {
|
||||||
Action,
|
Action,
|
||||||
|
27
Player.ts
27
Player.ts
@ -1,21 +1,21 @@
|
|||||||
import { Item } from "./data/data.ts";
|
import { Effect, Item, VesselProperties } from "./types.ts";
|
||||||
import Container from "./Container.ts";
|
import Vessel from "./Vessel.ts";
|
||||||
import User from "./User.ts";
|
|
||||||
|
|
||||||
export default class Player extends Container {
|
export default class Player extends Vessel<VesselProperties> {
|
||||||
#user: User;
|
#effects: Effect<VesselProperties>[];
|
||||||
|
|
||||||
constructor(items?: Item[]) {
|
constructor(items: Item[] = [], effects: Effect<VesselProperties>[] = []) {
|
||||||
super(items || []);
|
super({ items }, { effects: [] });
|
||||||
this.#user = new User();
|
|
||||||
|
this.#effects = effects;
|
||||||
}
|
}
|
||||||
|
|
||||||
put(item: Item) {
|
get effects() {
|
||||||
this.items.push(item);
|
return this.#effects;
|
||||||
}
|
}
|
||||||
|
|
||||||
look(): string {
|
look(): string {
|
||||||
const description = super.description(this.items);
|
const description = super.description(this._properties.items || []);
|
||||||
|
|
||||||
if (description) {
|
if (description) {
|
||||||
return `You have ${description}`;
|
return `You have ${description}`;
|
||||||
@ -23,4 +23,9 @@ export default class Player extends Container {
|
|||||||
return "You have nothing.";
|
return "You have nothing.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
put(item: Item) {
|
||||||
|
if (!this._properties.items) this._properties.items = [];
|
||||||
|
this._properties.items.push(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
48
Scene.ts
48
Scene.ts
@ -1,32 +1,46 @@
|
|||||||
import { GameData, Item } from "./data/data.ts";
|
import { GameData, Item, SceneProperties } from "./types.ts";
|
||||||
import Container from "./Container.ts";
|
import Vessel from "./Vessel.ts";
|
||||||
|
|
||||||
export default class Scene extends Container {
|
export default class Scene extends Vessel<SceneProperties> {
|
||||||
#roomDescription: string;
|
#isViewed: boolean;
|
||||||
|
|
||||||
constructor(gameData: GameData) {
|
constructor(gameData: GameData<SceneProperties>) {
|
||||||
super(gameData.items);
|
// TODO: [] is a placeholder for scene effects.
|
||||||
|
super(gameData.properties, gameData.conditions);
|
||||||
this.#roomDescription = gameData.description;
|
this.#isViewed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
look(): string {
|
look(activePlayerEffects: string[], activeSceneEffects: string[]): string {
|
||||||
const itemsDescription = super.description(this.items);
|
const properties = this.applyEffects(
|
||||||
let description = this.#roomDescription;
|
activePlayerEffects,
|
||||||
|
activeSceneEffects
|
||||||
|
);
|
||||||
|
const itemsDescription = super.description(properties.items || []);
|
||||||
|
const { description, shortDescription } = properties;
|
||||||
|
|
||||||
if (itemsDescription) {
|
let fullDescription = description || "Nothing to see here...";
|
||||||
description += `\n\nThere is ${itemsDescription}`;
|
|
||||||
|
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 {
|
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) {
|
if (idx >= 0) {
|
||||||
const item = this.items[idx];
|
const item = items[idx];
|
||||||
this.items.splice(idx, 1);
|
items.splice(idx, 1);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
13
State.ts
Normal file
13
State.ts
Normal file
@ -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<SceneProperties>) {
|
||||||
|
this.#player = new Player();
|
||||||
|
this.#scene = new Scene(gameData);
|
||||||
|
}
|
||||||
|
}
|
65
Vessel.ts
Normal file
65
Vessel.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import type { Conditions, Effect, Item, VesselProperties } from "./types.ts";
|
||||||
|
|
||||||
|
export default class Vessel<T extends VesselProperties> {
|
||||||
|
protected _conditions: Conditions<T>;
|
||||||
|
protected _properties: T;
|
||||||
|
protected _activeEffects: string[];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
properties: T,
|
||||||
|
conditions: Conditions<T>,
|
||||||
|
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<T> }, effect: Effect<T>) => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
10
data/data.ts
10
data/data.ts
@ -1,10 +0,0 @@
|
|||||||
export * from "./rooms.ts";
|
|
||||||
|
|
||||||
export interface Item {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GameData {
|
|
||||||
description: string;
|
|
||||||
items: Item[];
|
|
||||||
}
|
|
@ -1,8 +1,22 @@
|
|||||||
import { GameData } from "./data.ts";
|
import { GameData, SceneProperties } from "../types.ts";
|
||||||
|
|
||||||
export const hall: GameData = {
|
export const hall: GameData<SceneProperties> = {
|
||||||
description:
|
properties: {
|
||||||
"You are standing in a big hall. There's lots of nooks, crannies, and" +
|
description: "It's very dark",
|
||||||
"room for general testing. Aw yeah... sweet testing!",
|
items: [{ name: "flashlight" }],
|
||||||
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",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
2
main.ts
2
main.ts
@ -2,7 +2,7 @@ import Interpreter from "./Interpreter.ts";
|
|||||||
import Player from "./Player.ts";
|
import Player from "./Player.ts";
|
||||||
import Scene from "./Scene.ts";
|
import Scene from "./Scene.ts";
|
||||||
import User from "./User.ts";
|
import User from "./User.ts";
|
||||||
import { hall } from "./data/data.ts";
|
import { hall } from "./types.ts";
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const user = new User(); // for communication with the user
|
const user = new User(); // for communication with the user
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import Scene from "../Scene.ts";
|
import Scene from "../Scene.ts";
|
||||||
import Player from "../Player.ts";
|
import Player from "../Player.ts";
|
||||||
|
|
||||||
export function look(_player: Player, scene: Scene): string {
|
export function look(player: Player, scene: Scene): string {
|
||||||
return scene.look();
|
return scene.look(player.activeEffects, scene.activeEffects);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pickUpItem(
|
export function pickUpItem(
|
||||||
|
@ -21,8 +21,8 @@ const look: Action = {
|
|||||||
|
|
||||||
const take: Action = {
|
const take: Action = {
|
||||||
action: actions.pickUpItem,
|
action: actions.pickUpItem,
|
||||||
category: "action",
|
|
||||||
canPrecedeVariable: true,
|
canPrecedeVariable: true,
|
||||||
|
category: "action",
|
||||||
constant: "take",
|
constant: "take",
|
||||||
precedesCategories: [],
|
precedesCategories: [],
|
||||||
precedesConstants: [],
|
precedesConstants: [],
|
||||||
|
29
types.ts
Normal file
29
types.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export * from "./data/rooms.ts";
|
||||||
|
|
||||||
|
export interface Item {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Effect<T> {
|
||||||
|
name: string;
|
||||||
|
properties: T;
|
||||||
|
source: "player" | "scene";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VesselProperties {
|
||||||
|
items?: Item[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SceneProperties extends VesselProperties {
|
||||||
|
description?: string;
|
||||||
|
shortDescription?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Conditions<T> {
|
||||||
|
effects: Effect<T>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GameData<T> {
|
||||||
|
conditions: Conditions<T>;
|
||||||
|
properties: SceneProperties;
|
||||||
|
}
|
Reference in New Issue
Block a user