From 2cd901749a8ee8c7dc1e0220e2abc8735dcf3212 Mon Sep 17 00:00:00 2001 From: Andy Aylward Date: Sat, 13 Sep 2025 19:51:09 -0400 Subject: [PATCH] remove aurum siphon --- package-lock.json | 62 +- package.json | 4 +- src/App.tsx | 2 - src/components/AurumSiphonGame.tsx | 135 ---- .../AurumSiphonNavigation.module.css | 55 -- src/components/AurumSiphonNavigation.tsx | 25 - src/hooks/useAurumSiphonGame.ts | 647 ------------------ src/pages/AurumSiphonPage.tsx | 16 - src/utils/aurumSiphon/blackHoleTransition.ts | 237 ------- src/utils/aurumSiphon/gameState.ts | 49 -- src/utils/aurumSiphon/laserSystem.ts | 254 ------- src/utils/aurumSiphon/levelManager.ts | 173 ----- src/utils/aurumSiphon/magneticFunnel.ts | 197 ------ src/utils/aurumSiphon/networkManager.ts | 198 ------ src/utils/aurumSiphon/proceduralSpace.ts | 370 ---------- src/utils/aurumSiphon/solarWindSystem.ts | 181 ----- src/utils/aurumSiphon/spaceshipController.ts | 300 -------- src/utils/shared/cameraController.ts | 150 ---- src/utils/witchGame/birdSystem.ts | 294 -------- src/utils/witchGame/broomstickController.ts | 237 ------- src/utils/witchGame/nightSkyEnvironment.ts | 240 ------- 21 files changed, 3 insertions(+), 3823 deletions(-) delete mode 100644 src/components/AurumSiphonGame.tsx delete mode 100644 src/components/AurumSiphonNavigation.module.css delete mode 100644 src/components/AurumSiphonNavigation.tsx delete mode 100644 src/hooks/useAurumSiphonGame.ts delete mode 100644 src/pages/AurumSiphonPage.tsx delete mode 100644 src/utils/aurumSiphon/blackHoleTransition.ts delete mode 100644 src/utils/aurumSiphon/gameState.ts delete mode 100644 src/utils/aurumSiphon/laserSystem.ts delete mode 100644 src/utils/aurumSiphon/levelManager.ts delete mode 100644 src/utils/aurumSiphon/magneticFunnel.ts delete mode 100644 src/utils/aurumSiphon/networkManager.ts delete mode 100644 src/utils/aurumSiphon/proceduralSpace.ts delete mode 100644 src/utils/aurumSiphon/solarWindSystem.ts delete mode 100644 src/utils/aurumSiphon/spaceshipController.ts delete mode 100644 src/utils/shared/cameraController.ts delete mode 100644 src/utils/witchGame/birdSystem.ts delete mode 100644 src/utils/witchGame/broomstickController.ts delete mode 100644 src/utils/witchGame/nightSkyEnvironment.ts diff --git a/package-lock.json b/package-lock.json index d886d05..66336cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,11 @@ "name": "muchq-portfolio", "version": "0.0.0", "dependencies": { - "@types/three": "^0.179.0", "react": "^19.1.0", "react-dom": "^19.1.1", "react-router-dom": "^7.8.2", "react-select": "^5.10.2", - "recharts": "^3.1.2", - "three": "^0.179.1" + "recharts": "^3.1.2" }, "devDependencies": { "@testing-library/jest-dom": "^6.8.0", @@ -635,12 +633,6 @@ "node": ">=18" } }, - "node_modules/@dimforge/rapier3d-compat": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", - "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", - "license": "Apache-2.0" - }, "node_modules/@emnapi/runtime": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", @@ -2446,12 +2438,6 @@ } } }, - "node_modules/@tweenjs/tween.js": { - "version": "23.1.3", - "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", - "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", - "license": "MIT" - }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -2633,39 +2619,12 @@ "@types/react": "*" } }, - "node_modules/@types/stats.js": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", - "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", - "license": "MIT" - }, - "node_modules/@types/three": { - "version": "0.179.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.179.0.tgz", - "integrity": "sha512-VgbFG2Pgsm84BqdegZzr7w2aKbQxmgzIu4Dy7/75ygiD/0P68LKmp5ie08KMPNqGTQwIge8s6D1guZf1RnZE0A==", - "license": "MIT", - "dependencies": { - "@dimforge/rapier3d-compat": "~0.12.0", - "@tweenjs/tween.js": "~23.1.3", - "@types/stats.js": "*", - "@types/webxr": "*", - "@webgpu/types": "*", - "fflate": "~0.8.2", - "meshoptimizer": "~0.22.0" - } - }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, - "node_modules/@types/webxr": { - "version": "0.5.22", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.22.tgz", - "integrity": "sha512-Vr6Stjv5jPRqH690f5I5GLjVk8GSsoQSYJ2FVd/3jJF7KaqfwPi3ehfBS96mlQ2kPCwZaX6U0rG2+NGHBKkA/A==", - "license": "MIT" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.43.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz", @@ -3059,12 +3018,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@webgpu/types": { - "version": "0.1.64", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.64.tgz", - "integrity": "sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A==", - "license": "BSD-3-Clause" - }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -4206,6 +4159,7 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, "license": "MIT" }, "node_modules/file-entry-cache": { @@ -4816,12 +4770,6 @@ "node": ">= 8" } }, - "node_modules/meshoptimizer": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz", - "integrity": "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==", - "license": "MIT" - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -5881,12 +5829,6 @@ "dev": true, "license": "MIT" }, - "node_modules/three": { - "version": "0.179.1", - "resolved": "https://registry.npmjs.org/three/-/three-0.179.1.tgz", - "integrity": "sha512-5y/elSIQbrvKOISxpwXCR4sQqHtGiOI+MKLc3SsBdDXA2hz3Mdp3X59aUp8DyybMa34aeBwbFTpdoLJaUDEWSw==", - "license": "MIT" - }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", diff --git a/package.json b/package.json index 331b4f4..b256ba3 100644 --- a/package.json +++ b/package.json @@ -19,13 +19,11 @@ "deploy": "wrangler deploy" }, "dependencies": { - "@types/three": "^0.179.0", "react": "^19.1.0", "react-dom": "^19.1.1", "react-router-dom": "^7.8.2", "react-select": "^5.10.2", - "recharts": "^3.1.2", - "three": "^0.179.1" + "recharts": "^3.1.2" }, "devDependencies": { "@testing-library/jest-dom": "^6.8.0", diff --git a/src/App.tsx b/src/App.tsx index fc517de..6fb2953 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,6 @@ import GroupsPage from './pages/GroupsPage' import SetsPage from './pages/SetsPage' import GolfPage from './pages/GolfPage' import PartyPage from './pages/PartyPage' -import AurumSiphonPage from './pages/AurumSiphonPage' import QuestPage from './pages/QuestPage' import LearningPage from './pages/LearningPage' import TracyPage from './pages/TracyPage' @@ -22,7 +21,6 @@ function App() { } /> } /> } /> - } /> } /> } /> } /> diff --git a/src/components/AurumSiphonGame.tsx b/src/components/AurumSiphonGame.tsx deleted file mode 100644 index cae10a2..0000000 --- a/src/components/AurumSiphonGame.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import { useEffect, useRef } from 'react' -import { useAurumSiphonGame } from '@/hooks/useAurumSiphonGame' -import styles from './AurumSiphonGame.module.css' - -interface AurumSiphonGameProps { - onPlayerIdReceived: (playerId: string) => void -} - -const AurumSiphonGame = ({ onPlayerIdReceived }: AurumSiphonGameProps) => { - const containerRef = useRef(null) - const { initializeGame } = useAurumSiphonGame() - - useEffect(() => { - const container = containerRef.current - if (!container) return - - const cleanup = initializeGame(container, onPlayerIdReceived) - - return cleanup - }, [initializeGame, onPlayerIdReceived]) - - return ( -
-
- -
-
-
- Level - 1 -
-
- Gold Collected - 0 mg -
-
- -
-
- - 100% -
-
- 🧲 - 100% -
-
- 🛡️ - 100% -
-
-
- -
- -
- -
- - -
- -
-
-
- -
-
-
- -
-

Level Complete!

-

- -
- - - -
-
- -

How to Play Aurum Siphon

- -
-

🎮 Desktop Controls

-
-
W - Move forward
-
S - Move backward
-
A - Move left
-
D - Move right
-
Q - Move up
-
E - Move down
-
- Zoom in
-
- Zoom out
-
- Rotate camera left
-
- Rotate camera right
-
Space - Shoot laser
-
B - Boost (uses power)
-
F - Toggle magnetic funnel
-
H - Toggle this help screen
-
ESC - Close help
-
-
- -
-

📱 Mobile Controls

-

• Left joystick - Movement (forward/back/left/right)

-

• Right joystick - Camera (up/down for zoom, left/right for rotation)

-

• On-screen buttons for Boost & Funnel

-
- -
-

🎯 Objective

-

Collect gold ions from the solar wind using your magnetic funnel. Reach the target amount to advance to the next level.

-
- -
-

💡 Tips

-
    -
  • Gold particles are golden, platinum is silver
  • -
  • The magnetic funnel attracts heavier particles more strongly
  • -
  • Boost helps catch fast particles but drains power
  • -
  • Toggle the funnel off periodically to let it recharge
  • -
  • Watch the minimap to track incoming particle streams
  • -
  • Position yourself in the path of particle clusters
  • -
  • Press Space to shoot lasers and destroy asteroids and planets!
  • -
