🎮ArcadeLab

ماين كرافت العربية - Minecraft Arabic

by BlazingFlame46
707 lines25.0 KB🛠️ Three.js (3D graphics)
▶ Play
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ماين كرافت العربية - Minecraft Arabic</title>
<style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { overflow: hidden; font-family: 'Segoe UI', Tahoma, sans-serif; user-select: none; background: #000; }
    
    #ui {
        position: absolute; top: 10px; right: 10px;
        color: white; text-shadow: 2px 2px 4px #000;
        background: rgba(0,0,0,0.5);
        padding: 12px 18px; border-radius: 8px;
        pointer-events: none; backdrop-filter: blur(5px);
        border: 1px solid rgba(255,255,255,0.2);
    }
    #ui h2 { font-size: 16px; margin-bottom: 5px; color: #f39c12; }
    #ui p { font-size: 13px; }
    
    #crosshair {
        position: absolute; top: 50%; left: 50%;
        width: 20px; height: 20px;
        transform: translate(-50%, -50%);
        pointer-events: none;
    }
    #crosshair::before, #crosshair::after {
        content: ''; position: absolute; background: white;
        box-shadow: 0 0 3px rgba(0,0,0,0.8);
    }
    #crosshair::before { width: 2px; height: 20px; left: 9px; top: 0; }
    #crosshair::after { width: 20px; height: 2px; top: 9px; left: 0; }
    
    #hotbar {
        position: absolute; bottom: 20px; left: 50%;
        transform: translateX(-50%);
        display: flex; gap: 6px;
        background: rgba(0,0,0,0.7);
        padding: 8px; border-radius: 8px;
        border: 2px solid rgba(255,255,255,0.3);
    }
    .slot {
        width: 50px; height: 50px;
        border: 2px solid rgba(255,255,255,0.2);
        border-radius: 5px; cursor: pointer;
        position: relative; transition: all 0.15s;
        display: flex; align-items: center; justify-content: center;
        overflow: hidden;
    }
    .slot:hover { border-color: rgba(255,255,255,0.6); }
    .slot.active {
        border-color: #fff; transform: scale(1.08);
        box-shadow: 0 0 10px rgba(255,255,255,0.6);
    }
    .slot canvas { width: 100%; height: 100%; image-rendering: pixelated; }
    .slot .key {
        position: absolute; top: 2px; right: 4px;
        color: white; font-size: 10px;
        text-shadow: 1px 1px 2px #000;
    }
    
    #instructions {
        position: absolute; top: 0; left: 0;
        width: 100%; height: 100%;
        background: linear-gradient(135deg, rgba(0,0,0,0.9), rgba(20,20,40,0.9));
        color: white; display: flex; flex-direction: column;
        justify-content: center; align-items: center;
        cursor: pointer; z-index: 10;
    }
    #instructions h1 {
        font-size: 48px; margin-bottom: 15px;
        color: #f39c12; text-shadow: 3px 3px 6px #000;
    }
    #instructions .subtitle {
        font-size: 20px; color: #ecf0f1; margin-bottom: 30px;
    }
    #instructions .keys {
        background: rgba(255,255,255,0.1);
        padding: 20px 30px; border-radius: 10px;
        font-size: 15px; line-height: 2;
        border: 1px solid rgba(255,255,255,0.2);
    }
    #instructions .keys b { color: #f39c12; }
    
    #fps {
        position: absolute; top: 10px; left: 10px;
        color: #0f0; font-family: monospace; font-size: 12px;
        background: rgba(0,0,0,0.5); padding: 5px 8px;
        border-radius: 4px; pointer-events: none;
    }
    
    #selected-name {
        position: absolute; bottom: 85px; left: 50%;
        transform: translateX(-50%); color: white;
        text-shadow: 2px 2px 4px #000;
        font-size: 16px; opacity: 0; transition: opacity 0.3s;
        pointer-events: none;
    }
    #selected-name.show { opacity: 1; }
</style>
</head>
<body>

<div id="instructions">
    <h1>⛏️ ماين كرافت العربية</h1>
    <p class="subtitle">انقر للبدء في عالم الصحراء</p>
    <div class="keys">
        <b>WASD</b> - الحركة<br>
        <b>Space</b> - القفز<br>
        <b>Shift</b> - الجري<br>
        <b>زر الفأرة الأيسر</b> - هدم المكعب<br>
        <b>زر الفأرة الأيمن</b> - بناء مكعب<br>
        <b>عجلة الفأرة / 1-6</b> - تغيير المكعب<br>
        <b>ESC</b> - إيقاف مؤقت
    </div>
