From ad6420e3a997f5c36a9a0421b94ab99d0e972ec5 Mon Sep 17 00:00:00 2001 From: Talon Date: Fri, 22 Mar 2024 18:23:46 +0100 Subject: [PATCH] Add dynamic item support --- src/engine/commands/drop.js | 4 +- src/engine/commands/inventory.js | 28 ++++++------- src/engine/index.js | 2 +- src/engine/item.js | 30 ++++++++++++++ src/engine/player.js | 37 ++++++++--------- src/engine/serialization.js | 17 +++++++- src/game/items/cup.js | 26 +++++++----- src/game/rooms/lair/start.js | 69 ++++++++++++++++---------------- 8 files changed, 134 insertions(+), 79 deletions(-) diff --git a/src/engine/commands/drop.js b/src/engine/commands/drop.js index 9465bc9..253d647 100644 --- a/src/engine/commands/drop.js +++ b/src/engine/commands/drop.js @@ -11,8 +11,8 @@ export default function DropCommand(args, context) { if (!item) { context.print(`You're not carrying a ${args[1]}`); } else { - context.player.removeItem(item.id); - room.addItem(item.id); + context.player.removeItem(item.getID()); + room.addItem(item.getID()); context.print(`You set ${item.name} down on the floor.`); item.onDrop(); } diff --git a/src/engine/commands/inventory.js b/src/engine/commands/inventory.js index 89f2356..48329d9 100644 --- a/src/engine/commands/inventory.js +++ b/src/engine/commands/inventory.js @@ -1,15 +1,15 @@ -export default function InventoryCommand(args, context) { - const items = context.player.getInventory(); - if (items.length < 1) return context.print(`You're not carrying anything.`); - let itemDescription = `You are carrying `; - items.forEach((item, index) => { - if (index < items.length - 2) { - itemDescription += `${item.name}, `; - } else if (index < items.length - 1) { - itemDescription += `${item.name} and `; - } else { - itemDescription += item.name - } - }); - context.print(itemDescription + "."); +export default function InventoryCommand(args, context) { + const items = context.player.getInventory(); + if (items.length < 1) return context.print(`You're not carrying anything.`); + let itemDescription = `You are carrying `; + items.forEach((item, index) => { + if (index < items.length - 2) { + itemDescription += `${item.name}, `; + } else if (index < items.length - 1) { + itemDescription += `${item.name} and `; + } else { + itemDescription += item.name + } + }); + context.print(itemDescription + "."); } \ No newline at end of file diff --git a/src/engine/index.js b/src/engine/index.js index ecff6ed..dff3099 100644 --- a/src/engine/index.js +++ b/src/engine/index.js @@ -121,7 +121,7 @@ export default class Game { } getItem(id) { - return this.items.find((item) => item.id == id); + return this.items.find((item) => item.getID() == id); } wait(ms) { diff --git a/src/engine/item.js b/src/engine/item.js index a67301a..93dbe80 100644 --- a/src/engine/item.js +++ b/src/engine/item.js @@ -14,6 +14,8 @@ export default class Item { this.dropCallback = null; this.tickCallback = null; this.context = null; + this.dynamic = false; + this.dynamicID = null; } async onUse() { @@ -54,4 +56,32 @@ export default class Item { getState(key) { return this.state.get(key); } + + clone() { + const clonedItem = new Item(); + clonedItem.id = this.id; + clonedItem.name = this.name; + clonedItem.description = this.description; + clonedItem.type = this.type; + clonedItem.state = new State(); + clonedItem.usable = this.usable; + clonedItem.takeable = this.takeable; + if (this.useCallback) clonedItem.useCallback = this.useCallback.bind(clonedItem); + if (this.takeCallback) clonedItem.takeCallback = this.takeCallback.bind(clonedItem); + if (this.dropCallback) clonedItem.dropCallback = this.dropCallback.bind(clonedItem); + if (this.tickCallback) clonedItem.tickCallback = this.tickCallback.bind(clonedItem); + clonedItem.context = this.context; + clonedItem.dynamic = true; + clonedItem.dynamicID = generateUniqueID(); + this.context.items.push(clonedItem); + return clonedItem; + } + + getID() { + return this.dynamic ? this.dynamicID : this.id; + } +} + +function generateUniqueID() { + return `dynamic_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } \ No newline at end of file diff --git a/src/engine/player.js b/src/engine/player.js index b500abc..4c0fa18 100644 --- a/src/engine/player.js +++ b/src/engine/player.js @@ -1,19 +1,20 @@ -export default class Player { - constructor() { - this.inventory = []; - this.currentRoom = "start"; - this.context = null; - } - - addItem(id) { - this.inventory.push(id); - } - - removeItem(id) { - this.inventory = this.inventory.filter((item) => item != id); - } - - getInventory() { - return this.inventory.map((item) => this.context.getItem(item)); - } +export default class Player { + constructor() { + this.inventory = []; + this.currentRoom = "start"; + this.context = null; + } + + addItem(id) { + console.log(`Adding item ${id} to player inv`); + this.inventory.push(id); + } + + removeItem(id) { + this.inventory = this.inventory.filter((item) => item != id); + } + + getInventory() { + return this.inventory.map((item) => this.context.getItem(item)); + } } \ No newline at end of file diff --git a/src/engine/serialization.js b/src/engine/serialization.js index 07e657d..2b57c63 100644 --- a/src/engine/serialization.js +++ b/src/engine/serialization.js @@ -6,6 +6,7 @@ export default class Serialization { save() { const saveobj = { state: this.context.state.serialize(), + dynamics: this.serializeDynamicItems(), itemLocations: this.serializeItemLocations(), itemStates: this.serializeItemStates(), player: { @@ -24,6 +25,7 @@ export default class Serialization { load() { const loadobj = JSON.parse(localStorage.getItem("save")); this.context.state.deserialize(loadobj.state); + this.deserializeDynamicItems(loadobj.dynamics); this.deserializeItemLocations(loadobj.itemLocations); this.deserializeItemStates(loadobj.itemStates); this.deserializePlayer(loadobj.player); @@ -61,7 +63,20 @@ export default class Serialization { serializeItemStates() { return this.context.items.map((item) => { - return [item.id, item.state.serialize()] + return [item.getID(), item.state.serialize()] }); } + + serializeDynamicItems() { + return this.context.items.filter((item) => item.dynamic).map((item) => [item.id, item.dynamicID]); + } + + deserializeDynamicItems(items) { + items.forEach((item) => { + const base = this.context.getItem(item[0]); + const cloned = base.clone(); + cloned.dynamicID = item[1]; + this.context.items.push(cloned); + }) + } } \ No newline at end of file diff --git a/src/game/items/cup.js b/src/game/items/cup.js index a291aa6..59e0bb3 100644 --- a/src/game/items/cup.js +++ b/src/game/items/cup.js @@ -1,10 +1,18 @@ -import ItemBuilder from "../../engine/builders/item"; -import Item from "../../engine/item"; - -export default new ItemBuilder() -.withID("cup") -.withName("a cup") -.withDescription("A standard coffee cup") -.isTakeable(true) -.isUsable(false) +import ItemBuilder from "../../engine/builders/item"; +import Item from "../../engine/item"; + +export default new ItemBuilder() +.withID("cup") +.withName("a cup") +.withDescription("A standard coffee cup") +.isTakeable(true) +.isUsable(true) +.withUseCallback((context) => { + const item = context.getItem("cup"); + const cloned = item.clone(); + cloned.name = 'A cloned cup'; + console.log(cloned); + context.player.addItem(cloned.dynamicID); + context.print("You touch the cup and a new one somehow appears in your inventory."); +}) .create(); \ No newline at end of file diff --git a/src/game/rooms/lair/start.js b/src/game/rooms/lair/start.js index c3d9506..2f73143 100644 --- a/src/game/rooms/lair/start.js +++ b/src/game/rooms/lair/start.js @@ -1,35 +1,36 @@ -import RoomBuilder from '../../../engine/builders/room'; - -export default new RoomBuilder() -.withID("start") -.withTitle("A small spherical alcove") -.withFirstDescription( -`You find yourself in a small, spherical alcove. It feels cold and dark, save from a dim glow which seems to be eluminating the area from the north. -The surface appears to be unnaturally smooth, as if melted away using acidic means. It's warm to the touch.` -) -.withDescription( -`A spherical alcove. The smooth surface appears to be melted away using acidic means. There's a dim glow shining in from the north.` -) -.withExit("north", "tunnel1") -.withEnterCallback(async function(context) { - if (context.state.get("start.awoken")) return; - const { output, wait } = context; - context.enableCommandInput(false); - await context.tell([ - "You slowly wake up.", - "you're not sure if you were ever conscious about waking up, but right now, you're clearly aware that you were previously asleep.", - "In fact, a lot of your thoughts seem foreign to you.", - "You're not sure how to feel about this.", - "God the headache...", - "OK, time to think about this.", - "Huh, something else you never did before.", - "Where are you?", - "You reach up and touch your head.", - "OK, that seems to be in order. Your antennae are still there, your mouth parts seem in tact...", - "Hmm. All this is strange.", - "No use sitting around. You get up and slowly examine your surroundings." - ], 3000); - context.enableCommandInput(true); - context.state.set("start.awoken", true); -}) +import RoomBuilder from '../../../engine/builders/room'; + +export default new RoomBuilder() +.withID("start") +.withTitle("A small spherical alcove") +.withFirstDescription( +`You find yourself in a small, spherical alcove. It feels cold and dark, save from a dim glow which seems to be eluminating the area from the north. +The surface appears to be unnaturally smooth, as if melted away using acidic means. It's warm to the touch.` +) +.withDescription( +`A spherical alcove. The smooth surface appears to be melted away using acidic means. There's a dim glow shining in from the north.` +) +.withExit("north", "tunnel1") +.withEnterCallback(async function(context) { + if (context.state.get("start.awoken")) return; + const { output, wait } = context; + context.enableCommandInput(false); + // await context.tell([ + // "You slowly wake up.", + // "you're not sure if you were ever conscious about waking up, but right now, you're clearly aware that you were previously asleep.", + // "In fact, a lot of your thoughts seem foreign to you.", + // "You're not sure how to feel about this.", + // "God the headache...", + // "OK, time to think about this.", + // "Huh, something else you never did before.", + // "Where are you?", + // "You reach up and touch your head.", + // "OK, that seems to be in order. Your antennae are still there, your mouth parts seem in tact...", + // "Hmm. All this is strange.", + // "No use sitting around. You get up and slowly examine your surroundings." + // ], 3000); + context.enableCommandInput(true); + context.state.set("start.awoken", true); +}) +.withItem("cup") .create(); \ No newline at end of file