awacke1 commited on
Commit
756e345
·
verified ·
1 Parent(s): 960a9eb

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +122 -407
index.html CHANGED
@@ -3,441 +3,156 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>DEBUG - Procedural 3D Dungeon</title>
7
  <style>
8
- body { margin: 0; overflow: hidden; background-color: #000; color: white; font-family: monospace; }
9
- canvas { display: block; }
10
- #blocker { position: absolute; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; cursor: pointer; z-index: 10; }
11
- #instructions { width: 50%; text-align: center; padding: 20px; background: rgba(20, 20, 20, 0.8); border-radius: 10px; }
12
- #crosshair { position: absolute; top: 50%; left: 50%; width: 10px; height: 10px; border: 1px solid white; border-radius: 50%; transform: translate(-50%, -50%); pointer-events: none; mix-blend-mode: difference; display: none; z-index: 11; }
13
- </style>
14
- </head>
15
- <body>
16
- <div id="blocker">
17
- <div id="instructions">
18
- <h1>Dungeon Explorer (Debug Mode)</h1>
19
- <p>Click to Enter</p>
20
- <p>(W, A, S, D = Move, MOUSE = Look)</p>
21
- <p>Check F12 Console for Errors!</p>
22
- </div>
23
- </div>
24
- <div id="crosshair">+</div>
25
-
26
- <script type="importmap">
27
- {
28
- "imports": {
29
- "three": "https://unpkg.com/[email protected]/build/three.module.js",
30
- "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
31
- }
32
  }
33
- </script>
34
-
35
- <script type="module">
36
- import * as THREE from 'three';
37
- import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
38
- // BufferGeometryUtils not needed for this debug version
39
- // import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
40
-
41
- console.log("Script Start");
42
-
43
- // --- Config ---
44
- const DUNGEON_WIDTH = 10; // Smaller grid for debug
45
- const DUNGEON_HEIGHT = 10;
46
- const CELL_SIZE = 5;
47
- const WALL_HEIGHT = 4;
48
- const PLAYER_HEIGHT = 1.6;
49
- const PLAYER_RADIUS = 0.4;
50
- const PLAYER_SPEED = 5.0;
51
-
52
- // --- Three.js Setup ---
53
- let scene, camera, renderer;
54
- let controls;
55
- let clock;
56
- let flashlight;
57
-
58
- // --- Player State ---
59
- const playerVelocity = new THREE.Vector3();
60
- const playerDirection = new THREE.Vector3();
61
- let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false;
62
-
63
- // --- World Data ---
64
- let dungeonLayout = [];
65
- const worldMeshes = []; // Store refs to added meshes for potential cleanup
66
-
67
- // --- DOM Elements ---
68
- const blocker = document.getElementById('blocker');
69
- const instructions = document.getElementById('instructions');
70
- const crosshair = document.getElementById('crosshair');
71
-
72
- // --- Materials (Basic Colors) ---
73
- const floorMaterial = new THREE.MeshLambertMaterial({ color: 0x555555 }); // Use Lambert for basic lighting check
74
- const wallMaterial = new THREE.MeshLambertMaterial({ color: 0x884444 });
75
 
76
- // --- Initialization ---
77
- function init() {
78
- console.log("--- Initializing Game ---");
79
- clock = new THREE.Clock();
80
-
81
- // Clear previous scene if restarting
82
- if (scene) {
83
- console.log("Clearing previous scene objects...");
84
- worldMeshes.forEach(mesh => {
85
- if(mesh.parent) scene.remove(mesh);
86
- if(mesh.geometry) mesh.geometry.dispose();
87
- // Only dispose material if we know it's unique per object
88
- });
89
- worldMeshes.length = 0; // Clear the array
90
- } else {
91
- scene = new THREE.Scene();
92
- }
93
- scene.background = new THREE.Color(0x111111);
94
- scene.fog = new THREE.Fog(0x111111, 10, CELL_SIZE * 6);
95
-
96
- // Clear previous renderer if restarting
97
- if (renderer) {
98
- console.log("Disposing previous renderer...");
99
- renderer.dispose();
100
- if (renderer.domElement.parentNode) {
101
- renderer.domElement.parentNode.removeChild(renderer.domElement);
102
- }
103
- }
104
- renderer = new THREE.WebGLRenderer({ antialias: true });
105
- renderer.setSize(window.innerWidth, window.innerHeight);
106
- renderer.setPixelRatio(window.devicePixelRatio);
107
- renderer.shadowMap.enabled = true; // Keep shadows enabled
108
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
109
- document.body.appendChild(renderer.domElement);
110
- console.log("Renderer created/reset.");
111
-
112
-
113
- // Camera (First Person)
114
- camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
115
- camera.position.y = PLAYER_HEIGHT;
116
- console.log("Camera created.");
117
-
118
- // Lighting
119
- scene.add(new THREE.AmbientLight(0x404040, 0.8)); // Slightly brighter ambient
120
-
121
- flashlight = new THREE.SpotLight(0xffffff, 3, 30, Math.PI / 5, 0.4, 1.5);
122
- flashlight.position.set(0, 0, 0); // Relative to camera
123
- flashlight.target.position.set(0, 0, -1); // Relative to camera
124
- flashlight.castShadow = true;
125
- flashlight.shadow.mapSize.width = 1024;
126
- flashlight.shadow.mapSize.height = 1024;
127
- flashlight.shadow.camera.near = 0.5;
128
- flashlight.shadow.camera.far = 30;
129
- camera.add(flashlight);
130
- camera.add(flashlight.target);
131
- scene.add(camera); // Add camera (with light) to scene
132
- console.log("Lighting setup.");
133
-
134
- // Pointer Lock Controls
135
- controls = new PointerLockControls(camera, renderer.domElement);
136
- // We don't add controls.getObject() directly to scene IF flashlight is child of camera
137
- // scene.add(controls.getObject()); // Only if camera isn't manually added
138
-
139
- blocker.addEventListener('click', () => { controls.lock(); });
140
- controls.addEventListener('lock', () => { instructions.style.display = 'none'; blocker.style.display = 'none'; crosshair.style.display = 'block'; });
141
- controls.addEventListener('unlock', () => { blocker.style.display = 'flex'; instructions.style.display = ''; crosshair.style.display = 'none'; });
142
- console.log("Controls setup.");
143
-
144
- // Keyboard Listeners
145
- document.removeEventListener('keydown', onKeyDown); // Remove old listeners if restarting
146
- document.removeEventListener('keyup', onKeyUp);
147
- document.addEventListener('keydown', onKeyDown);
148
- document.addEventListener('keyup', onKeyUp);
149
-
150
- // Resize Listener
151
- window.removeEventListener('resize', onWindowResize); // Remove old
152
- window.addEventListener('resize', onWindowResize);
153
-
154
- // --- Generate FIXED Dungeon ---
155
- console.log("Generating FIXED dungeon layout...");
156
- dungeonLayout = generateFixedDungeonLayout(DUNGEON_WIDTH, DUNGEON_HEIGHT);
157
- console.log("Layout generated, creating meshes...");
158
- createDungeonMeshes_Direct(dungeonLayout); // Use direct mesh addition
159
- console.log("Dungeon meshes created.");
160
-
161
- // --- Set Player Start Position ---
162
- const startPos = findStartPosition(dungeonLayout);
163
- if (startPos) {
164
- // Position the camera (which is the player view)
165
- controls.getObject().position.set(startPos.x, PLAYER_HEIGHT, startPos.z);
166
- console.log("Player start position set at:", startPos);
167
- } else {
168
- console.error("Could not find valid start position! Placing at center.");
169
- const fallbackX = (DUNGEON_WIDTH / 2) * CELL_SIZE;
170
- const fallbackZ = (DUNGEON_HEIGHT / 2) * CELL_SIZE;
171
- controls.getObject().position.set(fallbackX, PLAYER_HEIGHT, fallbackZ);
172
- }
173
-
174
- // Add Axes Helper for orientation check
175
- const axesHelper = new THREE.AxesHelper(CELL_SIZE);
176
- axesHelper.position.copy(controls.getObject().position); // Place at start pos
177
- axesHelper.position.y = 0.1;
178
- scene.add(axesHelper);
179
- worldMeshes.push(axesHelper); // Track for cleanup
180
-
181
-
182
- console.log("--- Initialization Complete ---");
183
- animate(); // Start the loop
184
  }
185
 
186
- // --- Dungeon Generation (FIXED LAYOUT) ---
187
- function generateFixedDungeonLayout(width, height) {
188
- console.log("Generating FIXED 5x5 layout for debugging...");
189
- const grid = Array(height).fill(null).map(() => Array(width).fill(0)); // All walls
190
- // Simple 5x5 room centered
191
- const cx = Math.floor(width / 2);
192
- const cy = Math.floor(height / 2);
193
- for (let y = cy - 2; y <= cy + 2; y++) {
194
- for (let x = cx - 2; x <= cx + 2; x++) {
195
- if (y >= 0 && y < height && x >= 0 && x < width) {
196
- grid[y][x] = 1; // Floor
197
- }
198
- }
199
- }
200
- // Add a corridor
201
- for (let y = cy + 3; y < height -1 ; y++) {
202
- if (grid[y]) grid[y][cx] = 1;
203
- }
204
- console.log(`Fixed Layout Generated (${width}x${height}). Center: ${cx},${cy}`);
205
- // console.log("Grid:", grid.map(row => row.join('')).join('\n')); // Optional: Log grid visually
206
- return grid;
207
  }
208
 
209
-
210
- // --- Find Start Position (Same as before) ---
211
- function findStartPosition(grid) {
212
- const startY = Math.floor(grid.length / 2);
213
- const startX = Math.floor(grid[0].length / 2);
214
- console.log(`Searching for start near ${startX},${startY}`);
215
- for (let r = 0; r < Math.max(startX, startY); r++) {
216
- for (let y = startY - r; y <= startY + r; y++) {
217
- for (let x = startX - r; x <= startX + r; x++) {
218
- if (Math.abs(y - startY) === r || Math.abs(x - startX) === r || r === 0) {
219
- if (y >= 0 && y < grid.length && x >= 0 && x < grid[0].length && grid[y][x] === 1) {
220
- console.log(`Found start floor at ${x},${y}`);
221
- return { x: x * CELL_SIZE + CELL_SIZE / 2, z: y * CELL_SIZE + CELL_SIZE / 2 };
222
- }
223
- }
224
- }
225
- }
226
- }
227
- console.error("Valid start position (floor tile = 1) not found near center!");
228
- return null; // Fallback
229
  }
230
 
231
-
232
- // --- Dungeon Meshing (DIRECT ADDITION - NO MERGING) ---
233
- function createDungeonMeshes_Direct(grid) {
234
- console.log("Creating meshes directly (no merging)...");
235
- // Recreate geometries each time to avoid issues with disposed geometries if init is called again
236
- const floorGeo = new THREE.PlaneGeometry(CELL_SIZE, CELL_SIZE);
237
- const wallGeoN = new THREE.BoxGeometry(CELL_SIZE, WALL_HEIGHT, 0.1);
238
- const wallGeoS = new THREE.BoxGeometry(CELL_SIZE, WALL_HEIGHT, 0.1);
239
- const wallGeoE = new THREE.BoxGeometry(0.1, WALL_HEIGHT, CELL_SIZE);
240
- const wallGeoW = new THREE.BoxGeometry(0.1, WALL_HEIGHT, CELL_SIZE);
241
-
242
- for (let y = 0; y < grid.length; y++) {
243
- for (let x = 0; x < grid[y].length; x++) {
244
- if (grid[y][x] === 1) { // If it's a floor cell
245
- // Create Floor Tile Mesh
246
- const floorInstance = new THREE.Mesh(floorGeo, floorMaterial); // Use shared geometry instance
247
- floorInstance.rotation.x = -Math.PI / 2;
248
- floorInstance.position.set(x * CELL_SIZE + CELL_SIZE / 2, 0, y * CELL_SIZE + CELL_SIZE / 2);
249
- floorInstance.receiveShadow = true;
250
- scene.add(floorInstance);
251
- worldMeshes.push(floorInstance); // Track mesh
252
- // console.log(`Added floor mesh at ${x},${y}`);
253
-
254
- // Check neighbors for Walls
255
- // North Wall
256
- if (y === 0 || grid[y - 1][x] === 0) {
257
- const wallInstance = new THREE.Mesh(wallGeoN, wallMaterial);
258
- wallInstance.position.set(x * CELL_SIZE + CELL_SIZE / 2, WALL_HEIGHT / 2, y * CELL_SIZE);
259
- wallInstance.castShadow = true; wallInstance.receiveShadow = true;
260
- scene.add(wallInstance); worldMeshes.push(wallInstance);
261
- }
262
- // South Wall
263
- if (y === grid.length - 1 || grid[y + 1][x] === 0) {
264
- const wallInstance = new THREE.Mesh(wallGeoS, wallMaterial);
265
- wallInstance.position.set(x * CELL_SIZE + CELL_SIZE / 2, WALL_HEIGHT / 2, y * CELL_SIZE + CELL_SIZE);
266
- wallInstance.castShadow = true; wallInstance.receiveShadow = true;
267
- scene.add(wallInstance); worldMeshes.push(wallInstance);
268
- }
269
- // West Wall
270
- if (x === 0 || grid[y][x - 1] === 0) {
271
- const wallInstance = new THREE.Mesh(wallGeoW, wallMaterial);
272
- wallInstance.position.set(x * CELL_SIZE, WALL_HEIGHT / 2, y * CELL_SIZE + CELL_SIZE / 2);
273
- wallInstance.castShadow = true; wallInstance.receiveShadow = true;
274
- scene.add(wallInstance); worldMeshes.push(wallInstance);
275
- }
276
- // East Wall
277
- if (x === grid[y].length - 1 || grid[y][x + 1] === 0) {
278
- const wallInstance = new THREE.Mesh(wallGeoE, wallMaterial);
279
- wallInstance.position.set(x * CELL_SIZE + CELL_SIZE, WALL_HEIGHT / 2, y * CELL_SIZE + CELL_SIZE / 2);
280
- wallInstance.castShadow = true; wallInstance.receiveShadow = true;
281
- scene.add(wallInstance); worldMeshes.push(wallInstance);
282
- }
283
- }
284
- }
285
- }
286
- // Geometries are shared, no need to dispose here unless we cloned them.
287
- // If we were cloning: floorGeo.dispose(); wallGeoN.dispose(); ...
288
- console.log("Direct mesh creation complete.");
289
  }
290
 
291
-
292
- // --- Player Movement & Collision (No Physics) ---
293
- function handleInputAndMovement(deltaTime) {
294
- if (!controls || !controls.isLocked) return;
295
-
296
- const speed = PLAYER_SPEED * deltaTime;
297
- // Reset velocity, we calculate total displacement based on keys
298
- playerVelocity.x = 0;
299
- playerVelocity.z = 0;
300
-
301
- // Get camera direction (ignore Y)
302
- controls.getDirection(playerDirection); // Gets normalized direction vector
303
- playerDirection.y = 0;
304
- playerDirection.normalize();
305
-
306
- // Calculate right vector based on camera direction
307
- const rightDirection = new THREE.Vector3();
308
- rightDirection.crossVectors(camera.up, playerDirection).normalize(); // camera.up is (0,1,0)
309
-
310
- // Apply movement based on keys
311
- if (moveForward) playerVelocity.add(playerDirection);
312
- if (moveBackward) playerVelocity.sub(playerDirection);
313
- if (moveLeft) playerVelocity.sub(rightDirection);
314
- if (moveRight) playerVelocity.add(rightDirection);
315
-
316
- // Normalize diagonal velocity if needed and apply speed
317
- if (playerVelocity.lengthSq() > 0) {
318
- playerVelocity.normalize().multiplyScalar(speed);
319
- }
320
-
321
- // --- Basic Collision Detection BEFORE moving ---
322
- const currentPos = controls.getObject().position;
323
- let moveXAllowed = true;
324
- let moveZAllowed = true;
325
-
326
- // Check X Collision
327
- if (playerVelocity.x !== 0) {
328
- const nextX = currentPos.x + playerVelocity.x;
329
- // Check slightly ahead in X direction, at feet and head level Z
330
- const checkGridX = Math.floor((nextX + Math.sign(playerVelocity.x) * PLAYER_RADIUS) / CELL_SIZE);
331
- const checkGridZFeet = Math.floor((currentPos.z - PLAYER_RADIUS) / CELL_SIZE);
332
- const checkGridZHead = Math.floor((currentPos.z + PLAYER_RADIUS) / CELL_SIZE);
333
- if ((dungeonLayout[checkGridZFeet]?.[checkGridX] === 0) || (dungeonLayout[checkGridZHead]?.[checkGridX] === 0)) {
334
- moveXAllowed = false;
335
- // console.log(`Collision X at grid ${checkGridX},${checkGridZFeet}/${checkGridZHead}`);
336
- }
337
- }
338
-
339
- // Check Z Collision
340
- if (playerVelocity.z !== 0) {
341
- const nextZ = currentPos.z + playerVelocity.z;
342
- // Check slightly ahead in Z direction, at feet and head level X
343
- const checkGridZ = Math.floor((nextZ + Math.sign(playerVelocity.z) * PLAYER_RADIUS) / CELL_SIZE);
344
- const checkGridXFeet = Math.floor((currentPos.x - PLAYER_RADIUS) / CELL_SIZE);
345
- const checkGridXHead = Math.floor((currentPos.x + PLAYER_RADIUS) / CELL_SIZE);
346
- if ((dungeonLayout[checkGridZ]?.[checkGridXFeet] === 0) || (dungeonLayout[checkGridZ]?.[checkGridXHead] === 0)) {
347
- moveZAllowed = false;
348
- // console.log(`Collision Z at grid ${checkGridXFeet}/${checkGridXHead},${checkGridZ}`);
349
- }
350
- }
351
-
352
- // Apply movement only if allowed
353
- if (moveXAllowed) {
354
- controls.moveRight(playerVelocity.x); // moveRight uses internal right vector, so feed X velocity
355
- }
356
- if (moveZAllowed) {
357
- controls.moveForward(playerVelocity.z); // moveForward uses internal forward vector, so feed Z velocity
358
- }
359
-
360
- // Keep player at fixed height (no gravity/jump yet)
361
- controls.getObject().position.y = PLAYER_HEIGHT;
362
-
363
- // Log position occasionally
364
- // if (Math.random() < 0.05) console.log("Player Pos:", controls.getObject().position);
365
  }
366
 
 
 
 
 
 
367
 
368
- // --- Event Handlers ---
369
- function onKeyDown(event) {
370
- // console.log("KeyDown:", event.code); // Debug key codes
371
- switch (event.code) {
372
- case 'ArrowUp': case 'KeyW': moveForward = true; break;
373
- case 'ArrowLeft': case 'KeyA': moveLeft = true; break;
374
- case 'ArrowDown': case 'KeyS': moveBackward = true; break;
375
- case 'ArrowRight': case 'KeyD': moveRight = true; break;
376
- // QWE ZXC movement not implemented in this simplified non-physics version yet
377
- // Jump/F/Space not implemented yet
378
- }
379
  }
380
 
381
- function onKeyUp(event) {
382
- switch (event.code) {
383
- case 'ArrowUp': case 'KeyW': moveForward = false; break;
384
- case 'ArrowLeft': case 'KeyA': moveLeft = false; break;
385
- case 'ArrowDown': case 'KeyS': moveBackward = false; break;
386
- case 'ArrowRight': case 'KeyD': moveRight = false; break;
387
- }
 
 
 
 
 
 
 
388
  }
389
 
390
- function onWindowResize() {
391
- if (!camera || !renderer) return;
392
- console.log("Resizing...");
393
- camera.aspect = window.innerWidth / window.innerHeight;
394
- camera.updateProjectionMatrix();
395
- renderer.setSize(window.innerWidth, window.innerHeight);
396
  }
 
 
 
 
 
 
397
 
398
- // --- UI Update Functions (Simplified) ---
399
- function updateUI() {
400
- // Display basic position for debugging
401
- if (controls) {
402
- const pos = controls.getObject().position;
403
- statsElement.innerHTML = `<span>Pos: ${pos.x.toFixed(1)}, ${pos.y.toFixed(1)}, ${pos.z.toFixed(1)}</span>`;
404
- }
405
- // Inventory display can be added later
406
- inventoryElement.innerHTML = '<em>Inventory N/A</em>';
 
 
 
 
 
 
 
 
407
  }
408
- function addLog(message, type = "info") {
409
- const p = document.createElement('p');
410
- p.classList.add(type); // Add class for styling
411
- p.textContent = `[${new Date().toLocaleTimeString([], { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' })}] ${message}`; // Add timestamp
412
- logElement.appendChild(p);
413
- logElement.scrollTop = logElement.scrollHeight; // Auto-scroll
414
- }
415
 
416
 
417
- // --- Animation Loop ---
418
- function animate() {
419
- animationFrameId = requestAnimationFrame(animate);
420
 
421
- const delta = clock.getDelta();
 
 
422
 
423
- // Update movement only if controls are locked
424
- if (controls && controls.isLocked === true) {
425
- handleInputAndMovement(delta);
426
- updateUI(); // Update UI less frequently if needed
427
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
 
429
- renderer.render(scene, camera);
 
 
 
 
 
430
  }
 
431
 
432
- // --- Start ---
433
- console.log("Attempting to initialize...");
434
- try {
435
- init();
436
- } catch(err) {
437
- console.error("Initialization failed:", err);
438
- alert("Error during initialization. Check the console (F12).");
439
- }
440
 
441
- </script>
442
  </body>
443
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Choose Your Own 3D Adventure</title>
7
  <style>
8
+ body {
9
+ font-family: 'Courier New', monospace;
10
+ background-color: #222; /* Dark background */
11
+ color: #eee; /* Light text */
12
+ margin: 0;
13
+ padding: 0;
14
+ overflow: hidden; /* Prevent scrollbars from Three.js */
15
+ display: flex;
16
+ flex-direction: column;
17
+ height: 100vh;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ #game-container {
21
+ display: flex;
22
+ flex-grow: 1; /* Allow container to fill space */
23
+ overflow: hidden; /* Contain children */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
+ #scene-container {
27
+ flex-grow: 3; /* Give more space to 3D view */
28
+ position: relative; /* For potential overlays */
29
+ border-right: 2px solid #555;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
 
32
+ #ui-container {
33
+ flex-grow: 2; /* Space for UI elements */
34
+ padding: 20px;
35
+ overflow-y: auto; /* Allow scrolling for long text/options */
36
+ background-color: #333;
37
+ display: flex;
38
+ flex-direction: column;
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
40
 
41
+ #story-title {
42
+ color: #ffcc66; /* Goldish title */
43
+ margin-top: 0;
44
+ border-bottom: 1px solid #555;
45
+ padding-bottom: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
 
48
+ #story-content {
49
+ margin-bottom: 20px;
50
+ line-height: 1.6;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
 
53
+ #choices-container {
54
+ margin-top: auto; /* Push choices towards the bottom */
55
+ padding-top: 15px;
56
+ border-top: 1px solid #555;
57
+ }
58
 
59
+ #choices-container h3 {
60
+ margin-top: 0;
61
+ margin-bottom: 10px;
62
+ color: #aaa;
 
 
 
 
 
 
 
63
  }
64
 
65
+ .choice-button {
66
+ display: block;
67
+ width: calc(100% - 20px); /* Adjust width */
68
+ padding: 10px;
69
+ margin-bottom: 10px;
70
+ background-color: #555;
71
+ color: #eee;
72
+ border: 1px solid #777;
73
+ border-radius: 5px;
74
+ cursor: pointer;
75
+ text-align: left;
76
+ font-family: 'Courier New', monospace;
77
+ font-size: 1em;
78
+ transition: background-color 0.2s;
79
  }
80
 
81
+ .choice-button:hover {
82
+ background-color: #d4a017; /* Gold hover */
83
+ color: #222;
 
 
 
84
  }
85
+ .choice-button:disabled {
86
+ background-color: #444;
87
+ color: #888;
88
+ cursor: not-allowed;
89
+ border-color: #666;
90
+ }
91
 
92
+ #stats-inventory-container {
93
+ margin-top: 20px;
94
+ padding-top: 15px;
95
+ border-top: 1px solid #555;
96
+ font-size: 0.9em;
97
+ }
98
+ #stats-display, #inventory-display {
99
+ margin-bottom: 10px;
100
+ }
101
+ #stats-display span, #inventory-display span {
102
+ display: inline-block;
103
+ background-color: #444;
104
+ padding: 3px 8px;
105
+ border-radius: 15px;
106
+ margin-right: 8px;
107
+ margin-bottom: 5px; /* Wrap nicely */
108
+ border: 1px solid #666;
109
  }