-
-
-
-
- ) -} - -export default AurumSiphonGame \ No newline at end of file diff --git a/src/components/AurumSiphonNavigation.module.css b/src/components/AurumSiphonNavigation.module.css deleted file mode 100644 index de7e55b..0000000 --- a/src/components/AurumSiphonNavigation.module.css +++ /dev/null @@ -1,55 +0,0 @@ -/* Aurum Siphon Navigation Styles - Matching Thoughts/Golf Navigation */ -.nav { - position: fixed; - top: 0; - left: 0; - width: 100%; - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(10px); - border-bottom: 1px solid rgba(68, 68, 68, 0.1); - z-index: 1000; - transition: all 0.3s ease; -} - -.navContainer { - max-width: 650px; - margin: 0 auto; - padding: 0 20px; - display: flex; - justify-content: space-between; - align-items: center; - height: 60px; -} - -.centerContent { - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 4px; -} - -.navLogo { - font-size: 20px; - font-weight: 300; - color: #444; - text-decoration: none; -} - -.playerId { - font-size: 12px; - color: #888; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -/* Mobile Styles */ -@media (max-width: 768px) { - .playerId { - font-size: 10px; - } - - .centerContent { - gap: 2px; - } -} \ No newline at end of file diff --git a/src/components/AurumSiphonNavigation.tsx b/src/components/AurumSiphonNavigation.tsx deleted file mode 100644 index 2c028b1..0000000 --- a/src/components/AurumSiphonNavigation.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Link } from 'react-router-dom' -import styles from './AurumSiphonNavigation.module.css' - -interface AurumSiphonNavigationProps { - playerId: string | null -} - -const AurumSiphonNavigation = ({ playerId }: AurumSiphonNavigationProps) => { - return ( - - ) -} - -export default AurumSiphonNavigation \ No newline at end of file diff --git a/src/hooks/useAurumSiphonGame.ts b/src/hooks/useAurumSiphonGame.ts deleted file mode 100644 index 1292bd9..0000000 --- a/src/hooks/useAurumSiphonGame.ts +++ /dev/null @@ -1,647 +0,0 @@ -import { useCallback } from 'react' -import * as THREE from 'three' -import { GameState } from '@/utils/aurumSiphon/gameState' -import { SpaceshipController } from '@/utils/aurumSiphon/spaceshipController' -import { SolarWindSystem } from '@/utils/aurumSiphon/solarWindSystem' -import { MagneticFunnel } from '@/utils/aurumSiphon/magneticFunnel' -import { NetworkManager } from '@/utils/aurumSiphon/networkManager' -import { VirtualJoystick } from '@/utils/virtualJoystick' -import { LevelManager } from '@/utils/aurumSiphon/levelManager' -import { ProceduralSpace } from '@/utils/aurumSiphon/proceduralSpace' -import { LaserSystem } from '@/utils/aurumSiphon/laserSystem' -import { BlackHoleTransition } from '@/utils/aurumSiphon/blackHoleTransition' -import { BroomstickController } from '@/utils/witchGame/broomstickController' -import { NightSkyEnvironment } from '@/utils/witchGame/nightSkyEnvironment' -import { BirdSystem } from '@/utils/witchGame/birdSystem' - -export const useAurumSiphonGame = () => { - const initializeGame = useCallback((container: HTMLDivElement, onPlayerIdReceived?: (playerId: string) => void) => { - // Initializing Aurum Siphon game - - const scene = new THREE.Scene() - scene.fog = new THREE.Fog(0x000033, 100, 1500) - - const camera = new THREE.PerspectiveCamera( - 75, // Wider FOV for racing game feel - container.clientWidth / container.clientHeight, - 0.1, - 3000 - ) - // Camera position will be set by CameraController - - const renderer = new THREE.WebGLRenderer({ antialias: true }) - renderer.setSize(container.clientWidth, container.clientHeight) - renderer.setPixelRatio(window.devicePixelRatio) - renderer.shadowMap.enabled = true - renderer.shadowMap.type = THREE.PCFSoftShadowMap - container.appendChild(renderer.domElement) - - // Brighter ambient light for better visibility - const ambientLight = new THREE.AmbientLight(0x606080, 1.2) // Blueish ambient, brighter - scene.add(ambientLight) - - // Main sun light - brighter and warmer - const sunLight = new THREE.DirectionalLight(0xffffaa, 3) // Increased intensity - sunLight.position.set(-1000, 100, 0) // Slightly above for better shadows - sunLight.castShadow = true - sunLight.shadow.camera.left = -100 - sunLight.shadow.camera.right = 100 - sunLight.shadow.camera.top = 100 - sunLight.shadow.camera.bottom = -100 - scene.add(sunLight) - - // Add a secondary fill light from above - const fillLight = new THREE.DirectionalLight(0x8888ff, 0.5) - fillLight.position.set(0, 500, 0) - scene.add(fillLight) - - // Add some rim lighting from behind - const rimLight = new THREE.DirectionalLight(0x4466ff, 0.8) - rimLight.position.set(500, 200, 500) - scene.add(rimLight) - - // Create multi-layer parallax starfield with hyperspace effect - const starLayers: THREE.Points[] = [] - const createStarfield = () => { - // Create multiple layers for parallax effect - for (let layer = 0; layer < 3; layer++) { - const starsGeometry = new THREE.BufferGeometry() - const starCount = layer === 0 ? 5000 : layer === 1 ? 3000 : 2000 - const size = layer === 0 ? 0.3 : layer === 1 ? 0.6 : 1.0 - const spread = 1000 + layer * 500 - - const starsMaterial = new THREE.PointsMaterial({ - color: layer === 0 ? 0x8888ff : layer === 1 ? 0xaaaaff : 0xffffff, - size: size, - transparent: true, - opacity: 0.6 + layer * 0.2, - blending: THREE.AdditiveBlending - }) - - const starsVertices = [] - for (let i = 0; i < starCount; i++) { - const x = (Math.random() - 0.5) * spread * 2 - const y = (Math.random() - 0.5) * spread * 2 - const z = (Math.random() - 0.5) * spread * 2 - starsVertices.push(x, y, z) - } - - starsGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starsVertices, 3)) - const starField = new THREE.Points(starsGeometry, starsMaterial) - scene.add(starField) - starLayers.push(starField) - } - - // Add hyperspace streaks - const streakGeometry = new THREE.BufferGeometry() - const streakMaterial = new THREE.PointsMaterial({ - color: 0x4488ff, - size: 2, - transparent: true, - opacity: 0.3, - blending: THREE.AdditiveBlending, - vertexColors: true - }) - - const streakPositions = [] - const streakColors = [] - for (let i = 0; i < 500; i++) { - const angle = Math.random() * Math.PI * 2 - const radius = 100 + Math.random() * 900 - const x = Math.cos(angle) * radius - const y = (Math.random() - 0.5) * 500 - const z = Math.sin(angle) * radius - streakPositions.push(x, y, z) - - // Color variation for streaks - const intensity = Math.random() * 0.5 + 0.5 - streakColors.push(intensity * 0.4, intensity * 0.6, intensity) - } - - streakGeometry.setAttribute('position', new THREE.Float32BufferAttribute(streakPositions, 3)) - streakGeometry.setAttribute('color', new THREE.Float32BufferAttribute(streakColors, 3)) - const streaks = new THREE.Points(streakGeometry, streakMaterial) - scene.add(streaks) - starLayers.push(streaks) - } - createStarfield() - - // Note: Using ProceduralSpace for asteroids instead of static debris - - // Add distant space stations or structures - const createSpaceStructures = () => { - // Large space station in the distance - const stationGroup = new THREE.Group() - - // Main ring structure - const ringGeometry = new THREE.TorusGeometry(30, 5, 8, 20) - const ringMaterial = new THREE.MeshPhongMaterial({ - color: 0x445566, - emissive: 0x112233, - emissiveIntensity: 0.3 - }) - const ring = new THREE.Mesh(ringGeometry, ringMaterial) - ring.rotation.x = Math.PI / 2 - stationGroup.add(ring) - - // Central hub - const hubGeometry = new THREE.SphereGeometry(8, 16, 16) - const hubMaterial = new THREE.MeshPhongMaterial({ - color: 0x556677, - emissive: 0x223344, - emissiveIntensity: 0.4 - }) - const hub = new THREE.Mesh(hubGeometry, hubMaterial) - stationGroup.add(hub) - - // Add some lights to the station - const stationLight = new THREE.PointLight(0x6699ff, 0.5, 100) - stationGroup.add(stationLight) - - stationGroup.position.set(150, 30, -200) - scene.add(stationGroup) - - // Animate the station - const animateStation = () => { - stationGroup.rotation.y += 0.0005 - ring.rotation.z += 0.001 - } - - return animateStation - } - const animateStation = createSpaceStructures() - - const createSun = () => { - const sunGeometry = new THREE.SphereGeometry(50, 32, 32) - const sunMaterial = new THREE.MeshBasicMaterial({ - color: 0xffff88 - }) - const sun = new THREE.Mesh(sunGeometry, sunMaterial) - sun.position.set(-1000, 0, 0) - scene.add(sun) - - // Brighter, larger corona - const coronaGeometry = new THREE.SphereGeometry(80, 32, 32) - const coronaMaterial = new THREE.MeshBasicMaterial({ - color: 0xffcc00, - transparent: true, - opacity: 0.5 - }) - const corona = new THREE.Mesh(coronaGeometry, coronaMaterial) - corona.position.copy(sun.position) - scene.add(corona) - - // Add sun glow effect - const glowGeometry = new THREE.SphereGeometry(120, 16, 16) - const glowMaterial = new THREE.MeshBasicMaterial({ - color: 0xffaa00, - transparent: true, - opacity: 0.2 - }) - const glow = new THREE.Mesh(glowGeometry, glowMaterial) - glow.position.copy(sun.position) - scene.add(glow) - - // Add point light at sun position for actual lighting - const sunPointLight = new THREE.PointLight(0xffffaa, 2, 2000) - sunPointLight.position.copy(sun.position) - scene.add(sunPointLight) - } - createSun() - - const gameState = new GameState() - const networkManager = new NetworkManager(gameState) - const levelManager = new LevelManager(gameState) - - if (onPlayerIdReceived) { - networkManager.onPlayerIdReceived = onPlayerIdReceived - } - - const spaceshipController = new SpaceshipController(scene, camera) - const solarWindSystem = new SolarWindSystem(scene) - const magneticFunnel = new MagneticFunnel(scene, spaceshipController) - const proceduralSpace = new ProceduralSpace(scene) - const laserSystem = new LaserSystem(scene) - - const leftJoystickElement = document.getElementById('left-joystick') as HTMLElement - const leftKnobElement = document.getElementById('left-knob') as HTMLElement - const rightJoystickElement = document.getElementById('right-joystick') as HTMLElement - const rightKnobElement = document.getElementById('right-knob') as HTMLElement - - let leftJoystick: VirtualJoystick | null = null - let rightJoystick: VirtualJoystick | null = null - - if (leftJoystickElement && leftKnobElement) { - leftJoystick = new VirtualJoystick(leftJoystickElement, leftKnobElement) - } - - if (rightJoystickElement && rightKnobElement) { - rightJoystick = new VirtualJoystick(rightJoystickElement, rightKnobElement) - } - - const boostButton = document.getElementById('boost-button') - const funnelToggle = document.getElementById('funnel-toggle') - const nextLevelButton = document.getElementById('next-level-button') - const helpButton = document.getElementById('help-button') - const helpScreen = document.getElementById('help-screen') - const helpClose = document.getElementById('help-close') - - boostButton?.addEventListener('click', () => { - spaceshipController.activateBoost() - }) - - funnelToggle?.addEventListener('click', () => { - magneticFunnel.toggle() - funnelToggle.textContent = magneticFunnel.isActive ? 'FUNNEL: ON' : 'FUNNEL: OFF' - }) - - // Game mode state - let gameMode: 'spaceship' | 'witch' = 'spaceship' - let broomstickController: BroomstickController | null = null - let nightSkyEnvironment: NightSkyEnvironment | null = null - let birdSystem: BirdSystem | null = null - let birdsCollected = 0 - - const switchToWitchMode = () => { - // Clean up spaceship mode - spaceshipController.cleanup() - solarWindSystem.reset() - magneticFunnel.cleanup() - proceduralSpace.cleanup() - laserSystem.cleanup() - - // Clear the scene except camera and renderer - while(scene.children.length > 0) { - const child = scene.children[0] - scene.remove(child) - if (child instanceof THREE.Mesh || child instanceof THREE.Group) { - child.traverse((node) => { - if (node instanceof THREE.Mesh) { - node.geometry?.dispose() - if (node.material instanceof THREE.Material) { - node.material.dispose() - } - } - }) - } - } - - // Initialize witch mode - gameMode = 'witch' - nightSkyEnvironment = new NightSkyEnvironment(scene) - broomstickController = new BroomstickController(scene, camera) - birdSystem = new BirdSystem(scene) - birdsCollected = 0 - - // Update HUD labels for witch mode - const goldLabel = document.querySelector('#gold-display')?.previousElementSibling - if (goldLabel) goldLabel.textContent = 'Birds:' - - const powerLabel = document.querySelector('#power-display')?.previousElementSibling - if (powerLabel) powerLabel.textContent = 'Speed:' - - const magneticLabel = document.querySelector('#magnetic-display')?.previousElementSibling - if (magneticLabel) magneticLabel.textContent = 'Magic:' - - const shieldLabel = document.querySelector('#shield-display')?.previousElementSibling - if (shieldLabel) shieldLabel.textContent = 'Health:' - - // Hide spaceship-specific buttons - const funnelToggle = document.getElementById('funnel-toggle') - if (funnelToggle) funnelToggle.style.display = 'none' - } - - nextLevelButton?.addEventListener('click', () => { - const levelCompleteDiv = document.getElementById('level-complete') - if (levelCompleteDiv) { - levelCompleteDiv.style.display = 'none' - } - - if (levelManager.currentLevel === 1) { - // Trigger transition to witch mode - const transition = new BlackHoleTransition(scene, camera, () => { - switchToWitchMode() - }) - transition.start() - } - - levelManager.nextLevel() - if (gameMode === 'spaceship') { - solarWindSystem.setLevel(levelManager.currentLevel) - } - }) - - helpButton?.addEventListener('click', () => { - if (helpScreen) { - helpScreen.style.display = 'block' - } - }) - - helpClose?.addEventListener('click', () => { - if (helpScreen) { - helpScreen.style.display = 'none' - } - }) - - const keys: Record = {} - - const handleKeyDown = (e: KeyboardEvent) => { - // Store both the lowercase version and the original key - const key = e.key - keys[key] = true - keys[key.toLowerCase()] = true - - // Prevent default for arrow keys to avoid scrolling - if (key.startsWith('Arrow')) { - e.preventDefault() - } - - if (key === ' ' && gameMode === 'spaceship') { - e.preventDefault() - // Shoot laser only in spaceship mode - const shipPos = spaceshipController.getPosition() - const cameraDir = new THREE.Vector3() - camera.getWorldDirection(cameraDir) - laserSystem.shoot(shipPos, cameraDir) - } - - if (key.toLowerCase() === 'b') { - spaceshipController.activateBoost() - } - - if (key.toLowerCase() === 'f') { - magneticFunnel.toggle() - if (funnelToggle) { - funnelToggle.textContent = magneticFunnel.isActive ? 'FUNNEL: ON' : 'FUNNEL: OFF' - } - } - - if (key.toLowerCase() === 'h') { - if (helpScreen) { - helpScreen.style.display = helpScreen.style.display === 'block' ? 'none' : 'block' - } - } - - if (key === 'Escape' && helpScreen) { - helpScreen.style.display = 'none' - } - } - - const handleKeyUp = (e: KeyboardEvent) => { - const key = e.key - keys[key] = false - keys[key.toLowerCase()] = false - } - - window.addEventListener('keydown', handleKeyDown) - window.addEventListener('keyup', handleKeyUp) - - const handleResize = () => { - camera.aspect = container.clientWidth / container.clientHeight - camera.updateProjectionMatrix() - renderer.setSize(container.clientWidth, container.clientHeight) - } - window.addEventListener('resize', handleResize) - - const updateHUD = () => { - const levelDisplay = document.getElementById('level-display') - const goldDisplay = document.getElementById('gold-display') - const powerDisplay = document.getElementById('power-display') - const magneticDisplay = document.getElementById('magnetic-display') - const shieldDisplay = document.getElementById('shield-display') - - if (levelDisplay) levelDisplay.textContent = levelManager.currentLevel.toString() - - if (gameMode === 'witch') { - // Witch mode HUD - if (goldDisplay) { - goldDisplay.textContent = `${birdsCollected}` - } - if (powerDisplay) { - const speed = keys['Shift'] || keys['b'] ? 'FAST' : 'Normal' - powerDisplay.textContent = speed - } - if (magneticDisplay) { - magneticDisplay.textContent = '✨' - } - if (shieldDisplay) { - shieldDisplay.textContent = '100%' - } - } else { - // Spaceship mode HUD - if (goldDisplay) { - const pos = spaceshipController.getPosition() - // Display in kg when over 1000000, tons when over 1000000000 - let goldText = '' - if (gameState.goldCollected >= 1000000000) { - goldText = `${(gameState.goldCollected / 1000000000).toFixed(2)} tons` - } else if (gameState.goldCollected >= 1000000) { - goldText = `${(gameState.goldCollected / 1000000).toFixed(2)} kg` - } else if (gameState.goldCollected >= 1000) { - goldText = `${(gameState.goldCollected / 1000).toFixed(2)} g` - } else { - goldText = `${gameState.goldCollected.toFixed(2)} mg` - } - goldDisplay.textContent = `${goldText} | Pos: ${pos.x.toFixed(0)}, ${pos.y.toFixed(0)}, ${pos.z.toFixed(0)}` - } - if (powerDisplay) powerDisplay.textContent = `${Math.round(spaceshipController.power)}%` - if (magneticDisplay) magneticDisplay.textContent = `${Math.round(magneticFunnel.efficiency * 100)}%` - if (shieldDisplay) shieldDisplay.textContent = `${Math.round(spaceshipController.shield)}%` - } - } - - const updateMiniMap = () => { - const canvas = document.getElementById('mini-map-canvas') as HTMLCanvasElement - if (!canvas) return - - const ctx = canvas.getContext('2d') - if (!ctx) return - - canvas.width = 150 - canvas.height = 150 - - ctx.fillStyle = '#001122' - ctx.fillRect(0, 0, canvas.width, canvas.height) - - ctx.strokeStyle = '#0066cc' - ctx.strokeRect(0, 0, canvas.width, canvas.height) - - const shipPos = spaceshipController.getPosition() - const mapX = (shipPos.x / 200) * canvas.width / 2 + canvas.width / 2 - const mapZ = (shipPos.z / 200) * canvas.height / 2 + canvas.height / 2 - - ctx.fillStyle = '#00ff00' - ctx.beginPath() - ctx.arc(mapX, mapZ, 3, 0, Math.PI * 2) - ctx.fill() - - solarWindSystem.getParticles().forEach(particle => { - const pX = (particle.position.x / 200) * canvas.width / 2 + canvas.width / 2 - const pZ = (particle.position.z / 200) * canvas.height / 2 + canvas.height / 2 - - ctx.fillStyle = particle.userData.type === 'gold' ? '#ffdd00' : '#4488ff' - ctx.fillRect(pX - 1, pZ - 1, 2, 2) - }) - } - - let animationId: number - - const animate = () => { - animationId = requestAnimationFrame(animate) - - const moveInput = { - x: 0, - y: 0, - z: 0 - } - - const cameraInput = { - zoom: 0, - rotate: 0 - } - - // Check for joystick input first (mobile) - if (leftJoystick && leftJoystick.active) { - moveInput.x = leftJoystick.getInput().x - moveInput.z = -leftJoystick.getInput().y - } - - // Always check keyboard input (can override joystick) - if (keys['a']) moveInput.x = -1 - if (keys['d']) moveInput.x = 1 - if (keys['w']) moveInput.z = 1 // W should move forward (positive Z in camera space) - if (keys['s']) moveInput.z = -1 // S should move backward (negative Z in camera space) - if (keys['q']) moveInput.y = 1 // Fixed: Q should go up (positive Y) - if (keys['e']) moveInput.y = -1 // Fixed: E should go down (negative Y) - - // Camera control with right joystick or arrow keys - if (rightJoystick) { - cameraInput.zoom = -rightJoystick.getInput().y - cameraInput.rotate = rightJoystick.getInput().x - } - - // Arrow keys override joystick for camera - if (keys['ArrowUp']) cameraInput.zoom = -1 - if (keys['ArrowDown']) cameraInput.zoom = 1 - if (keys['ArrowLeft']) cameraInput.rotate = 1 // Left arrow rotates camera left (positive rotation) - if (keys['ArrowRight']) cameraInput.rotate = -1 // Right arrow rotates camera right (negative rotation) - - if (gameMode === 'spaceship') { - spaceshipController.update(moveInput, cameraInput) - solarWindSystem.update() - magneticFunnel.update() - proceduralSpace.update(spaceshipController.getPosition()) - laserSystem.update(proceduralSpace.getSpaceObjects()) - animateStation() - } else if (gameMode === 'witch' && broomstickController && nightSkyEnvironment && birdSystem) { - // Update witch mode - // For witch: A/D turns, W/S controls pitch (up/down), arrow keys rotate camera - const broomInput = { - move: new THREE.Vector2(moveInput.x, -moveInput.z), // W/S mapped to pitch - rotate: cameraInput.rotate, - zoom: cameraInput.zoom, - boost: keys['Shift'] || keys['b'] - } - - broomstickController.update(broomInput) - nightSkyEnvironment.update() - birdSystem.update(0.016, broomstickController.getPosition()) - - // Check bird collection - const collectedBirds = birdSystem.checkCollection(broomstickController.getPosition()) - collectedBirds.forEach(birdType => { - birdsCollected++ - gameState.score += birdType === 'phoenix' ? 1000 : - birdType === 'owl' ? 500 : - birdType === 'bat' ? 300 : 200 - }) - } - - if (gameMode === 'spaceship') { - // Animate parallax star layers - const shipVel = spaceshipController.getVelocity() - starLayers.forEach((layer, index) => { - const parallaxSpeed = 0.1 * (index + 1) * 0.3 // Different speeds for each layer - layer.position.x -= shipVel.x * parallaxSpeed - layer.position.z -= shipVel.z * parallaxSpeed - - // Wrap around when too far - if (Math.abs(layer.position.x) > 1000) layer.position.x *= -0.9 - if (Math.abs(layer.position.z) > 1000) layer.position.z *= -0.9 - - // Rotate slowly for hyperspace effect - if (index === 3) { // Streaks layer - layer.rotation.z += 0.0002 - } - }) - - const collectedParticles = magneticFunnel.checkCollection(solarWindSystem.getParticles()) - collectedParticles.forEach(particle => { - if (particle.userData.type === 'gold') { - gameState.goldCollected += 1000.0 // Massive gold collection - gameState.score += 100 - } else if (particle.userData.type === 'platinum') { - gameState.platinumCollected += 500.0 - gameState.score += 200 - } else { - gameState.otherMetalsCollected += 100.0 - gameState.score += 50 - } - solarWindSystem.removeParticle(particle) - }) - } - - if (levelManager.checkLevelComplete()) { - const levelCompleteDiv = document.getElementById('level-complete') - const levelStats = document.getElementById('level-stats') - if (levelCompleteDiv && levelStats) { - levelCompleteDiv.style.display = 'block' - let goldText = '' - if (gameState.goldCollected >= 1000000000) { - goldText = `${(gameState.goldCollected / 1000000000).toFixed(2)} tons` - } else if (gameState.goldCollected >= 1000000) { - goldText = `${(gameState.goldCollected / 1000000).toFixed(2)} kg` - } else if (gameState.goldCollected >= 1000) { - goldText = `${(gameState.goldCollected / 1000).toFixed(2)} g` - } else { - goldText = `${gameState.goldCollected.toFixed(2)} mg` - } - levelStats.textContent = `Gold collected: ${goldText} | Score: ${gameState.score}` - } - } - - updateHUD() - updateMiniMap() - - renderer.render(scene, camera) - } - - animate() - - const cleanup = () => { - window.removeEventListener('keydown', handleKeyDown) - window.removeEventListener('keyup', handleKeyUp) - window.removeEventListener('resize', handleResize) - - if (animationId) { - cancelAnimationFrame(animationId) - } - - // Event listeners are automatically cleaned up when elements are removed - - leftJoystick?.destroy() - rightJoystick?.destroy() - - laserSystem.cleanup() - - renderer.dispose() - renderer.domElement.remove() - - networkManager.disconnect() - } - - return cleanup - }, []) - - return { initializeGame } -} \ No newline at end of file diff --git a/src/pages/AurumSiphonPage.tsx b/src/pages/AurumSiphonPage.tsx deleted file mode 100644 index 0bb6794..0000000 --- a/src/pages/AurumSiphonPage.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { useState } from 'react' -import AurumSiphonNavigation from '@/components/AurumSiphonNavigation' -import AurumSiphonGame from '@/components/AurumSiphonGame' - -const AurumSiphonPage = () => { - const [playerId, setPlayerId] = useState(null) - - return ( -
- - -
- ) -} - -export default AurumSiphonPage \ No newline at end of file diff --git a/src/utils/aurumSiphon/blackHoleTransition.ts b/src/utils/aurumSiphon/blackHoleTransition.ts deleted file mode 100644 index b93beea..0000000 --- a/src/utils/aurumSiphon/blackHoleTransition.ts +++ /dev/null @@ -1,237 +0,0 @@ -import * as THREE from 'three' - -export class BlackHoleTransition { - private scene: THREE.Scene - private particles: THREE.Points[] = [] - private vortexMesh: THREE.Mesh | null = null - private transitionCallback: () => void - private isTransitioning: boolean = false - - constructor(scene: THREE.Scene, _camera: THREE.Camera, callback: () => void) { - this.scene = scene - this.transitionCallback = callback - } - - start() { - if (this.isTransitioning) return - this.isTransitioning = true - - // Create vortex geometry - const vortexGeometry = new THREE.ConeGeometry(50, 100, 32, 1, true) - const vortexMaterial = new THREE.MeshBasicMaterial({ - color: 0x000000, - transparent: true, - opacity: 0, - side: THREE.DoubleSide - }) - - this.vortexMesh = new THREE.Mesh(vortexGeometry, vortexMaterial) - this.vortexMesh.position.set(0, 0, -50) - this.scene.add(this.vortexMesh) - - // Create melting particles from screen edges - this.createMeltingParticles() - - // Start animation sequence - this.animateTransition() - } - - private createMeltingParticles() { - const particleCount = 5000 - - for (let i = 0; i < 10; i++) { - const geometry = new THREE.BufferGeometry() - const positions = new Float32Array(particleCount * 3) - const colors = new Float32Array(particleCount * 3) - const velocities = new Float32Array(particleCount * 3) - - for (let j = 0; j < particleCount; j++) { - // Start from screen edges - const angle = Math.random() * Math.PI * 2 - const radius = 100 + Math.random() * 50 - - positions[j * 3] = Math.cos(angle) * radius - positions[j * 3 + 1] = (Math.random() - 0.5) * 200 - positions[j * 3 + 2] = -50 + Math.random() * 10 - - // Rainbow colors - const hue = (i / 10 + j / particleCount) % 1 - const color = new THREE.Color() - color.setHSL(hue, 1, 0.5) - - colors[j * 3] = color.r - colors[j * 3 + 1] = color.g - colors[j * 3 + 2] = color.b - - // Spiral velocity toward center - velocities[j * 3] = -positions[j * 3] * 0.01 - velocities[j * 3 + 1] = -positions[j * 3 + 1] * 0.01 - velocities[j * 3 + 2] = -1 - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) - geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)) - geometry.userData.velocities = velocities - - const material = new THREE.PointsMaterial({ - size: 2, - vertexColors: true, - transparent: true, - opacity: 0, - blending: THREE.AdditiveBlending - }) - - const points = new THREE.Points(geometry, material) - this.particles.push(points) - this.scene.add(points) - } - } - - private animateTransition() { - let phase = 0 // 0: melt, 1: vortex, 2: explosion, 3: clear - let time = 0 - - const animate = () => { - time += 0.016 - - if (phase === 0) { - // Melting phase - this.particles.forEach((points, index) => { - const material = points.material as THREE.PointsMaterial - material.opacity = Math.min(1, time * 0.5) - - const positions = points.geometry.attributes.position.array as Float32Array - const velocities = points.geometry.userData.velocities as Float32Array - - for (let i = 0; i < positions.length; i += 3) { - // Spiral inward - const x = positions[i] - const y = positions[i + 1] - const angle = Math.atan2(y, x) + 0.05 - const radius = Math.sqrt(x * x + y * y) * 0.98 - - positions[i] = Math.cos(angle) * radius - positions[i + 1] = Math.sin(angle) * radius - positions[i + 2] += velocities[i + 2] - } - - points.geometry.attributes.position.needsUpdate = true - points.rotation.z += 0.01 * (index + 1) - }) - - if (this.vortexMesh) { - const material = this.vortexMesh.material as THREE.MeshBasicMaterial - material.opacity = Math.min(0.8, time * 0.3) - this.vortexMesh.rotation.z += 0.1 - this.vortexMesh.scale.setScalar(1 + Math.sin(time * 2) * 0.1) - } - - if (time > 3) { - phase = 1 - time = 0 - } - } else if (phase === 1) { - // Vortex collapse - this.particles.forEach((points) => { - const positions = points.geometry.attributes.position.array as Float32Array - - for (let i = 0; i < positions.length; i += 3) { - positions[i] *= 0.9 - positions[i + 1] *= 0.9 - positions[i + 2] *= 0.9 - } - - points.geometry.attributes.position.needsUpdate = true - points.rotation.z += 0.2 - }) - - if (this.vortexMesh) { - this.vortexMesh.scale.multiplyScalar(0.95) - } - - if (time > 1) { - phase = 2 - time = 0 - } - } else if (phase === 2) { - // Rainbow explosion - this.particles.forEach((points, index) => { - const positions = points.geometry.attributes.position.array as Float32Array - const colors = points.geometry.attributes.color.array as Float32Array - - for (let i = 0; i < positions.length; i += 3) { - // Explode outward - const velocity = new THREE.Vector3( - (Math.random() - 0.5) * 10, - (Math.random() - 0.5) * 10, - (Math.random() - 0.5) * 10 - ) - - positions[i] += velocity.x - positions[i + 1] += velocity.y - positions[i + 2] += velocity.z - - // Brighten colors - colors[i] = Math.min(1, colors[i] + 0.01) - colors[i + 1] = Math.min(1, colors[i + 1] + 0.01) - colors[i + 2] = Math.min(1, colors[i + 2] + 0.01) - } - - points.geometry.attributes.position.needsUpdate = true - points.geometry.attributes.color.needsUpdate = true - - const material = points.material as THREE.PointsMaterial - material.size = 3 + Math.sin(time * 10 + index) * 2 - }) - - if (time > 2) { - phase = 3 - time = 0 - } - } else if (phase === 3) { - // Fade out - this.particles.forEach((points) => { - const material = points.material as THREE.PointsMaterial - material.opacity *= 0.95 - }) - - if (this.vortexMesh) { - const material = this.vortexMesh.material as THREE.MeshBasicMaterial - material.opacity *= 0.95 - } - - if (time > 1) { - this.cleanup() - this.transitionCallback() - return - } - } - - requestAnimationFrame(animate) - } - - animate() - } - - private cleanup() { - this.particles.forEach(points => { - this.scene.remove(points) - points.geometry.dispose() - if (points.material instanceof THREE.Material) { - points.material.dispose() - } - }) - - if (this.vortexMesh) { - this.scene.remove(this.vortexMesh) - this.vortexMesh.geometry.dispose() - if (this.vortexMesh.material instanceof THREE.Material) { - this.vortexMesh.material.dispose() - } - } - - this.particles = [] - this.vortexMesh = null - this.isTransitioning = false - } -} \ No newline at end of file diff --git a/src/utils/aurumSiphon/gameState.ts b/src/utils/aurumSiphon/gameState.ts deleted file mode 100644 index e9cee1a..0000000 --- a/src/utils/aurumSiphon/gameState.ts +++ /dev/null @@ -1,49 +0,0 @@ -export interface Player { - id: string - position: { x: number; y: number; z: number } - rotation: { x: number; y: number; z: number } - velocity: { x: number; y: number; z: number } - score: number - goldCollected: number - isLocal: boolean -} - -export class GameState { - public players: Map = new Map() - public localPlayerId: string | null = null - public score: number = 0 - public goldCollected: number = 0 - public platinumCollected: number = 0 - public otherMetalsCollected: number = 0 - public currentLevel: number = 1 - - addPlayer(player: Player) { - this.players.set(player.id, player) - if (player.isLocal) { - this.localPlayerId = player.id - } - } - - removePlayer(playerId: string) { - this.players.delete(playerId) - } - - getLocalPlayer(): Player | undefined { - if (!this.localPlayerId) return undefined - return this.players.get(this.localPlayerId) - } - - updatePlayer(playerId: string, updates: Partial) { - const player = this.players.get(playerId) - if (player) { - Object.assign(player, updates) - } - } - - reset() { - this.score = 0 - this.goldCollected = 0 - this.platinumCollected = 0 - this.otherMetalsCollected = 0 - } -} \ No newline at end of file diff --git a/src/utils/aurumSiphon/laserSystem.ts b/src/utils/aurumSiphon/laserSystem.ts deleted file mode 100644 index fc34ae5..0000000 --- a/src/utils/aurumSiphon/laserSystem.ts +++ /dev/null @@ -1,254 +0,0 @@ -import * as THREE from 'three' -import { ProceduralSpace } from './proceduralSpace' - -interface Laser { - mesh: THREE.Object3D - velocity: THREE.Vector3 - lifeTime: number -} - -export class LaserSystem { - private scene: THREE.Scene - private lasers: Laser[] = [] - private laserGeometry: THREE.SphereGeometry - private laserMaterial: THREE.MeshBasicMaterial - private explosionParticles: THREE.Points[] = [] - - constructor(scene: THREE.Scene) { - this.scene = scene - - // Make MUCH larger glowing spheres - this.laserGeometry = new THREE.SphereGeometry(8, 16, 16) - this.laserMaterial = new THREE.MeshBasicMaterial({ - color: 0x00ff00, // Bright green for maximum visibility - transparent: false, - opacity: 1.0 - }) - } - - shoot(position: THREE.Vector3, direction: THREE.Vector3) { - // Normalize direction - const normalizedDir = direction.clone().normalize() - - // Create a group for the laser bolt with trail - const laserGroup = new THREE.Group() - - // Main laser sphere - const laser = new THREE.Mesh(this.laserGeometry.clone(), this.laserMaterial.clone()) - laserGroup.add(laser) - - // Add trail spheres - make them proportionally larger too - for (let i = 1; i <= 3; i++) { - const trailGeometry = new THREE.SphereGeometry(8 - i * 1.5, 8, 8) - const trailMaterial = new THREE.MeshBasicMaterial({ - color: 0x00ff00, - transparent: true, - opacity: 1.0 - i * 0.25 - }) - const trail = new THREE.Mesh(trailGeometry, trailMaterial) - trail.position.z = -i * 10 // Position behind the main sphere - laserGroup.add(trail) - } - - // Start the laser further in front of the ship due to its larger size - laserGroup.position.copy(position) - laserGroup.position.add(normalizedDir.clone().multiplyScalar(25)) - - // Orient the group to face the direction of travel - laserGroup.lookAt(laserGroup.position.clone().add(normalizedDir)) - - // Add a VERY bright light to make the laser glow - const laserLight = new THREE.PointLight(0x00ff00, 50, 100) - laserGroup.add(laserLight) - - this.scene.add(laserGroup) - - this.lasers.push({ - mesh: laserGroup, - velocity: normalizedDir.clone().multiplyScalar(50), - lifeTime: 200 - }) - - this.createMuzzleFlash(position, direction) - } - - private createMuzzleFlash(position: THREE.Vector3, direction: THREE.Vector3) { - const flashGeometry = new THREE.SphereGeometry(3, 8, 8) - const flashMaterial = new THREE.MeshBasicMaterial({ - color: 0xffaa00, - transparent: true, - opacity: 1.0 - }) - const flash = new THREE.Mesh(flashGeometry, flashMaterial) - flash.position.copy(position) - flash.position.add(direction.clone().normalize().multiplyScalar(5)) - this.scene.add(flash) - - let flashTime = 0 - const animateFlash = () => { - flashTime++ - flash.scale.multiplyScalar(1.1) - flashMaterial.opacity -= 0.15 - - if (flashTime < 6) { - requestAnimationFrame(animateFlash) - } else { - this.scene.remove(flash) - flashGeometry.dispose() - flashMaterial.dispose() - } - } - animateFlash() - } - - update(spaceObjects: ReturnType) { - for (let i = this.lasers.length - 1; i >= 0; i--) { - const laser = this.lasers[i] - - laser.mesh.position.add(laser.velocity) - laser.lifeTime-- - - let hit = false - for (const spaceObject of spaceObjects) { - const distance = laser.mesh.position.distanceTo(spaceObject.mesh.position) - const hitRadius = spaceObject.type === 'planet' ? - ((spaceObject.mesh.children[0] as THREE.Mesh)?.geometry as THREE.SphereGeometry)?.parameters?.radius || 20 : - spaceObject.type === 'asteroid' ? 5 : 8 - - // Add laser radius (8) to hit detection - if (distance < hitRadius + 8) { - this.createExplosion(spaceObject.mesh.position.clone(), spaceObject.type) - spaceObject.destroyed = true - hit = true - break - } - } - - if (hit || laser.lifeTime <= 0 || laser.mesh.position.length() > 2000) { - this.scene.remove(laser.mesh) - laser.mesh.traverse((child: THREE.Object3D) => { - if (child instanceof THREE.Mesh) { - child.geometry?.dispose() - if (child.material instanceof THREE.Material) { - child.material.dispose() - } - } - }) - this.lasers.splice(i, 1) - } - } - - for (let i = this.explosionParticles.length - 1; i >= 0; i--) { - const particles = this.explosionParticles[i] - const material = particles.material as THREE.PointsMaterial - material.opacity -= 0.02 - particles.scale.multiplyScalar(1.05) - - if (material.opacity <= 0) { - this.scene.remove(particles) - particles.geometry.dispose() - material.dispose() - this.explosionParticles.splice(i, 1) - } - } - } - - private createExplosion(position: THREE.Vector3, objectType: string) { - const particleCount = objectType === 'planet' ? 200 : 100 - const geometry = new THREE.BufferGeometry() - const positions = new Float32Array(particleCount * 3) - const velocities = new Float32Array(particleCount * 3) - - for (let i = 0; i < particleCount; i++) { - const theta = Math.random() * Math.PI * 2 - const phi = Math.acos(Math.random() * 2 - 1) - const speed = Math.random() * 2 + 1 - - positions[i * 3] = position.x - positions[i * 3 + 1] = position.y - positions[i * 3 + 2] = position.z - - velocities[i * 3] = Math.sin(phi) * Math.cos(theta) * speed - velocities[i * 3 + 1] = Math.sin(phi) * Math.sin(theta) * speed - velocities[i * 3 + 2] = Math.cos(phi) * speed - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) - geometry.userData.velocities = velocities - - const material = new THREE.PointsMaterial({ - color: objectType === 'planet' ? 0xffaa00 : 0xff6600, - size: objectType === 'planet' ? 3 : 2, - transparent: true, - opacity: 1, - blending: THREE.AdditiveBlending - }) - - const particles = new THREE.Points(geometry, material) - this.scene.add(particles) - this.explosionParticles.push(particles) - - const explosionLight = new THREE.PointLight(0xffaa00, 5, 50) - explosionLight.position.copy(position) - this.scene.add(explosionLight) - - let lightTime = 0 - const animateLight = () => { - lightTime++ - explosionLight.intensity *= 0.9 - - if (lightTime < 10) { - requestAnimationFrame(animateLight) - } else { - this.scene.remove(explosionLight) - } - } - animateLight() - - const animateExplosion = () => { - const positions = particles.geometry.attributes.position.array as Float32Array - const velocities = particles.geometry.userData.velocities as Float32Array - - for (let i = 0; i < particleCount; i++) { - positions[i * 3] += velocities[i * 3] - positions[i * 3 + 1] += velocities[i * 3 + 1] - positions[i * 3 + 2] += velocities[i * 3 + 2] - } - - particles.geometry.attributes.position.needsUpdate = true - - if (material.opacity > 0) { - requestAnimationFrame(animateExplosion) - } - } - animateExplosion() - } - - cleanup() { - this.lasers.forEach(laser => { - this.scene.remove(laser.mesh) - laser.mesh.traverse((child: THREE.Object3D) => { - if (child instanceof THREE.Mesh) { - child.geometry?.dispose() - if (child.material instanceof THREE.Material) { - child.material.dispose() - } - } - }) - }) - - this.explosionParticles.forEach(particles => { - this.scene.remove(particles) - particles.geometry.dispose() - if (particles.material instanceof THREE.Material) { - particles.material.dispose() - } - }) - - this.lasers = [] - this.explosionParticles = [] - - this.laserGeometry.dispose() - this.laserMaterial.dispose() - } -} \ No newline at end of file diff --git a/src/utils/aurumSiphon/levelManager.ts b/src/utils/aurumSiphon/levelManager.ts deleted file mode 100644 index 235f30b..0000000 --- a/src/utils/aurumSiphon/levelManager.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { GameState } from './gameState' - -interface LevelConfig { - level: number - name: string - goldTarget: number - timeLimit?: number - windSpeed: number - particleSpawnRate: number - goldChanceMultiplier: number - description: string -} - -export class LevelManager { - public currentLevel: number = 1 - private gameState: GameState - private levelStartTime: number = Date.now() - private levelConfigs: LevelConfig[] = [ - { - level: 1, - name: 'Solar Wind Introduction', - goldTarget: 10000000, // 10 million mg = 10 kg - windSpeed: 1, - particleSpawnRate: 5, - goldChanceMultiplier: 1, - description: 'Learn to navigate and collect gold particles from the solar wind' - }, - { - level: 2, - name: 'Increased Activity', - goldTarget: 100000000, // 100 kg - windSpeed: 1.5, - particleSpawnRate: 8, - goldChanceMultiplier: 1.2, - description: 'Solar activity increases - more particles but faster speeds' - }, - { - level: 3, - name: 'Solar Storm', - goldTarget: 500000000, // 500 kg - windSpeed: 2, - particleSpawnRate: 12, - goldChanceMultiplier: 1.5, - description: 'Navigate through a solar storm with high particle density' - }, - { - level: 4, - name: 'Precious Metals Rush', - goldTarget: 2000000000, // 2 tons - windSpeed: 2.5, - particleSpawnRate: 15, - goldChanceMultiplier: 2, - description: 'Rare opportunity - increased precious metal concentration' - }, - { - level: 5, - name: 'Coronal Mass Ejection', - goldTarget: 10000000000, // 10 tons - timeLimit: 180, - windSpeed: 3, - particleSpawnRate: 20, - goldChanceMultiplier: 2.5, - description: 'Survive and collect during a massive coronal mass ejection' - }, - { - level: 6, - name: 'Deep Space Mining', - goldTarget: 50000000000, // 50 tons - windSpeed: 2, - particleSpawnRate: 25, - goldChanceMultiplier: 3, - description: 'Advanced mining operation at Lagrange Point L1' - }, - { - level: 7, - name: 'Magnetic Anomaly', - goldTarget: 200000000000, // 200 tons - windSpeed: 2.5, - particleSpawnRate: 30, - goldChanceMultiplier: 3.5, - description: 'Strange magnetic fields affect particle behavior' - }, - { - level: 8, - name: 'Solar Maximum', - goldTarget: 1000000000000, // 1000 tons - timeLimit: 240, - windSpeed: 3.5, - particleSpawnRate: 35, - goldChanceMultiplier: 4, - description: 'Peak solar cycle - maximum particle output' - }, - { - level: 9, - name: 'Heliopause Approach', - goldTarget: 5000000000000, // 5000 tons - windSpeed: 4, - particleSpawnRate: 40, - goldChanceMultiplier: 4.5, - description: 'Venture closer to the sun for richer deposits' - }, - { - level: 10, - name: 'Master Collector', - goldTarget: 50000000000000, // 50,000 tons - timeLimit: 300, - windSpeed: 5, - particleSpawnRate: 50, - goldChanceMultiplier: 5, - description: 'Ultimate challenge - become a master of the Aurum Siphon' - } - ] - - constructor(gameState: GameState) { - this.gameState = gameState - } - - getCurrentLevelConfig(): LevelConfig { - return this.levelConfigs[Math.min(this.currentLevel - 1, this.levelConfigs.length - 1)] - } - - checkLevelComplete(): boolean { - const config = this.getCurrentLevelConfig() - - if (this.gameState.goldCollected >= config.goldTarget) { - return true - } - - if (config.timeLimit) { - const elapsed = (Date.now() - this.levelStartTime) / 1000 - if (elapsed >= config.timeLimit) { - return this.gameState.goldCollected >= config.goldTarget - } - } - - return false - } - - nextLevel() { - if (this.currentLevel < this.levelConfigs.length) { - this.currentLevel++ - } else { - this.currentLevel = 1 - this.gameState.score += 10000 - } - - this.levelStartTime = Date.now() - this.gameState.reset() - } - - resetToLevel(level: number) { - this.currentLevel = Math.max(1, Math.min(level, this.levelConfigs.length)) - this.levelStartTime = Date.now() - this.gameState.reset() - } - - getTimeRemaining(): number | null { - const config = this.getCurrentLevelConfig() - if (!config.timeLimit) return null - - const elapsed = (Date.now() - this.levelStartTime) / 1000 - return Math.max(0, config.timeLimit - elapsed) - } - - getLevelProgress(): number { - const config = this.getCurrentLevelConfig() - return Math.min(1, this.gameState.goldCollected / config.goldTarget) - } - - getTotalLevels(): number { - return this.levelConfigs.length - } -} \ No newline at end of file diff --git a/src/utils/aurumSiphon/magneticFunnel.ts b/src/utils/aurumSiphon/magneticFunnel.ts deleted file mode 100644 index 5bf2e3f..0000000 --- a/src/utils/aurumSiphon/magneticFunnel.ts +++ /dev/null @@ -1,197 +0,0 @@ -import * as THREE from 'three' -import { SpaceshipController } from './spaceshipController' - -export class MagneticFunnel { - private scene: THREE.Scene - private spaceship: SpaceshipController - private funnelMesh: THREE.Mesh - private fieldLines: THREE.LineSegments - public isActive: boolean = true - public efficiency: number = 1.0 - private attractionRadius: number = 40 // Doubled attraction range - private collectionRadius: number = 8 // Increased collection range - private fieldStrength: number = 1.0 // Doubled field strength - - constructor(scene: THREE.Scene, spaceship: SpaceshipController) { - this.scene = scene - this.spaceship = spaceship - this.funnelMesh = this.createFunnelMesh() - this.fieldLines = this.createFieldLines() - this.scene.add(this.funnelMesh) - this.scene.add(this.fieldLines) - } - - private createFunnelMesh(): THREE.Mesh { - const geometry = new THREE.ConeGeometry( - this.attractionRadius, - this.attractionRadius * 1.5, - 32, - 1, - true - ) - - const material = new THREE.MeshBasicMaterial({ - color: 0x00ffff, - transparent: true, - opacity: 0.1, - side: THREE.DoubleSide, - wireframe: true - }) - - const mesh = new THREE.Mesh(geometry, material) - // Orient toward the sun (negative X direction) - mesh.rotation.z = -Math.PI / 2 // Point cone toward -X (where sun is) - return mesh - } - - private createFieldLines(): THREE.LineSegments { - const geometry = new THREE.BufferGeometry() - const material = new THREE.LineBasicMaterial({ - color: 0x00aaff, - transparent: true, - opacity: 0.3, - linewidth: 2 - }) - - const positions: number[] = [] - const numLines = 8 - const numSegments = 10 - - for (let i = 0; i < numLines; i++) { - const angle = (i / numLines) * Math.PI * 2 - - for (let j = 0; j < numSegments; j++) { - const t = j / numSegments - const radius = this.attractionRadius * (1 - t * 0.8) - const height = -this.attractionRadius * 1.5 * t - - const x = Math.cos(angle) * radius - const z = Math.sin(angle) * radius - - positions.push(x, height, z) - - if (j < numSegments - 1) { - const nextT = (j + 1) / numSegments - const nextRadius = this.attractionRadius * (1 - nextT * 0.8) - const nextHeight = -this.attractionRadius * 1.5 * nextT - const nextX = Math.cos(angle) * nextRadius - const nextZ = Math.sin(angle) * nextRadius - - positions.push(nextX, nextHeight, nextZ) - } - } - } - - geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)) - return new THREE.LineSegments(geometry, material) - } - - update() { - const shipPos = this.spaceship.getPosition() - - // Position funnel in front of ship pointing toward sun - // The funnel should extend from the ship toward the sun (negative X) - this.funnelMesh.position.copy(shipPos) - this.funnelMesh.position.x -= 15 // Position it extending toward sun - - // Keep the funnel oriented toward the sun (along -X axis) - // Reset rotation to base orientation - this.funnelMesh.rotation.set(0, 0, -Math.PI / 2) // Point cone toward -X (sun direction) - - this.fieldLines.position.copy(this.funnelMesh.position) - - if (this.isActive) { - // Rotate field lines around the cone's axis (which is along X after rotation) - const time = Date.now() * 0.001 - this.fieldLines.rotation.set( - time * 0.5, // Rotate around X axis (along the cone) - 0, - -Math.PI / 2 // Keep base orientation toward sun - ) - - const pulseFactor = Math.sin(time) * 0.1 + 1 - this.funnelMesh.scale.setScalar(pulseFactor) - - // Also pulse the field lines slightly - this.fieldLines.scale.setScalar(pulseFactor * 0.95) - - this.efficiency = Math.max(0.3, this.efficiency - 0.001) - } else { - // Keep field lines aligned when inactive - this.fieldLines.rotation.copy(this.funnelMesh.rotation) - } - - this.funnelMesh.visible = this.isActive - this.fieldLines.visible = this.isActive - } - - checkCollection(particles: THREE.Mesh[]): THREE.Mesh[] { - if (!this.isActive) return [] - - const collected: THREE.Mesh[] = [] - const shipPos = this.spaceship.getPosition() - - particles.forEach(particle => { - const distance = particle.position.distanceTo(shipPos) - - if (distance < this.attractionRadius) { - const direction = new THREE.Vector3() - .subVectors(shipPos, particle.position) - .normalize() - - const particleType = particle.userData.type - const mass = this.getParticleMass(particleType) - const attractionForce = (this.fieldStrength * this.efficiency) / (mass * 0.1) - - particle.position.add(direction.multiplyScalar(attractionForce)) - - if (distance < this.collectionRadius) { - if (particleType === 'gold' || particleType === 'platinum' || particleType === 'iron') { - collected.push(particle) - } - } - } - }) - - return collected - } - - private getParticleMass(type: string): number { - switch (type) { - case 'gold': return 197 - case 'platinum': return 195 - case 'iron': return 56 - case 'helium': return 4 - case 'hydrogen': return 1 - default: return 1 - } - } - - toggle() { - this.isActive = !this.isActive - if (this.isActive) { - this.efficiency = 1.0 - } - } - - setFieldStrength(strength: number) { - this.fieldStrength = Math.max(0.1, Math.min(2.0, strength)) - } - - recharge() { - this.efficiency = Math.min(1.0, this.efficiency + 0.1) - } - - cleanup() { - this.scene.remove(this.funnelMesh) - this.scene.remove(this.fieldLines) - this.funnelMesh.geometry.dispose() - if (this.funnelMesh.material instanceof THREE.Material) { - this.funnelMesh.material.dispose() - } - this.fieldLines.geometry.dispose() - if (this.fieldLines.material instanceof THREE.Material) { - this.fieldLines.material.dispose() - } - } -} \ No newline at end of file diff --git a/src/utils/aurumSiphon/networkManager.ts b/src/utils/aurumSiphon/networkManager.ts deleted file mode 100644 index eb93ca9..0000000 --- a/src/utils/aurumSiphon/networkManager.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { GameState, Player } from './gameState' - -interface PlayerData { - position?: { x: number; y: number; z: number } - rotation?: { x: number; y: number; z: number } - score?: number - goldCollected?: number -} - -interface NetworkMessage { - type: 'welcome' | 'player_update' | 'player_joined' | 'player_left' | 'game_state' | 'collection_event' - playerId?: string - data?: PlayerData - timestamp: number -} - -export class NetworkManager { - private gameState: GameState - private ws: WebSocket | null = null - public isConnected: boolean = false - public isSimulated: boolean = false - public onPlayerIdReceived?: (playerId: string) => void - private reconnectAttempts: number = 0 - private maxReconnectAttempts: number = 5 - private reconnectDelay: number = 1000 - - constructor(gameState: GameState) { - this.gameState = gameState - - if (import.meta.env.VITE_AURUM_SIPHON_SIMULATED !== 'false') { - this.isSimulated = true - this.simulateConnection() - } - } - - private simulateConnection() { - setTimeout(() => { - const playerId = `player_${Math.random().toString(36).substr(2, 9)}` - - const welcomeMessage: NetworkMessage = { - type: 'welcome', - playerId, - timestamp: Date.now() - } - - this.handleMessage(welcomeMessage) - this.isConnected = true - - if (this.onPlayerIdReceived) { - this.onPlayerIdReceived(playerId) - } - }, 100) - } - - connect(url?: string) { - if (this.isSimulated) { - return - } - - const wsUrl = url || import.meta.env.VITE_AURUM_SIPHON_WEBSOCKET_URL || 'ws://localhost:8080/aurum-ws' - - try { - this.ws = new WebSocket(wsUrl) - - this.ws.onopen = () => { - // Connected to Aurum Siphon server - this.isConnected = true - this.reconnectAttempts = 0 - } - - this.ws.onmessage = (event) => { - try { - const message: NetworkMessage = JSON.parse(event.data) - this.handleMessage(message) - } catch (error) { - console.error('Failed to parse message:', error) - } - } - - this.ws.onerror = (error) => { - console.error('WebSocket error:', error) - } - - this.ws.onclose = () => { - // Disconnected from server - this.isConnected = false - this.attemptReconnect() - } - } catch (error) { - console.error('Failed to connect:', error) - this.isConnected = false - } - } - - private handleMessage(message: NetworkMessage) { - switch (message.type) { - case 'welcome': - if (message.playerId) { - const player: Player = { - id: message.playerId, - position: { x: 0, y: 0, z: 0 }, - rotation: { x: 0, y: 0, z: 0 }, - velocity: { x: 0, y: 0, z: 0 }, - score: 0, - goldCollected: 0, - isLocal: true - } - this.gameState.addPlayer(player) - - if (this.onPlayerIdReceived) { - this.onPlayerIdReceived(message.playerId) - } - } - break - - case 'player_joined': - if (message.data && message.playerId) { - const player: Player = { - id: message.playerId, - position: message.data.position || { x: 0, y: 0, z: 0 }, - rotation: message.data.rotation || { x: 0, y: 0, z: 0 }, - velocity: { x: 0, y: 0, z: 0 }, - score: 0, - goldCollected: 0, - isLocal: false - } - this.gameState.addPlayer(player) - } - break - - case 'player_left': - if (message.playerId) { - this.gameState.removePlayer(message.playerId) - } - break - - case 'player_update': - if (message.playerId && message.data) { - this.gameState.updatePlayer(message.playerId, message.data) - } - break - - case 'game_state': - if (message.data) { - Object.assign(this.gameState, message.data) - } - break - - case 'collection_event': - break - } - } - - sendMessage(data: PlayerData) { - if (!this.isConnected || this.isSimulated) return - - if (this.ws && this.ws.readyState === WebSocket.OPEN) { - const message: NetworkMessage = { - type: 'player_update', - playerId: this.gameState.localPlayerId || undefined, - data, - timestamp: Date.now() - } - - this.ws.send(JSON.stringify(message)) - } - } - - sendPlayerUpdate(position: { x: number; y: number; z: number }, rotation: { x: number; y: number; z: number }) { - this.sendMessage({ - position, - rotation, - score: this.gameState.score, - goldCollected: this.gameState.goldCollected - }) - } - - private attemptReconnect() { - if (this.isSimulated) return - - if (this.reconnectAttempts < this.maxReconnectAttempts) { - this.reconnectAttempts++ - // Attempting to reconnect - - setTimeout(() => { - this.connect() - }, this.reconnectDelay * this.reconnectAttempts) - } - } - - disconnect() { - if (this.ws) { - this.ws.close() - this.ws = null - } - this.isConnected = false - } -} \ No newline at end of file diff --git a/src/utils/aurumSiphon/proceduralSpace.ts b/src/utils/aurumSiphon/proceduralSpace.ts deleted file mode 100644 index 8f4a58f..0000000 --- a/src/utils/aurumSiphon/proceduralSpace.ts +++ /dev/null @@ -1,370 +0,0 @@ -import * as THREE from 'three' - -interface SpaceObject { - mesh: THREE.Object3D - velocity: THREE.Vector3 - rotationSpeed: THREE.Vector3 - type: 'asteroid' | 'planet' | 'ufo' - removeDistance: number - destroyed?: boolean -} - -export class ProceduralSpace { - private scene: THREE.Scene - private spaceObjects: SpaceObject[] = [] - private asteroidGeometries: THREE.BufferGeometry[] = [] - private planetMaterials: THREE.Material[] = [] - private lastUfoSpawn: number = Date.now() - private ufoSpawnInterval: number = 15000 // Spawn UFO every 15 seconds on average - - constructor(scene: THREE.Scene) { - this.scene = scene - this.initializeAssets() - this.generateInitialObjects() - } - - private initializeAssets() { - // Create various asteroid geometries - for (let i = 0; i < 5; i++) { - const geometry = new THREE.DodecahedronGeometry(1, 0) - // Distort vertices for unique shapes - const positions = geometry.attributes.position.array as Float32Array - for (let j = 0; j < positions.length; j += 3) { - const noise = 0.8 + Math.random() * 0.4 - positions[j] *= noise - positions[j + 1] *= noise - positions[j + 2] *= noise - } - geometry.computeVertexNormals() - this.asteroidGeometries.push(geometry) - } - - // Create various planet materials - const planetColors = [ - { color: 0x8B4513, emissive: 0x4B2808 }, // Brown/Mars-like - { color: 0x4169E1, emissive: 0x1E3A8A }, // Blue/Neptune-like - { color: 0xFF6347, emissive: 0x8B2500 }, // Red/Jupiter-like - { color: 0x9370DB, emissive: 0x4B0082 }, // Purple alien planet - { color: 0x20B2AA, emissive: 0x008B8B }, // Cyan ice planet - ] - - planetColors.forEach(colors => { - this.planetMaterials.push(new THREE.MeshPhongMaterial({ - color: colors.color, - emissive: colors.emissive, - emissiveIntensity: 0.2, - shininess: 30 - })) - }) - } - - private generateInitialObjects() { - // Generate initial asteroids - for (let i = 0; i < 30; i++) { - this.spawnAsteroid(true) - } - - // Generate initial planets - for (let i = 0; i < 5; i++) { - this.spawnPlanet(true) - } - } - - private spawnAsteroid(initial: boolean = false) { - const geometry = this.asteroidGeometries[Math.floor(Math.random() * this.asteroidGeometries.length)].clone() - const scale = 2 + Math.random() * 8 - geometry.scale(scale, scale, scale) - - const material = new THREE.MeshPhongMaterial({ - color: new THREE.Color().setHSL(0.05, 0.1, 0.3 + Math.random() * 0.3), - emissive: new THREE.Color().setHSL(0.05, 0.5, 0.1), - emissiveIntensity: 0.1, - shininess: 10 - }) - - const asteroid = new THREE.Mesh(geometry, material) - - if (initial) { - // Spawn randomly in space - asteroid.position.set( - (Math.random() - 0.5) * 400, - (Math.random() - 0.5) * 200, - (Math.random() - 0.5) * 400 - ) - } else { - // Spawn at edge of view - const side = Math.floor(Math.random() * 4) - switch(side) { - case 0: asteroid.position.set(-250, (Math.random() - 0.5) * 100, (Math.random() - 0.5) * 200); break - case 1: asteroid.position.set(250, (Math.random() - 0.5) * 100, (Math.random() - 0.5) * 200); break - case 2: asteroid.position.set((Math.random() - 0.5) * 200, (Math.random() - 0.5) * 100, -250); break - case 3: asteroid.position.set((Math.random() - 0.5) * 200, (Math.random() - 0.5) * 100, 250); break - } - } - - asteroid.castShadow = true - asteroid.receiveShadow = true - - const spaceObject: SpaceObject = { - mesh: asteroid, - velocity: new THREE.Vector3( - (Math.random() - 0.5) * 0.2, - (Math.random() - 0.5) * 0.1, - (Math.random() - 0.5) * 0.2 - ), - rotationSpeed: new THREE.Vector3( - Math.random() * 0.01 - 0.005, - Math.random() * 0.01 - 0.005, - Math.random() * 0.01 - 0.005 - ), - type: 'asteroid', - removeDistance: 300 - } - - this.scene.add(asteroid) - this.spaceObjects.push(spaceObject) - } - - private spawnPlanet(initial: boolean = false) { - const radius = 15 + Math.random() * 35 - const geometry = new THREE.SphereGeometry(radius, 32, 32) - const material = this.planetMaterials[Math.floor(Math.random() * this.planetMaterials.length)].clone() - - const planet = new THREE.Group() - const planetMesh = new THREE.Mesh(geometry, material) - planet.add(planetMesh) - - // Add rings to some planets - if (Math.random() > 0.5) { - const ringGeometry = new THREE.RingGeometry(radius * 1.2, radius * 1.8, 64) - const ringMaterial = new THREE.MeshBasicMaterial({ - color: new THREE.Color().setHSL(0.1, 0.5, 0.5), - side: THREE.DoubleSide, - transparent: true, - opacity: 0.6 - }) - const rings = new THREE.Mesh(ringGeometry, ringMaterial) - rings.rotation.x = Math.PI / 2 + (Math.random() - 0.5) * 0.4 - planet.add(rings) - } - - // Add atmosphere glow - const glowGeometry = new THREE.SphereGeometry(radius * 1.1, 16, 16) - const glowMaterial = new THREE.MeshBasicMaterial({ - color: (material as THREE.MeshPhongMaterial).color, - transparent: true, - opacity: 0.1 - }) - const glow = new THREE.Mesh(glowGeometry, glowMaterial) - planet.add(glow) - - if (initial) { - planet.position.set( - (Math.random() - 0.5) * 600, - (Math.random() - 0.5) * 300, - (Math.random() - 0.5) * 600 - ) - } else { - // Spawn far away - const angle = Math.random() * Math.PI * 2 - const distance = 400 + Math.random() * 200 - planet.position.set( - Math.cos(angle) * distance, - (Math.random() - 0.5) * 200, - Math.sin(angle) * distance - ) - } - - const spaceObject: SpaceObject = { - mesh: planet, - velocity: new THREE.Vector3( - (Math.random() - 0.5) * 0.05, - 0, - (Math.random() - 0.5) * 0.05 - ), - rotationSpeed: new THREE.Vector3( - 0, - Math.random() * 0.001 - 0.0005, - 0 - ), - type: 'planet', - removeDistance: 800 - } - - this.scene.add(planet) - this.spaceObjects.push(spaceObject) - } - - private spawnUFO() { - const ufoGroup = new THREE.Group() - - // Main saucer body - const bodyGeometry = new THREE.CylinderGeometry(3, 5, 1.5, 16) - const bodyMaterial = new THREE.MeshPhongMaterial({ - color: 0x888888, - emissive: 0x444488, - emissiveIntensity: 0.5, - shininess: 100 - }) - const body = new THREE.Mesh(bodyGeometry, bodyMaterial) - ufoGroup.add(body) - - // Dome - const domeGeometry = new THREE.SphereGeometry(2, 16, 8, 0, Math.PI * 2, 0, Math.PI / 2) - const domeMaterial = new THREE.MeshPhongMaterial({ - color: 0x88ccff, - emissive: 0x4488ff, - emissiveIntensity: 0.8, - transparent: true, - opacity: 0.7 - }) - const dome = new THREE.Mesh(domeGeometry, domeMaterial) - dome.position.y = 0.5 - ufoGroup.add(dome) - - // Lights around the rim - const lightCount = 8 - for (let i = 0; i < lightCount; i++) { - const angle = (i / lightCount) * Math.PI * 2 - const lightGeometry = new THREE.SphereGeometry(0.3, 8, 8) - const lightMaterial = new THREE.MeshBasicMaterial({ - color: Math.random() > 0.5 ? 0xff0000 : 0x00ff00 - }) - const light = new THREE.Mesh(lightGeometry, lightMaterial) - light.position.set(Math.cos(angle) * 4, 0, Math.sin(angle) * 4) - ufoGroup.add(light) - } - - // Add glow effect - const glowLight = new THREE.PointLight(0x4488ff, 1, 20) - glowLight.position.y = -1 - ufoGroup.add(glowLight) - - // Spawn from random edge - const startAngle = Math.random() * Math.PI * 2 - const endAngle = startAngle + Math.PI + (Math.random() - 0.5) * Math.PI / 2 - const distance = 150 - - ufoGroup.position.set( - Math.cos(startAngle) * distance, - (Math.random() - 0.5) * 50, - Math.sin(startAngle) * distance - ) - - // Calculate velocity to fly across view - const targetPos = new THREE.Vector3( - Math.cos(endAngle) * distance, - (Math.random() - 0.5) * 50, - Math.sin(endAngle) * distance - ) - - const direction = targetPos.sub(ufoGroup.position).normalize() - const speed = 1 + Math.random() * 2 - - const spaceObject: SpaceObject = { - mesh: ufoGroup, - velocity: direction.multiplyScalar(speed), - rotationSpeed: new THREE.Vector3(0, 0.05, 0), - type: 'ufo', - removeDistance: 250 - } - - this.scene.add(ufoGroup) - this.spaceObjects.push(spaceObject) - } - - update(shipPosition: THREE.Vector3) { - const now = Date.now() - - // Update all space objects - for (let i = this.spaceObjects.length - 1; i >= 0; i--) { - const obj = this.spaceObjects[i] - - // Remove if destroyed - if (obj.destroyed) { - this.scene.remove(obj.mesh) - obj.mesh.traverse((child: THREE.Object3D) => { - if (child instanceof THREE.Mesh) { - child.geometry?.dispose() - if (child.material instanceof THREE.Material) { - child.material.dispose() - } else if (Array.isArray(child.material)) { - child.material.forEach((m: THREE.Material) => m.dispose()) - } - } - }) - this.spaceObjects.splice(i, 1) - continue - } - - // Update position - obj.mesh.position.add(obj.velocity) - - // Update rotation - obj.mesh.rotation.x += obj.rotationSpeed.x - obj.mesh.rotation.y += obj.rotationSpeed.y - obj.mesh.rotation.z += obj.rotationSpeed.z - - // Animate UFO lights - if (obj.type === 'ufo') { - obj.mesh.children.forEach((child: THREE.Object3D, index: number) => { - if (index > 1 && child instanceof THREE.Mesh) { // Skip body and dome - const material = child.material as THREE.MeshBasicMaterial - material.opacity = Math.sin(now * 0.01 + index) * 0.3 + 0.7 - } - }) - } - - // Remove if too far from ship - const distance = obj.mesh.position.distanceTo(shipPosition) - if (distance > obj.removeDistance) { - this.scene.remove(obj.mesh) - this.spaceObjects.splice(i, 1) - - // Respawn to maintain population - if (obj.type === 'asteroid' && this.spaceObjects.filter(o => o.type === 'asteroid').length < 30) { - this.spawnAsteroid() - } else if (obj.type === 'planet' && this.spaceObjects.filter(o => o.type === 'planet').length < 5) { - this.spawnPlanet() - } - } - } - - // Spawn UFO occasionally - if (now - this.lastUfoSpawn > this.ufoSpawnInterval + Math.random() * 10000) { - this.spawnUFO() - this.lastUfoSpawn = now - } - - // Maintain minimum asteroid count - const asteroidCount = this.spaceObjects.filter(o => o.type === 'asteroid').length - if (asteroidCount < 20) { - this.spawnAsteroid() - } - } - - getObjects(): SpaceObject[] { - return this.spaceObjects - } - - getSpaceObjects(): SpaceObject[] { - return this.spaceObjects - } - - cleanup() { - this.spaceObjects.forEach(obj => { - this.scene.remove(obj.mesh) - obj.mesh.traverse((child: THREE.Object3D) => { - if (child instanceof THREE.Mesh) { - child.geometry?.dispose() - if (child.material instanceof THREE.Material) { - child.material.dispose() - } else if (Array.isArray(child.material)) { - child.material.forEach((m: THREE.Material) => m.dispose()) - } - } - }) - }) - this.spaceObjects = [] - } -} \ No newline at end of file diff --git a/src/utils/aurumSiphon/solarWindSystem.ts b/src/utils/aurumSiphon/solarWindSystem.ts deleted file mode 100644 index 3497850..0000000 --- a/src/utils/aurumSiphon/solarWindSystem.ts +++ /dev/null @@ -1,181 +0,0 @@ -import * as THREE from 'three' - -interface Particle { - mesh: THREE.Mesh - velocity: THREE.Vector3 - type: 'gold' | 'platinum' | 'iron' | 'helium' | 'hydrogen' - mass: number -} - -export class SolarWindSystem { - private scene: THREE.Scene - private particles: Particle[] = [] - private particlePool: THREE.Mesh[] = [] - private maxParticles: number = 5000 // Massive particle count - private spawnRate: number = 100 // Extreme spawn rate - private windSpeed: number = 1 - private currentLevel: number = 1 - - constructor(scene: THREE.Scene) { - this.scene = scene - this.initializeParticlePool() - } - - private initializeParticlePool() { - const geometries = { - gold: new THREE.SphereGeometry(1.2, 8, 8), // Much larger gold - platinum: new THREE.SphereGeometry(1.0, 8, 8), - iron: new THREE.SphereGeometry(0.8, 6, 6), - helium: new THREE.SphereGeometry(0.5, 6, 6), - hydrogen: new THREE.SphereGeometry(0.4, 6, 6) - } - - const materials = { - gold: new THREE.MeshPhongMaterial({ - color: 0xffd700, - emissive: 0xffaa00, - emissiveIntensity: 0.5, - shininess: 100 - }), - platinum: new THREE.MeshPhongMaterial({ - color: 0xe5e4e2, - emissive: 0xaaaaaa, - emissiveIntensity: 0.3, - shininess: 80 - }), - iron: new THREE.MeshPhongMaterial({ - color: 0x888888, - emissive: 0x444444, - emissiveIntensity: 0.2 - }), - helium: new THREE.MeshPhongMaterial({ - color: 0xffcccc, - emissive: 0xff8888, - emissiveIntensity: 0.3, - transparent: true, - opacity: 0.8 - }), - hydrogen: new THREE.MeshPhongMaterial({ - color: 0xccccff, - emissive: 0x8888ff, - emissiveIntensity: 0.3, - transparent: true, - opacity: 0.7 - }) - } - - for (let i = 0; i < this.maxParticles; i++) { - const type = this.getRandomParticleType() - const mesh = new THREE.Mesh(geometries[type], materials[type]) - mesh.visible = false - mesh.userData.type = type - this.scene.add(mesh) - this.particlePool.push(mesh) - } - } - - private getRandomParticleType(): 'gold' | 'platinum' | 'iron' | 'helium' | 'hydrogen' { - const rand = Math.random() * 100 - - // MASSIVE gold increase - mostly gold now! - const goldChance = 70 + (5 * this.currentLevel) // 70% base + 5% per level - const platinumChance = 10 - const ironChance = 5 - const heliumChance = 5 - - if (rand < goldChance) return 'gold' - if (rand < goldChance + platinumChance) return 'platinum' - if (rand < goldChance + platinumChance + ironChance) return 'iron' - if (rand < goldChance + platinumChance + ironChance + heliumChance) return 'helium' - return 'hydrogen' - } - - private spawnParticle() { - if (this.particles.length >= this.maxParticles) return - - const availableMesh = this.particlePool.find(mesh => !mesh.visible) - if (!availableMesh) return - - const x = -100 - // Create stream patterns - concentrate gold in specific bands - const streamAngle = Date.now() * 0.001 - const streamRadius = 30 + Math.sin(streamAngle) * 20 - const y = Math.cos(streamAngle * 2) * streamRadius + (Math.random() - 0.5) * 10 - const z = Math.sin(streamAngle * 2) * streamRadius + (Math.random() - 0.5) * 10 - - availableMesh.position.set(x, y, z) - availableMesh.visible = true - - const particle: Particle = { - mesh: availableMesh, - velocity: new THREE.Vector3( - this.windSpeed * 2 + Math.random() * 0.5, // Faster flow - (Math.random() - 0.5) * 0.2, - (Math.random() - 0.5) * 0.2 - ), - type: availableMesh.userData.type, - mass: this.getParticleMass(availableMesh.userData.type) - } - - this.particles.push(particle) - } - - private getParticleMass(type: string): number { - switch (type) { - case 'gold': return 197 - case 'platinum': return 195 - case 'iron': return 56 - case 'helium': return 4 - case 'hydrogen': return 1 - default: return 1 - } - } - - update() { - for (let i = 0; i < this.spawnRate; i++) { - // Always spawn particles (100% chance) - this.spawnParticle() - } - - for (let i = this.particles.length - 1; i >= 0; i--) { - const particle = this.particles[i] - - particle.mesh.position.add(particle.velocity) - - particle.mesh.rotation.x += 0.01 - particle.mesh.rotation.y += 0.02 - - if (particle.mesh.position.x > 100 || - Math.abs(particle.mesh.position.y) > 100 || - Math.abs(particle.mesh.position.z) > 100) { - particle.mesh.visible = false - this.particles.splice(i, 1) - } - } - } - - getParticles(): THREE.Mesh[] { - return this.particles.map(p => p.mesh) - } - - removeParticle(mesh: THREE.Mesh) { - const index = this.particles.findIndex(p => p.mesh === mesh) - if (index !== -1) { - mesh.visible = false - this.particles.splice(index, 1) - } - } - - setLevel(level: number) { - this.currentLevel = level - this.spawnRate = Math.min(5 + level * 2, 20) - this.windSpeed = Math.min(1 + level * 0.2, 3) - } - - reset() { - this.particles.forEach(particle => { - particle.mesh.visible = false - }) - this.particles = [] - } -} \ No newline at end of file diff --git a/src/utils/aurumSiphon/spaceshipController.ts b/src/utils/aurumSiphon/spaceshipController.ts deleted file mode 100644 index 0d05ffb..0000000 --- a/src/utils/aurumSiphon/spaceshipController.ts +++ /dev/null @@ -1,300 +0,0 @@ -import * as THREE from 'three' -import { CameraController } from '../shared/cameraController' - -export class SpaceshipController { - private spaceship: THREE.Group - private velocity: THREE.Vector3 - private scene: THREE.Scene - private cameraController: CameraController - public power: number = 100 - public shield: number = 100 - private boostActive: boolean = false - private boostCooldown: number = 0 - private engineLight: THREE.PointLight - private trailParticles: THREE.Points - - constructor(scene: THREE.Scene, camera: THREE.PerspectiveCamera) { - this.scene = scene - this.cameraController = new CameraController(camera, { - minDistance: 15, - maxDistance: 50, - minPolarAngle: Math.PI / 3, // Don't let camera go too high (above) - maxPolarAngle: Math.PI / 2 - 0.1, // Keep camera mostly behind (almost horizontal) - offsetY: 5, // Slight elevation to see over the ship - followSpeed: 0.15 // Slightly smoother following - }) - this.velocity = new THREE.Vector3() - this.spaceship = this.createSpaceship() - this.scene.add(this.spaceship) - - this.engineLight = new THREE.PointLight(0x00aaff, 1, 20) - this.engineLight.position.set(0, 0, 3) - this.spaceship.add(this.engineLight) - - this.trailParticles = this.createTrailParticles() - this.scene.add(this.trailParticles) - } - - private createSpaceship(): THREE.Group { - const group = new THREE.Group() - - // Main fuselage - sleeker design - const bodyGeometry = new THREE.CylinderGeometry(0.8, 2, 6, 8) - const bodyMaterial = new THREE.MeshPhongMaterial({ - color: 0x3366cc, - emissive: 0x001122, - shininess: 150, - specular: 0x4488ff - }) - const body = new THREE.Mesh(bodyGeometry, bodyMaterial) - body.rotation.x = Math.PI / 2 - body.castShadow = true - body.receiveShadow = true - group.add(body) - - // Angular wings - const wingShape = new THREE.Shape() - wingShape.moveTo(0, 0) - wingShape.lineTo(4, 0) - wingShape.lineTo(3, 2) - wingShape.lineTo(0, 1) - - const wingGeometry = new THREE.ExtrudeGeometry(wingShape, { - depth: 0.3, - bevelEnabled: true, - bevelThickness: 0.1, - bevelSize: 0.1, - bevelSegments: 3 - }) - - const wingMaterial = new THREE.MeshPhongMaterial({ - color: 0x5588ff, - emissive: 0x001133, - shininess: 100 - }) - - const leftWing = new THREE.Mesh(wingGeometry, wingMaterial) - leftWing.position.set(-0.15, 0, 0) - leftWing.rotation.z = Math.PI / 2 - leftWing.castShadow = true - group.add(leftWing) - - const rightWing = new THREE.Mesh(wingGeometry, wingMaterial) - rightWing.position.set(0.15, 0, 0) - rightWing.rotation.z = -Math.PI / 2 - rightWing.scale.x = -1 - rightWing.castShadow = true - group.add(rightWing) - - // Cockpit with glass effect - const cockpitGeometry = new THREE.SphereGeometry(1, 16, 16, 0, Math.PI * 2, 0, Math.PI * 0.6) - const cockpitMaterial = new THREE.MeshPhongMaterial({ - color: 0x001155, - emissive: 0x0088ff, - emissiveIntensity: 0.5, - transparent: true, - opacity: 0.7, - shininess: 200 - }) - const cockpit = new THREE.Mesh(cockpitGeometry, cockpitMaterial) - cockpit.position.z = -2 - cockpit.rotation.x = -Math.PI / 2 - group.add(cockpit) - - // Triple engines - const engineGroup = new THREE.Group() - for (let i = 0; i < 3; i++) { - const engineGeometry = new THREE.ConeGeometry(0.4, 2, 6) - const engineMaterial = new THREE.MeshPhongMaterial({ - color: 0x333333, - emissive: 0xff3300, - emissiveIntensity: 0.6 - }) - const engine = new THREE.Mesh(engineGeometry, engineMaterial) - engine.rotation.x = -Math.PI / 2 - engine.position.x = (i - 1) * 0.8 - engine.position.z = 3 - engineGroup.add(engine) - } - group.add(engineGroup) - - // Add detail panels - const panelGeometry = new THREE.BoxGeometry(0.5, 0.1, 1) - const panelMaterial = new THREE.MeshPhongMaterial({ - color: 0x223366, - emissive: 0x001122 - }) - - for (let i = 0; i < 4; i++) { - const panel = new THREE.Mesh(panelGeometry, panelMaterial) - const angle = (i / 4) * Math.PI * 2 - panel.position.x = Math.cos(angle) * 1.5 - panel.position.y = Math.sin(angle) * 1.5 - panel.position.z = (Math.random() - 0.5) * 2 - panel.lookAt(0, 0, panel.position.z) - group.add(panel) - } - - return group - } - - private createTrailParticles(): THREE.Points { - const geometry = new THREE.BufferGeometry() - const material = new THREE.PointsMaterial({ - color: 0x00aaff, - size: 0.3, - transparent: true, - opacity: 0.6, - blending: THREE.AdditiveBlending - }) - - const positions = new Float32Array(300) - for (let i = 0; i < 100; i++) { - positions[i * 3] = 0 - positions[i * 3 + 1] = 0 - positions[i * 3 + 2] = 0 - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) - return new THREE.Points(geometry, material) - } - - update(moveInput: { x: number; y: number; z: number }, cameraInput: { zoom: number; rotate: number }) { - const speed = this.boostActive ? 3.0 : 1.5 // Increased speed significantly - const verticalSpeed = speed * 0.8 - - // Calculate movement relative to camera angle - const cameraForward = new THREE.Vector3() - const cameraRight = new THREE.Vector3() - - // Get camera's forward direction from the camera controller's camera - const camera = this.cameraController.getCamera() - const cameraDir = new THREE.Vector3() - camera.getWorldDirection(cameraDir) - cameraForward.set(cameraDir.x, 0, cameraDir.z).normalize() - - // Get camera's right direction - cameraRight.crossVectors(cameraForward, new THREE.Vector3(0, 1, 0)).normalize() - - // Apply movement relative to camera view - this.velocity.set(0, 0, 0) - - // Add forward/backward movement - this.velocity.addScaledVector(cameraForward, moveInput.z * speed) - - // Add left/right movement - this.velocity.addScaledVector(cameraRight, moveInput.x * speed) - - // Add vertical movement (independent of camera) - this.velocity.y = moveInput.y * verticalSpeed - - // Apply velocity to position - this.spaceship.position.add(this.velocity) - - // Larger world bounds - this.spaceship.position.x = THREE.MathUtils.clamp(this.spaceship.position.x, -200, 200) - this.spaceship.position.y = THREE.MathUtils.clamp(this.spaceship.position.y, -50, 50) - this.spaceship.position.z = THREE.MathUtils.clamp(this.spaceship.position.z, -200, 200) - - // Add banking/tilting when moving for visual feedback - const targetBankZ = -moveInput.x * 0.5 // Bank when moving left/right (increased) - const targetPitchX = moveInput.z * 0.4 // Pitch when moving forward/back (increased) - - this.spaceship.rotation.z = THREE.MathUtils.lerp(this.spaceship.rotation.z, targetBankZ, 0.15) - this.spaceship.rotation.x = THREE.MathUtils.lerp(this.spaceship.rotation.x, targetPitchX, 0.15) - - // Update camera controller - this.cameraController.handleInput({ - rotate: cameraInput.rotate, - zoom: cameraInput.zoom - }) - this.cameraController.followTarget(this.spaceship.position) - this.cameraController.update() - - this.updateTrail() - - if (this.boostActive) { - this.power -= 0.5 - if (this.power <= 0) { - this.power = 0 - this.boostActive = false - } - } else { - this.power = Math.min(100, this.power + 0.1) - } - - if (this.boostCooldown > 0) { - this.boostCooldown-- - } - - // Engine light intensity based on speed - const speedFactor = this.velocity.length() / 3 - this.engineLight.intensity = this.boostActive ? 4 : (1 + speedFactor) - } - - private updateTrail() { - const positions = this.trailParticles.geometry.attributes.position.array as Float32Array - - // Shift existing trail positions - for (let i = positions.length - 3; i >= 3; i -= 3) { - positions[i] = positions[i - 3] - positions[i + 1] = positions[i - 2] - positions[i + 2] = positions[i - 1] - } - - // Add new trail position behind the ship with more spread when moving - const trailSpread = Math.max(0.5, this.velocity.length() * 0.3) - positions[0] = this.spaceship.position.x - this.velocity.x * 0.5 + (Math.random() - 0.5) * trailSpread - positions[1] = this.spaceship.position.y - this.velocity.y * 0.5 + (Math.random() - 0.5) * trailSpread - positions[2] = this.spaceship.position.z - this.velocity.z * 0.5 + 3 - - this.trailParticles.geometry.attributes.position.needsUpdate = true - - // Update trail opacity based on speed - const trailMaterial = this.trailParticles.material as THREE.PointsMaterial - trailMaterial.opacity = Math.min(0.8, 0.3 + this.velocity.length() * 0.1) - } - - activateBoost() { - if (this.power > 20 && this.boostCooldown === 0) { - this.boostActive = true - this.boostCooldown = 60 - setTimeout(() => { - this.boostActive = false - }, 2000) - } - } - - getPosition(): THREE.Vector3 { - return this.spaceship.position.clone() - } - - getVelocity(): THREE.Vector3 { - return this.velocity.clone() - } - - getSpaceship(): THREE.Group { - return this.spaceship - } - - takeDamage(amount: number) { - this.shield -= amount - if (this.shield < 0) this.shield = 0 - } - - repair(amount: number) { - this.shield = Math.min(100, this.shield + amount) - } - - cleanup() { - this.scene.remove(this.spaceship) - this.spaceship.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.geometry?.dispose() - if (child.material instanceof THREE.Material) { - child.material.dispose() - } - } - }) - } -} \ No newline at end of file diff --git a/src/utils/shared/cameraController.ts b/src/utils/shared/cameraController.ts deleted file mode 100644 index 57f98c5..0000000 --- a/src/utils/shared/cameraController.ts +++ /dev/null @@ -1,150 +0,0 @@ -import * as THREE from 'three' - -export interface CameraControllerOptions { - minDistance?: number - maxDistance?: number - minPolarAngle?: number // Vertical angle limits - maxPolarAngle?: number - rotationSpeed?: number - zoomSpeed?: number - followSpeed?: number // How quickly camera follows target - offsetY?: number // Vertical offset from target -} - -export class CameraController { - private camera: THREE.PerspectiveCamera - private target: THREE.Vector3 - private distance: number - private azimuthAngle: number = 0 // Horizontal rotation - private polarAngle: number = Math.PI / 2.5 // Around 72 degrees - more behind than above - private options: Required - - constructor(camera: THREE.PerspectiveCamera, options: CameraControllerOptions = {}) { - this.camera = camera - this.target = new THREE.Vector3() - this.distance = 20 // Closer for racing game feel - - this.options = { - minDistance: options.minDistance ?? 10, - maxDistance: options.maxDistance ?? 100, - minPolarAngle: options.minPolarAngle ?? 0, - maxPolarAngle: options.maxPolarAngle ?? Math.PI / 2, - rotationSpeed: options.rotationSpeed ?? 0.05, - zoomSpeed: options.zoomSpeed ?? 2, - followSpeed: options.followSpeed ?? 0.1, - offsetY: options.offsetY ?? 0 - } - } - - /** - * Update camera to follow a target position - * @param targetPosition - The position to follow - * @param deltaTime - Time since last update (for smooth movement) - */ - followTarget(targetPosition: THREE.Vector3, _deltaTime: number = 0.016) { - // Smoothly interpolate target position - this.target.lerp(targetPosition, this.options.followSpeed) - } - - /** - * Set the target position immediately (no smoothing) - */ - setTarget(targetPosition: THREE.Vector3) { - this.target.copy(targetPosition) - } - - /** - * Handle camera controls - * @param input - Control input for rotation and zoom - */ - handleInput(input: { rotate: number, zoom: number, rotateVertical?: number }) { - // Horizontal rotation - this.azimuthAngle += input.rotate * this.options.rotationSpeed - - // Vertical rotation (if provided) - if (input.rotateVertical !== undefined) { - this.polarAngle += input.rotateVertical * this.options.rotationSpeed - this.polarAngle = Math.max( - this.options.minPolarAngle, - Math.min(this.options.maxPolarAngle, this.polarAngle) - ) - } - - // Zoom - this.distance += input.zoom * this.options.zoomSpeed - this.distance = Math.max( - this.options.minDistance, - Math.min(this.options.maxDistance, this.distance) - ) - } - - /** - * Update camera position and orientation - */ - update() { - // Calculate camera position in spherical coordinates - const x = this.distance * Math.sin(this.polarAngle) * Math.sin(this.azimuthAngle) - const y = this.distance * Math.cos(this.polarAngle) + this.options.offsetY - const z = this.distance * Math.sin(this.polarAngle) * Math.cos(this.azimuthAngle) - - // Set camera position relative to target - this.camera.position.set( - this.target.x + x, - this.target.y + y, - this.target.z + z - ) - - // Look at target - this.camera.lookAt(this.target) - } - - /** - * Set zoom distance directly - */ - setDistance(distance: number) { - this.distance = Math.max( - this.options.minDistance, - Math.min(this.options.maxDistance, distance) - ) - } - - /** - * Get current distance from target - */ - getDistance(): number { - return this.distance - } - - /** - * Set horizontal rotation angle - */ - setAzimuthAngle(angle: number) { - this.azimuthAngle = angle - } - - /** - * Set vertical rotation angle - */ - setPolarAngle(angle: number) { - this.polarAngle = Math.max( - this.options.minPolarAngle, - Math.min(this.options.maxPolarAngle, angle) - ) - } - - /** - * Reset camera to default position - */ - reset() { - this.azimuthAngle = 0 - this.polarAngle = Math.PI / 2.5 - this.distance = 20 - } - - /** - * Get the camera instance - */ - getCamera(): THREE.PerspectiveCamera { - return this.camera - } -} \ No newline at end of file diff --git a/src/utils/witchGame/birdSystem.ts b/src/utils/witchGame/birdSystem.ts deleted file mode 100644 index c386784..0000000 --- a/src/utils/witchGame/birdSystem.ts +++ /dev/null @@ -1,294 +0,0 @@ -import * as THREE from 'three' - -interface Bird { - mesh: THREE.Group - velocity: THREE.Vector3 - targetPoint: THREE.Vector3 - wingPhase: number - type: 'raven' | 'owl' | 'bat' | 'phoenix' - collected: boolean -} - -export class BirdSystem { - private scene: THREE.Scene - private birds: Bird[] = [] - private maxBirds: number = 20 - private spawnTimer: number = 0 - - constructor(scene: THREE.Scene) { - this.scene = scene - } - - private createBird(type: Bird['type']): THREE.Group { - const group = new THREE.Group() - - // Bird body - const bodyGeometry = new THREE.SphereGeometry(2, 8, 6) - const bodyMaterial = new THREE.MeshPhongMaterial({ - color: type === 'raven' ? 0x111111 : - type === 'owl' ? 0x8b7355 : - type === 'bat' ? 0x2a1810 : - 0xff4500, // phoenix - emissive: type === 'phoenix' ? 0xff2200 : 0x000000, - emissiveIntensity: type === 'phoenix' ? 0.5 : 0 - }) - const body = new THREE.Mesh(bodyGeometry, bodyMaterial) - group.add(body) - - // Wings - const wingGeometry = new THREE.PlaneGeometry(8, 3) - const wingMaterial = new THREE.MeshPhongMaterial({ - color: bodyMaterial.color, - side: THREE.DoubleSide, - transparent: true, - opacity: 0.9 - }) - - const leftWing = new THREE.Mesh(wingGeometry, wingMaterial) - leftWing.position.set(-3, 0, 0) - leftWing.rotation.y = -0.3 - group.add(leftWing) - - const rightWing = new THREE.Mesh(wingGeometry, wingMaterial.clone()) - rightWing.position.set(3, 0, 0) - rightWing.rotation.y = 0.3 - group.add(rightWing) - - // Eyes - const eyeGeometry = new THREE.SphereGeometry(0.3, 6, 6) - const eyeMaterial = new THREE.MeshBasicMaterial({ - color: type === 'owl' ? 0xffff00 : 0xff0000 - }) - - const leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial) - leftEye.position.set(-0.7, 0.5, 1.5) - group.add(leftEye) - - const rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial) - rightEye.position.set(0.7, 0.5, 1.5) - group.add(rightEye) - - // Phoenix fire effect - if (type === 'phoenix') { - const fireLight = new THREE.PointLight(0xff6600, 3, 20) - group.add(fireLight) - - // Fire particles - const particleCount = 20 - const particleGeometry = new THREE.BufferGeometry() - const positions = new Float32Array(particleCount * 3) - - for (let i = 0; i < particleCount; i++) { - positions[i * 3] = (Math.random() - 0.5) * 4 - positions[i * 3 + 1] = (Math.random() - 0.5) * 4 - positions[i * 3 + 2] = (Math.random() - 0.5) * 4 - } - - particleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) - - const particleMaterial = new THREE.PointsMaterial({ - color: 0xff6600, - size: 1, - transparent: true, - opacity: 0.6, - blending: THREE.AdditiveBlending - }) - - const particles = new THREE.Points(particleGeometry, particleMaterial) - group.add(particles) - } - - return group - } - - spawnBird() { - if (this.birds.length >= this.maxBirds) return - - const types: Bird['type'][] = ['raven', 'owl', 'bat', 'phoenix'] - const type = types[Math.floor(Math.random() * types.length)] - - const bird: Bird = { - mesh: this.createBird(type), - velocity: new THREE.Vector3(), - targetPoint: new THREE.Vector3( - (Math.random() - 0.5) * 200, - 50 + Math.random() * 100, - (Math.random() - 0.5) * 200 - ), - wingPhase: Math.random() * Math.PI * 2, - type, - collected: false - } - - // Random spawn position - bird.mesh.position.set( - (Math.random() - 0.5) * 300, - 50 + Math.random() * 100, - (Math.random() - 0.5) * 300 - ) - - this.birds.push(bird) - this.scene.add(bird.mesh) - } - - update(deltaTime: number, playerPosition: THREE.Vector3) { - this.spawnTimer += deltaTime - if (this.spawnTimer > 2) { - this.spawnBird() - this.spawnTimer = 0 - } - - for (let i = this.birds.length - 1; i >= 0; i--) { - const bird = this.birds[i] - - if (bird.collected) { - // Fly up and fade when collected - bird.mesh.position.y += 2 - bird.mesh.scale.multiplyScalar(0.95) - - if (bird.mesh.scale.x < 0.01) { - this.scene.remove(bird.mesh) - this.birds.splice(i, 1) - } - continue - } - - // Flocking behavior - const separation = new THREE.Vector3() - const alignment = new THREE.Vector3() - const cohesion = new THREE.Vector3() - let neighbors = 0 - - this.birds.forEach(other => { - if (other === bird || other.collected) return - - const distance = bird.mesh.position.distanceTo(other.mesh.position) - if (distance < 30) { - // Separation - const diff = bird.mesh.position.clone().sub(other.mesh.position) - diff.normalize() - diff.divideScalar(distance) - separation.add(diff) - - // Alignment - alignment.add(other.velocity) - - // Cohesion - cohesion.add(other.mesh.position) - neighbors++ - } - }) - - if (neighbors > 0) { - separation.divideScalar(neighbors) - alignment.divideScalar(neighbors) - cohesion.divideScalar(neighbors) - cohesion.sub(bird.mesh.position) - } - - // Move toward target point - const toTarget = bird.targetPoint.clone().sub(bird.mesh.position) - toTarget.normalize() - - // Avoid player if too close - const toPlayer = bird.mesh.position.clone().sub(playerPosition) - const playerDistance = toPlayer.length() - if (playerDistance < 30) { - toPlayer.normalize() - toPlayer.multiplyScalar(2) - bird.velocity.add(toPlayer) - } - - // Combine all forces - bird.velocity.add(separation.multiplyScalar(1.5)) - bird.velocity.add(alignment.multiplyScalar(0.5)) - bird.velocity.add(cohesion.multiplyScalar(0.01)) - bird.velocity.add(toTarget.multiplyScalar(0.2)) - - // Limit speed - const speed = bird.type === 'phoenix' ? 15 : - bird.type === 'owl' ? 8 : - bird.type === 'bat' ? 12 : 10 - - if (bird.velocity.length() > speed) { - bird.velocity.normalize() - bird.velocity.multiplyScalar(speed) - } - - // Update position - bird.mesh.position.add(bird.velocity.clone().multiplyScalar(deltaTime)) - - // Face direction of movement - if (bird.velocity.length() > 0.1) { - bird.mesh.lookAt(bird.mesh.position.clone().add(bird.velocity)) - } - - // Animate wings - bird.wingPhase += deltaTime * 5 - const wingAngle = Math.sin(bird.wingPhase) * 0.5 - - const leftWing = bird.mesh.children[1] as THREE.Mesh - const rightWing = bird.mesh.children[2] as THREE.Mesh - - if (leftWing) leftWing.rotation.z = wingAngle - if (rightWing) rightWing.rotation.z = -wingAngle - - // Update phoenix particles - if (bird.type === 'phoenix') { - const particles = bird.mesh.children.find(child => child instanceof THREE.Points) as THREE.Points - if (particles) { - const positions = particles.geometry.attributes.position.array as Float32Array - for (let j = 0; j < positions.length; j += 3) { - positions[j + 1] -= 0.1 - if (positions[j + 1] < -2) { - positions[j] = (Math.random() - 0.5) * 4 - positions[j + 1] = 2 - positions[j + 2] = (Math.random() - 0.5) * 4 - } - } - particles.geometry.attributes.position.needsUpdate = true - } - } - - // Pick new target occasionally - if (Math.random() < 0.01) { - bird.targetPoint = new THREE.Vector3( - (Math.random() - 0.5) * 200, - 50 + Math.random() * 100, - (Math.random() - 0.5) * 200 - ) - } - } - } - - checkCollection(playerPosition: THREE.Vector3, collectionRadius: number = 10): Bird['type'][] { - const collected: Bird['type'][] = [] - - this.birds.forEach(bird => { - if (!bird.collected) { - const distance = bird.mesh.position.distanceTo(playerPosition) - if (distance < collectionRadius) { - bird.collected = true - collected.push(bird.type) - } - } - }) - - return collected - } - - cleanup() { - this.birds.forEach(bird => { - this.scene.remove(bird.mesh) - bird.mesh.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.geometry.dispose() - if (child.material instanceof THREE.Material) { - child.material.dispose() - } - } - }) - }) - this.birds = [] - } -} \ No newline at end of file diff --git a/src/utils/witchGame/broomstickController.ts b/src/utils/witchGame/broomstickController.ts deleted file mode 100644 index 158b310..0000000 --- a/src/utils/witchGame/broomstickController.ts +++ /dev/null @@ -1,237 +0,0 @@ -import * as THREE from 'three' -import { CameraController } from '../shared/cameraController' - -export class BroomstickController { - private scene: THREE.Scene - private cameraController: CameraController - private broomstick: THREE.Group - private witch: THREE.Group - private position: THREE.Vector3 - private velocity: THREE.Vector3 - private rotation: THREE.Euler - - constructor(scene: THREE.Scene, camera: THREE.PerspectiveCamera) { - this.scene = scene - this.cameraController = new CameraController(camera, { - minDistance: 15, - maxDistance: 60, - minPolarAngle: 0.1, - maxPolarAngle: Math.PI / 2.5, - offsetY: 10 - }) - this.position = new THREE.Vector3(0, 50, 0) - this.velocity = new THREE.Vector3(0, 0, -10) - this.rotation = new THREE.Euler(0, 0, 0) - - this.broomstick = this.createBroomstick() - this.witch = this.createWitch() - - this.broomstick.add(this.witch) - this.scene.add(this.broomstick) - - this.setupCamera() - } - - private createBroomstick(): THREE.Group { - const group = new THREE.Group() - - // Broomstick handle - const handleGeometry = new THREE.CylinderGeometry(0.5, 0.3, 20, 8) - const handleMaterial = new THREE.MeshPhongMaterial({ - color: 0x4a2c17, - emissive: 0x2a1c07, - emissiveIntensity: 0.2 - }) - const handle = new THREE.Mesh(handleGeometry, handleMaterial) - handle.rotation.z = Math.PI / 2 - group.add(handle) - - // Broom bristles - const bristlesGeometry = new THREE.ConeGeometry(3, 8, 16) - const bristlesMaterial = new THREE.MeshPhongMaterial({ - color: 0x8b7355, - emissive: 0x4b3315, - emissiveIntensity: 0.1 - }) - const bristles = new THREE.Mesh(bristlesGeometry, bristlesMaterial) - bristles.position.x = -12 - bristles.rotation.z = -Math.PI / 2 - group.add(bristles) - - // Magical glow - const glowLight = new THREE.PointLight(0x9400d3, 2, 20) - glowLight.position.set(0, -2, 0) - group.add(glowLight) - - // Particle trail - this.createMagicalTrail(group) - - return group - } - - private createWitch(): THREE.Group { - const group = new THREE.Group() - - // Witch body (simplified) - const bodyGeometry = new THREE.CapsuleGeometry(2, 6, 4, 8) - const bodyMaterial = new THREE.MeshPhongMaterial({ - color: 0x2c1810, - emissive: 0x1c0800, - emissiveIntensity: 0.1 - }) - const body = new THREE.Mesh(bodyGeometry, bodyMaterial) - body.position.y = 3 - group.add(body) - - // Witch hat - const hatConeGeometry = new THREE.ConeGeometry(3, 6, 8) - const hatMaterial = new THREE.MeshPhongMaterial({ - color: 0x1a0033, - emissive: 0x2a0043, - emissiveIntensity: 0.3 - }) - const hatCone = new THREE.Mesh(hatConeGeometry, hatMaterial) - hatCone.position.y = 9 - group.add(hatCone) - - const hatBrimGeometry = new THREE.CylinderGeometry(5, 5, 0.5, 16) - const hatBrim = new THREE.Mesh(hatBrimGeometry, hatMaterial) - hatBrim.position.y = 6 - group.add(hatBrim) - - // Cape - const capeGeometry = new THREE.PlaneGeometry(8, 10) - const capeMaterial = new THREE.MeshPhongMaterial({ - color: 0x1a0033, - side: THREE.DoubleSide, - transparent: true, - opacity: 0.9 - }) - const cape = new THREE.Mesh(capeGeometry, capeMaterial) - cape.position.set(-2, 2, 0) - cape.rotation.y = Math.PI / 2 - group.add(cape) - - return group - } - - private createMagicalTrail(parent: THREE.Group) { - const particleCount = 100 - const geometry = new THREE.BufferGeometry() - const positions = new Float32Array(particleCount * 3) - const colors = new Float32Array(particleCount * 3) - - for (let i = 0; i < particleCount; i++) { - positions[i * 3] = (Math.random() - 0.5) * 2 - positions[i * 3 + 1] = (Math.random() - 0.5) * 2 - positions[i * 3 + 2] = Math.random() * 10 - - // Purple to pink gradient - const t = i / particleCount - colors[i * 3] = 0.6 + t * 0.4 - colors[i * 3 + 1] = 0 - colors[i * 3 + 2] = 0.8 - t * 0.3 - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) - geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)) - - const material = new THREE.PointsMaterial({ - size: 2, - vertexColors: true, - transparent: true, - opacity: 0.6, - blending: THREE.AdditiveBlending - }) - - const particles = new THREE.Points(geometry, material) - parent.add(particles) - } - - private setupCamera() { - this.cameraController.setTarget(this.position) - this.cameraController.update() - } - - update(input: { move: THREE.Vector2, rotate: number, zoom: number, boost: boolean }) { - // Banking and turning - A/D turns the witch - const targetBank = -input.move.x * 0.5 - this.rotation.z += (targetBank - this.rotation.z) * 0.1 - - // Turn/yaw based on A/D input - this.rotation.y += input.move.x * 0.02 - - // Pitch control - W/S controls forward/backward tilt - const targetPitch = input.move.y * 0.3 - this.rotation.x += (targetPitch - this.rotation.x) * 0.1 - - // Variable speed - slower base speed, boost with shift - const forwardSpeed = input.boost ? 8 : 4 // Much slower, boost available - - // Calculate movement based on rotation - this.velocity.x = Math.sin(this.rotation.y) * forwardSpeed - this.velocity.z = -Math.cos(this.rotation.y) * forwardSpeed - - // Vertical movement based on pitch - this.velocity.y = -Math.sin(this.rotation.x) * forwardSpeed * 0.5 - - // Update position with delta time - this.position.add(this.velocity.clone().multiplyScalar(0.016)) - - // Keep within bounds - this.position.y = Math.max(20, Math.min(200, this.position.y)) - - // Update broomstick transform - this.broomstick.position.copy(this.position) - this.broomstick.rotation.copy(this.rotation) - - // Animate witch cape - const cape = this.witch.children.find(child => - child instanceof THREE.Mesh && child.geometry instanceof THREE.PlaneGeometry - ) - if (cape) { - cape.rotation.x = Math.sin(Date.now() * 0.002) * 0.1 - 0.2 - cape.rotation.z = Math.sin(Date.now() * 0.003) * 0.05 - } - - // Update camera controller - this.cameraController.handleInput({ - rotate: input.rotate, - zoom: input.zoom - }) - this.cameraController.followTarget(this.position) - this.cameraController.update() - - // Update magical trail - const particles = this.broomstick.children.find(child => child instanceof THREE.Points) as THREE.Points - if (particles) { - const positions = particles.geometry.attributes.position.array as Float32Array - for (let i = 0; i < positions.length; i += 3) { - positions[i + 2] += 0.5 - if (positions[i + 2] > 10) { - positions[i + 2] = 0 - positions[i] = (Math.random() - 0.5) * 2 - positions[i + 1] = (Math.random() - 0.5) * 2 - } - } - particles.geometry.attributes.position.needsUpdate = true - } - } - - getPosition(): THREE.Vector3 { - return this.position.clone() - } - - cleanup() { - this.scene.remove(this.broomstick) - // Dispose of geometries and materials - this.broomstick.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.geometry.dispose() - if (child.material instanceof THREE.Material) { - child.material.dispose() - } - } - }) - } -} \ No newline at end of file diff --git a/src/utils/witchGame/nightSkyEnvironment.ts b/src/utils/witchGame/nightSkyEnvironment.ts deleted file mode 100644 index f565acc..0000000 --- a/src/utils/witchGame/nightSkyEnvironment.ts +++ /dev/null @@ -1,240 +0,0 @@ -import * as THREE from 'three' - -export class NightSkyEnvironment { - private scene: THREE.Scene - private clouds: THREE.Group[] = [] - private stars!: THREE.Points - private moon!: THREE.Mesh - private fog!: THREE.Fog - - constructor(scene: THREE.Scene) { - this.scene = scene - - this.setupSky() - this.createMoon() - this.createStars() - this.createClouds() - this.setupLighting() - this.setupFog() - } - - private setupSky() { - // Dark night gradient - const skyGeometry = new THREE.SphereGeometry(500, 32, 32) - const skyMaterial = new THREE.ShaderMaterial({ - uniforms: { - topColor: { value: new THREE.Color(0x000033) }, - bottomColor: { value: new THREE.Color(0x000066) }, - offset: { value: 100 }, - exponent: { value: 0.6 } - }, - vertexShader: ` - varying vec3 vWorldPosition; - void main() { - vec4 worldPosition = modelMatrix * vec4(position, 1.0); - vWorldPosition = worldPosition.xyz; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, - fragmentShader: ` - uniform vec3 topColor; - uniform vec3 bottomColor; - uniform float offset; - uniform float exponent; - varying vec3 vWorldPosition; - void main() { - float h = normalize(vWorldPosition + offset).y; - gl_FragColor = vec4(mix(bottomColor, topColor, max(pow(max(h, 0.0), exponent), 0.0)), 1.0); - } - `, - side: THREE.BackSide - }) - - const sky = new THREE.Mesh(skyGeometry, skyMaterial) - this.scene.add(sky) - } - - private createMoon() { - const moonGeometry = new THREE.SphereGeometry(20, 32, 32) - const moonMaterial = new THREE.MeshPhongMaterial({ - color: 0xffffcc, - emissive: 0xffffaa, - emissiveIntensity: 0.5 - }) - - this.moon = new THREE.Mesh(moonGeometry, moonMaterial) - this.moon.position.set(100, 150, -200) - this.scene.add(this.moon) - - // Moon glow - const glowGeometry = new THREE.SphereGeometry(25, 32, 32) - const glowMaterial = new THREE.MeshBasicMaterial({ - color: 0xffffaa, - transparent: true, - opacity: 0.3 - }) - const glow = new THREE.Mesh(glowGeometry, glowMaterial) - this.moon.add(glow) - - // Moonlight - const moonLight = new THREE.DirectionalLight(0x6666ff, 0.5) - moonLight.position.copy(this.moon.position) - moonLight.castShadow = true - this.scene.add(moonLight) - } - - private createStars() { - const starCount = 2000 - const geometry = new THREE.BufferGeometry() - const positions = new Float32Array(starCount * 3) - const colors = new Float32Array(starCount * 3) - const sizes = new Float32Array(starCount) - - for (let i = 0; i < starCount; i++) { - const radius = 200 + Math.random() * 300 - const theta = Math.random() * Math.PI * 2 - const phi = Math.acos(Math.random() * 2 - 1) - - positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta) - positions[i * 3 + 1] = radius * Math.cos(phi) - positions[i * 3 + 2] = radius * Math.sin(phi) * Math.sin(theta) - - // Slightly varied star colors - const starColor = new THREE.Color() - starColor.setHSL(0.6, 0.1, 0.9 + Math.random() * 0.1) - colors[i * 3] = starColor.r - colors[i * 3 + 1] = starColor.g - colors[i * 3 + 2] = starColor.b - - sizes[i] = Math.random() * 2 - } - - geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) - geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)) - geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)) - - const material = new THREE.PointsMaterial({ - size: 1, - vertexColors: true, - transparent: true, - opacity: 0.8, - blending: THREE.AdditiveBlending - }) - - this.stars = new THREE.Points(geometry, material) - this.scene.add(this.stars) - } - - private createClouds() { - const cloudCount = 30 - - for (let i = 0; i < cloudCount; i++) { - const cloudGroup = new THREE.Group() - - // Create fluffy cloud from multiple spheres - const puffCount = 6 + Math.floor(Math.random() * 4) - for (let j = 0; j < puffCount; j++) { - const puffGeometry = new THREE.SphereGeometry( - 10 + Math.random() * 15, - 8, - 6 - ) - const puffMaterial = new THREE.MeshPhongMaterial({ - color: 0x444466, - transparent: true, - opacity: 0.7, - emissive: 0x222244, - emissiveIntensity: 0.2 - }) - - const puff = new THREE.Mesh(puffGeometry, puffMaterial) - puff.position.set( - (Math.random() - 0.5) * 20, - (Math.random() - 0.5) * 10, - (Math.random() - 0.5) * 15 - ) - cloudGroup.add(puff) - } - - cloudGroup.position.set( - (Math.random() - 0.5) * 400, - 30 + Math.random() * 100, - (Math.random() - 0.5) * 400 - ) - - this.clouds.push(cloudGroup) - this.scene.add(cloudGroup) - } - } - - private setupLighting() { - // Ambient light for overall visibility - const ambientLight = new THREE.AmbientLight(0x222244, 0.4) - this.scene.add(ambientLight) - - // Hemisphere light for sky/ground color variation - const hemisphereLight = new THREE.HemisphereLight(0x4444ff, 0x002200, 0.3) - this.scene.add(hemisphereLight) - } - - private setupFog() { - this.fog = new THREE.Fog(0x000033, 50, 400) - this.scene.fog = this.fog - } - - update() { - // Slowly rotate stars - this.stars.rotation.y += 0.0001 - - // Animate clouds - this.clouds.forEach((cloud, index) => { - cloud.position.x += Math.sin(Date.now() * 0.0001 + index) * 0.1 - cloud.position.z += Math.cos(Date.now() * 0.0001 + index) * 0.1 - cloud.rotation.y += 0.001 - - // Wrap clouds around - if (cloud.position.x > 250) cloud.position.x = -250 - if (cloud.position.x < -250) cloud.position.x = 250 - if (cloud.position.z > 250) cloud.position.z = -250 - if (cloud.position.z < -250) cloud.position.z = 250 - }) - - // Subtle moon glow animation - const moonGlow = this.moon.children[0] as THREE.Mesh - if (moonGlow && moonGlow.material instanceof THREE.MeshBasicMaterial) { - moonGlow.material.opacity = 0.3 + Math.sin(Date.now() * 0.001) * 0.05 - } - } - - cleanup() { - this.clouds.forEach(cloud => { - this.scene.remove(cloud) - cloud.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.geometry.dispose() - if (child.material instanceof THREE.Material) { - child.material.dispose() - } - } - }) - }) - - this.scene.remove(this.stars) - this.stars.geometry.dispose() - if (this.stars.material instanceof THREE.Material) { - this.stars.material.dispose() - } - - this.scene.remove(this.moon) - this.moon.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.geometry.dispose() - if (child.material instanceof THREE.Material) { - child.material.dispose() - } - } - }) - - this.scene.fog = null - } -} \ No newline at end of file