Add applyEffect
This commit is contained in:
@ -2,7 +2,7 @@ import type Player from "./Player.ts";
|
||||
import type Scene from "./Scene.ts";
|
||||
import { terms } from "./terms/terms.ts";
|
||||
import type {
|
||||
Action,
|
||||
ActionTerm,
|
||||
ActionFn,
|
||||
Category,
|
||||
Constant,
|
||||
@ -87,7 +87,7 @@ export default class Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
private processTerm(term: Action | Term): void {
|
||||
private processTerm(term: ActionTerm | Term): void {
|
||||
if ("action" in term) {
|
||||
this.addAction(term);
|
||||
}
|
||||
@ -105,7 +105,7 @@ export default class Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
private addAction(term: Action) {
|
||||
private addAction(term: ActionTerm) {
|
||||
// make sure we expected an action and process it if it was
|
||||
if (this.expectedCategories.includes("action")) {
|
||||
this.action = term.action;
|
||||
|
@ -10,10 +10,6 @@ export default class Player extends Vessel<VesselProperties> {
|
||||
this.#effects = effects;
|
||||
}
|
||||
|
||||
get effects() {
|
||||
return this.#effects;
|
||||
}
|
||||
|
||||
look(): string {
|
||||
const description = super.description(this._properties.items || []);
|
||||
|
||||
@ -28,4 +24,8 @@ export default class Player extends Vessel<VesselProperties> {
|
||||
if (!this._properties.items) this._properties.items = [];
|
||||
this._properties.items.push(item);
|
||||
}
|
||||
|
||||
get inventory(): Item[] | null {
|
||||
return this._properties.items || null;
|
||||
}
|
||||
}
|
||||
|
10
Scene.ts
10
Scene.ts
@ -2,12 +2,9 @@ import { GameData, Item, SceneProperties } from "./types.ts";
|
||||
import Vessel from "./Vessel.ts";
|
||||
|
||||
export default class Scene extends Vessel<SceneProperties> {
|
||||
#isViewed: boolean;
|
||||
|
||||
constructor(gameData: GameData<SceneProperties>) {
|
||||
// TODO: [] is a placeholder for scene effects.
|
||||
super(gameData.properties, gameData.conditions);
|
||||
this.#isViewed = false;
|
||||
}
|
||||
|
||||
look(activePlayerEffects: string[], activeSceneEffects: string[]): string {
|
||||
@ -16,14 +13,11 @@ export default class Scene extends Vessel<SceneProperties> {
|
||||
activeSceneEffects
|
||||
);
|
||||
const itemsDescription = super.description(properties.items || []);
|
||||
const { description, shortDescription } = properties;
|
||||
const { description } = properties;
|
||||
|
||||
let fullDescription = description || "Nothing to see here...";
|
||||
|
||||
if (this.#isViewed && shortDescription) {
|
||||
fullDescription = shortDescription;
|
||||
} else if (description) {
|
||||
this.#isViewed = true;
|
||||
if (description) {
|
||||
fullDescription = description;
|
||||
}
|
||||
|
||||
|
52
Vessel.ts
52
Vessel.ts
@ -19,24 +19,8 @@ export default class Vessel<T extends VesselProperties> {
|
||||
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;
|
||||
addEffect(effect: string): void {
|
||||
this.activeEffects.push(effect);
|
||||
}
|
||||
|
||||
// Player effects should be applied first, then scene effects should be applied.
|
||||
@ -62,4 +46,36 @@ export default class Vessel<T extends VesselProperties> {
|
||||
|
||||
return appliedProperties;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
hasActiveEffect(effect: string): boolean {
|
||||
return this._activeEffects.includes(effect);
|
||||
}
|
||||
|
||||
removeEffect(effect: string): void {
|
||||
const idx = this._activeEffects.findIndex((e) => e === effect);
|
||||
|
||||
if (idx >= 0) {
|
||||
this._activeEffects.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,17 +3,32 @@ import { GameData, SceneProperties } from "../types.ts";
|
||||
export const hall: GameData<SceneProperties> = {
|
||||
properties: {
|
||||
description: "It's very dark",
|
||||
items: [{ name: "flashlight" }],
|
||||
items: [
|
||||
{
|
||||
name: "flashlight",
|
||||
actions: {
|
||||
use: {
|
||||
args: {
|
||||
effect: "lit-by-flashlight",
|
||||
applyTo: "scene",
|
||||
result: "The flashlight is on.",
|
||||
reverseResult: "The flashlight is off.",
|
||||
reversible: true,
|
||||
},
|
||||
type: "applyEffect",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
conditions: {
|
||||
effects: [
|
||||
{
|
||||
name: "lights-on",
|
||||
name: "lit-by-flashlight",
|
||||
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",
|
||||
},
|
||||
|
@ -28,7 +28,7 @@ export interface Term {
|
||||
canPrecedeVariable: boolean;
|
||||
}
|
||||
|
||||
export interface Action extends Term {
|
||||
export interface ActionTerm extends Term {
|
||||
action: ActionFn;
|
||||
category: "action";
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
import Scene from "../Scene.ts";
|
||||
import Player from "../Player.ts";
|
||||
import type Scene from "../Scene.ts";
|
||||
import type Player from "../Player.ts";
|
||||
import type { ApplyEffectArgs, ItemAction } from "../types.ts";
|
||||
|
||||
const ITEM_MISSING = "You don't have that.";
|
||||
const ITEM_UNUSABLE = "I don't know how to use that.";
|
||||
const ITEM_ALREADY_USED = "I already did that.";
|
||||
|
||||
export function look(player: Player, scene: Scene): string {
|
||||
return scene.look(player.activeEffects, scene.activeEffects);
|
||||
@ -23,6 +28,66 @@ export function pickUpItem(
|
||||
return "Taken.";
|
||||
}
|
||||
|
||||
export function checkInventory(player: Player) {
|
||||
export function checkInventory(player: Player): string {
|
||||
return player.look();
|
||||
}
|
||||
|
||||
export function use(player: Player, scene: Scene, target?: string): string {
|
||||
if (!target) {
|
||||
return "What do you want to use?";
|
||||
}
|
||||
|
||||
const { inventory } = player;
|
||||
|
||||
if (inventory === null) {
|
||||
return ITEM_MISSING;
|
||||
}
|
||||
|
||||
const item = inventory.find((i) => i.name === target);
|
||||
|
||||
if (item === undefined) {
|
||||
return ITEM_MISSING;
|
||||
}
|
||||
|
||||
const itemAction = item?.actions["use"];
|
||||
|
||||
if (itemAction === undefined) {
|
||||
return ITEM_UNUSABLE;
|
||||
}
|
||||
|
||||
return executeItemAction(player, scene, itemAction);
|
||||
}
|
||||
|
||||
function executeItemAction(
|
||||
player: Player,
|
||||
scene: Scene,
|
||||
{ args, type }: ItemAction
|
||||
): string {
|
||||
switch (type) {
|
||||
case "applyEffect":
|
||||
return applyEffect(player, scene, args as ApplyEffectArgs);
|
||||
default:
|
||||
return ITEM_UNUSABLE;
|
||||
}
|
||||
}
|
||||
|
||||
function applyEffect(
|
||||
player: Player,
|
||||
scene: Scene,
|
||||
{ applyTo, effect, reverseResult, reversible, result }: ApplyEffectArgs
|
||||
): string {
|
||||
const target = applyTo === "player" ? player : scene;
|
||||
const isApplied = target.hasActiveEffect(effect);
|
||||
|
||||
if (!reversible && isApplied) {
|
||||
return ITEM_ALREADY_USED;
|
||||
}
|
||||
|
||||
if (isApplied) {
|
||||
target.removeEffect(effect);
|
||||
return reverseResult || result;
|
||||
} else {
|
||||
target.addEffect(effect);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as actions from "./actions.ts";
|
||||
import { Action } from "./Term.ts";
|
||||
import { ActionTerm } from "./Term.ts";
|
||||
|
||||
const inventory: Action = {
|
||||
const inventory: ActionTerm = {
|
||||
action: actions.checkInventory,
|
||||
canPrecedeVariable: false,
|
||||
category: "action",
|
||||
@ -10,7 +10,7 @@ const inventory: Action = {
|
||||
precedesConstants: [],
|
||||
};
|
||||
|
||||
const look: Action = {
|
||||
const look: ActionTerm = {
|
||||
action: actions.look,
|
||||
canPrecedeVariable: false,
|
||||
category: "action",
|
||||
@ -19,7 +19,7 @@ const look: Action = {
|
||||
precedesConstants: [],
|
||||
};
|
||||
|
||||
const take: Action = {
|
||||
const take: ActionTerm = {
|
||||
action: actions.pickUpItem,
|
||||
canPrecedeVariable: true,
|
||||
category: "action",
|
||||
@ -28,8 +28,18 @@ const take: Action = {
|
||||
precedesConstants: [],
|
||||
};
|
||||
|
||||
const use: ActionTerm = {
|
||||
action: actions.use,
|
||||
canPrecedeVariable: true,
|
||||
category: "action",
|
||||
constant: "use",
|
||||
precedesCategories: [],
|
||||
precedesConstants: [],
|
||||
};
|
||||
|
||||
export const terms = {
|
||||
[inventory.constant]: inventory,
|
||||
[look.constant]: look,
|
||||
[take.constant]: take,
|
||||
[use.constant]: use,
|
||||
};
|
||||
|
26
types.ts
26
types.ts
@ -1,7 +1,32 @@
|
||||
export * from "./data/rooms.ts";
|
||||
|
||||
export type ActionArgs = ApplyEffectArgs;
|
||||
|
||||
export type ItemActionType = "applyEffect";
|
||||
|
||||
export interface Args {
|
||||
result: string;
|
||||
}
|
||||
|
||||
export interface ItemAction {
|
||||
type: ItemActionType;
|
||||
args: ActionArgs;
|
||||
}
|
||||
|
||||
export interface ApplyEffectArgs extends Args {
|
||||
effect: string;
|
||||
applyTo: "player" | "scene";
|
||||
reversible: boolean;
|
||||
reverseResult?: string;
|
||||
}
|
||||
|
||||
export interface ApplyEffectItemAction extends ItemAction {
|
||||
type: "applyEffect";
|
||||
}
|
||||
|
||||
export interface Item {
|
||||
name: string;
|
||||
actions: { [name: string]: ItemAction };
|
||||
}
|
||||
|
||||
export interface Effect<T> {
|
||||
@ -16,7 +41,6 @@ export interface VesselProperties {
|
||||
|
||||
export interface SceneProperties extends VesselProperties {
|
||||
description?: string;
|
||||
shortDescription?: string;
|
||||
}
|
||||
|
||||
export interface Conditions<T> {
|
||||
|
Reference in New Issue
Block a user