add ability to move from room to room.
This commit is contained in:
@ -1,11 +1,11 @@
|
||||
/**
|
||||
* Vessel.ts contains the Vessel class which is a base class that handles effects and
|
||||
* Entity.ts contains the Entity class which is a base class that handles effects and
|
||||
* containing items. It is the base class for Player and Scene.
|
||||
*/
|
||||
|
||||
import type { Conditions, Effect, Item, VesselProperties } from "./types.ts";
|
||||
import type { Conditions, Item, EntityProperties } from "./types.ts";
|
||||
|
||||
export default class Vessel<T extends VesselProperties> {
|
||||
export default class Entity<T extends EntityProperties> {
|
||||
protected _conditions: Conditions<T>;
|
||||
protected _properties: T;
|
||||
protected _activeEffects: string[];
|
||||
@ -24,6 +24,18 @@ export default class Vessel<T extends VesselProperties> {
|
||||
return this._activeEffects;
|
||||
}
|
||||
|
||||
set conditions(conditions: Conditions<T>) {
|
||||
this._conditions = conditions;
|
||||
}
|
||||
|
||||
get properties(): T {
|
||||
return this._properties;
|
||||
}
|
||||
|
||||
set properties(properties: T) {
|
||||
this._properties = properties;
|
||||
}
|
||||
|
||||
addEffect(effect: string): void {
|
||||
this.activeEffects.push(effect);
|
||||
}
|
||||
@ -74,12 +86,12 @@ export default class Vessel<T extends VesselProperties> {
|
||||
return description;
|
||||
}
|
||||
|
||||
// Checks if a given effect exists as an active effect on this vessel
|
||||
// Checks if a given effect exists as an active effect on this entity
|
||||
hasActiveEffect(effect: string): boolean {
|
||||
return this._activeEffects.includes(effect);
|
||||
}
|
||||
|
||||
// Removes an effect if it is active on this vessel
|
||||
// Removes an effect if it is active on this entity
|
||||
removeEffect(effect: string): void {
|
||||
const idx = this._activeEffects.findIndex((e) => e === effect);
|
||||
|
10
Player.ts
10
Player.ts
@ -3,13 +3,13 @@
|
||||
* goes on.
|
||||
*/
|
||||
|
||||
import { Effect, Item, VesselProperties } from "./types.ts";
|
||||
import Vessel from "./Vessel.ts";
|
||||
import { Effect, Item, EntityProperties } from "./types.ts";
|
||||
import Entity from "./Entity.ts";
|
||||
|
||||
// The Player is a type of "Vessel" which is a generic object that can have effects
|
||||
// The Player is a type of "entity" which is a generic object that can have effects
|
||||
// and items.
|
||||
export default class Player extends Vessel<VesselProperties> {
|
||||
constructor(items: Item[] = [], _effects: Effect<VesselProperties>[] = []) {
|
||||
export default class Player extends Entity<EntityProperties> {
|
||||
constructor(items: Item[] = [], _effects: Effect<EntityProperties>[] = []) {
|
||||
super({ items }, { effects: [] });
|
||||
}
|
||||
|
||||
|
78
Scene.ts
78
Scene.ts
@ -3,14 +3,55 @@
|
||||
*/
|
||||
|
||||
import type { Exit, StoryScene, Item, SceneProperties } from "./types.ts";
|
||||
import Vessel from "./Vessel.ts";
|
||||
import Entity from "./Entity.ts";
|
||||
|
||||
// The Scene is a type of "Vessel" which is a generic object that can have effects
|
||||
// The Scene is a type of "Entity" which is a generic object that can have effects
|
||||
// and items.
|
||||
export default class Scene extends Vessel<SceneProperties> {
|
||||
constructor(gameData: StoryScene<SceneProperties>) {
|
||||
// TODO: [] is a placeholder for scene effects.
|
||||
super(gameData.properties, gameData.conditions);
|
||||
export default class Scene extends Entity<SceneProperties> {
|
||||
protected _map: StoryScene<SceneProperties>[];
|
||||
protected _identifier: string;
|
||||
|
||||
constructor(identifier: string, map: StoryScene<SceneProperties>[]) {
|
||||
const scene = Scene.getScene(identifier, map);
|
||||
|
||||
if (!scene) {
|
||||
throw new Error("cannot find scene!");
|
||||
}
|
||||
|
||||
const { properties, conditions } = scene;
|
||||
|
||||
super(properties, conditions);
|
||||
|
||||
this._identifier = identifier;
|
||||
this._map = map;
|
||||
}
|
||||
|
||||
changeScene(identifier: string) {
|
||||
const scene = Scene.getScene(identifier, this._map);
|
||||
|
||||
if (scene) {
|
||||
const { conditions, properties } = scene;
|
||||
|
||||
this.properties = properties;
|
||||
this.conditions = conditions;
|
||||
}
|
||||
}
|
||||
|
||||
// get removes an items from the scene and returns it
|
||||
get(target: string): Item | null {
|
||||
const { items = [] } = this._properties;
|
||||
const idx = items.findIndex(({ name }) => name === target);
|
||||
|
||||
// if we found an index for the given item
|
||||
if (idx >= 0) {
|
||||
const item = items[idx];
|
||||
items.splice(idx, 1);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
// if an item wasn't found and returned, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
// look returns a string that describes the scene and the items in it
|
||||
@ -41,23 +82,6 @@ export default class Scene extends Vessel<SceneProperties> {
|
||||
return fullDescription;
|
||||
}
|
||||
|
||||
// get removes an items from the scene and returns it
|
||||
get(target: string): Item | null {
|
||||
const { items = [] } = this._properties;
|
||||
const idx = items.findIndex(({ name }) => name === target);
|
||||
|
||||
// if we found an index for the given item
|
||||
if (idx >= 0) {
|
||||
const item = items[idx];
|
||||
items.splice(idx, 1);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
// if an item wasn't found and returned, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
// Turn exits into a string description with an "and" separating the last two items
|
||||
private static describeExits(exits: Exit[]) {
|
||||
const exitDescriptions = exits.map(({ description }) => description);
|
||||
@ -74,4 +98,12 @@ export default class Scene extends Vessel<SceneProperties> {
|
||||
return `${restExits.join(", ")} and ${lastExit}`;
|
||||
}
|
||||
}
|
||||
|
||||
// This needs to be static so that we can use it in the constructor
|
||||
private static getScene(
|
||||
identifier: string,
|
||||
map: StoryScene<SceneProperties>[]
|
||||
): StoryScene<SceneProperties> | null {
|
||||
return map.find((scene) => scene.identifier === identifier) || null;
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,12 @@ import { StoryScene, SceneProperties } from "../types.ts";
|
||||
const game: { map: StoryScene<SceneProperties>[] } = {
|
||||
map: [
|
||||
{
|
||||
identifier: "hall",
|
||||
// properties represent the base, default state for this room.
|
||||
properties: {
|
||||
// The room is called "Hall"
|
||||
name: "Hall",
|
||||
|
||||
// This is what the user will see if the look while in the room.
|
||||
description: "It's very dark",
|
||||
// This array lists all the items that are in the room.
|
||||
@ -63,12 +67,12 @@ const game: { map: StoryScene<SceneProperties>[] } = {
|
||||
{
|
||||
description: "a closet to the north",
|
||||
direction: "north",
|
||||
scene: "Hall Closet",
|
||||
scene: "hall closet",
|
||||
},
|
||||
{
|
||||
description: "a dining room to the south",
|
||||
direction: "south",
|
||||
scene: "Dining Room",
|
||||
scene: "dining room",
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -101,6 +105,22 @@ const game: { map: StoryScene<SceneProperties>[] } = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
identifier: "hall closet",
|
||||
properties: {
|
||||
name: "Hall Closet",
|
||||
description:
|
||||
"It's a closet. You feel a little silly just standing in it.",
|
||||
items: [
|
||||
{
|
||||
name: "raincoat",
|
||||
},
|
||||
],
|
||||
},
|
||||
conditions: {
|
||||
effects: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
2
main.ts
2
main.ts
@ -6,7 +6,7 @@ import game from "./data/rooms.ts";
|
||||
|
||||
async function main() {
|
||||
const user = new User(); // for communication with the user
|
||||
const scene = new Scene(game.map[0]); // the room that player is in.
|
||||
const scene = new Scene("hall", game.map); // the room that player is in.
|
||||
const player = new Player(); // the players current state
|
||||
let running = true; // running flag for the game loop. Stops on false.
|
||||
let statement = ""; // holds a statement for the user.
|
||||
|
@ -15,6 +15,7 @@ import type { ApplyEffectArgs, ItemAction } from "../types.ts";
|
||||
const ITEM_MISSING = "I can't find that.";
|
||||
const ITEM_UNUSABLE = "I don't know how to use that.";
|
||||
const ITEM_ALREADY_USED = "I already did that.";
|
||||
const DIRECTION_INACCESSIBLE = "I can't go that way.";
|
||||
|
||||
// look is what happens when a player uses the "look" verb. It gets a description of the
|
||||
// scene.
|
||||
@ -52,6 +53,34 @@ export function checkInventory(player: Player): string {
|
||||
return player.look();
|
||||
}
|
||||
|
||||
// move changes the scene around the player
|
||||
export function move(_player: Player, scene: Scene, target?: string) {
|
||||
if (!target) {
|
||||
return "Where do you want me to go?";
|
||||
}
|
||||
|
||||
const { exits } = scene.properties;
|
||||
|
||||
if (!exits) {
|
||||
return DIRECTION_INACCESSIBLE;
|
||||
}
|
||||
|
||||
const exit = exits.find(
|
||||
({ direction }) => direction === target.toLocaleLowerCase()
|
||||
);
|
||||
|
||||
if (!exit) {
|
||||
return DIRECTION_INACCESSIBLE;
|
||||
}
|
||||
|
||||
const { scene: identifier } = exit;
|
||||
scene.changeScene(identifier);
|
||||
|
||||
const { description, name } = scene.properties;
|
||||
|
||||
return `${name}\n${description}`;
|
||||
}
|
||||
|
||||
// use is what happens when a player uses the "use" verb. Different items do different
|
||||
// things when they are used, so the function needs to figure out what kind of usage an
|
||||
// item is written for, and then execute that logic with whatever arguments the
|
||||
@ -75,7 +104,7 @@ export function use(player: Player, scene: Scene, target?: string): string {
|
||||
return ITEM_MISSING;
|
||||
}
|
||||
|
||||
const itemAction = item?.actions["use"];
|
||||
const itemAction = item?.actions?.["use"];
|
||||
|
||||
// The item doesn't have action defined for "use", so it cannot be used.
|
||||
if (itemAction === undefined) {
|
||||
|
@ -24,6 +24,15 @@ const look: ActionTerm = {
|
||||
precedesConstants: [],
|
||||
};
|
||||
|
||||
const move: ActionTerm = {
|
||||
action: actions.move,
|
||||
canPrecedeVariable: true,
|
||||
category: "action",
|
||||
constant: "go",
|
||||
precedesCategories: [],
|
||||
precedesConstants: [],
|
||||
};
|
||||
|
||||
const take: ActionTerm = {
|
||||
action: actions.pickUpItem,
|
||||
canPrecedeVariable: true,
|
||||
@ -45,6 +54,7 @@ const use: ActionTerm = {
|
||||
export const terms = {
|
||||
[inventory.constant]: inventory,
|
||||
[look.constant]: look,
|
||||
[move.constant]: move,
|
||||
[take.constant]: take,
|
||||
[use.constant]: use,
|
||||
};
|
||||
|
18
types.ts
18
types.ts
@ -23,7 +23,7 @@ export interface Args {
|
||||
|
||||
// Conditions contains story elements that can be conditionally applied to the scene or
|
||||
// player
|
||||
export interface Conditions<T extends VesselProperties> {
|
||||
export interface Conditions<T extends EntityProperties> {
|
||||
effects: Effect<T>[];
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ export type Direction =
|
||||
| "down";
|
||||
|
||||
// Effect represents some effect that may be applied to a scene or player
|
||||
export interface Effect<T extends VesselProperties> {
|
||||
export interface Effect<T extends EntityProperties> {
|
||||
name: string;
|
||||
properties: T;
|
||||
source: "player" | "scene"; // where the effect is applied
|
||||
@ -57,7 +57,7 @@ export interface Exit {
|
||||
// Item represents some item either in the scene or in the player's inventory
|
||||
export interface Item {
|
||||
name: string;
|
||||
actions: { [name: string]: ItemAction };
|
||||
actions?: { [name: string]: ItemAction };
|
||||
}
|
||||
|
||||
// ItemActionType is a union of all the types of action that can be used
|
||||
@ -69,20 +69,22 @@ export interface ItemAction {
|
||||
args: ActionArgs;
|
||||
}
|
||||
|
||||
// SceneProperties are the properties (in addition to the VesselProperties) that are
|
||||
// SceneProperties are the properties (in addition to the EntityProperties) that are
|
||||
// needed by the scene
|
||||
export interface SceneProperties extends VesselProperties {
|
||||
export interface SceneProperties extends EntityProperties {
|
||||
description?: string;
|
||||
exits?: Exit[];
|
||||
name?: string;
|
||||
}
|
||||
|
||||
// StoryScene holds both the conditions and properties for a scene.
|
||||
export interface StoryScene<T extends VesselProperties> {
|
||||
export interface StoryScene<T extends EntityProperties> {
|
||||
identifier: string;
|
||||
conditions: Conditions<T>;
|
||||
properties: SceneProperties;
|
||||
}
|
||||
|
||||
// VesselProperties is a base interface for the properties that a user or scene might have
|
||||
export interface VesselProperties {
|
||||
// EntityProperties is a base interface for the properties that a user or scene might have
|
||||
export interface EntityProperties {
|
||||
items?: Item[];
|
||||
}
|
||||
|
Reference in New Issue
Block a user