Refactor object handling in index.html to improve room object management and interaction. Introduced a mapping system for room objects by type, streamlined sprite creation, and enhanced inventory handling with unique identifiers. Updated scenario and room JSON files to reflect new object types and connections.

This commit is contained in:
Z. Cliffe Schreuders
2025-03-27 16:22:27 +00:00
parent 4d2e7c473a
commit 6afbe0331b
3 changed files with 150 additions and 91 deletions

View File

@@ -183,6 +183,17 @@
"x":113.75128085701,
"y":36.7545412203075
},
{
"height":48,
"id":18,
"name":"safe",
"rotation":0,
"type":"",
"visible":true,
"width":48,
"x":265.75128085701,
"y":36.7545412203075
},
{
"height":48,
@@ -229,7 +240,6 @@
"y":251.312529110387
},
{
"gid":428,
"height":48,
"id":14,
"name":"pc2",

View File

@@ -1,9 +1,9 @@
{
"scenario_brief": "You are a curious traveler who stumbles upon Beckett, a ghost town shrouded in mystery. After entering the only standing building, the door slams shut, trapping you inside. A note from Mayor McFluffins warns: 'Fail to escape, and youll be turned into a llama.' Solve cryptographic puzzles and break the curse before time runs out, or grow fur and join the towns eerie fate!",
"startRoom": "room_reception",
"startRoom": "room_start",
"rooms": {
"room_reception": {
"type": "room_reception",
"room_start": {
"type": "room_office",
"connections": {
"north": "room_office"
},
@@ -30,7 +30,7 @@
"takeable": true,
"readable": true,
"observations": "Numbers are important, remember these to proceed:\n- Prime modulus (p): 23\n- Base (g): 5\n- Tim's private key (a): 3\n- Jullie's private key (b): 5\nCalculate Tim's public key, Jullie's public key, and the final shared secret key."
},
},
{
"type": "pc",
"name": "Computer",
@@ -40,7 +40,7 @@
},
{
"type": "safe",
"name": "Safe1",
"name": "Safe 1",
"takeable": false,
"locked": true,
"lockType": "password",
@@ -58,7 +58,7 @@
]
},
{
"type": "safe2",
"type": "safe",
"name": "Safe 2",
"takeable": false,
"locked": true,
@@ -77,13 +77,13 @@
]
},
{
"type": "safe3",
"name": "Safe 3",
"type": "suitcase",
"name": "Briefcase",
"takeable": false,
"locked": true,
"lockType": "password",
"requires": "19",
"observations": "A locked safe containing the key to the next room.",
"observations": "A locked briefcase containing the key to the next room.",
"contents": [
{
"type": "key",
@@ -99,7 +99,7 @@
"room_office": {
"type": "room_office",
"connections": {
"south": "room_reception",
"south": "room_start",
"north": "room_servers"
},
"locked": true,
@@ -150,13 +150,13 @@
"text": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCczVF4Oq+Njf1Olf/JNnZcSP0jbZVpdVJ+hySa7OPMSpjMsppb\nV1E8qytLIx+HfiU065I/Lhr0LhoKj+hWA3ceCUQa2GeSU+p8X5bseet6/hhrsBYV\nuT+4ajIQ8tDOi/0vrnSh+EMc912TpjAh1nEfeL65LXOwWHDf0rR8Uxv3AQIDAQAB\nAoGACiIVo/6s5im5Jzk32r2/h+a6ny2/hF1t25npsm5hKUxf1Aitw33g1Nod9eDa"
},
{
"type": "notes2",
"type": "notes",
"name": "Private Key Part 2",
"takeable": true,
"readable": true,
"observations": "8oNjLaiUnqsg2EtbaPfUVKysJ6WaFQ4BnFe6rKQH+kXDEjSOyMcQsoObO0Bcjk/3\nWpxdTH0qp71yHzg1D6h40cwSra5u/t/ZRFJI/08hBdbt8DECQQDPQwVS5hYfDfXa\ni5Rxwwp4EBZmvy8z/KXPJ+8sXfqi5pBkZTrQfWsiqCW2aRtnTUsC0b3HjRQxf2SV\n+1y9aqQpAkEAwaypvhpE7I2P1LgIPrgW2HM1wiP0oZiM9LizsDHYO/bKqSWL7hnS\n/s6NcQ5CLOyB3uxYBkDIovUSem6/Y6hXGQJBAKi/qaMAQLySEj0Y7gjdwzVT69lG",
"text": "Cfmq15ldq0cVUU62qJOFNCiyJLt36hSlaTFnZg5qlLjXbbyLO2s92BlErVkCQDaY\nH3kxGoC8HvFNtzVG21nEkEDbtdffksxhTHW8d0Hf/ZzUsq85pFqjiwd1h332ZV2b\nreyFUoltH/pXQagsCfECQFyG0RpJtc9ojIRUMOqDGQvoi8il4xM4yCiSKQAcLzuu\nqLrEVyNbKHcBf2Hn3xuEHs/DB6zCLVj/FJ7ZWONCJuU=\n-----END RSA PRIVATE KEY-----"
}
}
]
},
"room_closet": {

View File

@@ -2338,54 +2338,93 @@
if (objectsLayer && objectsLayer.objects) {
rooms[roomId].objects = {};
// Create a map of room objects by type for easy lookup
const roomObjectsByType = {};
objectsLayer.objects.forEach(obj => {
// Find matching object in scenario data
const scenarioObject = gameScenario.rooms[roomId].objects.find(
item => item.type === obj.name
);
// Check if this object should be active in the current scenario
const isActiveObject = scenarioObject !== undefined;
const sprite = this.add.sprite(
position.x + obj.x,
position.y + (obj.gid !== undefined ? obj.y - obj.height : obj.y),
obj.name
);
sprite.setOrigin(0, 0);
sprite.name = obj.name;
sprite.setInteractive({ useHandCursor: true });
sprite.setDepth(1001);
sprite.originalAlpha = 1;
sprite.active = isActiveObject;
// Store scenario data with sprite for later use
if (isActiveObject) {
sprite.scenarioData = scenarioObject;
if (!roomObjectsByType[obj.name]) {
roomObjectsByType[obj.name] = [];
}
// Initially hide all objects - they'll be shown when room is revealed
sprite.setVisible(false);
if (obj.rotation) {
sprite.setRotation(Phaser.Math.DegToRad(obj.rotation));
}
rooms[roomId].objects[obj.name] = sprite;
// Add click handler for all objects
sprite.on('pointerdown', () => {
if (isActiveObject) {
debugLog('OBJECT CLICKED', { name: obj.name }, 2);
handleObjectInteraction(sprite);
} else {
gameAlert("Nothing of note here", 'info', '', 2000);
}
});
roomObjectsByType[obj.name].push(obj);
});
// Process scenario objects first
if (gameScenario.rooms[roomId].objects) {
gameScenario.rooms[roomId].objects.forEach((scenarioObj, index) => {
const objType = scenarioObj.type;
// skip "inInventory": true,
if (scenarioObj.inInventory) {
return;
}
// Try to find a matching room object
let roomObj = null;
if (roomObjectsByType[objType] && roomObjectsByType[objType].length > 0) {
// Take the first available room object of this type
roomObj = roomObjectsByType[objType].shift();
}
let sprite;
if (roomObj) {
// Create sprite at the room object's position
sprite = this.add.sprite(
position.x + roomObj.x,
position.y + (roomObj.gid !== undefined ? roomObj.y - roomObj.height : roomObj.y),
objType
);
if (roomObj.rotation) {
sprite.setRotation(Phaser.Math.DegToRad(roomObj.rotation));
}
// Create a unique key using the room object's ID
sprite.objectId = `${objType}_${roomObj.id || index}`;
} else {
// No matching room object, create at random position
// Assuming room size is 10x9 tiles of 48px each
const roomWidth = 10 * 48;
const roomHeight = 9 * 48;
// Add some padding from the edges (2 tile width)
const padding = 48*2;
const randomX = position.x + padding + Math.random() * (roomWidth - padding * 2);
const randomY = position.y + padding + Math.random() * (roomHeight - padding * 2);
sprite = this.add.sprite(randomX, randomY, objType);
console.log(`Created object ${objType} at random position (${randomX}, ${randomY})`);
}
// SIMPLIFIED NAMING APPROACH
// Use a consistent format: roomId_type_index
const objectId = `${roomId}_${objType}_${index}`;
// Set common properties
sprite.setOrigin(0, 0);
sprite.name = objType; // Keep name as the object type for texture loading
sprite.objectId = objectId; // Use our simplified ID format
sprite.setInteractive({ useHandCursor: true });
sprite.setDepth(1001);
sprite.originalAlpha = 1;
sprite.active = true;
// Store scenario data with sprite
sprite.scenarioData = scenarioObj;
// Initially hide the object
sprite.setVisible(false);
// Store the object
rooms[roomId].objects[objectId] = sprite;
// Add click handler
sprite.on('pointerdown', () => {
debugLog('OBJECT CLICKED', { name: objType, id: objectId }, 2);
handleObjectInteraction(sprite);
});
});
}
}
} catch (error) {
console.error(`Error creating room ${roomId}:`, error);
console.error('Error details:', error.stack);
@@ -2985,7 +3024,8 @@
// Only log detailed object interactions at debug level 2+
debugLog('OBJECT INTERACTION', {
name: sprite.name,
type: sprite.scenarioData?.type
id: sprite.objectId,
scenarioData: sprite.scenarioData
}, 2);
if (!sprite || !sprite.scenarioData) {
@@ -2993,6 +3033,9 @@
return;
}
// Log the full object data to help debug
console.log("Interacting with object:", sprite.scenarioData);
// Handle the Crypto Workstation
if (sprite.scenarioData.type === "workstation") {
openCryptoWorkstation();
@@ -3010,7 +3053,8 @@
if (distanceSq > INTERACTION_RANGE_SQ) {
// Player is too far away to interact
debugLog('INTERACTION_OUT_OF_RANGE', {
objectName: sprite.name,
objectName: sprite.name,
objectId: sprite.objectId,
distance: Math.sqrt(distanceSq),
maxRange: Math.sqrt(INTERACTION_RANGE_SQ)
}, 2);
@@ -3114,6 +3158,8 @@
}
}
}
console.log("Interacting with object:", data);
if (data.takeable) {
// If it's a note type item that's already been read and added to notes,
@@ -3122,10 +3168,10 @@
data.type === 'notes' &&
data.readable &&
data.text &&
!data.hasSpecialPurpose; // Add this flag to notes that need to be in inventory
!data.hasNoSpecialPurpose; // Add this flag to notes that need to be in inventory
if (!isJustInformationalNote) {
message += `This item can be taken\n\n`;
// message += `This item can be taken\n\n`;
if (!inventory || !Array.isArray(inventory.items)) {
console.error('Inventory not properly initialized');
@@ -3133,9 +3179,9 @@
}
const isInRoom = currentRoom &&
rooms[currentRoom] &&
rooms[currentRoom].objects &&
rooms[currentRoom].objects[sprite.name];
rooms[currentRoom] &&
rooms[currentRoom].objects &&
rooms[currentRoom].objects[sprite.objectId];
const itemIdentifier = createItemIdentifier(sprite.scenarioData);
@@ -3152,9 +3198,9 @@
if (currentRoom &&
rooms[currentRoom] &&
rooms[currentRoom].objects &&
rooms[currentRoom].objects[sprite.name]) {
rooms[currentRoom].objects[sprite.objectId]) {
const roomObj = rooms[currentRoom].objects[sprite.name];
const roomObj = rooms[currentRoom].objects[sprite.objectId];
roomObj.setVisible(false);
roomObj.active = false;
@@ -3181,47 +3227,58 @@
}
try {
// Check if the item is already in the inventory using the unique identifier
const itemIdentifier = createItemIdentifier(sprite.scenarioData);
console.log(`Checking if item ${itemIdentifier} is already in inventory`);
// Debug logging
console.log("Trying to add to inventory:", {
objectId: sprite.objectId,
name: sprite.name,
type: sprite.scenarioData?.type,
currentRoom: currentRoom
});
// Check if the item is already in the inventory
const itemIdentifier = createItemIdentifier(sprite.scenarioData);
const isAlreadyInInventory = inventory.items.some(item =>
createItemIdentifier(item.scenarioData) === itemIdentifier
);
if (isAlreadyInInventory) {
console.log(`Item ${itemIdentifier} is already in inventory, not adding again`);
console.log(`Item ${itemIdentifier} is already in inventory`);
return false;
}
// Remove from room if it exists
if (currentRoom &&
rooms[currentRoom] &&
rooms[currentRoom].objects &&
rooms[currentRoom].objects[sprite.name]) {
const roomObj = rooms[currentRoom].objects[sprite.name];
roomObj.setVisible(false);
roomObj.active = false;
if (currentRoom && rooms[currentRoom] && rooms[currentRoom].objects) {
// Try to find by objectId first
if (rooms[currentRoom].objects[sprite.objectId]) {
const roomObj = rooms[currentRoom].objects[sprite.objectId];
roomObj.setVisible(false);
roomObj.active = false;
console.log(`Removed object ${sprite.objectId} from room`);
}
}
sprite.setVisible(false);
const scene = sprite.scene;
// SIMPLIFIED INVENTORY NAMING
// Use a consistent format: inventory_type_index
const inventoryId = `inventory_${sprite.name}_${inventory.items.length}`;
// Create new sprite for inventory
const inventorySprite = scene.add.sprite(
inventory.items.length * 60, // Remove the +100 offset
inventory.items.length * 60,
0,
sprite.name
);
// inventorySprite.setScale(0.8);
inventorySprite.setInteractive({ useHandCursor: true, pixelPerfect: true });
inventorySprite.scenarioData = {
...sprite.scenarioData,
foundIn: currentRoom ? gameScenario.rooms[currentRoom].name || currentRoom : 'unknown location'
};
inventorySprite.name = sprite.name;
inventorySprite.objectId = inventoryId;
// Set depth higher than container
inventorySprite.setDepth(2003);
@@ -3229,6 +3286,7 @@
// Add pointer events
inventorySprite.on('pointerdown', function(pointer) {
// Handle inventory item interaction
handleObjectInteraction(inventorySprite);
});
inventorySprite.on('pointerover', function() {
@@ -3461,17 +3519,8 @@
// Add this new helper function:
function createItemIdentifier(scenarioData) {
// Combine multiple properties to create a unique identifier
const identifierParts = [
scenarioData.type,
scenarioData.name,
// Add more unique properties if available
scenarioData.key_id, // For keys
scenarioData.requires, // For locks
scenarioData.text // For readable items
].filter(Boolean); // Remove any undefined/null values
return identifierParts.join('|');
// Just use type and name as the identifier
return `${scenarioData.type}|${scenarioData.name}`;
}
// Add this new function after the other function definitions