</div>

<div id="ui">
    <h2>🏜️ الصحراء العربية</h2>
    <p>الكتلة: <span id="current-block-name">رمل</span></p>
    <p>المكعبات: <span id="block-count">0</span></p>
</div>

<div id="fps">FPS: 0</div>
<div id="crosshair"></div>
<div id="selected-name"></div>
<div id="hotbar"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script>

<script>
// ============================================================
// إعداد المشهد
// ============================================================
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x9ac7e3);
scene.fog = new THREE.Fog(0xd4b88a, 30, 90);

const camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 500);
const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: 'high-performance' });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);

// ============================================================
// الإضاءة
// ============================================================
const ambient = new THREE.AmbientLight(0xffffff, 0.55);
scene.add(ambient);

const hemi = new THREE.HemisphereLight(0xa4d3ff, 0xd4b88a, 0.5);
scene.add(hemi);

const sun = new THREE.DirectionalLight(0xfff5e0, 1.0);
sun.position.set(60, 100, 40);
sun.castShadow = true;
sun.shadow.mapSize.set(2048, 2048);
sun.shadow.camera.near = 0.5;
sun.shadow.camera.far = 200;
sun.shadow.camera.left = -60;
sun.shadow.camera.right = 60;
sun.shadow.camera.top = 60;
sun.shadow.camera.bottom = -60;
sun.shadow.bias = -0.0005;
scene.add(sun);

// ============================================================
// نسيج المكعبات (Textures مولّدة برمجياً)
// ============================================================
function makeTexture(baseColor, varColor, pattern) {
    const c = document.createElement('canvas');
    c.width = c.height = 16;
    const ctx = c.getContext('2d');
    ctx.fillStyle = baseColor;
    ctx.fillRect(0, 0, 16, 16);
    
    // ضوضاء بكسلية
    const img = ctx.getImageData(0, 0, 16, 16);
    const d = img.data;
    for (let i = 0; i < d.length; i += 4) {
        const n = (Math.random() - 0.5) * 40;
        d[i] = Math.max(0, Math.min(255, d[i] + n));
        d[i+1] = Math.max(0, Math.min(255, d[i+1] + n));
        d[i+2] = Math.max(0, Math.min(255, d[i+2] + n));
    }
    ctx.putImageData(img, 0, 0);
    
    if (pattern === 'sand') {
        ctx.fillStyle = 'rgba(180,150,100,0.3)';
        for (let i = 0; i < 20; i++) {
            ctx.fillRect(Math.floor(Math.random()*16), Math.floor(Math.random()*16), 1, 1);
        }
    } else if (pattern === 'stone') {
        ctx.strokeStyle = 'rgba(80,80,80,0.5)';
        ctx.lineWidth = 1;
        ctx.strokeRect(2, 2, 5, 5);
        ctx.strokeRect(9, 8, 5, 5);
        ctx.strokeRect(4, 10, 4, 3);
    } else if (pattern === 'wood') {
        ctx.strokeStyle = 'rgba(60,30,10,0.6)';
        for (let y = 1; y < 16; y += 3) {
            ctx.beginPath();
            ctx.moveTo(0, y);
            ctx.lineTo(16, y);
            ctx.stroke();
        }
    } else if (pattern === 'leaves') {
        ctx.fillStyle = 'rgba(0,40,0,0.4)';
        for (let i = 0; i < 30; i++) {
            ctx.fillRect(Math.floor(Math.random()*16), Math.floor(Math.random()*16), 2, 2);
        }
    } else if (pattern === 'gold') {
        ctx.fillStyle = 'rgba(255,220,0,0.5)';
        ctx.fillRect(4, 4, 8, 8);
    } else if (pattern === 'water') {
        ctx.fillStyle = 'rgba(100,180,255,0.4)';
        for (let i = 0; i < 5; i++) {
            ctx.fillRect(0, i*3 + Math.floor(Math.random()*3), 16, 1);
        }
    }
    
    const tex = new THREE.CanvasTexture(c);
    tex.magFilter = THREE.NearestFilter;
    tex.minFilter = THREE.NearestFilter;
    return tex;
}

