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.
|
* 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 _conditions: Conditions<T>;
|
||||||
protected _properties: T;
|
protected _properties: T;
|
||||||
protected _activeEffects: string[];
|
protected _activeEffects: string[];
|
||||||
@ -24,6 +24,18 @@ export default class Vessel<T extends VesselProperties> {
|
|||||||
return this._activeEffects;
|
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 {
|
addEffect(effect: string): void {
|
||||||
this.activeEffects.push(effect);
|
this.activeEffects.push(effect);
|
||||||
}
|
}
|
||||||
@ -74,12 +86,12 @@ export default class Vessel<T extends VesselProperties> {
|
|||||||
return description;
|
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 {
|
hasActiveEffect(effect: string): boolean {
|
||||||
return this._activeEffects.includes(effect);
|
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 {
|
removeEffect(effect: string): void {
|
||||||
const idx = this._activeEffects.findIndex((e) => e === effect);
|
const idx = this._activeEffects.findIndex((e) => e === effect);
|
||||||
|
|
10
Player.ts
10
Player.ts
@ -3,13 +3,13 @@
|
|||||||
* goes on.
|
* goes on.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Effect, Item, VesselProperties } from "./types.ts";
|
import { Effect, Item, EntityProperties } from "./types.ts";
|
||||||
import Vessel from "./Vessel.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.
|
// and items.
|
||||||
export default class Player extends Vessel<VesselProperties> {
|
export default class Player extends Entity<EntityProperties> {
|
||||||
constructor(items: Item[] = [], _effects: Effect<VesselProperties>[] = []) {
|
constructor(items: Item[] = [], _effects: Effect<EntityProperties>[] = []) {
|
||||||
super({ items }, { effects: [] });
|
super({ items }, { effects: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
78
Scene.ts
78
Scene.ts
@ -3,14 +3,55 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Exit, StoryScene, Item, SceneProperties } from "./types.ts";
|
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.
|
// and items.
|
||||||
export default class Scene extends Vessel<SceneProperties> {
|
export default class Scene extends Entity<SceneProperties> {
|
||||||
constructor(gameData: StoryScene<SceneProperties>) {
|
protected _map: StoryScene<SceneProperties>[];
|
||||||
// TODO: [] is a placeholder for scene effects.
|
protected _identifier: string;
|
||||||
super(gameData.properties, gameData.conditions);
|
|
||||||
|
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
|
// 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;
|
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
|
// Turn exits into a string description with an "and" separating the last two items
|
||||||
private static describeExits(exits: Exit[]) {
|
private static describeExits(exits: Exit[]) {
|
||||||
const exitDescriptions = exits.map(({ description }) => description);
|
const exitDescriptions = exits.map(({ description }) => description);
|
||||||
@ -74,4 +98,12 @@ export default class Scene extends Vessel<SceneProperties> {
|
|||||||
return `${restExits.join(", ")} and ${lastExit}`;
|
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>[] } = {
|
const game: { map: StoryScene<SceneProperties>[] } = {
|
||||||
map: [
|
map: [
|
||||||
{
|
{
|
||||||
|
identifier: "hall",
|
||||||
// properties represent the base, default state for this room.
|
// properties represent the base, default state for this room.
|
||||||
properties: {
|
properties: {
|
||||||
|
// The room is called "Hall"
|
||||||
|
name: "Hall",
|
||||||
|
|
||||||
// This is what the user will see if the look while in the room.
|
// This is what the user will see if the look while in the room.
|
||||||
description: "It's very dark",
|
description: "It's very dark",
|
||||||
// This array lists all the items that are in the room.
|
// 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",
|
description: "a closet to the north",
|
||||||
direction: "north",
|
direction: "north",
|
||||||
scene: "Hall Closet",
|
scene: "hall closet",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "a dining room to the south",
|
description: "a dining room to the south",
|
||||||
direction: "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() {
|
async function main() {
|
||||||
const user = new User(); // for communication with the user
|
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
|
const player = new Player(); // the players current state
|
||||||
let running = true; // running flag for the game loop. Stops on false.
|
let running = true; // running flag for the game loop. Stops on false.
|
||||||
let statement = ""; // holds a statement for the user.
|
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_MISSING = "I can't find that.";
|
||||||
const ITEM_UNUSABLE = "I don't know how to use that.";
|
const ITEM_UNUSABLE = "I don't know how to use that.";
|
||||||
const ITEM_ALREADY_USED = "I already did 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
|
// look is what happens when a player uses the "look" verb. It gets a description of the
|
||||||
// scene.
|
// scene.
|
||||||
@ -52,6 +53,34 @@ export function checkInventory(player: Player): string {
|
|||||||
return player.look();
|
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
|
// 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
|
// 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
|
// 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;
|
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.
|
// The item doesn't have action defined for "use", so it cannot be used.
|
||||||
if (itemAction === undefined) {
|
if (itemAction === undefined) {
|
||||||
|
@ -24,6 +24,15 @@ const look: ActionTerm = {
|
|||||||
precedesConstants: [],
|
precedesConstants: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const move: ActionTerm = {
|
||||||
|
action: actions.move,
|
||||||
|
canPrecedeVariable: true,
|
||||||
|
category: "action",
|
||||||
|
constant: "go",
|
||||||
|
precedesCategories: [],
|
||||||
|
precedesConstants: [],
|
||||||
|
};
|
||||||
|
|
||||||
const take: ActionTerm = {
|
const take: ActionTerm = {
|
||||||
action: actions.pickUpItem,
|
action: actions.pickUpItem,
|
||||||
canPrecedeVariable: true,
|
canPrecedeVariable: true,
|
||||||
@ -45,6 +54,7 @@ const use: ActionTerm = {
|
|||||||
export const terms = {
|
export const terms = {
|
||||||
[inventory.constant]: inventory,
|
[inventory.constant]: inventory,
|
||||||
[look.constant]: look,
|
[look.constant]: look,
|
||||||
|
[move.constant]: move,
|
||||||
[take.constant]: take,
|
[take.constant]: take,
|
||||||
[use.constant]: use,
|
[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
|
// Conditions contains story elements that can be conditionally applied to the scene or
|
||||||
// player
|
// player
|
||||||
export interface Conditions<T extends VesselProperties> {
|
export interface Conditions<T extends EntityProperties> {
|
||||||
effects: Effect<T>[];
|
effects: Effect<T>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export type Direction =
|
|||||||
| "down";
|
| "down";
|
||||||
|
|
||||||
// Effect represents some effect that may be applied to a scene or player
|
// 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;
|
name: string;
|
||||||
properties: T;
|
properties: T;
|
||||||
source: "player" | "scene"; // where the effect is applied
|
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
|
// Item represents some item either in the scene or in the player's inventory
|
||||||
export interface Item {
|
export interface Item {
|
||||||
name: string;
|
name: string;
|
||||||
actions: { [name: string]: ItemAction };
|
actions?: { [name: string]: ItemAction };
|
||||||
}
|
}
|
||||||
|
|
||||||
// ItemActionType is a union of all the types of action that can be used
|
// ItemActionType is a union of all the types of action that can be used
|
||||||
@ -69,20 +69,22 @@ export interface ItemAction {
|
|||||||
args: ActionArgs;
|
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
|
// needed by the scene
|
||||||
export interface SceneProperties extends VesselProperties {
|
export interface SceneProperties extends EntityProperties {
|
||||||
description?: string;
|
description?: string;
|
||||||
exits?: Exit[];
|
exits?: Exit[];
|
||||||
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoryScene holds both the conditions and properties for a scene.
|
// 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>;
|
conditions: Conditions<T>;
|
||||||
properties: SceneProperties;
|
properties: SceneProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VesselProperties is a base interface for the properties that a user or scene might have
|
// EntityProperties is a base interface for the properties that a user or scene might have
|
||||||
export interface VesselProperties {
|
export interface EntityProperties {
|
||||||
items?: Item[];
|
items?: Item[];
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user