Files
HTML-Game/clicker.html
T
2026-06-16 15:24:58 +03:00

379 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
color: #eee;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.game-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
max-width: 1200px;
width: 100%;
}
.clicker-section, .shop-section {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 40px;
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.shop-section { max-height: 80vh; overflow-y: auto; }
.stats { text-align: center; margin-bottom: 30px; }
.score {
font-size: 48px; font-weight: bold; color: #ffd700;
text-shadow: 0 0 20px rgba(255, 215, 0, 0.5); margin-bottom: 10px;
}
.per-click { font-size: 18px; color: #aaa; }
.per-second { font-size: 16px; color: #888; margin-top: 5px; }
.click-button {
width: 200px; height: 200px; border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none; cursor: pointer; font-size: 24px; color: white;
font-weight: bold; box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4);
transition: all 0.1s ease; display: block; margin: 0 auto;
}
.click-button:hover { transform: scale(1.05); }
.click-button:active { transform: scale(0.95); }
.shop-title {
font-size: 32px; margin-bottom: 20px;
text-align: center; color: #ffd700;
}
.upgrade-item {
background: rgba(255, 255, 255, 0.1);
border-radius: 15px; padding: 20px; margin-bottom: 15px;
transition: all 0.3s ease; border: 2px solid transparent;
}
.upgrade-item:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 215, 0, 0.3);
}
.upgrade-item.cant-afford { opacity: 0.5; }
.upgrade-header {
display: flex; justify-content: space-between;
align-items: center; margin-bottom: 10px;
}
.upgrade-name { font-size: 20px; font-weight: bold; }
.upgrade-level {
font-size: 14px; color: #aaa;
background: rgba(255, 255, 255, 0.1);
padding: 5px 10px; border-radius: 10px;
}
.upgrade-description { font-size: 14px; color: #ccc; margin-bottom: 15px; }
.upgrade-footer { display: flex; justify-content: space-between; align-items: center; }
.upgrade-cost { font-size: 18px; color: #ffd700; font-weight: bold; }
.buy-button {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border: none; padding: 10px 25px; border-radius: 10px;
color: white; font-weight: bold; cursor: pointer;
transition: all 0.3s ease; font-size: 16px;
}
.buy-button:hover:not(:disabled) { transform: scale(1.05); }
.buy-button:disabled { opacity: 0.5; cursor: not-allowed; }
.reset-button {
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
border: none; padding: 15px 30px; border-radius: 15px;
color: white; font-weight: bold; cursor: pointer;
font-size: 16px; margin-top: 20px; width: 100%;
}
.click-animation {
position: absolute; pointer-events: none;
font-size: 24px; font-weight: bold; color: #ffd700;
animation: float-up 1s ease-out forwards;
}
@keyframes float-up {
0% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(-100px); }
}
@media (max-width: 768px) {
.game-container { grid-template-columns: 1fr; }
}
.api-status {
position: fixed; top: 10px; left: 10px;
padding: 8px 16px; border-radius: 20px;
font-size: 12px; background: rgba(0, 0, 0, 0.7);
color: #aaa; z-index: 1000;
}
.api-status.connected { color: #4caf50; }
.api-status.error { color: #f44336; }
</style>
</head>
<body>
<div class="api-status" id="apiStatus">⏳ API: ожидание...</div>
<div class="game-container">
<div class="clicker-section">
<div class="stats">
<div class="score" id="score">0</div>
<div class="per-click">За клик: <span id="perClick">1</span></div>
<div class="per-second">В секунду: <span id="perSecond">0</span></div>
</div>
<button class="click-button" id="clickButton">КЛИК!</button>
</div>
<div class="shop-section">
<h2 class="shop-title">🛒 Магазин</h2>
<div id="upgradesContainer"></div>
<button class="reset-button" id="resetButton">🔄 Сбросить прогресс</button>
</div>
</div>
<script>
// ═══════════════════════════════════════════════════════════
// КОНФИГУРАЦИЯ
// ══════════════════════════════════════════════════════════
const MY_SAVE = 'main_save';
const TABLE_NAME = 'game_save';
let gameState = {
score: 0,
totalClicks: 0,
upgrades: {
clickPower: { level: 0, baseCost: 10, cost: 10, power: 1 },
autoClicker: { level: 0, baseCost: 50, cost: 50, power: 1 },
superClick: { level: 0, baseCost: 500, cost: 500, power: 10 },
megaAuto: { level: 0, baseCost: 2000, cost: 2000, power: 10 },
ultraClick: { level: 0, baseCost: 10000, cost: 10000, power: 100 },
hyperAuto: { level: 0, baseCost: 50000, cost: 50000, power: 100 }
}
};
const upgradeDescriptions = {
clickPower: 'Увеличивает доход за клик на 1',
autoClicker: 'Автоматически добавляет 1 в секунду',
superClick: 'Увеличивает доход за клик на 10',
megaAuto: 'Автоматически добавляет 10 в секунду',
ultraClick: 'Увеличивает доход за клик на 100',
hyperAuto: 'Автоматически добавляет 100 в секунду'
};
const upgradeNames = {
clickPower: '💪 Сила клика',
autoClicker: '🤖 Автокликер',
superClick: '⚡ Супер клик',
megaAuto: '🚀 Мега автокликер',
ultraClick: '🔥 Ультра клик',
hyperAuto: '💎 Гипер автокликер'
};
// ═══════════════════════════════════════════════════════════
// API КЛИЕНТ
// ═══════════════════════════════════════════════════════════
let api = null;
const statusEl = document.getElementById('apiStatus');
function updateStatus(text, type = '') {
statusEl.textContent = text;
statusEl.className = 'api-status ' + type;
}
async function waitForAPI() {
if (window.gameApi) {
api = window.gameApi;
updateStatus('✓ API подключён', 'connected');
return true;
}
return new Promise((resolve) => {
const timeout = setTimeout(() => {
updateStatus(' API недоступен, используем localStorage', 'error');
resolve(false);
}, 5000);
window.addEventListener('gameApiReady', () => {
clearTimeout(timeout);
api = window.gameApi;
updateStatus('✓ API подключён', 'connected');
resolve(true);
});
});
}
// ══════════════════════════════════════════════════════════
// СОХРАНЕНИЕ / ЗАГРУЗКА
// ═══════════════════════════════════════════════════════════
async function loadGame() {
if (api) {
try {
const result = await api.selectData(TABLE_NAME, { key: 'game_state' }, MY_SAVE);
if (result.data && result.data.length > 0) {
const savedState = JSON.parse(result.data[0].value);
gameState = { ...gameState, ...savedState };
for (let key in gameState.upgrades) {
const u = gameState.upgrades[key];
u.cost = Math.floor(u.baseCost * Math.pow(1.15, u.level));
}
console.log('Загружено через API');
return;
}
} catch (e) {
console.warn('API загрузка не удалась:', e.message);
}
}
const saved = localStorage.getItem('clickerGame');
if (saved) {
const savedState = JSON.parse(saved);
gameState = { ...gameState, ...savedState };
for (let key in gameState.upgrades) {
const u = gameState.upgrades[key];
u.cost = Math.floor(u.baseCost * Math.pow(1.15, u.level));
}
}
}
async function saveGame() {
if (api) {
try {
await api.deleteData(TABLE_NAME, { key: 'game_state' }, MY_SAVE);
await api.insertData(TABLE_NAME, {
key: 'game_state',
value: JSON.stringify(gameState),
updated_at: new Date().toISOString()
}, MY_SAVE);
return;
} catch (e) {
console.warn('API сохранение не удалось:', e.message);
}
}
localStorage.setItem('clickerGame', JSON.stringify(gameState));
}
async function resetGame() {
if (!confirm('Сбросить весь прогресс?')) return;
if (api) {
try {
await api.deleteData(TABLE_NAME, { key: 'game_state' }, MY_SAVE);
} catch (e) {
console.warn('API удаление не удалось:', e.message);
}
}
localStorage.removeItem('clickerGame');
location.reload();
}
// ═══════════════════════════════════════════════════════════
// ИГРОВАЯ ЛОГИКА
// ═══════════════════════════════════════════════════════════
function getClickPower() {
let power = 1;
power += gameState.upgrades.clickPower.level * gameState.upgrades.clickPower.power;
power += gameState.upgrades.superClick.level * gameState.upgrades.superClick.power;
power += gameState.upgrades.ultraClick.level * gameState.upgrades.ultraClick.power;
return power;
}
function getPerSecond() {
let perSecond = 0;
perSecond += gameState.upgrades.autoClicker.level * gameState.upgrades.autoClicker.power;
perSecond += gameState.upgrades.megaAuto.level * gameState.upgrades.megaAuto.power;
perSecond += gameState.upgrades.hyperAuto.level * gameState.upgrades.hyperAuto.power;
return perSecond;
}
function updateUI() {
document.getElementById('score').textContent = Math.floor(gameState.score).toLocaleString();
document.getElementById('perClick').textContent = getClickPower().toLocaleString();
document.getElementById('perSecond').textContent = getPerSecond().toLocaleString();
const container = document.getElementById('upgradesContainer');
container.innerHTML = '';
for (let key in gameState.upgrades) {
const upgrade = gameState.upgrades[key];
const canAfford = gameState.score >= upgrade.cost;
const item = document.createElement('div');
item.className = `upgrade-item ${canAfford ? '' : 'cant-afford'}`;
item.innerHTML = `
<div class="upgrade-header">
<div class="upgrade-name">${upgradeNames[key]}</div>
<div class="upgrade-level">Ур. ${upgrade.level}</div>
</div>
<div class="upgrade-description">${upgradeDescriptions[key]}</div>
<div class="upgrade-footer">
<div class="upgrade-cost">💰 ${upgrade.cost.toLocaleString()}</div>
<button class="buy-button" ${canAfford ? '' : 'disabled'}>Купить</button>
</div>
`;
item.querySelector('.buy-button').addEventListener('click', () => buyUpgrade(key));
container.appendChild(item);
}
}
function buyUpgrade(upgradeKey) {
const upgrade = gameState.upgrades[upgradeKey];
if (gameState.score >= upgrade.cost) {
gameState.score -= upgrade.cost;
upgrade.level++;
upgrade.cost = Math.floor(upgrade.baseCost * Math.pow(1.15, upgrade.level));
updateUI();
saveGame();
}
}
function handleClick(event) {
const power = getClickPower();
gameState.score += power;
gameState.totalClicks++;
const animation = document.createElement('div');
animation.className = 'click-animation';
animation.textContent = `+${power}`;
animation.style.left = `${event.clientX}px`;
animation.style.top = `${event.clientY}px`;
document.body.appendChild(animation);
setTimeout(() => animation.remove(), 1000);
updateUI();
}
function autoClick() {
const perSecond = getPerSecond();
if (perSecond > 0) {
gameState.score += perSecond / 10;
updateUI();
}
}
// ═══════════════════════════════════════════════════════════
// ИНИЦИАЛИЗАЦИЯ
// ═══════════════════════════════════════════════════════════
(async () => {
await waitForAPI();
if (api) {
try {
await api.createTable(TABLE_NAME, {
'key': 'TEXT',
'value': 'TEXT',
'updated_at': 'TEXT'
}, MY_SAVE, 'Таблица сохранений кликера');
} catch (e) {
console.log('Таблица уже существует или:', e.message);
}
}
await loadGame();
updateUI();
document.getElementById('clickButton').addEventListener('click', handleClick);
document.getElementById('resetButton').addEventListener('click', resetGame);
setInterval(autoClick, 100);
setInterval(saveGame, 30000);
})();
</script>
</body>
</html>