// ============================================================
// تعريف أنواع المكعبات
// ============================================================
const BLOCK_TYPES = {
    sand:   { name: 'رمل',       color: '#e0cda3', pattern: 'sand' },
    stone:  { name: 'حجر',       color: '#8a8a8a', pattern: 'stone' },
    wood:   { name: 'جذع نخيل',  color: '#8d5524', pattern: 'wood' },
    leaves: { name: 'جريد',      color: '#2d5a27', pattern: 'leaves' },
    gold:   { name: 'ذهب',       color: '#f1c40f', pattern: 'gold' },
    water:  { name: 'ماء',       color: '#3498db', pattern: 'water', transparent: true, opacity: 0.75 }
};

const materials = {};
for (const [key, def] of Object.entries(BLOCK_TYPES)) {
    const tex = makeTexture(def.color, null, def.pattern);
    const mat = new THREE.MeshLambertMaterial({ map: tex });
    if (def.transparent) { mat.transparent = true; mat.opacity = def.opacity; }
    materials[key] = mat;
}

// هندسة المكعب الموحدة
const blockGeo = new THREE.BoxGeometry(1, 1, 1);

// ============================================================
// إدارة العالم
// ============================================================
const blocks = [];
const blockMap = new Map(); // للبحث السريع "x,y,z" -> mesh

function key(x, y, z) { return `${x},${y},${z}`; }

function addBlock(x, y, z, type, withShadow = true) {
    const mesh = new THREE.Mesh(blockGeo, materials[type]);
    mesh.position.set(x, y, z);
    mesh.userData.type = type;
    mesh.userData.gridPos = { x, y, z };
    if (withShadow) { mesh.castShadow = true; mesh.receiveShadow = true; }
    scene.add(mesh);
    blocks.push(mesh);
    blockMap.set(key(x, y, z), mesh);
    return mesh;
}

function removeBlock(mesh) {
    const p = mesh.userData.gridPos;
    blockMap.delete(key(p.x, p.y, p.z));
    const idx = blocks.indexOf(mesh);
    if (idx >= 0) blocks.splice(idx, 1);
    scene.remove(mesh);
    mesh.geometry && mesh.material && mesh.material.dispose && null; // مشترك
}

// ============================================================
// توليد العالم
// ============================================================
const WORLD_SIZE = 24;
const HALF = WORLD_SIZE / 2;

function generateWorld() {
    // الأرض الرملية
    for (let x = -HALF; x < HALF; x++) {
        for (let z = -HALF; z < HALF; z++) {
            // ارتفاعات بسيطة
            const h = Math.floor(Math.sin(x*0.3)*Math.cos(z*0.3)*1.2);
            addBlock(x, h, z, 'sand');
        }
    }
    
    // نخيل عربية
    const palmPositions = [
        [-8, -8], [7, -6], [-5, 9], [10, 8], [-11, 3],
        [3, 11], [-2, -10], [9, -11], [-9, -2]
    ];
    
    palmPositions.forEach(([x, z]) => {
        // جذع
        for (let y = 1; y <= 5; y++) {
            addBlock(x, y, z, 'wood');
        }
        // أوراق النخيل (شكل صليب)
        const leafPositions = [
            [0, 6, 0],
            [1, 6, 0], [-1, 6, 0], [0, 6, 1], [0, 6, -1],
            [2, 6, 1], [-2, 6, 1], [2, 6, -1], [-2, 6, -1],
            [1, 6, 2], [-1, 6, 2], [1, 6, -2], [-1, 6, -2],
            [0, 7, 0]
        ];
        leafPositions.forEach(([dx, dy, dz]) => {
            addBlock(x+dx, dy, z+dz, 'leaves');
        });
    });
    
    // بحيرة صغيرة
    for (let x = -3; x <= 3; x++) {
        for (let z = -3; z <= 3; z++) {
            if (x*x + z*z <= 9) {
                const m = blockMap.get(key(x, 0, z));
                if (m) removeBlock(m);
                addBlock(x, -1, z, 'water');
            }
        }
    }
    
    // بعض الصخور الذهبية المخبأة
    addBlock(5, 0, -5, 'gold');
    addBlock(-6, 1, 6, 'gold');
    addBlock(-4, 0, -7, 'stone');
    addBlock(4, 0, 4, 'stone');
    
    updateBlockCount();
}

