Загрузка данных
<!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>Reflex Trainer Pro</title>
<style>
:root {
--bg-color: #121212;
--surface-color: #1e1e1e;
--primary-color: #00f3ff;
--secondary-color: #bc13fe;
--success-color: #00ff41;
--danger-color: #ff0055;
--text-color: #ffffff;
--font-main: 'Segoe UI', Roboto, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; -webkit-tap-highlight-color: transparent; }
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-main);
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* --- ГЛОБАЛЬНЫЙ ПРИЦЕЛ --- */
#global-crosshair {
position: fixed;
top: 0;
left: 0;
pointer-events: none;
z-index: 9999;
transform: translate(-50%, -50%);
display: none;
}
.cross-dot {
width: 6px; height: 6px;
background: #00ff00;
border-radius: 50%;
box-shadow: 0 0 8px #00ff00, 0 0 20px #00ff00;
}
.cross-classic {
width: 24px; height: 24px;
border: 2px solid #00ff00;
border-radius: 50%;
box-shadow: 0 0 8px #00ff00;
}
.cross-sniper {
width: 40px; height: 2px;
background: #00ff00;
position: relative;
box-shadow: 0 0 8px #00ff00;
}
.cross-sniper::after {
content: ''; position: absolute; top: -19px; left: 19px;
width: 2px; height: 40px; background: #00ff00; box-shadow: 0 0 8px #00ff00;
}
/* Скрываем стандартный курсор в режиме прицеливания */
.aiming-mode {
cursor: none !important;
}
/* --- Экраны --- */
.screen {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
display: none; flex-direction: column; align-items: center; justify-content: center;
padding: 20px; opacity: 0; transition: opacity 0.3s ease; z-index: 1;
}
.screen.active { display: flex; opacity: 1; z-index: 10; }
h1 { font-size: 2rem; color: var(--primary-color); text-shadow: 0 0 10px var(--primary-color); text-align: center; margin-bottom: 20px; }
h2 { font-size: 1.2rem; color: #ccc; margin-bottom: 20px; text-align: center; }
p { margin-bottom: 10px; }
.btn {
background: transparent; border: 2px solid var(--primary-color); color: var(--primary-color);
padding: 12px 24px; font-size: 1rem; margin: 8px 0; cursor: pointer; border-radius: 5px;
width: 100%; max-width: 300px; text-transform: uppercase; font-weight: bold;
transition: all 0.2s; box-shadow: 0 0 5px var(--primary-color);
}
.btn:hover { background: var(--primary-color); color: #000; box-shadow: 0 0 20px var(--primary-color); }
.btn-secondary { border-color: var(--secondary-color); color: var(--secondary-color); box-shadow: 0 0 5px var(--secondary-color); }
.btn-secondary:hover { background: var(--secondary-color); color: white; box-shadow: 0 0 20px var(--secondary-color); }
.btn-back { position: absolute; top: 20px; left: 20px; width: auto; padding: 8px 16px; font-size: 0.9rem; z-index: 100; }
.settings-group { margin-bottom: 15px; width: 100%; max-width: 300px; text-align: center; }
.settings-label { font-size: 0.9rem; color: #888; margin-bottom: 5px; display: block; }
.options-row { display: flex; gap: 5px; justify-content: center; }
.opt-btn {
flex: 1; padding: 8px; border: 1px solid #444; background: #222; color: #aaa;
cursor: pointer; font-size: 0.8rem; border-radius: 4px; transition: 0.2s;
}
.opt-btn.selected {
border-color: var(--primary-color);
color: var(--primary-color);
background: rgba(0, 243, 255, 0.1);
box-shadow: 0 0 5px var(--primary-color);
}
.hud { position: absolute; top: 20px; right: 20px; text-align: right; font-weight: bold; z-index: 50; pointer-events: none; }
.record-display { font-size: 0.8rem; color: #888; margin-top: 5px; }
#accuracy-area {
width: 100%; max-width: 600px; height: 60vh;
background-color: var(--surface-color); border: 2px solid #333; border-radius: 10px;
position: relative; overflow: hidden;
}
.target {
background-color: var(--danger-color); border-radius: 50%; position: absolute;
box-shadow: 0 0 10px var(--danger-color); transition: transform 0.1s;
z-index: 10;
}
#reaction-zone {
width: 100%; height: 100%; display: flex; flex-direction: column;
align-items: center; justify-content: center; cursor: pointer;
position: absolute; top: 0; left: 0; z-index: 5; transition: background 0.1s;
}
.react-waiting { background-color: var(--danger-color) !important; }
.react-go { background-color: var(--success-color) !important; }
#reaction-msg { font-size: 3rem; font-weight: bold; text-align: center; color: white; text-shadow: 0 2px 5px rgba(0,0,0,0.5); }
#reaction-submsg { font-size: 1.2rem; margin-top: 10px; color: rgba(255,255,255,0.8); }
#color-word { font-size: 3rem; font-weight: 900; margin: 40px 0; text-transform: uppercase; text-align: center; line-height: 1.2; }
.color-controls { display: flex; gap: 20px; width: 100%; justify-content: center; }
.btn-color { flex: 1; max-width: 150px; padding: 20px; border: none; border-radius: 8px; font-size: 1.1rem; font-weight: bold; color: white; cursor: pointer; }
.btn-yes { background-color: var(--success-color); }
.btn-no { background-color: var(--danger-color); }
.btn-color:active { transform: scale(0.95); }
#fail-modal {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.9); z-index: 200; display: none;
flex-direction: column; align-items: center; justify-content: center;
text-align: center; padding: 20px;
}
#fail-modal h2 { color: var(--danger-color); font-size: 2rem; text-shadow: 0 0 20px red; margin-bottom: 20px; }
#fail-modal p { font-size: 1.5rem; color: white; margin-bottom: 30px; }
</style>
</head>
<body>
<!-- ГЛОБАЛЬНЫЙ ПРИЦЕЛ -->
<div id="global-crosshair"><div class="cross-dot"></div></div>
<!-- МЕНЮ -->
<div id="menu" class="screen active">
<h1>Reflex Trainer Pro</h1>
<div class="settings-group">
<span class="settings-label">Сложность</span>
<div class="options-row" id="diff-settings">
<button class="opt-btn selected" onclick="Game.setDifficulty('easy')">Легко</button>
<button class="opt-btn" onclick="Game.setDifficulty('medium')">Норм</button>
<button class="opt-btn" onclick="Game.setDifficulty('hard')">Хард</button>
</div>
</div>
<div class="settings-group">
<span class="settings-label">Прицел</span>
<div class="options-row" id="crosshair-settings">
<button class="opt-btn selected" onclick="Game.setCrosshair('dot')">Точка</button>
<button class="opt-btn" onclick="Game.setCrosshair('classic')">Классика</button>
<button class="opt-btn" onclick="Game.setCrosshair('sniper')">Снайпер</button>
</div>
</div>
<div class="settings-group">
<span class="settings-label">Время</span>
<div class="options-row" id="time-settings">
<button class="opt-btn selected" onclick="Game.setTime(30)">30с</button>
<button class="opt-btn" onclick="Game.setTime(45)">45с</button>
<button class="opt-btn" onclick="Game.setTime(60)">1м</button>
</div>
</div>
<div style="height: 20px;"></div>
<button class="btn" onclick="Game.startAccuracy()">Точность</button>
<button class="btn" onclick="Game.startReaction()">Реакция</button>
<button class="btn" onclick="Game.startColor()">Цвет</button>
<div style="margin-top: 30px; text-align: center; color: #666; font-size: 0.9rem;">
<p>Рекорды:</p>
<div id="menu-records"></div>
</div>
</div>
<!-- ИГРА 1: ТОЧНОСТЬ -->
<div id="game-accuracy" class="screen">
<button class="btn btn-secondary btn-back" onclick="Game.toMenu()">Меню</button>
<div class="hud">
<div>Время: <span id="acc-time">30</span></div>
<div>Счет: <span id="acc-score">0</span></div>
<div class="record-display">Рекорд: <span id="acc-rec">0</span></div>
</div>
<h2>Попадай по кругам!</h2>
<div id="accuracy-area"></div>
</div>
<!-- ИГРА 2: РЕАКЦИЯ -->
<div id="game-reaction" class="screen">
<button class="btn btn-secondary btn-back" onclick="Game.toMenu()">Меню</button>
<div id="reaction-zone" onmousedown="Game.handleReactionClick(event)" ontouchstart="Game.handleReactionClick(event)">
<div id="reaction-msg">Нажми для старта</div>
<div id="reaction-submsg"></div>
</div>
<div style="position: absolute; bottom: 20px; text-align: center; color: #888;">
Рекорд: <span id="react-rec">---</span> мс
</div>
</div>
<!-- ИГРА 3: ЦВЕТ -->
<div id="game-color" class="screen">
<button class="btn btn-secondary btn-back" onclick="Game.toMenu()">Меню</button>
<div class="hud">
<div>Время: <span id="col-time">60</span></div>
<div>Счет: <span id="col-score">0</span></div>
<div class="record-display">Рекорд: <span id="col-rec">0</span></div>
</div>
<h2>Цвет = слову?</h2>
<div id="color-word">СЛОВО</div>
<div class="color-controls">
<button class="btn-color btn-yes" onclick="Game.checkColor(true)">ДА</button>
<button class="btn-color btn-no" onclick="Game.checkColor(false)">НЕТ</button>
</div>
</div>
<!-- ОКНО ПРОМАХА -->
<div id="fail-modal">
<h2>ТЫ ПРОМАЗАЛ!</h2>
<p>А тебя размазали с калаша...</p>
<button class="btn" onclick="Game.closeFailModal()">В меню</button>
</div>
<script>
const Game = {
config: {
difficulty: 'easy',
crosshair: 'dot',
timeLimit: 30
},
sizes: {
easy: { min: 60, max: 90 },
medium: { min: 40, max: 60 },
hard: { min: 20, max: 35 }
},
state: {
accuracy: { score: 0, time: 0, timer: null, active: false },
reaction: { step: 0, results: [], startTime: 0, timeout: null, state: 'idle' },
color: { score: 0, time: 0, timer: null, active: false, isMatch: false }
},
records: { accuracy: 0, reaction: null, color: 0 },
colorsList: [
{ name: 'КРАСНЫЙ', hex: '#ff0055' },
{ name: 'СИНИЙ', hex: '#00f3ff' },
{ name: 'ЗЕЛЕНЫЙ', hex: '#00ff41' },
{ name: 'ЖЕЛТЫЙ', hex: '#ffee00' }
],
init() {
this.loadRecords();
this.updateMenuRecords();
// === ВАШ КОД: Слежение прицела за мышью ===
document.addEventListener('mousemove', (e) => {
const crosshair = document.getElementById('global-crosshair');
crosshair.style.left = e.clientX + 'px';
crosshair.style.top = e.clientY + 'px';
});
// ===========================================
// Промах по полю
document.getElementById('accuracy-area').addEventListener('mousedown', (e) => {
if(this.state.accuracy.active && e.target.id === 'accuracy-area') {
this.triggerFail();
}
});
},
// === ВАШ КОД: Включение прицела ===
showCrosshair(type) {
const crosshair = document.getElementById('global-crosshair');
crosshair.style.display = 'block';
document.body.classList.add('aiming-mode');
// Смена типа прицела
const inner = crosshair.querySelector('div');
inner.className = 'cross-' + (type || this.config.crosshair);
},
// =================================
hideCrosshair() {
document.getElementById('global-crosshair').style.display = 'none';
document.body.classList.remove('aiming-mode');
},
setDifficulty(level) {
this.config.difficulty = level;
this.updateSettingsUI('diff-settings', level);
},
setCrosshair(type) {
this.config.crosshair = type;
this.updateSettingsUI('crosshair-settings', type);
// Если прицел сейчас виден — обновляем его вид
if(document.getElementById('global-crosshair').style.display === 'block') {
this.showCrosshair(type);
}
},
setTime(sec) {
this.config.timeLimit = sec;
this.updateSettingsUI('time-settings', sec);
},
updateSettingsUI(parentId, value) {
const buttons = document.getElementById(parentId).querySelectorAll('.opt-btn');
buttons.forEach(btn => {
btn.classList.remove('selected');
const match = btn.getAttribute('onclick').match(/'([^']+)'/);
if (match && match[1] == value) btn.classList.add('selected');
});
},
showScreen(id) {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.getElementById(id).classList.add('active');
},
toMenu() {
this.stopAll();
this.updateMenuRecords();
this.showScreen('menu');
},
stopAll() {
clearInterval(this.state.accuracy.timer);
clearInterval(this.state.color.timer);
clearTimeout(this.state.reaction.timeout);
this.state.accuracy.active = false;
this.state.color.active = false;
this.state.reaction.state = 'idle';
this.hideCrosshair();
document.getElementById('reaction-zone').className = '';
},
loadRecords() {
const saved = localStorage.getItem('reflexGameRecordsPro');
if (saved) this.records = JSON.parse(saved);
},
saveRecord(type, value) {
let updated = false;
if (type === 'accuracy' && value > this.records.accuracy) {
this.records.accuracy = value; updated = true;
} else if (type === 'color' && value > this.records.color) {
this.records.color = value; updated = true;
} else if (type === 'reaction') {
if (this.records.reaction === null || value < this.records.reaction) {
this.records.reaction = Math.round(value); updated = true;
}
}
if (updated) localStorage.setItem('reflexGameRecordsPro', JSON.stringify(this.records));
},
updateMenuRecords() {
const r = this.records;
document.getElementById('menu-records').innerHTML = `
Точность: ${r.accuracy} | Реакция: ${r.reaction ? r.reaction+'мс' : 'Нет'} | Цвет: ${r.color}
`;
},
triggerFail() {
this.state.accuracy.active = false;
clearInterval(this.state.accuracy.timer);
document.getElementById('fail-modal').style.display = 'flex';
},
closeFailModal() {
document.getElementById('fail-modal').style.display = 'none';
this.toMenu();
},
// === ИГРА 1: ТОЧНОСТЬ ===
startAccuracy() {
this.showScreen('game-accuracy');
this.state.accuracy.score = 0;
this.state.accuracy.time = this.config.timeLimit;
this.state.accuracy.active = true;
document.getElementById('acc-score').innerText = '0';
document.getElementById('acc-time').innerText = this.config.timeLimit;
document.getElementById('acc-rec').innerText = this.records.accuracy;
document.getElementById('accuracy-area').innerHTML = '';
// === ВАШ КОД: Включаем прицел при входе в игру ===
this.showCrosshair(this.config.crosshair);
// ================================================
this.spawnTarget();
this.state.accuracy.timer = setInterval(() => {
this.state.accuracy.time--;
document.getElementById('acc-time').innerText = this.state.accuracy.time;
if (this.state.accuracy.time <= 0) this.endAccuracy();
}, 1000);
},
spawnTarget() {
if (!this.state.accuracy.active) return;
const area = document.getElementById('accuracy-area');
const target = document.createElement('div');
target.className = 'target';
const range = this.sizes[this.config.difficulty];
const size = Math.floor(Math.random() * (range.max - range.min + 1)) + range.min;
target.style.width = size + 'px';
target.style.height = size + 'px';
const maxX = area.clientWidth - size;
const maxY = area.clientHeight - size;
target.style.left = (Math.random() * maxX) + 'px';
target.style.top = (Math.random() * maxY) + 'px';
const hitHandler = (e) => {
e.stopPropagation();
if(e.type === 'touchstart') e.preventDefault();
this.state.accuracy.score++;
document.getElementById('acc-score').innerText = this.state.accuracy.score;
target.remove();
this.spawnTarget();
};
target.onmousedown = hitHandler;
target.ontouchstart = hitHandler;
area.appendChild(target);
},
endAccuracy() {
this.state.accuracy.active = false;
clearInterval(this.state.accuracy.timer);
this.saveRecord('accuracy', this.state.accuracy.score);
alert(`Время вышло! Счет: ${this.state.accuracy.score}`);
this.toMenu();
},
// === ИГРА 2: РЕАКЦИЯ ===
startReaction() {
this.showScreen('game-reaction');
this.state.reaction.step = 0;
this.state.reaction.results = [];
this.state.reaction.state = 'idle';
this.updateReactionUI();
},
updateReactionUI() {
document.getElementById('react-rec').innerText = this.records.reaction || '---';
},
handleReactionClick(e) {
if(e) e.preventDefault();
const zone = document.getElementById('reaction-zone');
const msg = document.getElementById('reaction-msg');
const sub = document.getElementById('reaction-submsg');
const st = this.state.reaction;
if (st.state === 'idle' || st.state === 'result') {
if (st.step >= 5) { this.endReaction(); return; }
st.state = 'waiting';
zone.className = 'react-waiting';
msg.innerText = 'Жди...'; sub.innerText = '';
const delay = 2000 + Math.random() * 3000;
st.timeout = setTimeout(() => {
st.state = 'ready';
st.startTime = Date.now();
zone.className = 'react-go';
msg.innerText = 'ЖМИ!';
}, delay);
return;
}
if (st.state === 'waiting') {
clearTimeout(st.timeout);
st.state = 'result';
zone.className = '';
zone.style.backgroundColor = '#ffa500';
msg.innerText = 'Фальстарт!';
sub.innerText = 'Нажми, чтобы начать заново';
st.step = 0; st.results = [];
return;
}
if (st.state === 'ready') {
const time = Date.now() - st.startTime;
st.results.push(time);
st.step++;
st.state = 'result';
zone.className = '';
if(st.step < 5) {
msg.innerText = `${time} мс`;
sub.innerText = `Нажми для след. (${st.step}/5)`;
} else {
this.endReaction();
}
}
},
endReaction() {
const res = this.state.reaction.results;
if(res.length === 0) return;
const avg = Math.round(res.reduce((a,b)=>a+b,0) / res.length);
this.saveRecord('reaction', avg);
document.getElementById('reaction-msg').innerText = `ИТОГ: ${avg} мс`;
document.getElementById('reaction-submsg').innerText = `Попытки: ${res.join(', ')}`;
this.state.reaction.state = 'finished';
},
// === ИГРА 3: ЦВЕТ ===
startColor() {
this.showScreen('game-color');
this.state.color.score = 0;
this.state.color.time = this.config.timeLimit;
this.state.color.active = true;
document.getElementById('col-score').innerText = '0';
document.getElementById('col-time').innerText = this.config.timeLimit;
document.getElementById('col-rec').innerText = this.records.color;
this.nextColorRound();
this.state.color.timer = setInterval(() => {
this.state.color.time--;
document.getElementById('col-time').innerText = this.state.color.time;
if (this.state.color.time <= 0) this.endColor();
}, 1000);
},
nextColorRound() {
if (!this.state.color.active) return;
const wordObj = this.colorsList[Math.floor(Math.random() * this.colorsList.length)];
const shouldMatch = Math.random() > 0.5;
let finalColorHex;
if(shouldMatch) {
finalColorHex = wordObj.hex;
} else {
const others = this.colorsList.filter(c => c.name !== wordObj.name);
finalColorHex = others[Math.floor(Math.random() * others.length)].hex;
}
this.state.color.isMatch = (wordObj.hex === finalColorHex);
const el = document.getElementById('color-word');
el.innerText = wordObj.name;
el.style.color = finalColorHex;
},
checkColor(userSaidYes) {
if (!this.state.color.active) return;
if (userSaidYes === this.state.color.isMatch) {
this.state.color.score++;
} else {
this.state.color.score = Math.max(0, this.state.color.score - 1);
}
document.getElementById('col-score').innerText = this.state.color.score;
this.nextColorRound();
},
endColor() {
this.state.color.active = false;
clearInterval(this.state.color.timer);
this.saveRecord('color', this.state.color.score);
alert(`Время вышло! Счет: ${this.state.color.score}`);
this.toMenu();
}
};
window.onload = () => Game.init();
</script>
</body>
</html>
//JAVA SCRIPT
document.addEventListener('mousemove', (e) => {
const crosshair = document.getElementById('global-crosshair');
crosshair.style.left = e.clientX + 'px';
crosshair.style.top = e.clientY + 'px';
});
showCrosshair(type)
{
const crosshair = document.getElementById('global-crosshair');
crosshair.style.display = 'block';
document.body.classList.add('aiming-mode');
const inner = crosshair.querySelector('div');
inner.className = 'cross-' + (type || this.config.crosshair);
};
this.showCrosshair(this.config.crosshair);