Spaces:
Running
Running
Update index.html
Browse files- index.html +44 -39
index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>Persistent Procedural World (Fixed)</title>
|
| 7 |
<style>
|
| 8 |
body { font-family: 'Courier New', monospace; background-color: #111; color: #eee; margin: 0; padding: 0; overflow: hidden; display: flex; flex-direction: column; height: 100vh; }
|
| 9 |
#game-container { display: flex; flex-grow: 1; overflow: hidden; }
|
|
@@ -37,7 +37,6 @@
|
|
| 37 |
.message-info { color: #aaa; border-left-color: #666; }
|
| 38 |
.message-item { color: #8bf; border-left-color: #46a; }
|
| 39 |
#action-info { position: absolute; bottom: 10px; left: 10px; background-color: rgba(0,0,0,0.7); color: #ffcc66; padding: 5px 10px; border-radius: 3px; font-size: 0.9em; display: none; z-index: 10;}
|
| 40 |
-
|
| 41 |
</style>
|
| 42 |
</head>
|
| 43 |
<body>
|
|
@@ -83,7 +82,7 @@
|
|
| 83 |
let locationGroups = {};
|
| 84 |
let currentMessage = "";
|
| 85 |
let currentPlacingItem = null;
|
| 86 |
-
let currentLights = [];
|
| 87 |
|
| 88 |
const MAT = {
|
| 89 |
stone: new THREE.MeshStandardMaterial({ color: 0x777788, roughness: 0.85, metalness: 0.1 }),
|
|
@@ -101,6 +100,42 @@
|
|
| 101 |
placementPreview: new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.5, wireframe: true }),
|
| 102 |
};
|
| 103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
function initThreeJS() {
|
| 105 |
scene = new THREE.Scene();
|
| 106 |
scene.background = new THREE.Color(0x1a1a1a);
|
|
@@ -290,41 +325,11 @@
|
|
| 290 |
return { group, lighting: 'cave', entryText: "It's dark and damp in here.", cameraPos: {x:0, y:4, z:8}, targetPos: {x:0, y:1, z:0} };
|
| 291 |
}
|
| 292 |
|
| 293 |
-
const locationData = {
|
| 294 |
-
'start': { creator: createDefaultZone },
|
| 295 |
-
'forest': { creator: createForestZone },
|
| 296 |
-
'cave': { creator: createCaveZone }
|
| 297 |
-
};
|
| 298 |
-
|
| 299 |
-
const pageGraph = {
|
| 300 |
-
'start': {
|
| 301 |
-
title: "The Crossroads",
|
| 302 |
-
options: [ { text: "Enter Forest", transitionTo: 'forest' }, { text: "Enter Cave", transitionTo: 'cave'} ]
|
| 303 |
-
},
|
| 304 |
-
'forest': {
|
| 305 |
-
title: "Dark Forest",
|
| 306 |
-
options: [ { text: "Return to Crossroads", transitionTo: 'start' } ]
|
| 307 |
-
},
|
| 308 |
-
'cave': {
|
| 309 |
-
title: "Dim Cave",
|
| 310 |
-
options: [ { text: "Leave Cave", transitionTo: 'start' } ]
|
| 311 |
-
}
|
| 312 |
-
};
|
| 313 |
-
|
| 314 |
-
let gameState = {
|
| 315 |
-
currentLocationId: null,
|
| 316 |
-
character: {
|
| 317 |
-
name: "Player",
|
| 318 |
-
stats: { hp: 20, maxHp: 20, xp: 0 },
|
| 319 |
-
inventory: []
|
| 320 |
-
}
|
| 321 |
-
};
|
| 322 |
-
|
| 323 |
function startGame() {
|
| 324 |
const defaultChar = {
|
| 325 |
name: "Player",
|
| 326 |
stats: { hp: 20, maxHp: 20, xp: 0 },
|
| 327 |
-
inventory: ["Rusty Sword"]
|
| 328 |
};
|
| 329 |
gameState = {
|
| 330 |
currentLocationId: null,
|
|
@@ -450,7 +455,7 @@
|
|
| 450 |
|
| 451 |
inventoryElement.querySelectorAll('.item-tag').forEach(tag => {
|
| 452 |
tag.onclick = (event) => {
|
| 453 |
-
event.stopPropagation();
|
| 454 |
togglePlacementMode(tag.dataset.itemname);
|
| 455 |
};
|
| 456 |
});
|
|
@@ -467,14 +472,14 @@
|
|
| 467 |
}
|
| 468 |
|
| 469 |
function pickupItem() {
|
| 470 |
-
if (currentPlacingItem) return;
|
| 471 |
|
| 472 |
raycaster.setFromCamera(mouse, camera);
|
| 473 |
const currentGroup = locationGroups[gameState.currentLocationId];
|
| 474 |
if (!currentGroup) return;
|
| 475 |
|
| 476 |
const pickupables = [];
|
| 477 |
-
currentGroup.traverseVisible(child => {
|
| 478 |
if (child.userData.isPickupable) {
|
| 479 |
pickupables.push(child);
|
| 480 |
}
|
|
@@ -542,8 +547,8 @@
|
|
| 542 |
else if(itemDef.type === 'consumable') itemMat.color.setHex(0xaa7744);
|
| 543 |
else itemMat.color.setHex(0x8888aa);
|
| 544 |
|
| 545 |
-
const placedMesh = createMesh(itemGeo, itemMat, {x: point.x, y: 0.25, z: point.z});
|
| 546 |
-
placedMesh.userData = { isPlacedItem: true, itemName: itemName, isPickupable: true, description: `Placed ${itemName}` };
|
| 547 |
currentGroup.add(placedMesh);
|
| 548 |
|
| 549 |
gameState.character.inventory = gameState.character.inventory.filter(i => i !== itemName);
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Persistent Procedural World (Fixed Again)</title>
|
| 7 |
<style>
|
| 8 |
body { font-family: 'Courier New', monospace; background-color: #111; color: #eee; margin: 0; padding: 0; overflow: hidden; display: flex; flex-direction: column; height: 100vh; }
|
| 9 |
#game-container { display: flex; flex-grow: 1; overflow: hidden; }
|
|
|
|
| 37 |
.message-info { color: #aaa; border-left-color: #666; }
|
| 38 |
.message-item { color: #8bf; border-left-color: #46a; }
|
| 39 |
#action-info { position: absolute; bottom: 10px; left: 10px; background-color: rgba(0,0,0,0.7); color: #ffcc66; padding: 5px 10px; border-radius: 3px; font-size: 0.9em; display: none; z-index: 10;}
|
|
|
|
| 40 |
</style>
|
| 41 |
</head>
|
| 42 |
<body>
|
|
|
|
| 82 |
let locationGroups = {};
|
| 83 |
let currentMessage = "";
|
| 84 |
let currentPlacingItem = null;
|
| 85 |
+
let currentLights = [];
|
| 86 |
|
| 87 |
const MAT = {
|
| 88 |
stone: new THREE.MeshStandardMaterial({ color: 0x777788, roughness: 0.85, metalness: 0.1 }),
|
|
|
|
| 100 |
placementPreview: new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.5, wireframe: true }),
|
| 101 |
};
|
| 102 |
|
| 103 |
+
// ---> Item Data Definition (Ensure this exists globally) <---
|
| 104 |
+
const itemsData = {
|
| 105 |
+
"Rusty Sword": {type:"weapon", description:"Old but sharp."},
|
| 106 |
+
"Torch": {type:"consumable", description:"Provides light.", use: "light"},
|
| 107 |
+
"Key": {type:"quest", description:"A simple iron key."},
|
| 108 |
+
"Mysterious Cube": {type:"unknown", description:"A plain stone cube."} // Added item from default zone
|
| 109 |
+
};
|
| 110 |
+
|
| 111 |
+
// ---> Location/Zone Definitions <---
|
| 112 |
+
const locationData = {
|
| 113 |
+
'start': { creator: createDefaultZone },
|
| 114 |
+
'forest': { creator: createForestZone },
|
| 115 |
+
'cave': { creator: createCaveZone }
|
| 116 |
+
};
|
| 117 |
+
|
| 118 |
+
// ---> Navigation/Story Graph <---
|
| 119 |
+
const pageGraph = {
|
| 120 |
+
'start': {
|
| 121 |
+
title: "The Crossroads",
|
| 122 |
+
options: [ { text: "Enter Forest", transitionTo: 'forest' }, { text: "Enter Cave", transitionTo: 'cave'} ]
|
| 123 |
+
},
|
| 124 |
+
'forest': {
|
| 125 |
+
title: "Dark Forest",
|
| 126 |
+
options: [ { text: "Return to Crossroads", transitionTo: 'start' } ]
|
| 127 |
+
},
|
| 128 |
+
'cave': {
|
| 129 |
+
title: "Dim Cave",
|
| 130 |
+
options: [ { text: "Leave Cave", transitionTo: 'start' } ]
|
| 131 |
+
}
|
| 132 |
+
};
|
| 133 |
+
|
| 134 |
+
// ---> Game State Variable <---
|
| 135 |
+
let gameState = {}; // Initialized in startGame
|
| 136 |
+
|
| 137 |
+
// --- Core Functions ---
|
| 138 |
+
|
| 139 |
function initThreeJS() {
|
| 140 |
scene = new THREE.Scene();
|
| 141 |
scene.background = new THREE.Color(0x1a1a1a);
|
|
|
|
| 325 |
return { group, lighting: 'cave', entryText: "It's dark and damp in here.", cameraPos: {x:0, y:4, z:8}, targetPos: {x:0, y:1, z:0} };
|
| 326 |
}
|
| 327 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
function startGame() {
|
| 329 |
const defaultChar = {
|
| 330 |
name: "Player",
|
| 331 |
stats: { hp: 20, maxHp: 20, xp: 0 },
|
| 332 |
+
inventory: ["Rusty Sword"]
|
| 333 |
};
|
| 334 |
gameState = {
|
| 335 |
currentLocationId: null,
|
|
|
|
| 455 |
|
| 456 |
inventoryElement.querySelectorAll('.item-tag').forEach(tag => {
|
| 457 |
tag.onclick = (event) => {
|
| 458 |
+
event.stopPropagation();
|
| 459 |
togglePlacementMode(tag.dataset.itemname);
|
| 460 |
};
|
| 461 |
});
|
|
|
|
| 472 |
}
|
| 473 |
|
| 474 |
function pickupItem() {
|
| 475 |
+
if (currentPlacingItem) return;
|
| 476 |
|
| 477 |
raycaster.setFromCamera(mouse, camera);
|
| 478 |
const currentGroup = locationGroups[gameState.currentLocationId];
|
| 479 |
if (!currentGroup) return;
|
| 480 |
|
| 481 |
const pickupables = [];
|
| 482 |
+
currentGroup.traverseVisible(child => {
|
| 483 |
if (child.userData.isPickupable) {
|
| 484 |
pickupables.push(child);
|
| 485 |
}
|
|
|
|
| 547 |
else if(itemDef.type === 'consumable') itemMat.color.setHex(0xaa7744);
|
| 548 |
else itemMat.color.setHex(0x8888aa);
|
| 549 |
|
| 550 |
+
const placedMesh = createMesh(itemGeo, itemMat, {x: point.x, y: 0.25, z: point.z});
|
| 551 |
+
placedMesh.userData = { isPlacedItem: true, itemName: itemName, isPickupable: true, description: `Placed ${itemName}` };
|
| 552 |
currentGroup.add(placedMesh);
|
| 553 |
|
| 554 |
gameState.character.inventory = gameState.character.inventory.filter(i => i !== itemName);
|