generateWorld();

// ============================================================
// التحكم
// ============================================================
const controls = new THREE.PointerLockControls(camera, document.body);
scene.add(controls.getObject());

const instructions = document.getElementById('instructions');
instructions.addEventListener('click', () => controls.lock());
controls.addEventListener('lock', () => { instructions.style.display = 'none'; });
controls.addEventListener('unlock', () => { instructions.style.display = 'flex'; });

// ============================================================
// لوحة الاختيار (Hotbar)
// ============================================================
const hotbar = document.getElementById('hotbar');
const blockKeys = Object.keys(BLOCK_TYPES);
let selectedType = 'sand';
let selectedIndex = 0;

// إنشاء أيقونات اللوحة
blockKeys.forEach((type, i) => {
    const def = BLOCK_TYPES[type];
    const slot = document.createElement('div');
    slot.className = 'slot' + (i === 0 ? ' active' : '');
    slot.dataset.type = type;
    slot.dataset.index = i;
    
    // أيقونة مصغرة
    const canvas = document.createElement('canvas');
    canvas.width = canvas.height = 16;
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = def.color;
    ctx.fillRect(0, 0, 16, 16);
    // نمط بسيط
    const img = ctx.getImageData(0, 0, 16, 16);
    const d = img.data;
    for (let j = 0; j < d.length; j += 4) {
        const n = (Math.random() - 0.5) * 50;
        d[j] = Math.max(0, Math.min(255, d[j] + n));
        d[j+1] = Math.max(0, Math.min(255, d[j+1] + n));
        d[j+2] = Math.max(0, Math.min(255, d[j+2] + n));
    }
    ctx.putImageData(img, 0, 0);
    slot.appendChild(canvas);
    
    // رقم المفتاح
    const keyLabel = document.createElement('span');
    keyLabel.className = 'key';
    keyLabel.textContent = i + 1;
    slot.appendChild(keyLabel);
    
    slot.addEventListener('click', (e) => {
        e.stopPropagation();
        selectBlock(i);
    });
    hotbar.appendChild(slot);
});

const selectedNameEl = document.getElementById('selected-name');
let nameTimeout;

function selectBlock(index) {
    if (index < 0 || index >= blockKeys.length) return;
    selectedIndex = index;
    selectedType = blockKeys[index];
    document.querySelectorAll('.slot').forEach((s, i) => {
        s.classList.toggle('active', i === index);
    });
    document.getElementById('current-block-name').textContent = BLOCK_TYPES[selectedType].name;
    
    // إظهار الاسم مؤقتاً
    selectedNameEl.textContent = BLOCK_TYPES[selectedType].name;
    selectedNameEl.classList.add('show');
    clearTimeout(nameTimeout);
    nameTimeout = setTimeout(() => selectedNameEl.classList.remove('show'), 1200);
}

// ============================================================
// الفيزياء والحركة
// ============================================================
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
const keys = { w: false, a: false, s: false, d: false, space: false, shift: false };
let canJump = true;
let prevTime = performance.now();
let bobTime = 0;

document.addEventListener('keydown', (e) => {
    switch(e.code) {
        case 'KeyW': keys.w = true; break;
        case 'KeyA': keys.a = true; break;
        case 'KeyS': keys.s = true; break;
        case 'KeyD': keys.d = true; break;
        case 'Space': keys.space = true; break;
        case 'ShiftLeft': case 'ShiftRight': keys.shift = true; break;
        case 'Digit1': selectBlock(0); break;
        case 'Digit2': selectBlock(1); break;
        case 'Digit3': selectBlock(2); break;
        case 'Digit4': selectBlock(3); break;
        case 'Digit5': selectBlock(4); break;
        case 'Digit6': selectBlock(5); break;
    }
});
document.addEventListener('keyup', (e) => {
    switch(e.code) {
        case 'KeyW': keys.w = false; break;
        case 'KeyA': keys.a = false; break;
        case 'KeyS': keys.s = false; break;
        case 'KeyD': keys.d = false; break;
        case 'Space': keys.space = false; break;
        case 'ShiftLeft': case 'ShiftRight': keys.shift = false; break;
    }
});

// عجلة الفأرة
document.addEventListener('wheel', (e) => {
    if (!controls.isLocked) return;
    const dir = e.deltaY > 0 ? 1 : -1;
    let newIdx = (selectedIndex + dir + blockKeys.length) % blockKeys.length;
    selectBlock(newIdx);
}, { passive: true });