110
+ #inventory-display .item-quest { background-color: #666030; border-color: #999048;}
111
+ #inventory-display .item-weapon { background-color: #663030; border-color: #994848;}
112
+ #inventory-display .item-armor { background-color: #306630; border-color: #489948;}
113
+ #inventory-display .item-spell { background-color: #303066; border-color: #484899;}
 
 
 
114
 
115
 
116
+ canvas { display: block; } /* Prevent extra space below canvas */
 
 
117
 
118
+ </style>
119
+ </head>
120
+ <body>
121
 
122
+ <div id="game-container">
123
+ <div id="scene-container"></div>
124
+
125
+ <div id="ui-container">
126
+ <h2 id="story-title">Loading Adventure...</h2>
127
+ <div id="story-content">
128
+ <p>Please wait while the adventure loads.</p>
129
+ </div>
130
+
131
+ <div id="stats-inventory-container">
132
+ <div id="stats-display">
133
+ </div>
134
+ <div id="inventory-display">
135
+ </div>
136
+ </div>
137
+
138
+ <div id="choices-container">
139
+ <h3>What will you do?</h3>
140
+ <div id="choices">
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
145
 
146
+ <script type="importmap">
147
+ {
148
+ "imports": {
149
+ "three": "https://unpkg.com/[email protected]/build/three.module.js",
150
+ "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
151
+ }
152
  }
153
+ </script>
154
 
155
+ <script type="module" src="game.js"></script>
 
 
 
 
 
 
 
156
 
 
157
  </body>
158
  </html>