1
боссфайт
Сообщений 1 страница 12 из 12
Поделиться22025-12-20 02:12:04
[html]
<div class="monster-attack-result" data-attack="CjxkaXYgY2xhc3M9Im1vbnN0ZXItYXR0YWNrLXBvc3QiIHN0eWxlPSIKICAgIGJvcmRlcjogM3B4IHNvbGlkICNmZjZiNmI7CiAgICBib3JkZXItcmFkaXVzOiAxNXB4OwogICAgcGFkZGluZzogMjBweDsKICAgIGJhY2tncm91bmQ6IGxpbmVhci1ncmFkaWVudCgxMzVkZWcsICMxYTFhMmUgMCUsICMxNjIxM2UgMTAwJSk7CiAgICBtYXJnaW46IDE1cHggMDsKICAgIGNvbG9yOiB3aGl0ZTsKICAgIGZvbnQtZmFtaWx5OiBBcmlhbCwgc2Fucy1zZXJpZjsKICAgIGJveC1zaGFkb3c6IDAgMTBweCAzMHB4IHJnYmEoMCwwLDAsMC41KTsKIj4KICAgIDxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsgbWFyZ2luLWJvdHRvbTogMjBweDsiPgogICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMjJweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiAjZmY2YjZiOyBtYXJnaW4tYm90dG9tOiA4cHg7Ij4KICAgICAgICAgICAgJiM5ODc2OyYjNjUwMzk7INCQ0KLQkNCa0JAg0J3QkCDQkdCe0KHQodCQICYjOTg3NjsmIzY1MDM5OwogICAgICAgIDwvZGl2PgogICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMTRweDsgY29sb3I6ICNhYWE7Ij4KICAgICAgICAgICAgMjAuMTIuMjAyNSwgMDI6MTI6MDQKICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgCiAgICA8ZGl2IHN0eWxlPSJkaXNwbGF5OiBmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47IGFsaWduLWl0ZW1zOiBjZW50ZXI7IG1hcmdpbi1ib3R0b206IDIwcHg7IHBhZGRpbmc6IDE1cHg7IGJhY2tncm91bmQ6IHJnYmEoMCwwLDAsMC4zKTsgYm9yZGVyLXJhZGl1czogMTBweDsiPgogICAgICAgIDxkaXYgc3R5bGU9ImZsZXg6IDE7Ij4KICAgICAgICAgICAgPGRpdiBzdHlsZT0iZm9udC1zaXplOiAxOHB4OyBmb250LXdlaWdodDogYm9sZDsgY29sb3I6ICM0ZWNkYzQ7IG1hcmdpbi1ib3R0b206IDVweDsiPgogICAgICAgICAgICAgICAgYWRtaW4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMTNweDsgY29sb3I6ICM4ODg7Ij4KICAgICAgICAgICAgICAgINCj0YDQvtC9OiA0MyB8INCQ0YLQsNC6OiAxCiAgICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvZGl2PgogICAgICAgIAogICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMzJweDsgbWFyZ2luOiAwIDIwcHg7Ij4KICAgICAgICAgICAgJiM5ODc2OyYjNjUwMzk7CiAgICAgICAgPC9kaXY+CiAgICAgICAgCiAgICAgICAgPGRpdiBzdHlsZT0iZmxleDogMTsgdGV4dC1hbGlnbjogcmlnaHQ7Ij4KICAgICAgICAgICAgPGRpdiBzdHlsZT0iZm9udC1zaXplOiAxOHB4OyBmb250LXdlaWdodDogYm9sZDsgY29sb3I6ICNmZjZiNmI7IG1hcmdpbi1ib3R0b206IDVweDsiPgogICAgICAgICAgICAgICAgJiMxMjgyOTM7INCT0J3QldCS0J3Qq9CZINCU0KDQkNCa0J7QnQogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBzdHlsZT0iZm9udC1zaXplOiAxM3B4OyBjb2xvcjogIzg4ODsiPgogICAgICAgICAgICAgICAgSFA6IDk1Ny8xMDAwCiAgICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvZGl2PgogICAgPC9kaXY+CiAgICAKICAgIDxkaXYgc3R5bGU9ImJhY2tncm91bmQ6IHJnYmEoMCwwLDAsMC40KTsgYm9yZGVyLXJhZGl1czogMTBweDsgcGFkZGluZzogMjBweDsgbWFyZ2luOiAxNXB4IDA7Ij4KICAgICAgICA8ZGl2IHN0eWxlPSJmb250LXNpemU6IDI0cHg7IHRleHQtYWxpZ246IGNlbnRlcjsgbWFyZ2luLWJvdHRvbTogMTVweDsiPgogICAgICAgICAgICA8c3BhbiBzdHlsZT0iY29sb3I6ICNmZmQxNjY7Ij7QndCw0L3QtdGB0LXQvdC+INGD0YDQvtC90LA6PC9zcGFuPgogICAgICAgICAgICA8c3BhbiBzdHlsZT0iZm9udC1zaXplOiAzMnB4OyBmb250LXdlaWdodDogYm9sZDsgY29sb3I6ICNmZjZiNmI7IG1hcmdpbi1sZWZ0OiAxNXB4OyI+CiAgICAgICAgICAgICAgICA0MwogICAgICAgICAgICA8L3NwYW4+CiAgICAgICAgPC9kaXY+CiAgICAgICAgCiAgICAgICAgPGRpdiBzdHlsZT0ibWFyZ2luOiAyMHB4IDA7IHBhZGRpbmc6IDEwcHg7IGJhY2tncm91bmQ6IHJnYmEoMCwwLDAsMC4zKTsgYm9yZGVyLXJhZGl1czogOHB4OyI+CiAgICAgICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMTRweDsgY29sb3I6ICNhYWE7IG1hcmdpbi1ib3R0b206IDVweDsiPtCX0LTQvtGA0L7QstGM0LUg0LHQvtGB0YHQsDo8L2Rpdj4KICAgICAgICAgICAgPGRpdiBzdHlsZT0iaGVpZ2h0OiAyNXB4OyBiYWNrZ3JvdW5kOiAjMzMzOyBib3JkZXItcmFkaXVzOiAxMnB4OyBvdmVyZmxvdzogaGlkZGVuOyBwb3NpdGlvbjogcmVsYXRpdmU7Ij4KICAgICAgICAgICAgICAgIDxkaXYgc3R5bGU9IgogICAgICAgICAgICAgICAgICAgIGhlaWdodDogMTAwJTsKICAgICAgICAgICAgICAgICAgICB3aWR0aDogOTUuNyU7CiAgICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZDogbGluZWFyLWdyYWRpZW50KDkwZGVnLCAjNENBRjUwLCAjOEJDMzRBKTsKICAgICAgICAgICAgICAgICAgICB0cmFuc2l0aW9uOiB3aWR0aCAxcyBlYXNlOwogICAgICAgICAgICAgICAgICAgIGJvcmRlci1yYWRpdXM6IDEycHg7CiAgICAgICAgICAgICAgICAiPjwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBzdHlsZT0icG9zaXRpb246IGFic29sdXRlOyB0b3A6IDA7IGxlZnQ6IDA7IHJpZ2h0OiAwOyBib3R0b206IDA7IGRpc3BsYXk6IGZsZXg7IGFsaWduLWl0ZW1zOiBjZW50ZXI7IGp1c3RpZnktY29udGVudDogY2VudGVyOyBjb2xvcjogd2hpdGU7IGZvbnQtd2VpZ2h0OiBib2xkOyI+CiAgICAgICAgICAgICAgICAgICAgOTU3LzEwMDAgKDk2JSkKICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgICAgICAKICAgICAgICAKICAgICAgICAKICAgICAgICAKICAgIDwvZGl2PgogICAgCiAgICA8ZGl2IHN0eWxlPSJkaXNwbGF5OiBmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47IGZvbnQtc2l6ZTogMTJweDsgY29sb3I6ICM2NjY7IG1hcmdpbi10b3A6IDIwcHg7IHBhZGRpbmctdG9wOiAxNXB4OyBib3JkZXItdG9wOiAxcHggc29saWQgIzQ0NDsiPgogICAgICAgIDxkaXY+TW9uc3RlciBCYXR0bGUgdjEuMCB8IElEOiAyPC9kaXY+CiAgICAgICAgPGRpdj7Qm9GD0YfRiNC40Lkg0YPQtNCw0YA6IDQzPC9kaXY+CiAgICAgICAgPGRpdj7Qo9Cx0LjRgtC+INCx0L7RgdGB0L7QsjogMDwvZGl2PgogICAgPC9kaXY+CjwvZGl2Pg==">
Загрузка результата атаки...
</div>
<script>
(function(){
try {
var encoded = document.querySelector(".monster-attack-result").getAttribute("data-attack");
var html = decodeURIComponent(escape(atob(encoded)));
document.querySelector(".monster-attack-result").innerHTML = html;
// Анимация
var el = document.querySelector(".monster-attack-result");
el.style.animation = "monsterPulse 0.5s";
setTimeout(() => el.style.animation = "", 500);
} catch(e){
document.querySelector(".monster-attack-result").innerHTML = "<b>Ошибка загрузки результата</b>";
}
})();
</script>
<style>
@keyframes monsterPulse {
0% { transform: scale(1); opacity: 0.8; }
50% { transform: scale(1.02); opacity: 1; }
100% { transform: scale(1); opacity: 0.8; }
}
</style>
[/html]
Поделиться32025-12-20 03:45:30
[html]<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Битва с монстром</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; font-family:Arial, sans-serif; }
body { background:#0a0a14; color:#fff; padding:20px; }
.game-box {
display:flex; gap:20px; max-width:1200px; margin:0 auto;
background:#1a1a2e; border-radius:10px; padding:20px;
}
.left-side { width:250px; }
.right-side { flex:1; }
.block { background:#252540; border-radius:8px; padding:15px; margin-bottom:15px; }
.title { color:#ff9a3c; font-weight:bold; margin-bottom:10px; border-bottom:1px solid #444; padding-bottom:8px; }
.player { display:flex; justify-content:space-between; padding:8px; background:#2a2a40; margin-bottom:5px; border-radius:5px; }
.player.me { background:#1e3a2e; border-left:3px solid #4CAF50; }
.record { margin-bottom:10px; }
.record-name { font-size:12px; color:#aaa; }
.record-value { font-size:14px; color:#fff; font-weight:bold; }
.log { padding:8px; background:#2a2a40; margin-bottom:5px; border-radius:5px; font-size:12px; color:#ccc; }
.boss-area { position:relative; margin-bottom:20px; }
.boss-img { width:100%; height:300px; object-fit:cover; border-radius:8px; border:2px solid #4a4a6d; }
.boss-info { position:absolute; top:10px; left:10px; right:10px; }
.boss-name { color:#ff6b6b; font-size:20px; font-weight:bold; margin-bottom:10px; }
.health-box { background:rgba(0,0,0,0.7); padding:10px; border-radius:6px; }
.health-text { display:flex; justify-content:space-between; margin-bottom:8px; color:#ffd166; }
.health-bar { height:20px; background:#333; border-radius:10px; overflow:hidden; }
.health-fill { height:100%; background:#4CAF50; width:100%; }
.attack-box { background:#252540; border-radius:8px; padding:20px; }
.attack-msg { width:100%; padding:12px; margin-bottom:15px; background:#2a2a40; color:#fff; border:1px solid #444; border-radius:6px; min-height:60px; }
.attack-btn { width:100%; padding:15px; background:#ff6b6b; color:white; border:none; border-radius:8px; font-size:18px; font-weight:bold; cursor:pointer; }
.attack-btn:disabled { background:#444; color:#888; cursor:not-allowed; }
.last-hit { text-align:center; color:#ff6b6b; margin-top:15px; padding:10px; background:rgba(255,107,107,0.1); border-radius:6px; }
.status { background:#333; padding:10px; margin-top:20px; border-radius:6px; font-size:12px; }
</style>
</head>
<body>
<div class="game-box">
<div class="left-side">
<div class="block">
<div class="title">🏆 ТОП ИГРОКОВ</div>
<div id="top-players">
<div class="player"><span>Загрузка...</span><span>0</span></div>
</div>
</div>
<div class="block">
<div class="title">🎯 РЕКОРДЫ</div>
<div class="record">
<div class="record-name">МАКСИМАЛЬНЫЙ УРОН:</div>
<div class="record-value" id="max-damage">-</div>
</div>
<div class="record">
<div class="record-name">МИНИМАЛЬНЫЙ УРОН:</div>
<div class="record-value" id="min-damage">-</div>
</div>
</div>
<div class="block">
<div class="title">📜 ЖУРНАЛ</div>
<div id="logs">
<div class="log">Загрузка...</div>
</div>
</div>
</div>
<div class="right-side">
<div class="boss-area">
<img class="boss-img" src="https://upforme.ru/uploads/001c/84/76/2/433839.jpg" alt="Босс">
<div class="boss-info">
<div class="boss-name" id="boss-name">🔥 ГНЕВНЫЙ ДРАКОН</div>
<div class="health-box">
<div class="health-text">
<span>ЗДОРОВЬЕ</span>
<span id="health-text">1000 / 1000</span>
</div>
<div class="health-bar">
<div class="health-fill" id="health-bar"></div>
</div>
</div>
</div>
</div>
<div class="attack-box">
<textarea class="attack-msg" id="attack-msg" placeholder="Ваше сообщение (необязательно)"></textarea>
<button class="attack-btn" id="attack-btn" onclick="attack()">АТАКОВАТЬ</button>
<div class="last-hit" id="last-hit">-</div>
</div>
<div class="status" id="status">
Статус: Загрузка...
</div>
</div>
</div>
<script>
// === ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ===
let gameData = {
boss: null,
player: null,
canAttack: true
};
// === ФУНКЦИИ ОБЩЕНИЯ (СТАРЫЙ ФОРМАТ) ===
function sendToParent(action, extraData = {}) {
return new Promise((resolve, reject) => {
const requestId = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
const data = {
_monsterGame: true, // Старый формат
type: "gameRequest",
requestId: requestId,
action: action,
...extraData
};
console.log('📤 Sending (old format):', action);
// Отправляем сообщение
window.parent.postMessage(data, '*');
// Ждем ответ
const handleMessage = (event) => {
if (event.data?._monsterGame === true && event.data.requestId === requestId) {
window.removeEventListener('message', handleMessage);
clearTimeout(timeout);
if (event.data.error) {
reject(new Error(event.data.error));
} else {
resolve(event.data);
}
}
// Также пробуем новый формат
if (event.data?.gameType === 'monster' && event.data.requestId === requestId) {
window.removeEventListener('message', handleMessage);
clearTimeout(timeout);
if (event.data.error) {
reject(new Error(event.data.error));
} else {
// Конвертируем новый формат в старый
const convertedResponse = {
_monsterGame: true,
type: "gameResponse",
requestId: requestId,
data: event.data.data || event.data
};
resolve(convertedResponse);
}
}
};
window.addEventListener('message', handleMessage);
// Таймаут 5 секунд
const timeout = setTimeout(() => {
window.removeEventListener('message', handleMessage);
reject(new Error('Таймаут 5 секунд'));
}, 5000);
});
}
// === ЗАГРУЗКА ДАННЫХ ===
async function loadData() {
try {
updateStatus('Загрузка данных...');
// Пробуем загрузить все данные одним запросом
const response = await sendToParent('getGameData');
if (response.data) {
gameData.boss = response.data.boss || response.data;
gameData.allPlayers = response.data.players || {};
gameData.logs = response.data.logs || [];
gameData.records = response.data.records || {};
updateUI();
updateStatus('Данные загружены');
return true;
}
} catch (error) {
updateStatus('Ошибка загрузки: ' + error.message);
console.error('❌ Load error:', error);
}
return false;
}
// === ОБНОВЛЕНИЕ ИНТЕРФЕЙСА ===
function updateUI() {
// Босс
if (gameData.boss) {
const boss = gameData.boss;
const isAlive = boss.isAlive !== false && boss.currentHealth > 0;
const healthPercent = isAlive ? (boss.currentHealth / boss.maxHealth) * 100 : 0;
document.getElementById('health-bar').style.width = healthPercent + '%';
document.getElementById('health-text').textContent =
isAlive ? `${boss.currentHealth} / ${boss.maxHealth}` : '☠️ ПОБЕЖДЕН';
// Цвет полоски
const bar = document.getElementById('health-bar');
if (isAlive) {
if (healthPercent > 70) bar.style.background = '#4CAF50';
else if (healthPercent > 30) bar.style.background = '#FF9800';
else bar.style.background = '#f44336';
} else {
bar.style.background = '#666';
}
// Кнопка
const btn = document.getElementById('attack-btn');
btn.disabled = !isAlive;
btn.textContent = isAlive ? 'АТАКОВАТЬ' : 'БОСС ПОБЕЖДЕН';
// Поле сообщения
document.getElementById('attack-msg').disabled = !isAlive;
}
// Топ игроков
if (gameData.allPlayers) {
const container = document.getElementById('top-players');
const players = Object.values(gameData.allPlayers)
.filter(p => p && p.totalDamage !== undefined)
.sort((a, b) => b.totalDamage - a.totalDamage)
.slice(0, 10);
container.innerHTML = '';
players.forEach((p, i) => {
const div = document.createElement('div');
div.className = 'player';
if (gameData.player && p.id === gameData.player.id) {
div.classList.add('me');
}
div.innerHTML = `<span>${i+1}. ${p.name || p.nickname || 'Игрок'}</span><span>${p.totalDamage}</span>`;
container.appendChild(div);
});
}
// Рекорды
if (gameData.records) {
if (gameData.records.maxDamage) {
document.getElementById('max-damage').textContent =
gameData.records.maxDamage.player ?
`${gameData.records.maxDamage.player}: ${gameData.records.maxDamage.damage}` : '-';
}
if (gameData.records.minDamage) {
document.getElementById('min-damage').textContent =
gameData.records.minDamage.player ?
`${gameData.records.minDamage.player}: ${gameData.records.minDamage.damage}` : '-';
}
}
// Логи
if (gameData.logs) {
const container = document.getElementById('logs');
container.innerHTML = '';
gameData.logs.slice(-15).reverse().forEach(log => {
const div = document.createElement('div');
div.className = 'log';
let logText = '';
if (typeof log === 'string') {
logText = log;
} else if (log.time && log.player) {
logText = `${log.time} - ${log.player}: ${log.damage} урона`;
} else {
logText = JSON.stringify(log);
}
div.textContent = logText;
container.appendChild(div);
});
}
}
// === АТАКА ===
async function attack() {
try {
const btn = document.getElementById('attack-btn');
const msg = document.getElementById('attack-msg');
// Проверяем можно ли атаковать
if (!gameData.boss || gameData.boss.currentHealth <= 0 || gameData.boss.isAlive === false) {
alert('Босс уже побежден!');
return;
}
// Генерируем урон
const damage = Math.floor(Math.random() * 100) + 1;
const message = msg.value.trim();
// Отключаем кнопку
btn.disabled = true;
btn.textContent = 'Атакуем...';
updateStatus('Атака...');
// Отправляем атаку
const response = await sendToParent('processAttack', {
attackData: {
damage: damage,
message: message,
timestamp: Date.now()
}
});
if (response.data && response.data.success !== false) {
// Обновляем данные
if (response.data.player) gameData.player = response.data.player;
if (response.data.boss) gameData.boss = response.data.boss;
if (response.data.gameData) {
gameData.boss = response.data.gameData.boss;
gameData.allPlayers = response.data.gameData.players || {};
gameData.logs = response.data.gameData.logs || [];
}
// Показываем урон
document.getElementById('last-hit').textContent = `Нанесено: ${damage} урона`;
// Очищаем поле
msg.value = '';
// Обновляем интерфейс
updateUI();
// Загружаем свежие данные
await loadData();
// Сообщение о победе
if (response.data.bossDied || (gameData.boss && gameData.boss.currentHealth <= 0)) {
setTimeout(() => {
alert('🎉 БОСС ПОБЕЖДЕН!');
}, 500);
}
updateStatus('Атака успешна!');
} else {
alert(response.error || 'Ошибка атаки');
updateStatus('Ошибка: ' + response.error);
}
} catch (error) {
console.error('❌ Attack error:', error);
alert('Ошибка: ' + error.message);
updateStatus('Ошибка: ' + error.message);
} finally {
updateUI();
}
}
// === ВСПОМОГАТЕЛЬНЫЕ ===
function updateStatus(text) {
document.getElementById('status').textContent = 'Статус: ' + text;
console.log('📝 ' + text);
}
// === ЗАПУСК ИГРЫ ===
async function startGame() {
updateStatus('Запуск игры...');
// Загружаем данные
await loadData();
// Обновляем каждые 5 секунд
setInterval(loadData, 5000);
updateStatus('✅ Игра готова');
}
// Экспортируем функцию атаки для кнопки
window.attack = attack;
// Запускаем при загрузке
window.onload = startGame;
</script>
</body>
</html>[/html]
Поделиться42025-12-20 13:49:29
[html]<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Битва с монстром</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
color: #fff;
min-height: 100vh;
padding: 20px;
}
.container {
display: flex;
gap: 20px;
max-width: 1400px;
margin: 0 auto;
}
.left-panel, .right-panel {
background: rgba(30, 30, 46, 0.9);
border-radius: 15px;
padding: 20px;
border: 2px solid #3a506b;
}
.left-panel {
flex: 0 0 300px;
}
.right-panel {
flex: 1;
}
.section-title {
color: #ff9a3c;
text-align: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #3a506b;
font-size: 20px;
}
/* ТОП ИГРОКОВ */
.top-players {
max-height: 300px;
overflow-y: auto;
margin-bottom: 25px;
}
.player-item {
display: flex;
justify-content: space-between;
padding: 10px 15px;
margin-bottom: 8px;
background: rgba(50, 50, 70, 0.6);
border-radius: 8px;
border-left: 4px solid #ff9a3c;
transition: background 0.2s;
}
.player-item:hover {
background: rgba(60, 60, 80, 0.8);
}
.player-item.you {
border-left-color: #4CAF50;
background: rgba(76, 175, 80, 0.1);
}
/* ЖУРНАЛ БОЯ */
.battle-logs {
max-height: 300px;
overflow-y: auto;
}
.log-item {
padding: 10px 12px;
margin-bottom: 8px;
background: rgba(40, 40, 60, 0.6);
border-radius: 8px;
font-size: 14px;
border-left: 4px solid #4ecdc4;
}
/* ОСНОВНАЯ ПАНЕЛЬ */
.boss-container {
text-align: center;
margin-bottom: 30px;
}
.boss-name {
font-size: 32px;
color: #ff6b6b;
margin-bottom: 15px;
text-shadow: 0 0 15px rgba(255, 107, 107, 0.5);
}
.health-container {
width: 100%;
margin: 20px 0;
}
.health-text {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
font-size: 18px;
color: #ffd166;
}
.health-bar {
width: 100%;
height: 40px;
background: #333;
border-radius: 20px;
overflow: hidden;
border: 3px solid #555;
}
.health-fill {
height: 100%;
background: linear-gradient(90deg, #ef476f, #ff9a3c);
width: 100%;
transition: width 0.5s ease;
position: relative;
}
.boss-image {
width: 100%;
max-width: 500px;
height: 300px;
object-fit: cover;
border-radius: 15px;
border: 4px solid #ff6b6b;
margin: 20px auto;
box-shadow: 0 0 30px rgba(255, 107, 107, 0.4);
}
/* ИНТЕРФЕЙС АТАКИ */
.attack-interface {
background: rgba(40, 40, 60, 0.8);
border-radius: 15px;
padding: 25px;
margin-top: 20px;
}
.attack-message {
width: 100%;
padding: 15px;
margin-bottom: 20px;
border: 2px solid #4ecdc4;
border-radius: 10px;
background: rgba(30, 30, 46, 0.9);
color: white;
font-size: 16px;
resize: vertical;
min-height: 80px;
}
.attack-button {
width: 100%;
padding: 20px;
font-size: 28px;
font-weight: bold;
background: linear-gradient(145deg, #ef476f, #ff6b6b);
color: white;
border: none;
border-radius: 12px;
cursor: pointer;
transition: all 0.2s;
text-transform: uppercase;
}
.attack-button:hover:not(:disabled) {
transform: scale(1.02);
box-shadow: 0 0 30px rgba(255, 107, 107, 0.6);
}
.attack-button:disabled {
background: #4a6178;
cursor: not-allowed;
opacity: 0.7;
}
/* СТАТИСТИКА */
.player-stats {
margin-top: 30px;
padding: 20px;
background: rgba(40, 40, 60, 0.8);
border-radius: 15px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
margin-top: 15px;
}
.stat-item {
background: rgba(50, 50, 70, 0.6);
padding: 15px;
border-radius: 10px;
display: flex;
justify-content: space-between;
}
.stat-label {
color: #a0c4ff;
}
.stat-value {
color: #ffd166;
font-weight: bold;
font-size: 18px;
}
/* УРОН */
.damage-display {
text-align: center;
font-size: 24px;
font-weight: bold;
color: #ff6b6b;
margin: 15px 0;
padding: 15px;
background: rgba(255, 107, 107, 0.1);
border-radius: 10px;
border: 2px solid #ff6b6b;
}
/* МОБИЛЬНАЯ ВЕРСИЯ */
@media (max-width: 1100px) {
.container {
flex-direction: column;
}
.left-panel {
flex: none;
width: 100%;
}
.top-players, .battle-logs {
max-height: 200px;
}
}
</style>
</head>
<body>
<div class="container">
<!-- ЛЕВАЯ ПАНЕЛЬ -->
<div class="left-panel">
<div class="top-players">
<h2 class="section-title">🏆 ТОП ИГРОКОВ</h2>
<div id="top-players-list">
<div class="player-item">
<span>Загрузка...</span>
<span>0</span>
</div>
</div>
</div>
<div class="battle-logs">
<h2 class="section-title">📜 ЖУРНАЛ БОЯ</h2>
<div id="logs-list">
<div class="log-item">Загрузка логов...</div>
</div>
</div>
</div>
<!-- ПРАВАЯ ПАНЕЛЬ -->
<div class="right-panel">
<!-- БОСС -->
<div class="boss-container">
<h1 class="boss-name" id="bossName">🔥 ГНЕВНЫЙ ДРАКОН</h1>
<div class="health-container">
<div class="health-text">
<span>ЗДОРОВЬЕ БОССА</span>
<span id="bossHealthText">1000 / 1000 (100%)</span>
</div>
<div class="health-bar">
<div class="health-fill" id="bossHealthBar"></div>
</div>
</div>
<img class="boss-image" id="bossImage" src="https://via.placeholder.com/500x300/8b0000/ffffff?text=ГНЕВНЫЙ+ДРАКОН" alt="Монстр">
</div>
<!-- ИНТЕРФЕЙС АТАКИ -->
<div class="attack-interface">
<textarea class="attack-message" id="attackMessage"
placeholder="Введите ваше боевое сообщение (необязательно)..."></textarea>
<button class="attack-button" id="attackButton" disabled>АТАКОВАТЬ!</button>
<div class="damage-display" id="damageDisplay">
Последний урон: -
</div>
</div>
<!-- СТАТИСТИКА ИГРОКА -->
<div class="player-stats">
<h2 class="section-title">📊 ВАША СТАТИСТИКА</h2>
<div class="stats-grid">
<div class="stat-item">
<span class="stat-label">Всего урона:</span>
<span class="stat-value" id="statDamage">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Количество атак:</span>
<span class="stat-value" id="statAttacks">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Лучший удар:</span>
<span class="stat-value" id="statBestHit">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Убито боссов:</span>
<span class="stat-value" id="statKills">0</span>
</div>
</div>
</div>
</div>
</div>
<script>
(function(){
console.log('🎮 Monster Game Frame started');
// === ГЛОБАЛЬНОЕ СОСТОЯНИЕ ===
let gameState = {
player: null,
boss: null,
topPlayers: [],
logs: []
};
// === КОММУНИКАЦИЯ С РОДИТЕЛЕМ ===
function sendToParent(data) {
try {
window.parent.postMessage(data, '*');
return true;
} catch (e) {
console.error('❌ Send failed:', e);
return false;
}
}
function sendGameRequest(action, data = {}) {
return new Promise((resolve, reject) => {
const requestId = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
const message = {
_monsterGame: true,
type: "gameRequest",
requestId: requestId,
action: action,
...data
};
const responseHandler = (event) => {
const response = event.data;
if (response && response._monsterGame === true &&
response.type === "gameResponse" &&
response.requestId === requestId) {
window.removeEventListener("message", responseHandler);
clearTimeout(timeout);
if (response.error) {
reject(new Error(response.error));
} else {
resolve(response.data);
}
}
};
window.addEventListener("message", responseHandler);
const timeout = setTimeout(() => {
window.removeEventListener("message", responseHandler);
reject(new Error(`Timeout: ${action}`));
}, 10000);
if (!sendToParent(message)) {
window.removeEventListener("message", responseHandler);
clearTimeout(timeout);
reject(new Error("Failed to send message"));
}
});
}
// === ЗАГРУЗКА ДАННЫХ ===
async function loadGameData() {
try {
const data = await sendGameRequest("getGameData");
return data;
} catch (error) {
console.error('❌ Load game data error:', error);
return null;
}
}
async function loadPlayerData() {
try {
const data = await sendGameRequest("getPlayerData");
return data;
} catch (error) {
console.error('❌ Load player data error:', error);
return null;
}
}
async function loadTopPlayers() {
try {
const players = await sendGameRequest("getTopPlayers");
return players || [];
} catch (error) {
console.error('❌ Load top players error:', error);
return [];
}
}
async function loadLogs() {
try {
const logs = await sendGameRequest("getRecentLogs");
return logs || [];
} catch (error) {
console.error('❌ Load logs error:', error);
return [];
}
}
// === ОБНОВЛЕНИЕ ИНТЕРФЕЙСА ===
function updateBossUI() {
if (!gameState.boss) return;
const boss = gameState.boss;
const healthPercent = (boss.currentHealth / boss.maxHealth) * 100;
const healthBar = document.getElementById('bossHealthBar');
const healthText = document.getElementById('bossHealthText');
if (healthBar) {
healthBar.style.width = `${healthPercent}%`;
// Цвет в зависимости от здоровья
if (healthPercent > 70) {
healthBar.style.background = 'linear-gradient(90deg, #4CAF50, #8BC34A)';
} else if (healthPercent > 30) {
healthBar.style.background = 'linear-gradient(90deg, #FF9800, #FFC107)';
} else {
healthBar.style.background = 'linear-gradient(90deg, #f44336, #ef5350)';
}
}
if (healthText) {
healthText.textContent = `${boss.currentHealth} / ${boss.maxHealth} (${Math.round(healthPercent)}%)`;
}
// Обновляем кнопку атаки
updateAttackButton();
}
function updatePlayerUI() {
if (!gameState.player) return;
const player = gameState.player;
document.getElementById('statDamage').textContent = player.totalDamage || 0;
document.getElementById('statAttacks').textContent = player.attacks || 0;
document.getElementById('statBestHit').textContent = player.bestHit || 0;
document.getElementById('statKills').textContent = player.bossKills || 0;
}
async function updateTopPlayersUI() {
try {
gameState.topPlayers = await loadTopPlayers();
const container = document.getElementById('top-players-list');
if (!container) return;
if (gameState.topPlayers.length === 0) {
container.innerHTML = '<div class="player-item"><span>Нет игроков</span><span>0</span></div>';
return;
}
container.innerHTML = '';
gameState.topPlayers.forEach((player, index) => {
const div = document.createElement('div');
div.className = 'player-item';
// Подсвечиваем текущего игрока
if (gameState.player && player.userId === gameState.player.userId) {
div.classList.add('you');
}
div.innerHTML = `
<span>${index + 1}. ${player.nickname}</span>
<span>${player.totalDamage}</span>
`;
container.appendChild(div);
});
} catch (error) {
console.error('❌ Update top players error:', error);
}
}
async function updateLogsUI() {
try {
gameState.logs = await loadLogs();
const container = document.getElementById('logs-list');
if (!container) return;
if (gameState.logs.length === 0) {
container.innerHTML = '<div class="log-item">Нет записей в журнале</div>';
return;
}
container.innerHTML = '';
gameState.logs.reverse().forEach(log => {
const div = document.createElement('div');
div.className = 'log-item';
div.textContent = log;
container.appendChild(div);
});
// Прокрутка вниз
container.scrollTop = 0;
} catch (error) {
console.error('❌ Update logs error:', error);
}
}
function updateAttackButton() {
const btn = document.getElementById('attackButton');
const message = document.getElementById('attackMessage');
if (!btn) return;
const isBossAlive = gameState.boss && gameState.boss.currentHealth > 0;
if (isBossAlive) {
btn.disabled = false;
btn.textContent = 'АТАКОВАТЬ!';
if (message) message.disabled = false;
} else {
btn.disabled = true;
btn.textContent = 'БОСС ПОБЕЖДЕН';
if (message) message.disabled = true;
}
}
// === ОСНОВНАЯ ИГРОВАЯ ЛОГИКА ===
async function performAttack() {
if (!gameState.boss || gameState.boss.currentHealth <= 0) {
alert('Босс уже побежден!');
return;
}
const btn = document.getElementById('attackButton');
const messageInput = document.getElementById('attackMessage');
if (btn) btn.disabled = true;
try {
// Генерация урона
const damage = Math.floor(Math.random() * 100) + 1;
const message = messageInput ? messageInput.value.trim() : '';
// Отправляем атаку
const result = await sendGameRequest("processAttack", {
attackData: {
damage: damage,
message: message,
timestamp: Date.now()
}
});
if (result.success) {
// Обновляем состояние
gameState.player = result.player;
gameState.boss = result.gameData.boss;
// Обновляем UI
updateBossUI();
updatePlayerUI();
// Показываем урон
const damageDisplay = document.getElementById('damageDisplay');
if (damageDisplay) {
damageDisplay.textContent = `Нанесено урона: ${damage}`;
damageDisplay.style.animation = 'none';
setTimeout(() => {
damageDisplay.style.animation = 'pulse 0.5s';
}, 10);
}
// Обновляем топ и логи
await updateTopPlayersUI();
await updateLogsUI();
// Очищаем поле ввода
if (messageInput) messageInput.value = '';
// Сообщение о победе
if (result.gameData.boss.currentHealth <= 0) {
setTimeout(() => {
alert('🎉 БОСС ПОБЕЖДЕН! Он воскреснет с полным здоровьем.');
}, 500);
}
} else {
alert('Ошибка атаки: ' + (result.error || 'Неизвестная ошибка'));
}
} catch (error) {
console.error('❌ Attack error:', error);
alert('Ошибка: ' + error.message);
} finally {
updateAttackButton();
}
}
// === ПЕРИОДИЧЕСКОЕ ОБНОВЛЕНИЕ ===
let refreshInterval;
async function refreshGameState() {
try {
const data = await loadGameData();
if (data) {
gameState.boss = data.boss;
updateBossUI();
}
await updateTopPlayersUI();
await updateLogsUI();
} catch (error) {
console.error('❌ Refresh error:', error);
}
}
// === ИНИЦИАЛИЗАЦИЯ ===
async function init() {
console.log('🎮 Initializing monster game...');
try {
// Загружаем данные
const playerData = await loadPlayerData();
if (playerData) {
gameState.player = playerData.player;
gameState.boss = playerData.boss;
} else {
const gameData = await loadGameData();
if (gameData) {
gameState.boss = gameData.boss;
}
}
// Обновляем UI
updateBossUI();
updatePlayerUI();
await updateTopPlayersUI();
await updateLogsUI();
// Настраиваем кнопку
const attackBtn = document.getElementById('attackButton');
if (attackBtn) {
attackBtn.addEventListener('click', performAttack);
}
// Запускаем автообновление
refreshInterval = setInterval(refreshGameState, 3000);
console.log('✅ Monster game initialized!');
} catch (error) {
console.error('❌ Init error:', error);
alert('Ошибка загрузки игры. Обновите страницу.');
}
}
// Запуск
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// Очистка при закрытии
window.addEventListener('beforeunload', () => {
if (refreshInterval) clearInterval(refreshInterval);
});
})();
</script>
</body>
</html>[/html]
Поделиться52025-12-20 13:49:59
[html]
<div class="monster-attack-result" data-attack="CjxkaXYgY2xhc3M9Im1vbnN0ZXItYXR0YWNrLXBvc3QiIHN0eWxlPSIKICAgIGJvcmRlcjogM3B4IHNvbGlkICNmZjZiNmI7CiAgICBib3JkZXItcmFkaXVzOiAxNXB4OwogICAgcGFkZGluZzogMjBweDsKICAgIGJhY2tncm91bmQ6IGxpbmVhci1ncmFkaWVudCgxMzVkZWcsICMxYTFhMmUgMCUsICMxNjIxM2UgMTAwJSk7CiAgICBtYXJnaW46IDE1cHggMDsKICAgIGNvbG9yOiB3aGl0ZTsKICAgIGZvbnQtZmFtaWx5OiBBcmlhbCwgc2Fucy1zZXJpZjsKICAgIGJveC1zaGFkb3c6IDAgMTBweCAzMHB4IHJnYmEoMCwwLDAsMC41KTsKIj4KICAgIDxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsgbWFyZ2luLWJvdHRvbTogMjBweDsiPgogICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMjJweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiAjZmY2YjZiOyBtYXJnaW4tYm90dG9tOiA4cHg7Ij4KICAgICAgICAgICAgJiM5ODc2OyYjNjUwMzk7INCQ0KLQkNCa0JAg0J3QkCDQkdCe0KHQodCQICYjOTg3NjsmIzY1MDM5OwogICAgICAgIDwvZGl2PgogICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMTRweDsgY29sb3I6ICNhYWE7Ij4KICAgICAgICAgICAgMjAuMTIuMjAyNSwgMTM6NDk6NTgKICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgCiAgICA8ZGl2IHN0eWxlPSJkaXNwbGF5OiBmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47IGFsaWduLWl0ZW1zOiBjZW50ZXI7IG1hcmdpbi1ib3R0b206IDIwcHg7IHBhZGRpbmc6IDE1cHg7IGJhY2tncm91bmQ6IHJnYmEoMCwwLDAsMC4zKTsgYm9yZGVyLXJhZGl1czogMTBweDsiPgogICAgICAgIDxkaXYgc3R5bGU9ImZsZXg6IDE7Ij4KICAgICAgICAgICAgPGRpdiBzdHlsZT0iZm9udC1zaXplOiAxOHB4OyBmb250LXdlaWdodDogYm9sZDsgY29sb3I6ICM0ZWNkYzQ7IG1hcmdpbi1ib3R0b206IDVweDsiPgogICAgICAgICAgICAgICAgYWRtaW4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMTNweDsgY29sb3I6ICM4ODg7Ij4KICAgICAgICAgICAgICAgINCj0YDQvtC9OiAzNDEgfCDQkNGC0LDQujogNgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgICAgICAKICAgICAgICA8ZGl2IHN0eWxlPSJmb250LXNpemU6IDMycHg7IG1hcmdpbjogMCAyMHB4OyI+CiAgICAgICAgICAgICYjOTg3NjsmIzY1MDM5OwogICAgICAgIDwvZGl2PgogICAgICAgIAogICAgICAgIDxkaXYgc3R5bGU9ImZsZXg6IDE7IHRleHQtYWxpZ246IHJpZ2h0OyI+CiAgICAgICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMThweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiAjZmY2YjZiOyBtYXJnaW4tYm90dG9tOiA1cHg7Ij4KICAgICAgICAgICAgICAgICYjMTI4MjkzOyDQk9Cd0JXQktCd0KvQmSDQlNCg0JDQmtCe0J0KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMTNweDsgY29sb3I6ICM4ODg7Ij4KICAgICAgICAgICAgICAgIEhQOiA2NTkvMTAwMAogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgCiAgICA8ZGl2IHN0eWxlPSJiYWNrZ3JvdW5kOiByZ2JhKDAsMCwwLDAuNCk7IGJvcmRlci1yYWRpdXM6IDEwcHg7IHBhZGRpbmc6IDIwcHg7IG1hcmdpbjogMTVweCAwOyI+CiAgICAgICAgPGRpdiBzdHlsZT0iZm9udC1zaXplOiAyNHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7IG1hcmdpbi1ib3R0b206IDE1cHg7Ij4KICAgICAgICAgICAgPHNwYW4gc3R5bGU9ImNvbG9yOiAjZmZkMTY2OyI+0J3QsNC90LXRgdC10L3QviDRg9GA0L7QvdCwOjwvc3Bhbj4KICAgICAgICAgICAgPHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTogMzJweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiAjZmY2YjZiOyBtYXJnaW4tbGVmdDogMTVweDsiPgogICAgICAgICAgICAgICAgNzUKICAgICAgICAgICAgPC9zcGFuPgogICAgICAgIDwvZGl2PgogICAgICAgIAogICAgICAgIDxkaXYgc3R5bGU9Im1hcmdpbjogMjBweCAwOyBwYWRkaW5nOiAxMHB4OyBiYWNrZ3JvdW5kOiByZ2JhKDAsMCwwLDAuMyk7IGJvcmRlci1yYWRpdXM6IDhweDsiPgogICAgICAgICAgICA8ZGl2IHN0eWxlPSJmb250LXNpemU6IDE0cHg7IGNvbG9yOiAjYWFhOyBtYXJnaW4tYm90dG9tOiA1cHg7Ij7Ql9C00L7RgNC+0LLRjNC1INCx0L7RgdGB0LA6PC9kaXY+CiAgICAgICAgICAgIDxkaXYgc3R5bGU9ImhlaWdodDogMjVweDsgYmFja2dyb3VuZDogIzMzMzsgYm9yZGVyLXJhZGl1czogMTJweDsgb3ZlcmZsb3c6IGhpZGRlbjsgcG9zaXRpb246IHJlbGF0aXZlOyI+CiAgICAgICAgICAgICAgICA8ZGl2IHN0eWxlPSIKICAgICAgICAgICAgICAgICAgICBoZWlnaHQ6IDEwMCU7CiAgICAgICAgICAgICAgICAgICAgd2lkdGg6IDY1LjklOwogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQ6IGxpbmVhci1ncmFkaWVudCg5MGRlZywgI0ZGOTgwMCwgI0ZGQzEwNyk7CiAgICAgICAgICAgICAgICAgICAgdHJhbnNpdGlvbjogd2lkdGggMXMgZWFzZTsKICAgICAgICAgICAgICAgICAgICBib3JkZXItcmFkaXVzOiAxMnB4OwogICAgICAgICAgICAgICAgIj48L2Rpdj4KICAgICAgICAgICAgICAgIDxkaXYgc3R5bGU9InBvc2l0aW9uOiBhYnNvbHV0ZTsgdG9wOiAwOyBsZWZ0OiAwOyByaWdodDogMDsgYm90dG9tOiAwOyBkaXNwbGF5OiBmbGV4OyBhbGlnbi1pdGVtczogY2VudGVyOyBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjsgY29sb3I6IHdoaXRlOyBmb250LXdlaWdodDogYm9sZDsiPgogICAgICAgICAgICAgICAgICAgIDY1OS8xMDAwICg2NiUpCiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9kaXY+CiAgICAgICAgCiAgICAgICAgCiAgICAgICAgCiAgICAgICAgCiAgICA8L2Rpdj4KICAgIAogICAgPGRpdiBzdHlsZT0iZGlzcGxheTogZmxleDsganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOyBmb250LXNpemU6IDEycHg7IGNvbG9yOiAjNjY2OyBtYXJnaW4tdG9wOiAyMHB4OyBwYWRkaW5nLXRvcDogMTVweDsgYm9yZGVyLXRvcDogMXB4IHNvbGlkICM0NDQ7Ij4KICAgICAgICA8ZGl2Pk1vbnN0ZXIgQmF0dGxlIHYxLjAgfCBJRDogMjwvZGl2PgogICAgICAgIDxkaXY+0JvRg9GH0YjQuNC5INGD0LTQsNGAOiA4ODwvZGl2PgogICAgICAgIDxkaXY+0KPQsdC40YLQviDQsdC+0YHRgdC+0LI6IDA8L2Rpdj4KICAgIDwvZGl2Pgo8L2Rpdj4=">
Загрузка результата атаки...
</div>
<script>
(function(){
try {
var encoded = document.querySelector(".monster-attack-result").getAttribute("data-attack");
var html = decodeURIComponent(escape(atob(encoded)));
document.querySelector(".monster-attack-result").innerHTML = html;
// Анимация
var el = document.querySelector(".monster-attack-result");
el.style.animation = "monsterPulse 0.5s";
setTimeout(() => el.style.animation = "", 500);
} catch(e){
document.querySelector(".monster-attack-result").innerHTML = "<b>Ошибка загрузки результата</b>";
}
})();
</script>
<style>
@keyframes monsterPulse {
0% { transform: scale(1); opacity: 0.8; }
50% { transform: scale(1.02); opacity: 1; }
100% { transform: scale(1); opacity: 0.8; }
}
</style>
[/html]
Поделиться62025-12-26 22:18:03
[html]<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;700&family=Roboto+Condensed:wght@400;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0a0a0c;
background-image: radial-gradient(circle at center, #1a1a2e 0%, #0a0a0c 100%);
color: #e0e0e0;
font-family: 'Roboto Condensed', sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.game-container {
display: grid;
grid-template-columns: 300px 1fr;
gap: 25px;
max-width: 1100px;
width: 100%;
background: rgba(0, 0, 0, 0.6);
padding: 30px;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
backdrop-filter: blur(10px);
}
/* --- Левая панель --- */
.sidebar { display: flex; flex-direction: column; gap: 20px; }
.panel {
background: rgba(255, 255, 255, 0.03);
border-left: 3px solid #ff4655;
padding: 15px;
border-radius: 4px;
}
.panel-h {
font-family: 'Oswald', sans-serif;
font-size: 14px;
letter-spacing: 2px;
color: #ff4655;
margin-bottom: 12px;
text-transform: uppercase;
}
.stat-row {
display: flex;
justify-content: space-between;
padding: 6px 0;
border-bottom: 1px solid rgba(255,255,255,0.05);
font-size: 14px;
}
.record-info {
display: flex;
flex-direction: column;
gap: 4px;
}
.record-player {
color: #aaa;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 180px;
}
.log-container {
font-size: 13px;
height: 200px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #ff4655 transparent;
}
.log-entry {
padding: 4px 0;
color: #aaa;
border-bottom: 1px solid rgba(255,255,255,0.02);
}
.log-entry b { color: #ff4655; }
/* --- Центральная часть --- */
.main-content { display: flex; flex-direction: column; gap: 20px; }
.boss-frame {
position: relative;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 0 30px rgba(255, 70, 85, 0.2);
border: 1px solid rgba(255, 70, 85, 0.3);
transition: transform 0.1s;
}
.boss-img {
width: 100%;
height: 450px;
object-fit: cover;
display: block;
}
.boss-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 40px 20px 20px;
background: linear-gradient(to top, rgba(0,0,0,0.9) 0%, transparent 100%);
}
.boss-info {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 8px;
}
.boss-name {
font-family: 'Oswald', sans-serif;
font-size: 32px;
font-weight: 700;
color: #fff;
}
.hp-text { font-family: 'Oswald', sans-serif; color: #ff4655; font-size: 18px; }
.hp-bar-bg {
height: 12px;
background: rgba(255,255,255,0.1);
border-radius: 6px;
overflow: hidden;
}
.hp-bar-fill {
height: 100%;
background: #ff4655; /* Упростил для корректного отображения */
background: linear-gradient(90deg, #ff4655, #ff8a71);
box-shadow: 0 0 15px #ff4655;
width: 100%;
transition: width 0.4s ease-out;
}
/* --- Управление --- */
.controls {
display: grid;
grid-template-columns: 1fr 200px;
gap: 15px;
}
.game-input {
width: 100%;
height: 60px;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 8px;
padding: 0 20px;
color: #fff;
font-size: 16px;
outline: none;
}
.btn-attack {
height: 60px;
background: #ff4655;
color: #fff;
border: none;
border-radius: 8px;
font-family: 'Oswald', sans-serif;
font-size: 20px;
font-weight: 700;
cursor: pointer;
text-transform: uppercase;
transition: 0.2s;
}
.btn-attack:hover:not(:disabled) { background: #ff5e6a; transform: translateY(-2px); }
.btn-attack:disabled { background: #333; cursor: not-allowed; }
.damage-popup {
text-align: center;
font-family: 'Oswald', sans-serif;
font-size: 24px;
color: #ff4655;
height: 30px;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="game-container">
<aside class="sidebar">
<div class="panel">
<div class="panel-h">🏆 Лидеры</div>
<div id="topList"></div>
</div>
<div class="panel">
<div class="panel-h">🎯 Рекорды</div>
<div class="stat-row">
<span>Макс. урон</span>
<div class="record-info">
<b id="maxDmg" style="color:#ff4655">-</b>
<div id="maxPlayer" class="record-player">-</div>
</div>
</div>
<div class="stat-row">
<span>Мин. урон</span>
<div class="record-info">
<b id="minDmg" style="color:#fff">-</b>
<div id="minPlayer" class="record-player">-</div>
</div>
</div>
</div>
<div class="panel" style="flex-grow: 1;">
<div class="panel-h">📜 Журнал боя</div>
<div id="logs" class="log-container"></div>
</div>
</aside>
<main class="main-content">
<div class="boss-frame" id="bossFrame">
<img src="https://upforme.ru/uploads/001c/84/76/2/433839.jpg" class="boss-img" alt="Boss">
<div class="boss-overlay">
<div class="boss-info">
<div class="boss-name">ДРЕВНИЙ СТРАЖ</div>
<div id="hpText" class="hp-text">... / ...</div>
</div>
<div class="hp-bar-bg">
<div id="hpBar" class="hp-bar-fill" style="width: 100%;"></div>
</div>
</div>
</div>
<div class="controls">
<input type="text" id="msg" class="game-input" placeholder="Введите боевой клич...">
<button id="atkBtn" class="btn-attack">УДАРИТЬ</button>
</div>
<div id="dmgRes" class="damage-popup"></div>
</main>
</div>
<script>
const send = (action, data = {}) => {
const requestId = Math.random().toString(16).slice(2);
window.parent.postMessage({ _monsterGame: true, type: "gameRequest", requestId, action, ...data }, '*');
return new Promise(resolve => {
const handler = (e) => {
if(e.data?.requestId === requestId) {
window.removeEventListener("message", handler);
resolve(e.data.data);
}
};
window.addEventListener("message", handler);
});
};
async function updateUI() {
try {
const [game, top, logs] = await Promise.all([
send("getGameData"),
send("getTopPlayers"),
send("getRecentLogs")
]);
// Обновление полоски здоровья
const currentHp = game.boss.currentHealth;
const maxHp = game.boss.maxHealth;
const hpPct = Math.max(0, (currentHp / maxHp) * 100);
const bar = document.getElementById('hpBar');
bar.style.width = hpPct + '%';
document.getElementById('hpText').textContent = `${currentHp} / ${maxHp}`;
document.getElementById('atkBtn').disabled = currentHp <= 0;
// Лидеры
document.getElementById('topList').innerHTML = top.slice(0, 5).map(p =>
`<div class="stat-row"><span>${p.nickname}</span><b>${p.totalDamage}</b></div>`
).join('') || 'Нет данных';
// Логи
document.getElementById('logs').innerHTML = logs.slice(-20).reverse().map(l => {
const parts = l.split(' нанес ');
return `<div class="log-entry"><b>${parts[0] || 'Игрок'}</b> нанес ${parts[1] || ''}</div>`;
}).join('');
// Рекорды с именами игроков
const damageEntries = logs.map(l => {
const match = l.match(/(.+?) нанес (\d+) урона/);
return match ? { name: match[1], val: parseInt(match[2]) } : null;
}).filter(x => x);
if (damageEntries.length > 0) {
const max = damageEntries.reduce((prev, curr) => (curr.val > prev.val) ? curr : prev);
const min = damageEntries.reduce((prev, curr) => (curr.val < prev.val) ? curr : prev);
// Обновление максимального урона с именем игрока
document.getElementById('maxDmg').textContent = `${max.val}`;
document.getElementById('maxPlayer').textContent = max.name;
// Обновление минимального урона с именем игрока
document.getElementById('minDmg').textContent = `${min.val}`;
document.getElementById('minPlayer').textContent = min.name;
} else {
// Сброс значений, если нет данных
document.getElementById('maxDmg').textContent = '-';
document.getElementById('maxPlayer').textContent = '-';
document.getElementById('minDmg').textContent = '-';
document.getElementById('minPlayer').textContent = '-';
}
} catch (e) { console.error("UI Update Error:", e); }
}
document.getElementById('atkBtn').onclick = async () => {
const dmg = Math.floor(Math.random() * 100) + 1;
const msgInput = document.getElementById('msg');
const res = await send("processAttack", {
attackData: { damage: dmg, message: msgInput.value, timestamp: Date.now() }
});
if(res.success) {
const resDiv = document.getElementById('dmgRes');
resDiv.textContent = `УДАР: -${dmg} HP!`;
msgInput.value = '';
// Эффект тряски
const frame = document.getElementById('bossFrame');
frame.style.transform = 'translate(4px, 4px) rotate(1deg)';
setTimeout(() => frame.style.transform = 'none', 100);
await updateUI();
}
};
setInterval(updateUI, 4000);
updateUI();
</script>
</body>
</html>[/html]
Поделиться72026-01-10 19:15:38
[html]<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;700&family=Roboto+Condensed:wght@400;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0a0a0c;
background-image: radial-gradient(circle at center, #1a1a2e 0%, #0a0a0c 100%);
color: #e0e0e0;
font-family: 'Roboto Condensed', sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.game-container {
display: grid;
grid-template-columns: 300px 1fr;
gap: 25px;
max-width: 1100px;
width: 100%;
background: rgba(0, 0, 0, 0.6);
padding: 30px;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
backdrop-filter: blur(10px);
}
/* --- Левая панель --- */
.sidebar { display: flex; flex-direction: column; gap: 20px; }
.panel {
background: rgba(255, 255, 255, 0.03);
border-left: 3px solid #ff4655;
padding: 15px;
border-radius: 4px;
}
.panel-h {
font-family: 'Oswald', sans-serif;
font-size: 14px;
letter-spacing: 2px;
color: #ff4655;
margin-bottom: 12px;
text-transform: uppercase;
}
.stat-row {
display: flex;
justify-content: space-between;
padding: 6px 0;
border-bottom: 1px solid rgba(255,255,255,0.05);
font-size: 14px;
gap: 10px;
}
.record-info {
display: flex;
flex-direction: column;
gap: 4px;
text-align: right;
}
.record-player {
color: #aaa;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 180px;
}
.log-container {
font-size: 13px;
height: 200px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #ff4655 transparent;
}
.log-entry {
padding: 4px 0;
color: #aaa;
border-bottom: 1px solid rgba(255,255,255,0.02);
}
.log-entry b { color: #ff4655; }
/* --- Центральная часть --- */
.main-content { display: flex; flex-direction: column; gap: 20px; }
.boss-frame {
position: relative;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 0 30px rgba(255, 70, 85, 0.2);
border: 1px solid rgba(255, 70, 85, 0.3);
transition: transform 0.1s;
}
.boss-img {
width: 100%;
height: 450px;
object-fit: cover;
display: block;
}
.boss-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 40px 20px 20px;
background: linear-gradient(to top, rgba(0,0,0,0.9) 0%, transparent 100%);
}
.boss-info {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 8px;
gap: 10px;
}
.boss-name {
font-family: 'Oswald', sans-serif;
font-size: 32px;
font-weight: 700;
color: #fff;
}
.hp-text { font-family: 'Oswald', sans-serif; color: #ff4655; font-size: 18px; }
.hp-bar-bg {
height: 12px;
background: rgba(255,255,255,0.1);
border-radius: 6px;
overflow: hidden;
}
.hp-bar-fill {
height: 100%;
background: linear-gradient(90deg, #ff4655, #ff8a71);
box-shadow: 0 0 15px #ff4655;
width: 100%;
transition: width 0.4s ease-out;
}
/* --- Управление --- */
.controls {
display: grid;
grid-template-columns: 1fr 200px;
gap: 15px;
}
.game-input {
width: 100%;
height: 60px;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 8px;
padding: 0 20px;
color: #fff;
font-size: 16px;
outline: none;
}
.btn-attack {
height: 60px;
background: #ff4655;
color: #fff;
border: none;
border-radius: 8px;
font-family: 'Oswald', sans-serif;
font-size: 20px;
font-weight: 700;
cursor: pointer;
text-transform: uppercase;
transition: 0.2s;
}
.btn-attack:hover:not(:disabled) { background: #ff5e6a; transform: translateY(-2px); }
.btn-attack:disabled { background: #333; cursor: not-allowed; }
.damage-popup {
text-align: center;
font-family: 'Oswald', sans-serif;
font-size: 24px;
color: #ff4655;
height: 30px;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="game-container">
<aside class="sidebar">
<div class="panel">
<div class="panel-h">🏆 Лидеры</div>
<div id="topList"></div>
</div>
<div class="panel">
<div class="panel-h">🎯 Рекорды</div>
<div class="stat-row">
<span>Макс. урон</span>
<div class="record-info">
<b id="maxDmg" style="color:#ff4655">-</b>
<div id="maxPlayer" class="record-player">-</div>
</div>
</div>
<div class="stat-row">
<span>Мин. урон</span>
<div class="record-info">
<b id="minDmg" style="color:#fff">-</b>
<div id="minPlayer" class="record-player">-</div>
</div>
</div>
</div>
<div class="panel" style="flex-grow: 1;">
<div class="panel-h">📜 Журнал боя</div>
<div id="logs" class="log-container"></div>
</div>
</aside>
<main class="main-content">
<div class="boss-frame" id="bossFrame">
<img src="https://upforme.ru/uploads/001c/84/76/2/433839.jpg" class="boss-img" alt="Boss">
<div class="boss-overlay">
<div class="boss-info">
<div class="boss-name">ДРЕВНИЙ СТРАЖ</div>
<div id="hpText" class="hp-text">... / ...</div>
</div>
<div class="hp-bar-bg">
<div id="hpBar" class="hp-bar-fill" style="width: 100%;"></div>
</div>
</div>
</div>
<div class="controls">
<input type="text" id="msg" class="game-input" placeholder="Введите боевой клич...">
<button id="atkBtn" class="btn-attack">УДАРИТЬ</button>
</div>
<div id="dmgRes" class="damage-popup"></div>
</main>
</div>
<script>
const send = (action, data = {}) => {
const requestId = Math.random().toString(16).slice(2);
window.parent.postMessage({ _monsterGame: true, type: "gameRequest", requestId, action, ...data }, '*');
return new Promise(resolve => {
const handler = (e) => {
if(e.data?.requestId === requestId) {
window.removeEventListener("message", handler);
resolve(e.data.data);
}
};
window.addEventListener("message", handler);
});
};
async function updateUI() {
try {
const [game, top, logs] = await Promise.all([
send("getGameData"),
send("getTopPlayers"),
send("getRecentLogs")
]);
const currentHp = game.boss.currentHealth;
const maxHp = game.boss.maxHealth;
const hpPct = Math.max(0, (currentHp / maxHp) * 100);
const bar = document.getElementById('hpBar');
bar.style.width = hpPct + '%';
document.getElementById('hpText').textContent = `${currentHp} / ${maxHp}`;
// ✅ Блокируем кнопку, если бой завершен
const isOver = (currentHp <= 0) || !!game.boss.isDefeated;
document.getElementById('atkBtn').disabled = isOver;
// Лидеры
document.getElementById('topList').innerHTML = top.slice(0, 5).map(p =>
`<div class="stat-row"><span>${p.nickname}</span><b>${p.totalDamage}</b></div>`
).join('') || 'Нет данных';
// Логи
document.getElementById('logs').innerHTML = logs.slice(-20).reverse().map(l => {
const parts = l.split(' нанес ');
return `<div class="log-entry"><b>${parts[0] || 'Игрок'}</b> нанес ${parts[1] || ''}</div>`;
}).join('');
// ✅ Рекорды: показываем всех игроков при одинаковых значениях
const damageEntries = logs.map(l => {
const match = l.match(/(.+?) нанес (\d+) урона/);
return match ? { name: match[1], val: parseInt(match[2], 10) } : null;
}).filter(Boolean);
if (damageEntries.length > 0) {
const values = damageEntries.map(x => x.val);
const maxVal = Math.max(...values);
const minVal = Math.min(...values);
const maxPlayers = [...new Set(damageEntries.filter(x => x.val === maxVal).map(x => x.name))];
const minPlayers = [...new Set(damageEntries.filter(x => x.val === minVal).map(x => x.name))];
document.getElementById('maxDmg').textContent = `${maxVal}`;
document.getElementById('maxPlayer').textContent = maxPlayers.join(', ');
document.getElementById('minDmg').textContent = `${minVal}`;
document.getElementById('minPlayer').textContent = minPlayers.join(', ');
} else {
document.getElementById('maxDmg').textContent = '-';
document.getElementById('maxPlayer').textContent = '-';
document.getElementById('minDmg').textContent = '-';
document.getElementById('minPlayer').textContent = '-';
}
} catch (e) { console.error("UI Update Error:", e); }
}
document.getElementById('atkBtn').onclick = async () => {
const dmg = Math.floor(Math.random() * 100) + 1;
const msgInput = document.getElementById('msg');
const res = await send("processAttack", {
attackData: { damage: dmg, message: msgInput.value, timestamp: Date.now() }
});
// ✅ если бой завершен — показываем сообщение и просто обновляем UI
if (res && res.gameOver) {
document.getElementById('dmgRes').textContent = 'БОЙ ЗАВЕРШЕН!';
await updateUI();
return;
}
if(res && res.success) {
const resDiv = document.getElementById('dmgRes');
resDiv.textContent = `УДАР: -${dmg} HP!`;
msgInput.value = '';
const frame = document.getElementById('bossFrame');
frame.style.transform = 'translate(4px, 4px) rotate(1deg)';
setTimeout(() => frame.style.transform = 'none', 100);
await updateUI();
}
};
setInterval(updateUI, 4000);
updateUI();
</script>
</body>
</html>[/html]
Поделиться82026-01-10 20:25:14
[html]<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;700&family=Roboto+Condensed:wght@400;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0a0a0c;
background-image: radial-gradient(circle at center, #1a1a2e 0%, #0a0a0c 100%);
color: #e0e0e0;
font-family: 'Roboto Condensed', sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.game-container {
display: grid;
grid-template-columns: 300px 1fr;
gap: 25px;
max-width: 1100px;
width: 100%;
background: rgba(0, 0, 0, 0.6);
padding: 30px;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
backdrop-filter: blur(10px);
}
.sidebar { display: flex; flex-direction: column; gap: 20px; }
.panel { background: rgba(255,255,255,0.03); border-left: 3px solid #ff4655; padding: 15px; border-radius: 4px; }
.panel-h { font-family:'Oswald',sans-serif; font-size:14px; letter-spacing:2px; color:#ff4655; margin-bottom:12px; text-transform:uppercase; }
.stat-row { display:flex; justify-content:space-between; padding:6px 0; border-bottom:1px solid rgba(255,255,255,0.05); font-size:14px; gap:10px; }
.record-info { display:flex; flex-direction:column; gap:4px; text-align:right; }
.record-player { color:#aaa; font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:180px; }
.log-container { font-size:13px; height:200px; overflow-y:auto; scrollbar-width:thin; scrollbar-color:#ff4655 transparent; }
.log-entry { padding:4px 0; color:#aaa; border-bottom:1px solid rgba(255,255,255,0.02); }
.log-entry b { color:#ff4655; }
.main-content { display:flex; flex-direction:column; gap:20px; }
.boss-frame { position:relative; border-radius:12px; overflow:hidden; box-shadow:0 0 30px rgba(255,70,85,0.2); border:1px solid rgba(255,70,85,0.3); transition:transform 0.1s; }
.boss-img { width:100%; height:450px; object-fit:cover; display:block; }
.boss-overlay { position:absolute; bottom:0; left:0; right:0; padding:40px 20px 20px; background:linear-gradient(to top, rgba(0,0,0,0.9) 0%, transparent 100%); }
.boss-info { display:flex; justify-content:space-between; align-items:flex-end; margin-bottom:8px; gap:10px; }
.boss-name { font-family:'Oswald',sans-serif; font-size:32px; font-weight:700; color:#fff; }
.hp-text { font-family:'Oswald',sans-serif; color:#ff4655; font-size:18px; }
.hp-bar-bg { height:12px; background:rgba(255,255,255,0.1); border-radius:6px; overflow:hidden; }
.hp-bar-fill { height:100%; background:linear-gradient(90deg, #ff4655, #ff8a71); box-shadow:0 0 15px #ff4655; width:100%; transition:width 0.4s ease-out; }
.controls { display:grid; grid-template-columns:1fr 200px; gap:15px; }
.game-input { width:100%; height:60px; background:rgba(255,255,255,0.05); border:1px solid rgba(255,255,255,0.1); border-radius:8px; padding:0 20px; color:#fff; font-size:16px; outline:none; }
.btn-attack { height:60px; background:#ff4655; color:#fff; border:none; border-radius:8px; font-family:'Oswald',sans-serif; font-size:20px; font-weight:700; cursor:pointer; text-transform:uppercase; transition:0.2s; }
.btn-attack:hover:not(:disabled) { background:#ff5e6a; transform:translateY(-2px); }
.btn-attack:disabled { background:#333; cursor:not-allowed; }
.damage-popup { text-align:center; font-family:'Oswald',sans-serif; font-size:24px; color:#ff4655; height:30px; margin-top:10px; }
</style>
</head>
<body>
<div class="game-container">
<aside class="sidebar">
<div class="panel">
<div class="panel-h">🏆 Лидеры</div>
<div id="topList"></div>
</div>
<div class="panel">
<div class="panel-h">🎯 Рекорды</div>
<div class="stat-row">
<span>Макс. урон</span>
<div class="record-info">
<b id="maxDmg" style="color:#ff4655">-</b>
<div id="maxPlayer" class="record-player">-</div>
</div>
</div>
<div class="stat-row">
<span>Мин. урон</span>
<div class="record-info">
<b id="minDmg" style="color:#fff">-</b>
<div id="minPlayer" class="record-player">-</div>
</div>
</div>
</div>
<div class="panel" style="flex-grow: 1;">
<div class="panel-h">📜 Журнал боя</div>
<div id="logs" class="log-container"></div>
</div>
</aside>
<main class="main-content">
<div class="boss-frame" id="bossFrame">
<img src="https://upforme.ru/uploads/001c/84/76/2/433839.jpg" class="boss-img" alt="Boss">
<div class="boss-overlay">
<div class="boss-info">
<div class="boss-name">ДРЕВНИЙ СТРАЖ</div>
<div id="hpText" class="hp-text">... / ...</div>
</div>
<div class="hp-bar-bg">
<div id="hpBar" class="hp-bar-fill" style="width: 100%;"></div>
</div>
</div>
</div>
<div class="controls">
<input type="text" id="msg" class="game-input" placeholder="Введите боевой клич...">
<button id="atkBtn" class="btn-attack">УДАРИТЬ</button>
</div>
<div id="dmgRes" class="damage-popup"></div>
</main>
</div>
<script>
const send = (action, data = {}) => {
const requestId = Math.random().toString(16).slice(2);
window.parent.postMessage({ _monsterGame: true, type: "gameRequest", requestId, action, ...data }, '*');
return new Promise(resolve => {
const handler = (e) => {
if (e.data?.requestId === requestId) {
window.removeEventListener("message", handler);
resolve(e.data.data);
}
};
window.addEventListener("message", handler);
});
};
async function updateUI() {
try {
const snap = await send("getSnapshot");
const game = snap.game;
const top = snap.top || [];
const logs = snap.logs || [];
const rec = snap.records || { maxVal: null, maxPlayers: [], minVal: null, minPlayers: [] };
const currentHp = game.boss.currentHealth;
const maxHp = game.boss.maxHealth;
const hpPct = Math.max(0, (currentHp / maxHp) * 100);
document.getElementById('hpBar').style.width = hpPct + '%';
document.getElementById('hpText').textContent = `${currentHp} / ${maxHp}`;
const isOver = (currentHp <= 0) || !!game.boss.isDefeated;
document.getElementById('atkBtn').disabled = isOver;
document.getElementById('topList').innerHTML =
top.slice(0, 5).map(p =>
`<div class="stat-row"><span>${p.nickname}</span><b>${p.totalDamage}</b></div>`
).join('') || 'Нет данных';
document.getElementById('logs').innerHTML =
logs.slice(-20).reverse().map(l => {
const parts = l.split(' нанес ');
return `<div class="log-entry"><b>${parts[0] || 'Игрок'}</b> нанес ${parts[1] || ''}</div>`;
}).join('');
if (rec.maxVal != null) {
document.getElementById('maxDmg').textContent = String(rec.maxVal);
document.getElementById('maxPlayer').textContent = (rec.maxPlayers || []).join(', ') || '-';
} else {
document.getElementById('maxDmg').textContent = '-';
document.getElementById('maxPlayer').textContent = '-';
}
if (rec.minVal != null) {
document.getElementById('minDmg').textContent = String(rec.minVal);
document.getElementById('minPlayer').textContent = (rec.minPlayers || []).join(', ') || '-';
} else {
document.getElementById('minDmg').textContent = '-';
document.getElementById('minPlayer').textContent = '-';
}
} catch (e) {
console.error("UI Update Error:", e);
}
}
document.getElementById('atkBtn').onclick = async () => {
const msgInput = document.getElementById('msg');
const btn = document.getElementById('atkBtn');
btn.disabled = true;
// ✅ урон считает родитель, мы шлём только текст
const res = await send("processAttack", { attackData: { message: msgInput.value } });
if (res && res.gameOver) {
document.getElementById('dmgRes').textContent = 'БОЙ ЗАВЕРШЕН!';
await updateUI();
return;
}
if (res && res.success) {
document.getElementById('dmgRes').textContent = `УДАР: -${res.damage} HP!`;
msgInput.value = '';
const frame = document.getElementById('bossFrame');
frame.style.transform = 'translate(4px, 4px) rotate(1deg)';
setTimeout(() => frame.style.transform = 'none', 100);
await updateUI();
}
setTimeout(() => {
document.getElementById('dmgRes').textContent = '';
updateUI().then(() => {});
}, 2500);
};
setInterval(updateUI, 15000);
updateUI();
</script>
</body>
</html>[/html]
Поделиться92026-01-10 20:25:56
[html]<div style="border:3px solid #ff6b6b;border-radius:15px;padding:20px;background:#494a4c;color:white;text-align:center;font-family:sans-serif;">
<h2 style="color:#ff6b6b; margin:0; font-size:20px;">⚔️ АТАКА: 93 УРОНА!</h2>
<p style="margin:10px 0;">admin ударил 🔥 ГНЕВНЫЙ ДРАКОН</p>
<p style="font-size:13px; color:#aaa; margin-top:10px;">Осталось HP: 907 / 1000</p>
</div>[/html]
Поделиться102026-01-10 20:37:54
[html]<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;700&family=Roboto+Condensed:wght@400;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0a0a0c;
background-image: radial-gradient(circle at center, #1a1a2e 0%, #0a0a0c 100%);
color: #e0e0e0;
font-family: 'Roboto Condensed', sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.game-container {
display: grid;
grid-template-columns: 300px 1fr;
gap: 25px;
max-width: 1100px;
width: 100%;
background: rgba(0, 0, 0, 0.6);
padding: 30px;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
backdrop-filter: blur(10px);
}
.sidebar { display:flex; flex-direction:column; gap:20px; }
.panel { background: rgba(255,255,255,0.03); border-left:3px solid #ff4655; padding:15px; border-radius:4px; }
.panel-h { font-family:'Oswald',sans-serif; font-size:14px; letter-spacing:2px; color:#ff4655; margin-bottom:12px; text-transform:uppercase; }
.stat-row { display:flex; justify-content:space-between; padding:6px 0; border-bottom:1px solid rgba(255,255,255,0.05); font-size:14px; gap:10px; }
.record-info { display:flex; flex-direction:column; gap:4px; text-align:right; }
.record-player { color:#aaa; font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:180px; }
.log-container { font-size:13px; height:200px; overflow-y:auto; scrollbar-width:thin; scrollbar-color:#ff4655 transparent; }
.log-entry { padding:4px 0; color:#aaa; border-bottom:1px solid rgba(255,255,255,0.02); }
.log-entry b { color:#ff4655; }
.main-content { display:flex; flex-direction:column; gap:20px; }
.boss-frame { position:relative; border-radius:12px; overflow:hidden; box-shadow:0 0 30px rgba(255,70,85,0.2); border:1px solid rgba(255,70,85,0.3); transition: transform 0.1s; }
.boss-img { width:100%; height:450px; object-fit:cover; display:block; }
.boss-overlay { position:absolute; bottom:0; left:0; right:0; padding:40px 20px 20px; background:linear-gradient(to top, rgba(0,0,0,0.9) 0%, transparent 100%); }
.boss-info { display:flex; justify-content:space-between; align-items:flex-end; margin-bottom:8px; gap:10px; }
.boss-name { font-family:'Oswald',sans-serif; font-size:32px; font-weight:700; color:#fff; }
.hp-text { font-family:'Oswald',sans-serif; color:#ff4655; font-size:18px; }
.hp-bar-bg { height:12px; background:rgba(255,255,255,0.1); border-radius:6px; overflow:hidden; }
.hp-bar-fill { height:100%; background:linear-gradient(90deg, #ff4655, #ff8a71); box-shadow:0 0 15px #ff4655; width:100%; transition:width 0.4s ease-out; }
.controls { display:grid; grid-template-columns:1fr 200px; gap:15px; }
.game-input { width:100%; height:60px; background:rgba(255,255,255,0.05); border:1px solid rgba(255,255,255,0.1); border-radius:8px; padding:0 20px; color:#fff; font-size:16px; outline:none; }
.btn-attack { height:60px; background:#ff4655; color:#fff; border:none; border-radius:8px; font-family:'Oswald',sans-serif; font-size:20px; font-weight:700; cursor:pointer; text-transform:uppercase; transition:0.2s; }
.btn-attack:hover:not(:disabled) { background:#ff5e6a; transform:translateY(-2px); }
.btn-attack:disabled { background:#333; cursor:not-allowed; }
.damage-popup { text-align:center; font-family:'Oswald',sans-serif; font-size:24px; color:#ff4655; height:30px; margin-top:10px; }
</style>
</head>
<body>
<div class="game-container">
<aside class="sidebar">
<div class="panel">
<div class="panel-h">🏆 Лидеры</div>
<div id="topList"></div>
</div>
<div class="panel">
<div class="panel-h">🎯 Рекорды</div>
<div class="stat-row">
<span>Макс. урон</span>
<div class="record-info">
<b id="maxDmg" style="color:#ff4655">-</b>
<div id="maxPlayer" class="record-player">-</div>
</div>
</div>
<div class="stat-row">
<span>Мин. урон</span>
<div class="record-info">
<b id="minDmg" style="color:#fff">-</b>
<div id="minPlayer" class="record-player">-</div>
</div>
</div>
</div>
<div class="panel" style="flex-grow:1;">
<div class="panel-h">📜 Журнал боя</div>
<div id="logs" class="log-container"></div>
</div>
</aside>
<main class="main-content">
<div class="boss-frame" id="bossFrame">
<img src="https://upforme.ru/uploads/001c/84/76/2/433839.jpg" class="boss-img" alt="Boss">
<div class="boss-overlay">
<div class="boss-info">
<div class="boss-name">ДРЕВНИЙ СТРАЖ</div>
<div id="hpText" class="hp-text">... / ...</div>
</div>
<div class="hp-bar-bg">
<div id="hpBar" class="hp-bar-fill" style="width: 100%;"></div>
</div>
</div>
</div>
<div class="controls">
<input type="text" id="msg" class="game-input" placeholder="Введите боевой клич...">
<button id="atkBtn" class="btn-attack">УДАРИТЬ</button>
</div>
<div id="dmgRes" class="damage-popup"></div>
</main>
</div>
<script>
const send = (action, data = {}) => {
const requestId = Math.random().toString(16).slice(2);
window.parent.postMessage({ _monsterGame: true, type: "gameRequest", requestId, action, ...data }, '*');
return new Promise(resolve => {
const handler = (e) => {
if (e.data?.requestId === requestId) {
window.removeEventListener("message", handler);
resolve(e.data.data);
}
};
window.addEventListener("message", handler);
});
};
async function updateUI() {
try {
const snap = await send("getSnapshot");
const game = snap.game;
const top = snap.top || [];
const logs = snap.logs || [];
const rec = snap.records || { maxVal: null, maxPlayers: [], minVal: null, minPlayers: [] };
const currentHp = game.boss.currentHealth;
const maxHp = game.boss.maxHealth;
const hpPct = Math.max(0, (currentHp / maxHp) * 100);
document.getElementById('hpBar').style.width = hpPct + '%';
document.getElementById('hpText').textContent = `${currentHp} / ${maxHp}`;
const isOver = (currentHp <= 0) || !!game.boss.isDefeated;
document.getElementById('atkBtn').disabled = isOver;
document.getElementById('topList').innerHTML =
top.slice(0, 5).map(p => `<div class="stat-row"><span>${p.nickname}</span><b>${p.totalDamage}</b></div>`).join('')
|| 'Нет данных';
document.getElementById('logs').innerHTML =
logs.slice(-20).reverse().map(l => {
const parts = l.split(' нанес ');
return `<div class="log-entry"><b>${parts[0] || 'Игрок'}</b> нанес ${parts[1] || ''}</div>`;
}).join('');
if (rec.maxVal != null) {
document.getElementById('maxDmg').textContent = String(rec.maxVal);
document.getElementById('maxPlayer').textContent = (rec.maxPlayers || []).join(', ') || '-';
} else {
document.getElementById('maxDmg').textContent = '-';
document.getElementById('maxPlayer').textContent = '-';
}
if (rec.minVal != null) {
document.getElementById('minDmg').textContent = String(rec.minVal);
document.getElementById('minPlayer').textContent = (rec.minPlayers || []).join(', ') || '-';
} else {
document.getElementById('minDmg').textContent = '-';
document.getElementById('minPlayer').textContent = '-';
}
} catch (e) {
console.error("UI Update Error:", e);
}
}
document.getElementById('atkBtn').onclick = async () => {
const msgInput = document.getElementById('msg');
const btn = document.getElementById('atkBtn');
btn.disabled = true;
// ✅ шлём только message, урон считает родитель
const res = await send("processAttack", { attackData: { message: msgInput.value } });
if (res && res.gameOver) {
document.getElementById('dmgRes').textContent = 'БОЙ ЗАВЕРШЕН!';
await updateUI();
return;
}
if (res && res.success) {
document.getElementById('dmgRes').textContent = `УДАР: -${res.damage} HP!`;
msgInput.value = '';
const frame = document.getElementById('bossFrame');
frame.style.transform = 'translate(4px, 4px) rotate(1deg)';
setTimeout(() => frame.style.transform = 'none', 100);
// ✅ один апдейт (без второго “лишнего”)
await updateUI();
}
setTimeout(() => { document.getElementById('dmgRes').textContent = ''; }, 2500);
};
setInterval(updateUI, 15000);
updateUI();
</script>
</body>
</html>[/html]
Поделиться112026-01-10 20:37:59
[html]<div style="border:3px solid #ff6b6b;border-radius:15px;padding:20px;background:#494a4c;color:white;text-align:center;font-family:sans-serif;">
<h2 style="color:#ff6b6b; margin:0; font-size:20px;">⚔️ АТАКА: 92 УРОНА!</h2>
<p style="margin:10px 0;">admin ударил 🔥 ГНЕВНЫЙ ДРАКОН</p>
<p style="font-size:13px; color:#aaa; margin-top:10px;">Осталось HP: 908 / 1000</p>
</div>[/html]
Поделиться122026-01-10 20:38:42
[html]<div style="border:3px solid #ff6b6b;border-radius:15px;padding:20px;background:#494a4c;color:white;text-align:center;font-family:sans-serif;">
<h2 style="color:#ff6b6b; margin:0; font-size:20px;">⚔️ АТАКА: 88 УРОНА!</h2>
<p style="margin:10px 0;">admin ударил 🔥 ГНЕВНЫЙ ДРАКОН</p>
<p style="font-size:13px; color:#aaa; margin-top:10px;">Осталось HP: 912 / 1000</p>
</div>[/html]