| <!DOCTYPE html> |
| <html lang="ar" dir="rtl"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
| <title>EATER HOLE 3D ULTRA - لعبة الحفرة الآكلة</title> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| } |
| |
| body { |
| overflow: hidden; |
| background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); |
| height: 100vh; |
| color: white; |
| touch-action: none; |
| } |
| |
| #gameContainer { |
| position: relative; |
| width: 100%; |
| height: 100vh; |
| } |
| |
| #gameCanvas { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| z-index: 1; |
| } |
| |
| #menuScreen { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| display: flex; |
| flex-direction: column; |
| justify-content: center; |
| align-items: center; |
| background: rgba(0, 0, 0, 0.8); |
| z-index: 10; |
| transition: opacity 0.5s; |
| } |
| |
| .gameTitle { |
| font-size: 3.5rem; |
| margin-bottom: 1rem; |
| text-align: center; |
| background: linear-gradient(45deg, #ff00cc, #3333ff); |
| -webkit-background-clip: text; |
| background-clip: text; |
| color: transparent; |
| text-shadow: 0 0 10px rgba(255, 0, 204, 0.5); |
| font-weight: bold; |
| letter-spacing: 2px; |
| } |
| |
| .developer { |
| font-size: 1.2rem; |
| margin-bottom: 2rem; |
| color: #aaa; |
| } |
| |
| .btn { |
| background: linear-gradient(45deg, #ff00cc, #3333ff); |
| color: white; |
| border: none; |
| padding: 15px 30px; |
| font-size: 1.2rem; |
| margin: 10px; |
| border-radius: 50px; |
| cursor: pointer; |
| width: 250px; |
| text-align: center; |
| transition: all 0.3s; |
| box-shadow: 0 0 15px rgba(255, 0, 204, 0.5); |
| } |
| |
| .btn:hover { |
| transform: scale(1.05); |
| box-shadow: 0 0 20px rgba(255, 0, 204, 0.8); |
| } |
| |
| .skinSelection { |
| display: flex; |
| margin: 20px 0; |
| } |
| |
| .skinOption { |
| width: 60px; |
| height: 60px; |
| margin: 0 10px; |
| border-radius: 50%; |
| cursor: pointer; |
| border: 2px solid transparent; |
| transition: all 0.3s; |
| } |
| |
| .skinOption.selected { |
| border-color: #ff00cc; |
| transform: scale(1.1); |
| box-shadow: 0 0 10px #ff00cc; |
| } |
| |
| #classicSkin { |
| background: linear-gradient(45deg, #000, #333); |
| } |
| |
| #fireSkin { |
| background: linear-gradient(45deg, #ff0000, #ff9900); |
| } |
| |
| #discoSkin { |
| background: linear-gradient(45deg, #00ff00, #0000ff); |
| } |
| |
| #hud { |
| position: absolute; |
| top: 20px; |
| left: 20px; |
| z-index: 5; |
| background: rgba(0, 0, 0, 0.6); |
| padding: 15px; |
| border-radius: 15px; |
| min-width: 200px; |
| } |
| |
| .hudItem { |
| margin: 5px 0; |
| display: flex; |
| justify-content: space-between; |
| } |
| |
| .hudLabel { |
| color: #aaa; |
| } |
| |
| .hudValue { |
| color: #ff00cc; |
| font-weight: bold; |
| } |
| |
| #mobileControls { |
| position: absolute; |
| bottom: 30px; |
| left: 0; |
| width: 100%; |
| display: flex; |
| justify-content: space-around; |
| z-index: 5; |
| } |
| |
| .controlBtn { |
| width: 80px; |
| height: 80px; |
| background: rgba(255, 255, 255, 0.2); |
| border-radius: 50%; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| font-size: 2rem; |
| color: white; |
| user-select: none; |
| touch-action: none; |
| backdrop-filter: blur(5px); |
| border: 2px solid rgba(255, 0, 204, 0.5); |
| } |
| |
| #levelComplete { |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| background: rgba(0, 0, 0, 0.8); |
| padding: 30px; |
| border-radius: 20px; |
| text-align: center; |
| z-index: 20; |
| display: none; |
| } |
| |
| .levelTitle { |
| font-size: 2.5rem; |
| color: #ff00cc; |
| margin-bottom: 20px; |
| } |
| |
| .credits { |
| position: absolute; |
| bottom: 20px; |
| right: 20px; |
| color: #aaa; |
| font-size: 0.9rem; |
| z-index: 5; |
| } |
| |
| .credits a { |
| color: #ff00cc; |
| text-decoration: none; |
| } |
| |
| @media (max-width: 768px) { |
| .gameTitle { |
| font-size: 2.5rem; |
| } |
| |
| .btn { |
| width: 200px; |
| padding: 12px 20px; |
| font-size: 1rem; |
| } |
| |
| .controlBtn { |
| width: 70px; |
| height: 70px; |
| font-size: 1.5rem; |
| } |
| |
| #hud { |
| top: 10px; |
| left: 10px; |
| padding: 10px; |
| min-width: 160px; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div id="gameContainer"> |
| <canvas id="gameCanvas"></canvas> |
| |
| <div id="menuScreen"> |
| <h1 class="gameTitle">EATER HOLE 3D ULTRA</h1> |
| <p class="developer">تم التطوير بواسطة: أدهم ياسر محمد</p> |
| |
| <div class="skinSelection"> |
| <div id="classicSkin" class="skinOption selected"></div> |
| <div id="fireSkin" class="skinOption"></div> |
| <div id="discoSkin" class="skinOption"></div> |
| </div> |
| |
| <button id="startBtn" class="btn">بدء اللعبة</button> |
| <button id="howToPlayBtn" class="btn">كيفية اللعب</button> |
| <button id="contactBtn" class="btn">الاتصال بالمطور</button> |
| </div> |
| |
| <div id="hud"> |
| <div class="hudItem"> |
| <span class="hudLabel">المستوى:</span> |
| <span id="levelValue" class="hudValue">1</span> |
| </div> |
| <div class="hudItem"> |
| <span class="hudLabel">حجم الحفرة:</span> |
| <span id="sizeValue" class="hudValue">1.0</span> |
| </div> |
| <div class="hudItem"> |
| <span class="hudLabel">الأجسام المتبقية:</span> |
| <span id="objectsValue" class="hudValue">50</span> |
| </div> |
| <div class="hudItem"> |
| <span class="hudLabel">النقاط:</span> |
| <span id="scoreValue" class="hudValue">0</span> |
| </div> |
| </div> |
| |
| <div id="mobileControls"> |
| <div class="controlBtn" id="upBtn">↑</div> |
| <div class="controlBtn" id="leftBtn">←</div> |
| <div class="controlBtn" id="downBtn">↓</div> |
| <div class="controlBtn" id="rightBtn">→</div> |
| </div> |
| |
| <div id="levelComplete"> |
| <h2 class="levelTitle">تهانينا! أكملت المستوى</h2> |
| <p>تم التهام <span id="eatenCount">0</span> جسم</p> |
| <p>حصلت على <span id="pointsEarned">0</span> نقطة</p> |
| <button id="nextLevelBtn" class="btn">المستوى التالي</button> |
| </div> |
| |
| <div class="credits"> |
| Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a> |
| </div> |
| </div> |
|
|
| <script> |
| |
| let scene, camera, renderer; |
| let player, npcs = []; |
| let objects = []; |
| let ground; |
| let level = 1; |
| let playerSize = 1; |
| let score = 0; |
| let objectsToEat = 50; |
| let currentSkin = 'classic'; |
| let gameStarted = false; |
| let playerVelocity = { x: 0, z: 0 }; |
| const playerSpeed = 0.1; |
| |
| |
| function init() { |
| |
| scene = new THREE.Scene(); |
| scene.background = new THREE.Color(0x0a0a2a); |
| scene.fog = new THREE.Fog(0x0a0a2a, 20, 100); |
| |
| |
| camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
| camera.position.set(0, 20, 0); |
| camera.lookAt(0, 0, 0); |
| |
| |
| renderer = new THREE.WebGLRenderer({ |
| canvas: document.getElementById('gameCanvas'), |
| antialias: true |
| }); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| renderer.setPixelRatio(window.devicePixelRatio); |
| |
| |
| const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); |
| scene.add(ambientLight); |
| |
| const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); |
| directionalLight.position.set(10, 20, 10); |
| scene.add(directionalLight); |
| |
| |
| createGround(); |
| |
| |
| createPlayer(); |
| |
| |
| createNPCs(); |
| |
| |
| createObjects(); |
| |
| |
| setupEventListeners(); |
| |
| |
| animate(); |
| } |
| |
| |
| function createGround() { |
| const groundGeometry = new THREE.PlaneGeometry(100, 100); |
| const groundMaterial = new THREE.MeshStandardMaterial({ |
| color: level === 1 ? 0xffffff : 0xdddddd, |
| roughness: 0.8, |
| metalness: 0.2 |
| }); |
| ground = new THREE.Mesh(groundGeometry, groundMaterial); |
| ground.rotation.x = -Math.PI / 2; |
| scene.add(ground); |
| |
| |
| if (level === 1) { |
| const gridHelper = new THREE.GridHelper(100, 20, 0x444444, 0x222222); |
| scene.add(gridHelper); |
| } |
| } |
| |
| |
| function createPlayer() { |
| const geometry = new THREE.CylinderGeometry(1, 1, 0.5, 32); |
| const material = new THREE.MeshStandardMaterial({ |
| color: 0x000000, |
| emissive: 0x111111, |
| roughness: 0.7, |
| metalness: 0.3 |
| }); |
| player = new THREE.Mesh(geometry, material); |
| player.position.y = 0.25; |
| scene.add(player); |
| } |
| |
| |
| function createNPCs() { |
| for (let i = 0; i < 20; i++) { |
| const geometry = new THREE.CylinderGeometry(0.5, 0.5, 0.4, 32); |
| const material = new THREE.MeshStandardMaterial({ |
| color: 0xff0000, |
| emissive: 0x220000, |
| roughness: 0.7, |
| metalness: 0.3 |
| }); |
| const npc = new THREE.Mesh(geometry, material); |
| |
| |
| npc.position.x = (Math.random() - 0.5) * 80; |
| npc.position.z = (Math.random() - 0.5) * 80; |
| npc.position.y = 0.2; |
| |
| |
| npc.userData.direction = new THREE.Vector3( |
| Math.random() - 0.5, |
| 0, |
| Math.random() - 0.5 |
| ).normalize(); |
| |
| npc.userData.speed = 0.02 + Math.random() * 0.03; |
| npc.userData.size = 0.5; |
| |
| scene.add(npc); |
| npcs.push(npc); |
| } |
| } |
| |
| |
| function createObjects() { |
| objects = []; |
| |
| for (let i = 0; i < objectsToEat; i++) { |
| let object; |
| |
| |
| const objectType = Math.floor(Math.random() * 3); |
| |
| switch(objectType) { |
| case 0: |
| const width = 1 + Math.random() * 3; |
| const height = 2 + Math.random() * 5; |
| const depth = 1 + Math.random() * 3; |
| const geometry = new THREE.BoxGeometry(width, height, depth); |
| const material = new THREE.MeshStandardMaterial({ |
| color: 0x3498db, |
| roughness: 0.8 |
| }); |
| object = new THREE.Mesh(geometry, material); |
| object.position.y = height / 2; |
| object.userData.size = Math.max(width, depth); |
| break; |
| |
| case 1: |
| const trunkGeometry = new THREE.CylinderGeometry(0.3, 0.4, 2, 8); |
| const trunkMaterial = new THREE.MeshStandardMaterial({ color: 0x8B4513 }); |
| const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial); |
| trunk.position.y = 1; |
| |
| const leavesGeometry = new THREE.ConeGeometry(1.5, 3, 8); |
| const leavesMaterial = new THREE.MeshStandardMaterial({ color: 0x27ae60 }); |
| const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial); |
| leaves.position.y = 3.5; |
| |
| object = new THREE.Group(); |
| object.add(trunk); |
| object.add(leaves); |
| object.userData.size = 1.5; |
| break; |
| |
| case 2: |
| const rockGeometry = new THREE.DodecahedronGeometry(0.5 + Math.random() * 1, 0); |
| const rockMaterial = new THREE.MeshStandardMaterial({ |
| color: 0x7f8c8d, |
| roughness: 0.9 |
| }); |
| object = new THREE.Mesh(rockGeometry, rockMaterial); |
| object.userData.size = 0.8 + Math.random() * 0.7; |
| break; |
| } |
| |
| |
| object.position.x = (Math.random() - 0.5) * 80; |
| object.position.z = (Math.random() - 0.5) * 80; |
| |
| |
| object.rotation.y = Math.random() * Math.PI * 2; |
| |
| scene.add(object); |
| objects.push(object); |
| } |
| |
| |
| document.getElementById('objectsValue').textContent = objects.length; |
| } |
| |
| |
| function updateNPCs() { |
| for (const npc of npcs) { |
| |
| npc.position.x += npc.userData.direction.x * npc.userData.speed; |
| npc.position.z += npc.userData.direction.z * npc.userData.speed; |
| |
| |
| if (Math.abs(npc.position.x) > 45 || Math.abs(npc.position.z) > 45) { |
| npc.userData.direction.x = -npc.userData.direction.x; |
| npc.userData.direction.z = -npc.userData.direction.z; |
| } |
| |
| |
| if (Math.random() < 0.01) { |
| npc.userData.direction = new THREE.Vector3( |
| Math.random() - 0.5, |
| 0, |
| Math.random() - 0.5 |
| ).normalize(); |
| } |
| |
| |
| for (let i = objects.length - 1; i >= 0; i--) { |
| const object = objects[i]; |
| const distance = npc.position.distanceTo(object.position); |
| |
| if (distance < npc.userData.size + 0.5 && object.userData.size < npc.userData.size) { |
| |
| scene.remove(object); |
| objects.splice(i, 1); |
| |
| |
| npc.userData.size += 0.05; |
| npc.scale.set( |
| npc.userData.size / 0.5, |
| 1, |
| npc.userData.size / 0.5 |
| ); |
| |
| |
| document.getElementById('objectsValue').textContent = objects.length; |
| } |
| } |
| } |
| } |
| |
| |
| function checkCollisions() { |
| for (let i = objects.length - 1; i >= 0; i--) { |
| const object = objects[i]; |
| const distance = player.position.distanceTo(object.position); |
| |
| if (distance < playerSize + 1 && object.userData.size < playerSize) { |
| |
| scene.remove(object); |
| objects.splice(i, 1); |
| |
| |
| playerSize += 0.1; |
| player.scale.set(playerSize, 1, playerSize); |
| |
| |
| score += Math.floor(object.userData.size * 10); |
| |
| |
| playEatSound(); |
| |
| |
| document.getElementById('sizeValue').textContent = playerSize.toFixed(1); |
| document.getElementById('objectsValue').textContent = objects.length; |
| document.getElementById('scoreValue').textContent = score; |
| |
| |
| if (objects.length === 0) { |
| completeLevel(); |
| } |
| } |
| } |
| } |
| |
| |
| function completeLevel() { |
| gameStarted = false; |
| document.getElementById('levelComplete').style.display = 'block'; |
| document.getElementById('eatenCount').textContent = objectsToEat; |
| document.getElementById('pointsEarned').textContent = score; |
| } |
| |
| |
| function playEatSound() { |
| try { |
| |
| const audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
| |
| |
| const oscillator = audioContext.createOscillator(); |
| const gainNode = audioContext.createGain(); |
| |
| oscillator.connect(gainNode); |
| gainNode.connect(audioContext.destination); |
| |
| |
| oscillator.type = 'sine'; |
| oscillator.frequency.setValueAtTime(300, audioContext.currentTime); |
| oscillator.frequency.exponentialRampToValueAtTime(50, audioContext.currentTime + 0.3); |
| |
| gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); |
| gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3); |
| |
| |
| oscillator.start(); |
| oscillator.stop(audioContext.currentTime + 0.3); |
| } catch (e) { |
| console.log("Audio not supported:", e); |
| } |
| } |
| |
| |
| function setupEventListeners() { |
| |
| document.getElementById('startBtn').addEventListener('click', () => { |
| document.getElementById('menuScreen').style.display = 'none'; |
| gameStarted = true; |
| }); |
| |
| |
| document.getElementById('classicSkin').addEventListener('click', () => { |
| selectSkin('classic'); |
| }); |
| |
| document.getElementById('fireSkin').addEventListener('click', () => { |
| selectSkin('fire'); |
| }); |
| |
| document.getElementById('discoSkin').addEventListener('click', () => { |
| selectSkin('disco'); |
| }); |
| |
| |
| document.getElementById('contactBtn').addEventListener('click', () => { |
| window.location.href = 'https://wa.me/201097399624'; |
| }); |
| |
| |
| document.getElementById('nextLevelBtn').addEventListener('click', () => { |
| level++; |
| objectsToEat = 50 + level * 5; |
| document.getElementById('levelComplete').style.display = 'none'; |
| document.getElementById('levelValue').textContent = level; |
| |
| |
| while(scene.children.length > 0) { |
| scene.remove(scene.children[0]); |
| } |
| |
| |
| createGround(); |
| createPlayer(); |
| createNPCs(); |
| createObjects(); |
| |
| |
| playerSize = 1; |
| player.scale.set(1, 1, 1); |
| |
| |
| document.getElementById('sizeValue').textContent = playerSize.toFixed(1); |
| document.getElementById('objectsValue').textContent = objects.length; |
| |
| gameStarted = true; |
| }); |
| |
| |
| const controlBtns = document.querySelectorAll('.controlBtn'); |
| controlBtns.forEach(btn => { |
| btn.addEventListener('touchstart', (e) => { |
| e.preventDefault(); |
| handleControl(btn.id); |
| }); |
| }); |
| |
| |
| window.addEventListener('keydown', (e) => { |
| if (!gameStarted) return; |
| |
| switch(e.key) { |
| case 'ArrowUp': |
| case 'w': |
| case 'W': |
| playerVelocity.z = -playerSpeed; |
| break; |
| case 'ArrowDown': |
| case 's': |
| case 'S': |
| playerVelocity.z = playerSpeed; |
| break; |
| case 'ArrowLeft': |
| case 'a': |
| case 'A': |
| playerVelocity.x = -playerSpeed; |
| break; |
| case 'ArrowRight': |
| case 'd': |
| case 'D': |
| playerVelocity.x = playerSpeed; |
| break; |
| } |
| }); |
| |
| window.addEventListener('keyup', (e) => { |
| switch(e.key) { |
| case 'ArrowUp': |
| case 'w': |
| case 'W': |
| case 'ArrowDown': |
| case 's': |
| case 'S': |
| playerVelocity.z = 0; |
| break; |
| case 'ArrowLeft': |
| case 'a': |
| case 'A': |
| case 'ArrowRight': |
| case 'd': |
| case 'D': |
| playerVelocity.x = 0; |
| break; |
| } |
| }); |
| |
| |
| window.addEventListener('resize', () => { |
| camera.aspect = window.innerWidth / window.innerHeight; |
| camera.updateProjectionMatrix(); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| }); |
| } |
| |
| |
| function handleControl(btnId) { |
| if (!gameStarted) return; |
| |
| switch(btnId) { |
| case 'upBtn': |
| playerVelocity.z = -playerSpeed; |
| break; |
| case 'downBtn': |
| playerVelocity.z = playerSpeed; |
| break; |
| case 'leftBtn': |
| playerVelocity.x = -playerSpeed; |
| break; |
| case 'rightBtn': |
| playerVelocity.x = playerSpeed; |
| break; |
| } |
| |
| |
| setTimeout(() => { |
| playerVelocity.x = 0; |
| playerVelocity.z = 0; |
| }, 200); |
| } |
| |
| |
| function selectSkin(skin) { |
| currentSkin = skin; |
| |
| |
| document.querySelectorAll('.skinOption').forEach(option => { |
| option.classList.remove('selected'); |
| }); |
| document.getElementById(skin + 'Skin').classList.add('selected'); |
| |
| |
| } |
| |
| |
| function animate() { |
| requestAnimationFrame(animate); |
| |
| if (gameStarted) { |
| |
| player.position.x += playerVelocity.x; |
| player.position.z += playerVelocity.z; |
| |
| |
| if (player.position.x < -45) player.position.x = -45; |
| if (player.position.x > 45) player.position.x = 45; |
| if (player.position.z < -45) player.position.z = -45; |
| if (player.position.z > 45) player.position.z = 45; |
| |
| |
| camera.position.x = player.position.x; |
| camera.position.z = player.position.z + 10; |
| camera.lookAt(player.position.x, 0, player.position.z); |
| |
| |
| updateNPCs(); |
| |
| |
| checkCollisions(); |
| } |
| |
| renderer.render(scene, camera); |
| } |
| |
| |
| window.onload = init; |
| </script> |
| </body> |
| </html> |