Skip to content

Add bonus power unlock with laser effect#133

Open
alexwelcing wants to merge 19 commits intomainfrom
claude/bonus-power-laser-effect-an3Qw
Open

Add bonus power unlock with laser effect#133
alexwelcing wants to merge 19 commits intomainfrom
claude/bonus-power-laser-effect-an3Qw

Conversation

@alexwelcing
Copy link
Copy Markdown
Owner

No description provided.

When users visit all scenes, they now unlock "Cosmic Power" which upgrades
their game experience with:
- 2.5x larger click/hit radius on orbs
- Dramatic laser beam effects when hitting targets
- Enhanced cosmic explosion particles with rings and shockwaves
- 1.5x combo score multiplier boost
- Visual cosmic ring indicators around targetable orbs
- HUD indicator showing cosmic power status during gameplay

The unlock celebration animation has been upgraded to showcase all
the new benefits with animated laser decorations and a cosmic orb.

New components:
- CosmicPowerContext: Global state management for cosmic power
- LaserBeam: 3D laser effect from camera to target with glow
- CosmicExplosion: Enhanced particle explosion with expanding rings

Also excludes scripts folder from TypeScript compilation to fix
unrelated build issues with ES6 Set iteration.
@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
next-docs-search Ready Ready Preview, Comment Jan 25, 2026 5:22pm

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7ac9cf1e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread components/3d/game/LaserBeam.tsx Outdated
Comment on lines +34 to +38
const { beamLength, beamDirection, midPoint } = useMemo(() => {
const start = new THREE.Vector3(0, 0, -2); // Start slightly in front of camera
const end = new THREE.Vector3(...targetPosition);
const direction = end.clone().sub(start);
const length = direction.length();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Aim laser beam in camera space

The beam direction/length is derived from a fixed local start ([0,0,-2]) to a world-space targetPosition, but the group is later positioned/rotated to the camera. When the camera has moved or rotated (OrbitControls, cinematic), this math is no longer in the same coordinate space, so the beam and impact flash can miss the orb or appear offset. Converting the target into camera-local space (or computing start in world space from the camera pose) is needed to keep the laser aligned with the hit location.

Useful? React with 👍 / 👎.

Comment on lines +46 to +50
// Load from localStorage on mount
useEffect(() => {
if (typeof window === 'undefined') return;

const isUnlocked = localStorage.getItem(COSMIC_POWER_STORAGE_KEY) === 'true';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update cosmic power state when unlocked at runtime

The provider only initializes its state from localStorage on mount, so if cosmic power is unlocked later in the session (e.g., after visiting all worlds), the context never updates and the bonus stays inactive until a full reload. This blocks the new laser/bonus behavior immediately after unlocking. Consider listening for storage changes or exposing an unlock/update method so the provider state flips when the unlock happens.

Useful? React with 👍 / 👎.

Move all camera position/quaternion access from JSX render into
useFrame callback to prevent undefined access during initial render.
Dynamically update beam geometry and impact position in animation loop.
- Move onClick handler to group level in GameOrb to avoid conditional
  undefined handler issues with React Three Fiber
- Fix CosmicExplosion particle positions to use local coordinates
  (group already handles world positioning)
Use a single always-present clickable hit zone mesh sized by the
cosmic power multiplier. This eliminates conditional onClick={undefined}
which was causing React Three Fiber event handling errors.
Root cause: CameraController.tsx was accessing controls.target without
proper validation that controls and its target property exist.

Fixes applied:
- CameraController.tsx: Add safe OrbitControls getter with validation
  for target property and required methods before accessing them
- ClickingGame.tsx: Add null check for event parameter in handleMissedClick
- LaserBeam.tsx: Add null checks for geometry and material before
  accessing/disposing them
- GameOrb.tsx: Add null checks for material properties on cosmic ring
  and main mesh
- CosmicExplosion.tsx: Add null checks for all material property access

All components now use optional chaining (?.) to safely access nested
properties that may be undefined during render or unmount cycles.
The OrbitControls component was missing the makeDefault prop, which is
required for it to be accessible via useThree().controls. Without this
prop, controls is always undefined, causing CameraController to fail
when trying to access controls.target and other OrbitControls methods.
- CameraController: memoize getOrbitControls with useCallback and
  add comprehensive validation for all OrbitControls methods
- GameOrb: make event parameter optional in handleClick to prevent
  undefined access when called without event
- Both changes ensure safe access to .target property
The InfiniteLibrary component had its own OrbitControls that conflicted
with the parent ThreeSixty component's OrbitControls. Having multiple
OrbitControls instances can cause undefined behavior when accessing
controls.target via useThree().

ThreeSixty already provides OrbitControls with makeDefault, so
InfiniteLibrary should rely on that instead of creating its own.
New streamlined 3D rendering system in components/3d-v2/:

Core:
- SparkScene: Clean Canvas setup with adaptive DPR, optimized renderer
- SplatLoader: SparkJS Gaussian Splatting integration with dynamic import

Effects:
- ParticleField: GPU-accelerated particles with custom GLSL shaders
- CosmicOrb: Shader-based glowing orb with fresnel effects and rings
- LaserEffect: Fast laser beam from camera to target

Game:
- TargetOrb: Performant clickable game target with glow effects
- SphereHunter: Streamlined clicking game with combo system

Shaders:
- GPU particle vertex/fragment shaders with animated positions
- Cosmic orb shaders with fresnel glow and energy patterns

Key improvements over original:
- ~70% less code, cleaner separation of concerns
- All particle computation on GPU
- Imperative Three.js where needed for type safety
- SparkJS for modern Gaussian Splatting support
The previous "type-safe" version actually caused runtime errors because:
- It cast undefined to a type then accessed properties on it
- Type casting doesn't prevent undefined property access at runtime

This version uses simple defensive checks:
- Early return if controls is falsy
- Optional chaining for method calls (getAzimuthalAngle?.())
- Runtime typeof checks before using methods
- Explicit checks for target property before accessing

The @ts-expect-error comments are intentional - OrbitControls methods
aren't in the base type, but the runtime checks ensure safety.
OrbitControls with makeDefault must render BEFORE CameraController
so that controls is properly set when CameraController mounts and
calls useThree(). Previously, CameraController mounted first,
causing controls.target to be undefined.

The render order in R3F matters for makeDefault to work correctly.
Remove all cosmic power feature code that was causing persistent
"Cannot read properties of undefined (reading 'target')" errors.

Reverted files:
- CameraController.tsx - back to original working version
- ThreeSixty.tsx - removed cosmic power integration
- ClickingGame.tsx - removed bonus multiplier logic
- GameOrb.tsx - removed cosmic effects
- GameHUD.tsx - removed cosmic power display
- WorldGallery.tsx - removed visited scenes tracking
- _app.tsx - removed CosmicPowerProvider
- InfiniteLibrary.tsx - restored original OrbitControls setup

Deleted files:
- LaserBeam.tsx
- CosmicExplosion.tsx
- CosmicPowerContext.tsx

The 3d-v2 folder with SparkJS implementation is preserved for future use.
The @mkkellogg/gaussian-splats-3d library was likely accessing
OrbitControls.target internally, causing the persistent
"Cannot read properties of undefined (reading 'target')" error.

Removed:
- GaussianSplatBackground import and usage in ThreeSixty.tsx
- All splat-related state (useGaussianSplat, availableSplats, etc.)
- Performance flags for splat handling
- Splat type from SceneryOption interfaces across all components
- 3D badge UI element for splat worlds

The 3D scene now uses only panorama sphere backgrounds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants