Sg / index.html
Poorpoor6976's picture
Update index.html
26d1e2d verified
Raw
History Blame Contribute Delete
19.3 kB
<!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>World Conquest MMO - Pixel Engine</title>
<style>
/* CSS Reset & Base */
* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; -webkit-user-select: none; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; -webkit-tap-highlight-color: transparent; outline: none; }
body { background-color: #050a14; color: #e0e0e0; overflow: hidden; touch-action: none; width: 100vw; height: 100vh; }
/* تأثير الضباب المتحرك */
.fog-overlay {
position: fixed; top: -50%; left: -50%; width: 200%; height: 200%;
background: url('data:image/svg+xml;utf8,<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"><filter id="noise"><feTurbulence type="fractalNoise" baseFrequency="0.01" numOctaves="3" stitchTiles="stitch"/></filter><rect width="100%" height="100%" filter="url(%23noise)" opacity="0.08"/></svg>');
animation: fogAnim 40s linear infinite; pointer-events: none; z-index: 1;
}
@keyframes fogAnim { 0% { transform: translate(0, 0); } 100% { transform: translate(10%, 10%); } }
/* شاشة التحميل */
#loading-screen {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: radial-gradient(circle at center, #1a1a2e 0%, #0f0f1a 100%);
display: flex; flex-direction: column; justify-content: center; align-items: center;
z-index: 9999; transition: opacity 0.5s ease;
}
.loader-ring {
width: 60px; height: 60px; border: 4px solid rgba(0, 255, 136, 0.1);
border-top: 4px solid #00ff88; border-radius: 50%; animation: spin 1s linear infinite;
box-shadow: 0 0 15px rgba(0, 255, 136, 0.4);
}
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
.loading-text { margin-top: 20px; font-size: 16px; color: #00ff88; letter-spacing: 1px; text-shadow: 0 0 10px rgba(0,255,136,0.5); }
/* شاشة اختيار الخوادم */
#server-selection {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: radial-gradient(circle at top, #16222a 0%, #3a6073 100%);
z-index: 1000; display: flex; flex-direction: column; align-items: center; padding: 40px 20px;
}
.glass-panel {
background: rgba(20, 20, 25, 0.7); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1);
border-radius: 20px; padding: 25px; width: 100%; max-width: 400px; box-shadow: 0 10px 30px rgba(0,0,0,0.5);
display: flex; flex-direction: column; align-items: center; z-index: 2;
}
.title { color: #fff; font-size: 24px; margin-bottom: 20px; text-shadow: 0 2px 4px rgba(0,0,0,0.5); }
.input-name {
width: 100%; padding: 15px; border-radius: 12px; border: 1px solid #444; background: rgba(0,0,0,0.5);
color: #fff; text-align: center; font-size: 16px; margin-bottom: 20px; outline: none; transition: 0.3s;
}
.input-name:focus { border-color: #00ff88; box-shadow: 0 0 10px rgba(0,255,136,0.3); }
.servers-container { width: 100%; max-height: 40vh; overflow-y: auto; margin-bottom: 20px; padding-right: 5px; }
.servers-container::-webkit-scrollbar { width: 5px; }
.servers-container::-webkit-scrollbar-thumb { background: #00ff88; border-radius: 5px; }
.server-btn {
width: 100%; background: linear-gradient(145deg, #2a2a35, #1f1f28); border: 1px solid #333;
border-radius: 12px; padding: 15px 20px; margin-bottom: 10px; display: flex; justify-content: space-between;
align-items: center; color: #fff; font-size: 16px; transition: all 0.2s ease; cursor: pointer;
}
.server-btn:active { transform: scale(0.96); border-color: #00ff88; }
.server-btn.full { opacity: 0.6; border-color: #ff4444; pointer-events: none; filter: grayscale(100%); }
.server-info { display: flex; align-items: center; gap: 10px; }
.icon { width: 20px; height: 20px; fill: currentColor; }
.action-btn {
display: flex; align-items: center; justify-content: center; gap: 8px;
width: 100%; padding: 15px; border-radius: 12px; background: linear-gradient(45deg, #0077ff, #00bfff);
color: #fff; border: none; font-size: 16px; font-weight: bold; box-shadow: 0 4px 15px rgba(0,119,255,0.4);
transition: 0.2s; cursor: pointer;
}
.action-btn:active { transform: scale(0.96); }
/* حاوية الخريطة البكسلية */
#map-container { position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; background: #050a14; overflow: hidden; }
/* إعدادات الـ Canvas لضمان أداء عالي وعدم تشويش البكسلات عند التكبير */
#world-canvas {
transform-origin: 0 0;
position: absolute;
top: 0;
left: 0;
image-rendering: pixelated; /* مهم جداً للحفاظ على حدة البكسلات */
will-change: transform;
}
/* الإشعارات الذكية */
#notification {
position: fixed; top: 30px; left: 50%; transform: translateX(-50%) translateY(-20px);
background: rgba(255, 68, 68, 0.9); backdrop-filter: blur(5px); padding: 12px 25px;
border-radius: 25px; z-index: 10000; color: white; font-weight: bold; font-size: 14px;
box-shadow: 0 5px 15px rgba(255,68,68,0.4); opacity: 0; transition: all 0.3s ease; pointer-events: none;
}
#notification.show { transform: translateX(-50%) translateY(0); opacity: 1; }
</style>
</head>
<body>
<div class="fog-overlay"></div>
<div id="loading-screen">
<div class="loader-ring"></div>
<p class="loading-text" id="loading-msg">جاري الاتصال بالخادم الآمن...</p>
</div>
<div id="notification"></div>
<div id="server-selection" style="display: none;">
<div class="glass-panel">
<h2 class="title">اختر العالم</h2>
<input type="text" id="player-name" class="input-name" placeholder="أدخل اسمك (20 حرف كحد أقصى)" maxlength="20">
<div class="servers-container" id="servers-list"></div>
<button id="refresh-btn" class="action-btn">
<svg class="icon" viewBox="0 0 24 24"><path d="M17.65 6.35A7.958 7.958 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
تحديث القائمة
</button>
</div>
</div>
<div id="map-container">
<!-- تم استبدال SVG بـ Canvas -->
<canvas id="world-canvas"></canvas>
</div>
<script type="module">
import { Packr } from 'https://esm.sh/msgpackr@1.10.1';
const packr = new Packr();
let ws;
let myColor = '';
let myName = '';
let currentScale = 1;
let currentX = 0;
let currentY = 0;
let lastRefreshTime = 0;
// إعدادات الخريطة البكسلية
let mapWidth = 0;
let mapHeight = 0;
let mapGrid = null;
const TILE_SIZE = 10; // حجم كل بكسل/مربع على الشاشة
const DOM = {
loading: document.getElementById('loading-screen'),
loadingMsg: document.getElementById('loading-msg'),
serverSel: document.getElementById('server-selection'),
serverList: document.getElementById('servers-list'),
playerName: document.getElementById('player-name'),
refreshBtn: document.getElementById('refresh-btn'),
mapContainer: document.getElementById('map-container'),
worldCanvas: document.getElementById('world-canvas'),
notif: document.getElementById('notification')
};
function connect() {
DOM.loadingMsg.innerText = "جاري الاتصال بالخادم الآمن...";
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
ws = new WebSocket(`${protocol}//${location.host}/`);
ws.binaryType = 'arraybuffer';
const timeout = setTimeout(() => {
if (ws.readyState !== WebSocket.OPEN) {
showNotification("فشل الاتصال، يرجى تحديث الصفحة");
DOM.loadingMsg.innerText = "فشل الاتصال بالخادم";
}
}, 5000);
ws.onopen = () => {
clearTimeout(timeout);
DOM.loading.style.opacity = '0';
setTimeout(() => {
DOM.loading.style.display = 'none';
DOM.serverSel.style.display = 'flex';
}, 500);
};
ws.onmessage = async (event) => {
try {
const data = packr.unpack(new Uint8Array(event.data));
handleServerMessage(data);
} catch (e) {
console.error("Data unpack error", e);
}
};
ws.onclose = () => {
showNotification("انقطع الاتصال، جاري إعادة المحاولة...");
DOM.loading.style.display = 'flex';
DOM.loading.style.opacity = '1';
DOM.serverSel.style.display = 'none';
setTimeout(connect, 3000);
};
}
function handleServerMessage(data) {
switch (data.type) {
case 'SERVER_LIST':
renderServers(data.data);
break;
case 'JOIN_SUCCESS':
myColor = data.color;
myName = data.name;
DOM.serverSel.style.display = 'none';
DOM.loading.style.display = 'flex';
DOM.loading.style.opacity = '1';
DOM.loadingMsg.innerText = "جاري تحميل خريطة العالم...";
break;
case 'MAP_DATA':
// استلام الخريطة البكسلية من الخادم
mapWidth = data.width;
mapHeight = data.height;
mapGrid = data.grid;
renderCanvasMap();
setupTouchControls();
DOM.loading.style.opacity = '0';
setTimeout(() => DOM.loading.style.display = 'none', 500);
break;
case 'ERROR':
showNotification(data.msg);
DOM.loading.style.display = 'none';
break;
}
}
function renderServers(servers) {
DOM.serverList.innerHTML = '';
servers.forEach(s => {
const isFull = s.players >= 30;
const btn = document.createElement('div');
btn.className = `server-btn ${isFull ? 'full' : ''}`;
btn.innerHTML = `
<div class="server-info">
<svg class="icon" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/></svg>
<span>عالم ${s.id}</span>
</div>
<span>${s.players}/30</span>
`;
btn.onclick = () => {
if (isFull) showNotification("هذا الخادم ممتلئ، اختر خادماً آخر");
else joinWorld(s.id);
};
DOM.serverList.appendChild(btn);
});
}
function joinWorld(id) {
const name = DOM.playerName.value.trim() || 'قائد مجهول';
ws.send(packr.pack({ type: 'JOIN_WORLD', worldId: id, playerName: name }));
DOM.loadingMsg.innerText = "جاري تجهيز العالم...";
DOM.loading.style.display = 'flex';
DOM.loading.style.opacity = '1';
}
// دالة رسم الخريطة البكسلية على الـ Canvas
function renderCanvasMap() {
const canvas = DOM.worldCanvas;
const ctx = canvas.getContext('2d', { alpha: false }); // تحسين الأداء
canvas.width = mapWidth * TILE_SIZE;
canvas.height = mapHeight * TILE_SIZE;
// رسم المحيط (الماء)
ctx.fillStyle = '#0a192f';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// رسم القارات (اليابسة)
ctx.fillStyle = '#1e1e28';
for (let y = 0; y < mapHeight; y++) {
for (let x = 0; x < mapWidth; x++) {
if (mapGrid[y * mapWidth + x] === 1) {
ctx.fillRect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
}
}
function setupTouchControls() {
let startDist = 0, startScale = 1;
let startX = 0, startY = 0;
let isPanning = false;
let focalX = 0, focalY = 0;
// توسيط الخريطة مبدئياً
function centerMapInitially() {
const winWidth = window.innerWidth;
const winHeight = window.innerHeight;
const baseMapWidth = DOM.worldCanvas.width;
const baseMapHeight = DOM.worldCanvas.height;
const minScaleX = winWidth / baseMapWidth;
const minScaleY = winHeight / baseMapHeight;
currentScale = Math.max(minScaleX, minScaleY);
const scaledWidth = baseMapWidth * currentScale;
const scaledHeight = baseMapHeight * currentScale;
currentX = (winWidth - scaledWidth) / 2;
currentY = (winHeight - scaledHeight) / 2;
clampBounds();
updateTransform();
}
centerMapInitially();
DOM.mapContainer.addEventListener('touchstart', (e) => {
if (e.touches.length === 2) {
isPanning = false;
startDist = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
startScale = currentScale;
focalX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
focalY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
} else if (e.touches.length === 1) {
isPanning = true;
startX = e.touches[0].clientX - currentX;
startY = e.touches[0].clientY - currentY;
}
}, {passive: true});
DOM.mapContainer.addEventListener('touchmove', (e) => {
if (e.touches.length === 2) {
const currentDist = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
let newScale = startScale * (currentDist / startDist);
newScale = Math.min(Math.max(0.1, newScale), 20);
const scaleRatio = newScale / currentScale;
currentX = focalX - (focalX - currentX) * scaleRatio;
currentY = focalY - (focalY - currentY) * scaleRatio;
currentScale = newScale;
clampBounds();
updateTransform();
} else if (e.touches.length === 1 && isPanning) {
currentX = e.touches[0].clientX - startX;
currentY = e.touches[0].clientY - startY;
clampBounds();
updateTransform();
}
}, {passive: true});
DOM.mapContainer.addEventListener('touchend', () => isPanning = false);
function clampBounds() {
const winWidth = window.innerWidth;
const winHeight = window.innerHeight;
const baseMapWidth = DOM.worldCanvas.width;
const baseMapHeight = DOM.worldCanvas.height;
const minScaleX = winWidth / baseMapWidth;
const minScaleY = winHeight / baseMapHeight;
const minScale = Math.max(minScaleX, minScaleY);
if (currentScale < minScale) currentScale = minScale;
const scaledWidth = baseMapWidth * currentScale;
const scaledHeight = baseMapHeight * currentScale;
if (currentX > 0) currentX = 0;
else if (currentX < winWidth - scaledWidth) currentX = winWidth - scaledWidth;
if (currentY > 0) currentY = 0;
else if (currentY < winHeight - scaledHeight) currentY = winHeight - scaledHeight;
}
function updateTransform() {
DOM.worldCanvas.style.transform = `translate(${currentX}px, ${currentY}px) scale(${currentScale})`;
}
}
function showNotification(msg) {
DOM.notif.innerText = msg;
DOM.notif.classList.add('show');
setTimeout(() => DOM.notif.classList.remove('show'), 3000);
}
DOM.refreshBtn.addEventListener('click', () => {
const now = Date.now();
if (now - lastRefreshTime < 5000) {
showNotification("يرجى الانتظار 5 ثواني قبل التحديث");
return;
}
lastRefreshTime = now;
DOM.refreshBtn.disabled = true;
DOM.refreshBtn.innerHTML = '<div class="loader-ring" style="width:20px;height:20px;border-width:2px;"></div> جاري التحديث...';
if(ws.readyState === WebSocket.OPEN) {
ws.send(packr.pack({ type: 'REFRESH_SERVERS' }));
}
setTimeout(() => {
DOM.refreshBtn.disabled = false;
DOM.refreshBtn.innerHTML = '<svg class="icon" viewBox="0 0 24 24"><path d="M17.65 6.35A7.958 7.958 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg> تحديث القائمة';
}, 5000);
});
connect();
</script>
</body>
</html>