import React, { useState, useRef, useEffect, Suspense } from 'react';
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { OrbitControls, useGLTF, Text, Sky, Stars, Html, useAnimations } from '@react-three/drei';
import { Physics, useBox, usePlane, useSphere } from '@react-three/cannon';
import * as THREE from 'three';
// Game state management
const GameContext = React.createContext();
const GameProvider = ({ children }) => {
const [gameState, setGameState] = useState('menu'); // menu, playing, gameover
const [score, setScore] = useState(0);
const [health, setHealth] = useState(100);
const [ammo, setAmmo] = useState(6);
const [wave, setWave] = useState(1);
const [zombies, setZombies] = useState([]);
const [reloading, setReloading] = useState(false);
const addScore = (points) => setScore(prev => prev + points);
const damage = (amount) => setHealth(prev => Math.max(0, prev - amount));
const useAmmo = () => setAmmo(prev => prev - 1);
const reload = () => {
setReloading(true);
setTimeout(() => {
setAmmo(6);
setReloading(false);
}, 2000);
};
const startGame = () => {
setGameState('playing');
setScore(0);
setHealth(100);
setAmmo(6);
setWave(1);
setZombies(generateZombies(wave));
};
const endGame = () => {
setGameState('gameover');
};
const nextWave = () => {
setWave(prev => prev + 1);
setZombies(generateZombies(wave + 1));
};
const generateZombies = (currentWave) => {
const count = 3 + currentWave * 2;
return Array.from({ length: count }, (_, i) => ({
id: `zombie-${Date.now()}-${i}`,
position: [
(Math.random() - 0.5) * 20,
0,
(Math.random() - 0.5) * 20
],
health: 100,
speed: 0.03 + Math.random() * 0.02,
type: Math.random() > 0.7 ? 'fast' : 'normal'
}));
};
const removeZombie = (id) => {
setZombies(prev => {
const newZombies = prev.filter(z => z.id !== id);
if (newZombies.length === 0) {
nextWave();
}
return newZombies;
});
};
useEffect(() => {
if (health <= 0 && gameState === 'playing') {
endGame();
}
}, [health, gameState]);
return (
{children}
);
};
// Player character with chicken mask
const Player = ({ position = [0, 0, 0] }) => {
const { camera } = useThree();
const playerRef = useRef();
const gunRef = useRef();
const [playerPos, setPlayerPos] = useState(new THREE.Vector3(...position));
const [rotation, setRotation] = useState(0);
const { gameState, ammo, useAmmo, reload, reloading, damage } = React.useContext(GameContext);
const [ref, api] = useBox(() => ({
mass: 1,
position,
args: [0.5, 1.8, 0.5],
type: 'Dynamic',
fixedRotation: true,
onCollide: (e) => {
if (e.body.userData && e.body.userData.type === 'zombie') {
damage(5);
}
}
}));
// Movement controls
useEffect(() => {
const keys = {
KeyW: false,
KeyS: false,
KeyA: false,
KeyD: false,
Space: false,
KeyR: false
};
const handleKeyDown = (e) => {
if (keys[e.code] !== undefined) {
keys[e.code] = true;
}
// Shooting
if (e.code === 'MouseLeft' && ammo > 0 && !reloading && gameState === 'playing') {
shoot();
}
// Reloading
if (e.code === 'KeyR' && !reloading && ammo < 6 && gameState === 'playing') {
reload();
}
};
const handleKeyUp = (e) => {
if (keys[e.code] !== undefined) {
keys[e.code] = false;
}
};
const handleMouseMove = (e) => {
if (gameState === 'playing') {
setRotation(-e.movementX * 0.01);
}
};
const handleMouseDown = () => {
if (ammo > 0 && !reloading && gameState === 'playing') {
shoot();
}
};
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mousedown', handleMouseDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mousedown', handleMouseDown);
};
}, [ammo, reloading, gameState]);
const shoot = () => {
useAmmo();
// Create a raycaster for shooting
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(new THREE.Vector2(), camera);
// TODO: Check for zombie hits and handle damage
};
useFrame((state, delta) => {
if (gameState !== 'playing') return;
// Update player position based on keyboard input
const moveSpeed = 0.1;
const direction = new THREE.Vector3();
if (keys.KeyW) direction.z -= moveSpeed;
if (keys.KeyS) direction.z += moveSpeed;
if (keys.KeyA) direction.x -= moveSpeed;
if (keys.KeyD) direction.x += moveSpeed;
// Apply rotation to movement direction
direction.applyAxisAngle(new THREE.Vector3(0, 1, 0), playerRef.current.rotation.y);
// Update position
api.velocity.set(direction.x, 0, direction.z);
// Update rotation
playerRef.current.rotation.y += rotation;
setRotation(0);
// Update camera position to follow player
const newPos = new THREE.Vector3();
ref.current.getWorldPosition(newPos);
setPlayerPos(newPos);
// Position camera behind player
const cameraOffset = new THREE.Vector3(0, 2, 5);
cameraOffset.applyAxisAngle(new THREE.Vector3(0, 1, 0), playerRef.current.rotation.y);
camera.position.copy(newPos).add(cameraOffset);
camera.lookAt(newPos);
});
return (
{/* Player body */}
{/* Chicken mask */}
{/* Beak */}
{/* Eyes */}
{/* Gun - 1851 Navy Colt */}
{/* Gun barrel */}
{/* Gun body */}
{/* Gun handle */}
{/* Cylinder */}
);
};
// Zombie enemy
const Zombie = ({ zombie }) => {
const zombieRef = useRef();
const { removeZombie, addScore, playerPos } = React.useContext(GameContext);
const [ref, api] = useBox(() => ({
mass: 1,
position: zombie.position,
args: [0.6, 1.8, 0.6],
userData: { type: 'zombie', id: zombie.id },
onCollide: (e) => {
// Handle collisions
}
}));
useFrame(() => {
if (!zombieRef.current || !playerPos) return;
// Move zombie towards player
const direction = new THREE.Vector3()
.subVectors(playerPos, new THREE.Vector3(...zombie.position))
.normalize()
.multiplyScalar(zombie.speed);
zombie.position[0] += direction.x;
zombie.position[2] += direction.z;
api.position.set(zombie.position[0], zombie.position[1], zombie.position[2]);
// Rotate zombie to face player
zombieRef.current.lookAt(playerPos);
});
const handleShot = () => {
zombie.health -= 50;
if (zombie.health <= 0) {
removeZombie(zombie.id);
addScore(100);
}
};
return (
{/* Zombie body */}
{/* Zombie head */}
{/* Eyes */}
{/* Mouth */}
{/* Arms */}
);
};
// Environment
const Ground = () => {
const [ref] = usePlane(() => ({
rotation: [-Math.PI / 2, 0, 0],
position: [0, -0.5, 0],
args: [100, 100]
}));
return (
);
};
// UI Components
const HUD = () => {
const { health, ammo, score, wave, reloading } = React.useContext(GameContext);
return (
Ammo: {reloading ? 'Reloading...' : `${ammo}/6`}
Score: {score}
Wave: {wave}
);
};
const MenuScreen = () => {
const { startGame, gameState, score } = React.useContext(GameContext);
return (
CHICKEN MASK ZOMBIE HUNTER
{gameState === 'gameover' && (
Game Over! Score: {score}
)}
{gameState === 'gameover' ? 'Play Again' : 'Start Game'}
Controls:
WASD - Move | MOUSE - Aim | LEFT CLICK - Shoot | R - Reload
Survive waves of zombies with your trusty 1851 Navy Colt revolver.
Your mysterious chicken mask is your only protection!
);
};
// Main Game Component
const ZombieGame = () => {
const { gameState, zombies } = React.useContext(GameContext);
return (
);
};
// Main App
export default function App() {
return (
);
}