
Флуд
Сообщений 1 страница 4 из 4
Поделиться22026-01-29 15:20:10
[html]<div id="val-smooth-game" style="width: 100%; max-width: 800px; margin: 0 auto; aspect-ratio: 16 / 9; position: relative; background: #fff0f3; overflow: hidden; font-family: 'Segoe UI', Arial, sans-serif; border: 4px solid #ff4d6d; border-radius: 20px; transition: background 0.8s; touch-action: none;">
<div id="v-ui" style="display: none; position: absolute; top: 10px; width: 100%; padding: 0 20px; z-index: 10; justify-content: space-between; align-items: center; font-weight: bold; color: #ff4d6d; pointer-events: none; text-shadow: 1px 1px 2px rgba(0,0,0,0.1);">
<div id="v-score">Счёт: 0</div>
<div id="v-cheer" style="font-size: clamp(12px, 2.5vw, 16px); text-align: center; flex: 1; margin: 0 10px; opacity: 0; transition: opacity 0.8s; font-style: italic;"></div>
<div id="v-timer">60s</div>
</div>
<div id="v-start" style="position: absolute; inset: 0; background: #fff0f3; z-index: 20; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;">
<h2 style="color: #ff4d6d;">Ловец Сердец ❤️</h2>
<input type="text" id="v-name" placeholder="Имя героя" maxlength="12" style="padding: 10px; border: 2px solid #ff4d6d; border-radius: 8px; margin-bottom: 15px; outline: none; text-align: center;">
<button id="v-btn" style="padding: 10px 25px; background: #ff4d6d; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold;">Начать!</button>
<div id="v-board" style="margin-top: 15px; font-size: 13px; color: #888;"></div>
</div>
<div id="v-p" style="position: absolute; bottom: 15px; left: 50%; width: 70px; height: 50px; transform: translateX(-50%); display: none; z-index: 5; will-change: left;">
<svg viewBox="0 0 100 60"><path d="M10 10H90L80 50H20L10 10Z" fill="#d97706" stroke="#92400e" stroke-width="4"/><path d="M5 10H95" stroke="#92400e" stroke-width="6" stroke-linecap="round"/></svg>
</div>
<script>
(function() {
const stage = document.getElementById('val-smooth-game'), start = document.getElementById('v-start'), ui = document.getElementById('v-ui');
const player = document.getElementById('v-p'), scoreDisp = document.getElementById('v-score');
const timeDisp = document.getElementById('v-timer'), cheerDisp = document.getElementById('v-cheer');
let score = 0, time = 60, active = false, pName = "", phrases = [];
const rawPhrases = [
"Весь магический мир в шоке от тебя!", "Толпы поклонниц плачут в восторге!",
"[NAME], это незаконно — быть таким ловким!", "Амур нервно курит в сторонке!",
"Магнит для любви активирован!", "Даже котики завидуют твоей реакции!",
"Ты разбиваешь статистику форума!", "Сердечный маг 80-го уровня!",
"Где ты научился так красть сердца, [NAME]?", "Поклонницы уже строчат тебе письма!"
];
const shufflePhrases = () => { phrases = [...rawPhrases].sort(() => Math.random() - 0.5); };
function play() {
pName = document.getElementById('v-name').value.trim() || "Герой";
active = true; score = 0; time = 60; shufflePhrases();
start.style.display = 'none'; player.style.display = 'block'; ui.style.display = 'flex';
const t = setInterval(() => {
time--; timeDisp.innerText = time + 's';
if (time <= 0) { clearInterval(t); finish(); }
}, 1000);
spawn();
}
function finish() {
active = false;
const d = JSON.parse(localStorage.getItem('v_rank_final') || '[]');
d.push({n: pName, s: score});
d.sort((a,b) => b.s - a.s);
localStorage.setItem('v_rank_final', JSON.stringify(d));
alert(`ФИНАЛ! ${pName}, твой счёт: ${score}`);
location.reload();
}
const move = (cx) => {
if (!active) return;
const r = stage.getBoundingClientRect();
player.style.left = Math.max(35, Math.min(cx - r.left, r.width - 35)) + 'px';
};
stage.addEventListener('mousemove', (e) => move(e.clientX));
stage.addEventListener('touchmove', (e) => { e.preventDefault(); move(e.touches[0].clientX); }, {passive: false});
function spawn() {
if (!active) return;
const h = document.createElement('div');
h.style.cssText = `position: absolute; top: -40px; width: 30px; height: 30px; transform: translateX(-50%); pointer-events: none; will-change: top;`;
const heartColor = score >= 30 ? '#fff' : '#ff4d6d';
h.innerHTML = `<svg viewBox="0 0 24 24" fill="${heartColor}"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>`;
h.style.left = (20 + Math.random() * (stage.offsetWidth - 40)) + 'px';
stage.appendChild(h);
let y = -40, spd = 2.2 + (score * 0.04) + Math.random() * 1.8; // Чуть снизил базовую скорость
function f() {
if (!active) { h.remove(); return; }
y += spd; h.style.top = y + 'px';
const pR = player.getBoundingClientRect(), hR = h.getBoundingClientRect();
if (hR.bottom >= pR.top && hR.right >= pR.left && hR.left <= pR.right && hR.top <= pR.bottom) {
score++; scoreDisp.innerText = `Счёт: ${score}`;
if (score === 15) { stage.style.background = "#ffb3c1"; ui.style.color = "#8d001a"; }
if (score === 30) { stage.style.background = "#ff4d6d"; ui.style.color = "#ffffff"; }
// Фраза теперь появляется каждые 12 очков
if (score % 12 === 0) {
if (phrases.length === 0) shufflePhrases();
cheerDisp.innerText = phrases.pop().replace("[NAME]", pName);
cheerDisp.style.opacity = 1;
// Текст висит 4 секунды
setTimeout(() => { cheerDisp.style.opacity = 0; }, 4000);
}
h.remove(); return;
}
if (y > stage.offsetHeight) { h.remove(); return; }
requestAnimationFrame(f);
}
requestAnimationFrame(f);
setTimeout(spawn, Math.max(350, 800 - score * 7));
}
document.getElementById('v-btn').onclick = play;
const updateBoard = () => {
const d = JSON.parse(localStorage.getItem('v_rank_final') || '[]');
document.getElementById('v-board').innerHTML = d.length ? "<b>Рекорды:</b><br>" + d.slice(0,3).map(i => `${i.n}: ${i.s}`).join('<br>') : "";
};
updateBoard();
})();
</script>
</div>
[/html]
Поделиться32026-01-29 16:00:57
[html]<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Ловец сердец</title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700;900&family=Pacifico&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Montserrat', sans-serif;
background: #1a1a1a;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
touch-action: none;
overflow: hidden;
}
.game-wrap {
width: 95vw;
max-width: 800px;
aspect-ratio: 16/9;
position: relative;
background: linear-gradient(135deg, #fff0f3, #ffe5ec);
border: 3px solid #ff4d6d;
border-radius: 20px;
overflow: hidden;
}
h2 {
font-family: 'Pacifico', cursive;
color: #ff4d6d;
line-height: 1.1;
font-size: clamp(24px, 5vw, 34px);
margin-bottom: 8px;
}
.ui { position: absolute; top: 12px; width: 100%; padding: 0 20px; display: none; justify-content: space-between; z-index: 10; color: #ff4d6d; pointer-events: none; }
.score, .timer { font-size: clamp(16px, 3.2vw, 22px); font-weight: 700; }
.cheer {
position: absolute; top: 30%; left: 50%; transform: translate(-50%, -50%);
font-size: clamp(14px, 2.8vw, 19px); text-align: center; color: #ff4d6d;
opacity: 0; max-width: 80%; font-weight: 800;
z-index: 15; pointer-events: none;
text-shadow: 1px 1px 0px #fff;
}
.cheer.show { animation: cheerPop 2.5s ease-out forwards; }
@keyframes cheerPop {
0% { opacity: 0; transform: translate(-50%, -20%) scale(0.9); }
15% { opacity: 1; transform: translate(-50%, -50%) scale(1.05); }
100% { opacity: 0; transform: translate(-50%, -80%) scale(1); }
}
.player { position: absolute; bottom: 12px; left: 50%; width: 70px; height: 45px; transform: translateX(-50%); z-index: 20; display: none; }
.basket { width: 100%; height: 100%; background: #d97706; border: 2px solid #92400e; border-radius: 5px 5px 20px 20px; }
.screen { position: absolute; inset: 0; z-index: 30; display: flex; flex-direction: column; align-items: center; justify-content: center; background: linear-gradient(135deg, #fff0f3, #ffe5ec); padding: 15px; }
.logo { width: clamp(70px, 12vw, 90px); height: auto; margin-bottom: 10px; border-radius: 50%; }
.screen input { padding: 10px; border: 2px solid #ff4d6d; border-radius: 12px; margin-bottom: 12px; font-size: 15px; font-family: inherit; text-align: center; width: 200px; outline: none; }
.screen button { padding: 10px 30px; background: #ff4d6d; color: white; border: none; border-radius: 50px; cursor: pointer; font-weight: 700; font-size: 17px; box-shadow: 0 4px 0 #c41c3b; transition: 0.2s; }
/* Дизайн финального вывода */
#end-screen { display: none; z-index: 40; }
.res-info { margin-bottom: 5px; text-align: center; }
/* Крупный нарядный курсив для статуса */
.res-info .status {
font-family: 'Pacifico', cursive;
color: #d9480f;
font-size: clamp(20px, 4.5vw, 30px);
margin-bottom: 2px;
display: block;
}
.res-info .score-val {
font-size: 48px;
color: #ff4d6d;
font-weight: 900;
line-height: 0.9;
margin-bottom: 4px;
}
.res-info .p-label { color: #888; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; }
.status.legend {
color: #ff4d6d !important;
text-shadow: 2px 2px 0px #fff;
filter: drop-shadow(0 0 5px rgba(255, 77, 109, 0.3));
}
.leaderboard-box { width: 85%; max-width: 270px; background: rgba(255,255,255,0.5); padding: 10px; border-radius: 15px; border: 1px solid #ffb3c1; margin-bottom: 10px; }
.leader-row { display: flex; justify-content: space-between; font-size: 13px; padding: 4px 8px; border-radius: 8px; color: #444; }
.leader-row:nth-child(even) { background: rgba(255, 77, 109, 0.06); }
</style>
</head>
<body>
<div class="game-wrap" id="game-container">
<div class="ui" id="game-ui">
<div class="score">❤️ <span id="val-score">0</span></div>
<div class="timer"><span id="val-time">60</span>s</div>
</div>
<div class="cheer" id="val-cheer"></div>
<div class="screen" id="start-screen">
<img src="https://i.imgur.com/G42b7SB.png" alt="Heart Icon" class="logo">
<h2>Ловец сердец</h2>
<input type="text" id="p-name" placeholder="Имя ловца" maxlength="12">
<button id="start-btn">Начать!</button>
<div id="leaderboard" class="leaderboard-box" style="margin-top: 10px;"></div>
</div>
<div class="player" id="game-player"><div class="basket"></div></div>
<div class="screen" id="end-screen">
<h2>Ритуал завершен!</h2>
<div class="res-info">
<span id="res-status" class="status"></span>
<div id="res-score" class="score-val"></div>
<div id="res-name" class="p-label"></div>
</div>
<div class="leaderboard-box" id="final-board"></div>
<button onclick="location.reload()">Еще разок</button>
</div>
</div>
<script>
const game = {
score: 0, time: 60, active: false, name: '',
phrasesPool: [], currentPhrases: [], timerInt: null,
lastPhraseScore: 0,
init() {
this.updateBoard('leaderboard');
this.phrasesPool = [
"Магический рекорд!",
"Фанатки в восторге!",
"[NAME], ты легенда!",
"[NAME], ты ювелир!",
"Настоящий магнит для любви!",
"Поймай и моё сердце, крошка)",
"Где ты так научился, [NAME]?",
"Твои движения безупречны!",
"Руки загребуки!",
"Поток заряжен!",
"Создатель бы тобой гордился, [NAME]!",
"[NAME], а ты точно волшебник?",
"Это всё твои чары?",
"Слишком хорош для этого мира!",
"[NAME], ты просто космос!",
"Сердца так и липнут к тебе!",
"Магия вне Салема, чесслово!",
"Кто-то остановите [NAME]!",
"Мастер амурных дел в здании!"
];
this.resetPhrases();
document.getElementById('start-btn').onclick = () => this.start();
},
resetPhrases() { this.currentPhrases = [...this.phrasesPool].sort(() => Math.random() - 0.5); },
start() {
this.name = document.getElementById('p-name').value.trim() || 'Гость';
this.active = true;
document.getElementById('start-screen').style.display = 'none';
document.getElementById('game-ui').style.display = 'flex';
document.getElementById('game-player').style.display = 'block';
this.timerInt = setInterval(() => {
this.time--;
document.getElementById('val-time').textContent = this.time;
if (this.time <= 0) this.finish();
}, 1000);
this.spawn();
},
spawn() {
if (!this.active) return;
const h = document.createElement('div');
h.style.cssText = `position: absolute; top: -40px; width: 32px; height: 32px; z-index: 5;`;
const isBonus = Math.random() < 0.15;
h.innerHTML = isBonus ? `<svg viewBox="0 0 24 24" fill="#ffcc00"><path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z"/></svg>` : `<svg viewBox="0 0 24 24" fill="#ff4d6d"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>`;
const wrap = document.getElementById('game-container');
h.style.left = (Math.random() * (wrap.offsetWidth - 40)) + 'px';
wrap.appendChild(h);
let y = -40;
const speed = 3.6 + (this.score * 0.04);
const fall = () => {
if (!this.active) { h.remove(); return; }
y += speed; h.style.top = y + 'px';
const pR = document.getElementById('game-player').getBoundingClientRect();
const hR = h.getBoundingClientRect();
if (hR.bottom >= pR.top && hR.right >= pR.left && hR.left <= pR.right && hR.top <= pR.bottom) {
this.score += isBonus ? 5 : 1;
document.getElementById('val-score').textContent = this.score;
if (this.score >= this.lastPhraseScore + 5) { this.showCheer(); this.lastPhraseScore = this.score; }
h.remove(); return;
}
if (y > wrap.offsetHeight) { h.remove(); return; }
requestAnimationFrame(fall);
};
requestAnimationFrame(fall);
setTimeout(() => this.spawn(), Math.max(250, 850 - this.score * 4));
},
showCheer() {
if (this.currentPhrases.length === 0) this.resetPhrases();
const c = document.getElementById('val-cheer');
c.textContent = this.currentPhrases.pop().replace('[NAME]', this.name);
c.classList.remove('show'); void c.offsetWidth; c.classList.add('show');
},
finish() {
this.active = false;
clearInterval(this.timerInt);
const s = JSON.parse(localStorage.getItem('m_scores_v17') || '[]');
s.push({n: this.name, s: this.score});
const sorted = s.sort((a,b)=>b.s-a.s).slice(0,5);
localStorage.setItem('m_scores_v17', JSON.stringify(sorted));
document.getElementById('end-screen').style.display = 'flex';
document.getElementById('res-name').textContent = this.name;
document.getElementById('res-score').textContent = this.score;
const st = document.getElementById('res-status');
st.classList.remove('legend');
if (this.score >= 80) { st.textContent = "Маг любви 80 уровня!"; st.classList.add('legend'); }
else if (this.score > 50) st.textContent = "Мастер амурных дел";
else if (this.score > 20) st.textContent = "Уверенный пользователь сердец";
else st.textContent = "Наивняк";
this.updateBoard('final-board');
},
updateBoard(tid) {
const s = JSON.parse(localStorage.getItem('m_scores_v17') || '[]');
const c = document.getElementById(tid);
if (s.length > 0) {
c.style.display = 'block';
c.innerHTML = '<b style="color:#ff4d6d;font-size:10px;display:block;margin-bottom:4px;padding-left:8px;text-transform:uppercase;">Топ ловцов:</b>' +
s.map((x,i)=>`<div class="leader-row"><span>${i+1}. ${x.n}</span><b>${x.s} ❤️</b></div>`).join('');
} else { c.style.display = 'none'; }
}
};
const mv = (e) => {
if (!game.active) return;
const r = document.getElementById('game-container').getBoundingClientRect();
const x = (e.touches ? e.touches[0].clientX : e.clientX) - r.left;
document.getElementById('game-player').style.left = Math.max(35, Math.min(x, r.width - 35)) + 'px';
};
window.addEventListener('mousemove', mv);
window.addEventListener('touchmove', (e) => { if(game.active) e.preventDefault(); mv(e); }, {passive: false});
game.init();
</script>
</body>
</html>[/html]
Поделиться42026-01-29 17:37:08
[html]<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Ловец сердец</title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700;900&family=Pacifico&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Montserrat', sans-serif;
background: #1a1a1a;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
touch-action: none;
overflow: hidden;
}
.game-wrap {
width: 95vw;
max-width: 800px;
aspect-ratio: 16/9;
position: relative;
background: linear-gradient(135deg, #fff0f3, #ffe5ec);
border: 3px solid #ff4d6d;
border-radius: 20px;
overflow: hidden;
}
h2 {
font-family: 'Pacifico', cursive;
color: #ff4d6d;
line-height: 1.1;
font-size: clamp(24px, 5vw, 34px);
margin-bottom: 8px;
}
.ui { position: absolute; top: 12px; width: 100%; padding: 0 20px; display: none; justify-content: space-between; z-index: 10; color: #ff4d6d; pointer-events: none; }
.score, .timer { font-size: clamp(16px, 3.2vw, 22px); font-weight: 700; }
.cheer {
position: absolute; top: 30%; left: 50%; transform: translate(-50%, -50%);
font-size: clamp(14px, 2.8vw, 19px); text-align: center; color: #ff4d6d;
opacity: 0; max-width: 80%; font-weight: 800;
z-index: 15; pointer-events: none;
text-shadow: 1px 1px 0px #fff;
}
.cheer.show { animation: cheerPop 2.5s ease-out forwards; }
@keyframes cheerPop {
0% { opacity: 0; transform: translate(-50%, -20%) scale(0.9); }
15% { opacity: 1; transform: translate(-50%, -50%) scale(1.05); }
100% { opacity: 0; transform: translate(-50%, -80%) scale(1); }
}
.player { position: absolute; bottom: 12px; left: 50%; width: 70px; height: 45px; transform: translateX(-50%); z-index: 20; display: none; transition: transform 0.1s; }
.basket { width: 100%; height: 100%; background: #d97706; border: 2px solid #92400e; border-radius: 5px 5px 20px 20px; }
/* Эффект поимки */
@keyframes catchEffect {
0% { transform: translateX(-50%) scale(1); filter: brightness(1); }
50% { transform: translateX(-50%) scale(1.2); filter: brightness(1.4); }
100% { transform: translateX(-50%) scale(1); filter: brightness(1); }
}
.catch-anim { animation: catchEffect 0.2s ease-out; }
.screen { position: absolute; inset: 0; z-index: 30; display: flex; flex-direction: column; align-items: center; justify-content: center; background: linear-gradient(135deg, #fff0f3, #ffe5ec); padding: 15px; }
.logo { width: clamp(70px, 12vw, 90px); height: auto; margin-bottom: 10px; border-radius: 50%; }
.screen input { padding: 10px; border: 2px solid #ff4d6d; border-radius: 12px; margin-bottom: 12px; font-size: 15px; font-family: inherit; text-align: center; width: 200px; outline: none; }
.screen button { padding: 10px 30px; background: #ff4d6d; color: white; border: none; border-radius: 50px; cursor: pointer; font-weight: 700; font-size: 17px; box-shadow: 0 4px 0 #c41c3b; transition: 0.2s; }
#end-screen { display: none; z-index: 40; }
.res-info { margin-bottom: 5px; text-align: center; }
.res-info .status {
font-family: 'Pacifico', cursive;
color: #d9480f;
font-size: clamp(20px, 4.5vw, 30px);
margin-bottom: 2px;
display: block;
}
.res-info .score-val {
font-size: 48px;
color: #ff4d6d;
font-weight: 900;
line-height: 0.9;
margin-bottom: 4px;
}
.res-info .p-label { color: #888; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; }
.status.legend {
color: #ff4d6d !important;
text-shadow: 2px 2px 0px #fff;
filter: drop-shadow(0 0 5px rgba(255, 77, 109, 0.3));
}
.leaderboard-box { width: 85%; max-width: 270px; background: rgba(255,255,255,0.5); padding: 10px; border-radius: 15px; border: 1px solid #ffb3c1; margin-bottom: 10px; }
.leader-row { display: flex; justify-content: space-between; font-size: 13px; padding: 4px 8px; border-radius: 8px; color: #444; }
.leader-row:nth-child(even) { background: rgba(255, 77, 109, 0.06); }
</style>
</head>
<body>
<div class="game-wrap" id="game-container">
<div class="ui" id="game-ui">
<div class="score">❤️ <span id="val-score">0</span></div>
<div class="timer"><span id="val-time">60</span>s</div>
</div>
<div class="cheer" id="val-cheer"></div>
<div class="screen" id="start-screen">
<img src="https://i.imgur.com/G42b7SB.png" alt="Heart Icon" class="logo">
<h2>Ловец сердец</h2>
<input type="text" id="p-name" placeholder="Имя ловца" maxlength="12">
<button id="start-btn">Начать!</button>
<div id="leaderboard" class="leaderboard-box" style="margin-top: 10px;"></div>
</div>
<div class="player" id="game-player"><div class="basket"></div></div>
<div class="screen" id="end-screen">
<h2>Ритуал завершен!</h2>
<div class="res-info">
<span id="res-status" class="status"></span>
<div id="res-score" class="score-val"></div>
<div id="res-name" class="p-label"></div>
</div>
<div class="leaderboard-box" id="final-board"></div>
<button onclick="location.reload()">Еще разок</button>
</div>
</div>
<script>
const game = {
score: 0, time: 60, active: false, name: '',
phrasesPool: [], currentPhrases: [], timerInt: null,
lastPhraseScore: 0,
init() {
this.updateBoard('leaderboard');
this.phrasesPool = [
"Магический рекорд!", "Фанатки в восторге!", "[NAME], ты легенда!",
"[NAME], ты ювелир!", "Настоящий магнит для любви!", "Поймай и моё сердце, крошка)",
"Где ты так научился, [NAME]?", "Твои движения безупречны!", "Руки загребуки!",
"Поток заряжен!", "Создатель бы тобой гордился, [NAME]!", "[NAME], а ты точно волшебник?",
"Это всё твои чары?", "Слишком хорош для этого мира!", "[NAME], ты просто космос!",
"Сердца так и липнут к тебе!", "Магия вне Салема, чесслово!", "Кто-то остановите [NAME]!",
"Мастер амурных дел в здании!"
];
this.resetPhrases();
document.getElementById('start-btn').onclick = () => this.start();
},
resetPhrases() { this.currentPhrases = [...this.phrasesPool].sort(() => Math.random() - 0.5); },
start() {
this.name = document.getElementById('p-name').value.trim() || 'Гость';
this.active = true;
document.getElementById('start-screen').style.display = 'none';
document.getElementById('game-ui').style.display = 'flex';
document.getElementById('game-player').style.display = 'block';
this.timerInt = setInterval(() => {
this.time--;
document.getElementById('val-time').textContent = this.time;
if (this.time <= 0) this.finish();
}, 1000);
this.spawn();
},
spawn() {
if (!this.active) return;
const h = document.createElement('div');
h.style.cssText = `position: absolute; top: -40px; width: 32px; height: 32px; z-index: 5;`;
const isBonus = Math.random() < 0.15;
h.innerHTML = isBonus ? `<svg viewBox="0 0 24 24" fill="#ffcc00"><path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z"/></svg>` : `<svg viewBox="0 0 24 24" fill="#ff4d6d"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>`;
const wrap = document.getElementById('game-container');
h.style.left = (Math.random() * (wrap.offsetWidth - 40)) + 'px';
wrap.appendChild(h);
let y = -40;
const speed = 3.6 + (this.score * 0.04);
const fall = () => {
if (!this.active) { h.remove(); return; }
y += speed; h.style.top = y + 'px';
const pEl = document.getElementById('game-player');
const pR = pEl.getBoundingClientRect();
const hR = h.getBoundingClientRect();
if (hR.bottom >= pR.top && hR.right >= pR.left && hR.left <= pR.right && hR.top <= pR.bottom) {
this.score += isBonus ? 5 : 1;
document.getElementById('val-score').textContent = this.score;
// ЭФФЕКТ ПОИМКИ
pEl.classList.remove('catch-anim');
void pEl.offsetWidth; // сброс анимации
pEl.classList.add('catch-anim');
if (this.score >= this.lastPhraseScore + 5) { this.showCheer(); this.lastPhraseScore = this.score; }
h.remove(); return;
}
if (y > wrap.offsetHeight) { h.remove(); return; }
requestAnimationFrame(fall);
};
requestAnimationFrame(fall);
setTimeout(() => this.spawn(), Math.max(250, 850 - this.score * 4));
},
showCheer() {
if (this.currentPhrases.length === 0) this.resetPhrases();
const c = document.getElementById('val-cheer');
c.textContent = this.currentPhrases.pop().replace('[NAME]', this.name);
c.classList.remove('show'); void c.offsetWidth; c.classList.add('show');
},
finish() {
this.active = false;
clearInterval(this.timerInt);
const s = JSON.parse(localStorage.getItem('m_scores_v17') || '[]');
s.push({n: this.name, s: this.score});
const sorted = s.sort((a,b)=>b.s-a.s).slice(0,5);
localStorage.setItem('m_scores_v17', JSON.stringify(sorted));
document.getElementById('end-screen').style.display = 'flex';
document.getElementById('res-name').textContent = this.name;
document.getElementById('res-score').textContent = this.score;
const st = document.getElementById('res-status');
st.classList.remove('legend');
if (this.score >= 80) { st.textContent = "Маг любви 80 уровня!"; st.classList.add('legend'); }
else if (this.score > 50) st.textContent = "Мастер амурных дел";
else if (this.score > 20) st.textContent = "Уверенный пользователь сердец";
else st.textContent = "Наивняк";
this.updateBoard('final-board');
},
updateBoard(tid) {
const s = JSON.parse(localStorage.getItem('m_scores_v17') || '[]');
const c = document.getElementById(tid);
if (s && s.length > 0) {
c.style.display = 'block';
c.innerHTML = '<b style="color:#ff4d6d;font-size:10px;display:block;margin-bottom:4px;padding-left:8px;text-transform:uppercase;">Топ ловцов:</b>' +
s.map((x,i)=>`<div class="leader-row"><span>${i+1}. ${x.n}</span><b>${x.s} ❤️</b></div>`).join('');
} else if (c) { c.style.display = 'none'; }
}
};
const mv = (e) => {
if (!game.active) return;
const r = document.getElementById('game-container').getBoundingClientRect();
const x = (e.touches ? e.touches[0].clientX : e.clientX) - r.left;
document.getElementById('game-player').style.left = Math.max(35, Math.min(x, r.width - 35)) + 'px';
};
window.addEventListener('mousemove', mv);
window.addEventListener('touchmove', (e) => { if(game.active) e.preventDefault(); mv(e); }, {passive: false});
game.init();
</script>
</body>
</html>[/html]