// ============================================================
// الرماية (Raycasting) للهدم والبناء
// ============================================================
const raycaster = new THREE.Raycaster();
raycaster.far = 8;
const centerMouse = new THREE.Vector2(0, 0);

// مؤشر اختيار
const highlightGeo = new THREE.BoxGeometry(1.02, 1.02, 1.02);
const highlightMat = new THREE.MeshBasicMaterial({ 
    color: 0xffffff, wireframe: true, transparent: true, opacity: 0.6 
});
const highlight = new THREE.Mesh(highlightGeo, highlightMat);
highlight.visible = false;
scene.add(highlight);

function updateHighlight() {
    raycaster.setFromCamera(centerMouse, camera);
    const intersects = raycaster.intersectObjects(blocks, false);
    if (intersects.length > 0) {
        const hit = intersects[0];
        highlight.position.copy(hit.object.position);
        highlight.visible = true;
        return hit;
    }
    highlight.visible = false;
    return null;
}

document.addEventListener('mousedown', (e) => {
    if (!controls.isLocked) return;
    const hit = updateHighlight();
    if (!hit) return;
    
    if (e.button === 0) {
        // هدم
        if (hit.object.userData.type === 'water') return; // لا يمكن هدم الماء
        removeBlock(hit.object);
        playSound('break');
        updateBlockCount();
    } else if (e.button === 2) {
        // بناء
        const normal = hit.face.normal.clone();
        const newPos = hit.object.position.clone().add(normal);
        const np = { x: Math.round(newPos.x), y: Math.round(newPos.y), z: Math.round(newPos.z) };
        
        // التأكد من عدم وجود مكعب
        if (!blockMap.has(key(np.x, np.y, np.z))) {
            // منع البناء داخل اللاعب
            const playerPos = controls.getObject().position;
            const dx = np.x + 0.5 - playerPos.x;
            const dy = np.y - (playerPos.y - 1.5);
            const dz = np.z + 0.5 - playerPos.z;
            const dist = Math.sqrt(dx*dx + dy*dy + dz*dz);
            if (dist > 0.8) {
                addBlock(np.x, np.y, np.z, selectedType);
                playSound('place');
                updateBlockCount();
            }
        }
    }
});

document.addEventListener('contextmenu', e => e.preventDefault());

// ============================================================
// الأصوات (Web Audio API - بسيطة)
// ============================================================
let audioCtx = null;
function playSound(type) {
    try {
        if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
        const osc = audioCtx.createOscillator();
        const gain = audioCtx.createGain();
        osc.connect(gain);
        gain.connect(audioCtx.destination);
        
        if (type === 'break') {
            osc.type = 'sawtooth';
            osc.frequency.setValueAtTime(150, audioCtx.currentTime);
            osc.frequency.exponentialRampToValueAtTime(60, audioCtx.currentTime + 0.1);
            gain.gain.setValueAtTime(0.15, audioCtx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.12);
            osc.start(); osc.stop(audioCtx.currentTime + 0.13);
        } else if (type === 'place') {
            osc.type = 'square';
            osc.frequency.setValueAtTime(300, audioCtx.currentTime);
            osc.frequency.exponentialRampToValueAtTime(180, audioCtx.currentTime + 0.08);
            gain.gain.setValueAtTime(0.12, audioCtx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.1);
            osc.start(); osc.stop(audioCtx.currentTime + 0.11);
        } else if (type === 'jump') {
            osc.type = 'sine';
            osc.frequency.setValueAtTime(400, audioCtx.currentTime);
            osc.frequency.exponentialRampToValueAtTime(600, audioCtx.currentTime + 0.1);
            gain.gain.setValueAtTime(0.08, audioCtx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.12);
            osc.start(); osc.stop(audioCtx.currentTime + 0.13);
        }
    } catch(e) {}
}

// ============================================================
// تحديث عدد المكعبات
// ============================================================
function updateBlockCount() {
    document.getElementById('block-count').textContent = blocks.length;
}

// ============================================================
// حلقة الرسم
// ============================================================
const PLAYER_HEIGHT = 1.7;
const GRAVITY = 28;
const JUMP_SPEED = 8.5;
const WALK_SPEED = 4.5;
const RUN_SPEED = 8;

camera.position.set(0, 3, 0);

// تصحيح دوران الكاميرا الابتدائي
controls.getObject().rotation.y = 0;

let fpsFrames = 0;
let fpsTime = 0;
const fpsEl = document.getElementById('fps');

function animate() {
    requestAnimationFrame(animate);
    
    const now = performance.now();
    const delta = Math.min((now - prevTime) / 1000, 0.1);
    prevTime = now;
    
    // FPS
    fpsFrames++;
    fpsTime += delta;
    if (fpsTime >= 0.5) {
        fpsEl.textContent = 'FPS: ' + Math.round(fpsFrames / fpsTime);
        fpsFrames = 0; fpsTime = 0;
    }
    
    if (controls.isLocked) {
        // الاتجاه
        direction.z = Number(keys.w) - Number(keys.s);
        direction.x = Number(keys.d) - Number(keys.a);
        direction.normalize();
        
        const speed = keys.shift ? RUN_SPEED : WALK_SPEED;
        
        // تسريع / تباطؤ
        const accel = 12.0;
        if (keys.w || keys.s) velocity.z -= direction.z * accel * delta;
        if (keys.a || keys.d) velocity.x -= direction.x * accel * delta;
        
        // حد السرعة
        const maxVel = speed;
        velocity.x = THREE.MathUtils.clamp(velocity.x, -maxVel, maxVel);
        velocity.z = THREE.MathUtils.clamp(velocity.z, -maxVel, maxVel);
        
        // تباطؤ
        const friction = 6.0;
        velocity.x *= Math.max(0, 1 - friction * delta);
        velocity.z *= Math.max(0, 1 - friction * delta);
        
        // القفز والجاذبية
        velocity.y -= GRAVITY * delta;
        
        if (keys.space && canJump) {
            velocity.y = JUMP_SPEED;
            canJump = false;
            playSound('jump');
        }
        
        // الحركة
        controls.moveRight(-velocity.x * delta);
        controls.moveForward(-velocity.z * delta);
        
        const player = controls.getObject();
        player.position.y += velocity.y * delta;
        
        // اكتشاف الأرض (بناءً على أعلى مكعب تحت اللاعب)
        const px = Math.round(player.position.x);
        const pz = Math.round(player.position.z);
        let groundY = -10;
        for (let dy = 0; dy >= -5; dy--) {
            const blockY = Math.round(player.position.y - PLAYER_HEIGHT) + dy;
            if (blockMap.has(key(px, blockY, pz)) || blockMap.has(key(px - 1, blockY, pz)) 
                || blockMap.has(key(px + 1, blockY, pz)) || blockMap.has(key(px, blockY, pz - 1))
                || blockMap.has(key(px, blockY, pz + 1))) {
                groundY = Math.max(groundY, blockY + 0.5);
            }
        }
        
        const minY = groundY + PLAYER_HEIGHT;
        if (player.position.y <= minY) {
            player.position.y = minY;
            velocity.y = 0;
            canJump = true;
        }
        
        // حدود العالم
        const lim = HALF + 5;
        player.position.x = THREE.MathUtils.clamp(player.position.x, -lim, lim);
        player.position.z = THREE.MathUtils.clamp(player.position.z, -lim, lim);
        
        // تأثير المشي البسيط (Head Bob)
        const speedMag = Math.sqrt(velocity.x * velocity.x + velocity.z * velocity.z);
        if (speedMag > 0.5 && canJump) {
            bobTime += delta * speedMag * 2;
            const bob = Math.sin(bobTime) * 0.04;
            camera.position.y = bob; // نسبة إلى controls object
        } else {
            camera.position.y *= Math.max(0, 1 - 8 * delta);
        }
        
        // تحديث التمييز
        updateHighlight();
    }
    
    renderer.render(scene, camera);
}

animate();

// ============================================================
// إعادة ضبط الحجم
// ============================================================
window.addEventListener('resize', () => {
    camera.aspect = innerWidth / innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(innerWidth, innerHeight);
});

// التهيئة الأولية
selectBlock(0);
updateBlockCount();
</script>
</body>
</html>

Game Source: ماين كرافت العربية - Minecraft Arabic

Creator: BlazingFlame46

Libraries: three

Complexity: complex (707 lines, 25.0 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: minecraft-arabic-blazingflame46" to link back to the original. Then publish at arcadelab.ai/publish.