Files
HTML-Game/kripto-magnat.html

1033 lines
48 KiB
HTML
Raw Permalink 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', sans-serif;
background: linear-gradient(135deg, #0a0e27 0%, #1a1a3e 100%);
color: #fff; min-height: 100vh; padding: 20px;
}
.container { max-width: 1400px; margin: 0 auto; }
h1 { text-align: center; margin-bottom: 30px; font-size: 42px;
background: linear-gradient(135deg, #00f2fe, #4facfe);
-webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.status-bar {
background: rgba(0,0,0,0.4); border-radius: 15px;
padding: 20px; margin-bottom: 20px;
display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px;
}
.status-item { text-align: center; }
.status-label { font-size: 11px; opacity: 0.7; text-transform: uppercase; }
.status-value { font-size: 24px; font-weight: bold; color: #00f2fe; margin-top: 5px; }
.tabs { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; }
.tab {
flex: 1; min-width: 120px; padding: 15px; border: none; border-radius: 10px;
background: rgba(255,255,255,0.1); color: #fff; cursor: pointer;
font-size: 14px; font-weight: bold; transition: all 0.3s;
}
.tab.active { background: linear-gradient(135deg, #00f2fe, #4facfe); color: #000; }
.tab:hover:not(.active) { background: rgba(255,255,255,0.2); }
.tab-content { display: none; }
.tab-content.active { display: block; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 768px) { .grid { grid-template-columns: 1fr; } }
.card {
background: rgba(0,0,0,0.4); border-radius: 12px;
padding: 20px; border: 1px solid rgba(255,255,255,0.1);
}
.card h2 { margin-bottom: 15px; color: #00f2fe; font-size: 20px; }
.crypto-item {
background: rgba(255,255,255,0.05); padding: 15px;
border-radius: 8px; margin-bottom: 10px;
border: 1px solid rgba(255,255,255,0.1);
}
.crypto-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 10px; flex-wrap: wrap; gap: 10px;
}
.crypto-name { font-weight: bold; font-size: 18px; }
.crypto-price { color: #00f2fe; font-size: 20px; font-weight: bold; }
.crypto-change { font-size: 14px; padding: 3px 8px; border-radius: 5px; }
.crypto-change.up { color: #4caf50; background: rgba(76, 175, 80, 0.2); }
.crypto-change.down { color: #f44336; background: rgba(244, 67, 54, 0.2); }
.btn {
padding: 10px 20px; border: none; border-radius: 8px;
font-size: 14px; font-weight: bold; cursor: pointer;
color: white; transition: all 0.2s; margin: 5px;
}
.btn-buy { background: linear-gradient(135deg, #4caf50, #45a049); }
.btn-sell { background: linear-gradient(135deg, #f44336, #d32f2f); }
.btn-transfer { background: linear-gradient(135deg, #ff9800, #f57c00); }
.btn-action { background: linear-gradient(135deg, #9c27b0, #7b1fa2); }
.btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.3); }
.btn:active { transform: translateY(0); }
.quick-actions { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 10px; }
.quick-actions button { flex: 1; min-width: 80px; padding: 8px; font-size: 12px; }
.progress-bar {
background: rgba(255,255,255,0.1); border-radius: 10px;
height: 40px; overflow: hidden; margin: 10px 0;
}
.progress-fill {
height: 100%; background: linear-gradient(90deg, #00f2fe, #4facfe);
transition: width 0.5s; display: flex; align-items: center;
justify-content: center; font-weight: bold; font-size: 16px;
}
.shop-item {
background: rgba(255,255,255,0.05); padding: 15px;
border-radius: 8px; margin-bottom: 10px;
display: flex; justify-content: space-between; align-items: center;
border: 1px solid rgba(255,255,255,0.1); flex-wrap: wrap; gap: 10px;
}
.shop-item.owned { border-color: #4caf50; background: rgba(76, 175, 80, 0.1); }
.item-info { flex: 1; min-width: 200px; }
.item-name { font-weight: bold; font-size: 16px; margin-bottom: 5px; }
.item-desc { font-size: 13px; opacity: 0.8; }
.item-price { color: #ffd700; font-weight: bold; font-size: 18px; margin-right: 15px; }
.log {
background: rgba(0,0,0,0.6); border-radius: 8px;
padding: 15px; margin-top: 20px; font-family: monospace;
font-size: 12px; max-height: 250px; overflow-y: auto;
border: 1px solid rgba(255,255,255,0.1);
}
.log-entry { margin-bottom: 5px; padding: 3px 0; }
.log-entry.error { color: #f44336; }
.log-entry.success { color: #4caf50; }
.log-entry.info { color: #aaa; }
.log-entry.warning { color: #ff9800; }
.api-status {
position: fixed; top: 10px; right: 10px;
padding: 10px 20px; border-radius: 20px;
font-size: 12px; background: rgba(0, 0, 0, 0.8);
color: #aaa; z-index: 1000;
}
.api-status.connected { color: #4caf50; border: 1px solid #4caf50; }
.api-status.fallback { color: #ff9800; border: 1px solid #ff9800; }
.transfer-section {
background: rgba(255, 152, 0, 0.1); border: 1px solid rgba(255, 152, 0, 0.3);
border-radius: 8px; padding: 15px; margin-bottom: 15px;
}
.transfer-section h3 { color: #ff9800; margin-bottom: 10px; font-size: 16px; }
.transfer-input {
width: 100%; padding: 12px; border: 1px solid rgba(255,255,255,0.2);
border-radius: 6px; background: rgba(0,0,0,0.4); color: #fff;
margin-bottom: 10px; font-size: 14px;
}
.available-info {
background: rgba(0,255,0,0.1); padding: 8px; border-radius: 5px;
margin-bottom: 10px; font-size: 13px; color: #4caf50;
}
.construction-stage {
background: rgba(255,255,255,0.05); padding: 20px;
border-radius: 8px; margin-bottom: 15px;
border: 2px solid rgba(255,255,255,0.1);
}
.construction-stage.completed { border-color: #4caf50; background: rgba(76, 175, 80, 0.15); }
.construction-stage.active { border-color: #00f2fe; background: rgba(0, 242, 254, 0.1); }
.save-indicator {
position: fixed; bottom: 20px; right: 20px;
padding: 12px 24px; border-radius: 25px;
font-size: 14px; background: linear-gradient(135deg, #4caf50, #45a049);
color: white; z-index: 1000; opacity: 0; transition: opacity 0.3s;
box-shadow: 0 5px 20px rgba(76, 175, 80, 0.4);
}
.save-indicator.show { opacity: 1; }
.money-display {
font-size: 28px; font-weight: bold; color: #4caf50;
margin: 10px 0;
}
</style>
</head>
<body>
<div class="api-status" id="apiStatus">⏳ Загрузка...</div>
<div class="save-indicator" id="saveIndicator">💾 Сохранено!</div>
<div class="container">
<h1>💎 Крипто-Магнат</h1>
<div class="status-bar">
<div class="status-item">
<div class="status-label">💵 Наличные</div>
<div class="status-value" id="cash">$10 000</div>
</div>
<div class="status-item">
<div class="status-label">₿ Крипто</div>
<div class="status-value" id="cryptoValue">$0</div>
</div>
<div class="status-item">
<div class="status-label">🏢 Доход</div>
<div class="status-value" id="businessIncome">$0/сек</div>
</div>
<div class="status-item">
<div class="status-label">🏠 Дом</div>
<div class="status-value" id="houseLevel">0%</div>
</div>
</div>
<div class="tabs">
<button class="tab active" data-tab="crypto">₿ Биржа</button>
<button class="tab" data-tab="transfer">💸 Переводы</button>
<button class="tab" data-tab="house">🏠 Дом</button>
<button class="tab" data-tab="business">🏢 Бизнесы</button>
<button class="tab" data-tab="luxury">💎 Роскошь</button>
</div>
<!-- КРИПТО БИРЖА -->
<div class="tab-content active" id="crypto">
<div class="grid">
<div class="card">
<h2>📊 Криптовалюты</h2>
<div id="cryptoList"></div>
</div>
<div class="card">
<h2>💼 Мой портфель</h2>
<div id="portfolio"></div>
</div>
</div>
</div>
<!-- ПЕРЕВОДЫ -->
<div class="tab-content" id="transfer">
<div class="grid">
<div class="card">
<h2>📥 Ввод средств</h2>
<div class="transfer-section">
<h3>Из Кликера</h3>
<div class="available-info">Доступно: $<span id="clickerAvailable">0</span></div>
<input type="number" class="transfer-input" id="fromClicker" placeholder="Сумма перевода">
<button class="btn btn-transfer" onclick="transferFrom('clicker')">💸 Перевести в крипто</button>
</div>
<div class="transfer-section">
<h3>Из Инвестора</h3>
<div class="available-info">Доступно: $<span id="investorAvailable">0</span></div>
<input type="number" class="transfer-input" id="fromInvestor" placeholder="Сумма перевода">
<button class="btn btn-transfer" onclick="transferFrom('investor')">💸 Перевести в крипто</button>
</div>
</div>
<div class="card">
<h2>📤 Вывод средств</h2>
<div class="transfer-section">
<h3>В Кликер</h3>
<div class="available-info">Ваш баланс: $<span id="myCash1">0</span></div>
<input type="number" class="transfer-input" id="toClicker" placeholder="Сумма перевода">
<button class="btn btn-transfer" onclick="transferTo('clicker')">💸 Отправить</button>
</div>
<div class="transfer-section">
<h3>В Инвестор</h3>
<div class="available-info">Ваш баланс: $<span id="myCash2">0</span></div>
<input type="number" class="transfer-input" id="toInvestor" placeholder="Сумма перевода">
<button class="btn btn-transfer" onclick="transferTo('investor')">💸 Отправить</button>
</div>
</div>
</div>
</div>
<!-- СТРОИТЕЛЬСТВО ДОМА -->
<div class="tab-content" id="house">
<div class="card">
<h2>🏗️ Строительство дома</h2>
<div class="money-display">Ваши деньги: $<span id="cashForHouse">10000</span></div>
<div id="constructionStages"></div>
</div>
</div>
<!-- БИЗНЕСЫ -->
<div class="tab-content" id="business">
<div class="card">
<h2>🏢 Мои бизнесы</h2>
<div class="money-display">Ваши деньги: $<span id="cashForBusiness">10000</span></div>
<div id="businessList"></div>
</div>
</div>
<!-- РОСКОШЬ -->
<div class="tab-content" id="luxury">
<div class="card">
<h2>💎 Предметы роскоши</h2>
<div class="money-display">Ваши деньги: $<span id="cashForLuxury">10000</span></div>
<div id="luxuryList"></div>
</div>
</div>
<div class="log" id="log"></div>
</div>
<script>
// ═══════════════════════════════════════════════════════════
// КОНФИГУРАЦИЯ
// ══════════════════════════════════════════════════════════
const CLICKER_SAVE = 'main_save';
const CLICKER_TABLE = 'game_save';
const INVESTOR_SAVE = 'investor_save';
const INVESTOR_TABLE = 'investor_data';
const MY_SAVE = 'crypto_save';
const MY_TABLE = 'crypto_data';
const LOCAL_STORAGE_KEY = 'cryptoTycoonSave_v2';
let myState = {
cash: 10000,
portfolio: {},
businesses: {},
luxury: {},
house: { stage: 0, progress: 0 }
};
const cryptos = {
BTC: { name: 'Bitcoin', price: 50000, change: 0 },
ETH: { name: 'Ethereum', price: 3000, change: 0 },
DOGE: { name: 'Dogecoin', price: 0.15, change: 0 },
SOL: { name: 'Solana', price: 120, change: 0 },
SHIB: { name: 'Shiba Inu', price: 0.000025, change: 0 }
};
const houseStages = [
{ name: 'Фундамент', cost: 50000, time: 60 },
{ name: 'Стены', cost: 100000, time: 120 },
{ name: 'Крыша', cost: 75000, time: 90 },
{ name: 'Отделка', cost: 150000, time: 180 },
{ name: 'Мебель', cost: 200000, time: 240 }
];
const businesses = [
{ id: 'shop1', name: '🏪 Продуктовый магазин', cost: 25000, income: 50 },
{ id: 'shop2', name: '👔 Магазин одежды', cost: 75000, income: 150 },
{ id: 'shop3', name: '🍽️ Ресторан', cost: 200000, income: 400 },
{ id: 'shop4', name: '🚗 Автосалон', cost: 500000, income: 1000 },
{ id: 'shop5', name: '🏬 Торговый центр', cost: 2000000, income: 5000 }
];
const luxuryItems = [
{ id: 'watch1', name: '⌚ Часы Rolex', cost: 50000, desc: 'Статус +10%' },
{ id: 'car1', name: '🚗 Tesla Model S', cost: 150000, desc: 'Статус +20%' },
{ id: 'suit1', name: '👔 Костюм Brioni', cost: 75000, desc: 'Статус +15%' },
{ id: 'yacht1', name: '🛥️ Яхта', cost: 1000000, desc: 'Статус +50%' },
{ id: 'jet1', name: '✈️ Частный самолёт', cost: 5000000, desc: 'Статус +100%' }
];
let api = null;
const statusEl = document.getElementById('apiStatus');
const saveIndicator = document.getElementById('saveIndicator');
function updateStatus(text, type = '') {
statusEl.textContent = text;
statusEl.className = 'api-status ' + type;
}
function log(msg, type = 'info') {
const el = document.getElementById('log');
const entry = document.createElement('div');
entry.className = 'log-entry ' + type;
entry.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
el.insertBefore(entry, el.firstChild);
while (el.children.length > 50) el.removeChild(el.lastChild);
}
function showSaveIndicator() {
saveIndicator.classList.add('show');
setTimeout(() => saveIndicator.classList.remove('show'), 2000);
}
// ═══════════════════════════════════════════════════════════
// ИНИЦИАЛИЗАЦИЯ
// ═══════════════════════════════════════════════════════════
async function initGame() {
await loadState();
updateUI();
renderCrypto();
renderHouse();
renderBusinesses();
renderLuxury();
setInterval(tick, 1000);
setInterval(updateCryptoPrices, 3000);
setInterval(saveState, 10000);
log('Игра загружена!', 'success');
}
window.addEventListener('gameApiReady', async () => {
api = window.gameApi;
updateStatus('✓ API подключён', 'connected');
log('API подключён', 'success');
try {
await api.createTable(MY_TABLE, {
'key': 'TEXT', 'value': 'TEXT'
}, MY_SAVE, 'Данные крипто-магната');
} catch (e) {
log('Таблица уже существует');
}
await initGame();
});
setTimeout(() => {
if (!api) {
updateStatus('⚠️ localStorage', 'fallback');
log('Работаем с localStorage', 'warning');
initGame();
}
}, 3000);
// ═══════════════════════════════════════════════════════════
// СОХРАНЕНИЕ / ЗАГРУЗКА
// ═══════════════════════════════════════════════════════════
async function loadState() {
if (api) {
try {
const result = await api.selectData(MY_TABLE, { key: 'state' }, MY_SAVE);
if (result.data && result.data.length > 0) {
myState = JSON.parse(result.data[0].value);
log('Загружено из API', 'success');
return;
}
} catch (e) {
log('API ошибка: ' + e.message, 'error');
}
}
const saved = localStorage.getItem(LOCAL_STORAGE_KEY);
if (saved) {
try {
myState = JSON.parse(saved);
log('Загружено из localStorage', 'success');
} catch (e) {
log('Ошибка загрузки', 'error');
}
}
}
async function saveState() {
if (api) {
try {
await api.deleteData(MY_TABLE, { key: 'state' }, MY_SAVE);
await api.insertData(MY_TABLE, {
key: 'state',
value: JSON.stringify(myState),
updated_at: new Date().toISOString()
}, MY_SAVE);
showSaveIndicator();
return;
} catch (e) {
log('API save error', 'error');
}
}
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(myState));
showSaveIndicator();
}
// ═══════════════════════════════════════════════════════════
// КРИПТО БИРЖА
// ═══════════════════════════════════════════════════════════
function updateCryptoPrices() {
for (let symbol in cryptos) {
const change = (Math.random() - 0.5) * 0.1;
cryptos[symbol].change = change;
cryptos[symbol].price *= (1 + change);
if (cryptos[symbol].price < 0.000001) cryptos[symbol].price = 0.000001;
}
renderCrypto();
updateUI();
}
function renderCrypto() {
const list = document.getElementById('cryptoList');
list.innerHTML = '';
for (let symbol in cryptos) {
const crypto = cryptos[symbol];
const changeClass = crypto.change >= 0 ? 'up' : 'down';
const changeSign = crypto.change >= 0 ? '+' : '';
const priceDisplay = crypto.price < 1 ? crypto.price.toFixed(6) : crypto.price.toFixed(2);
const owned = myState.portfolio[symbol] || 0;
const ownedValue = owned * crypto.price;
const div = document.createElement('div');
div.className = 'crypto-item';
div.innerHTML = `
<div class="crypto-header">
<div>
<div class="crypto-name">${symbol}</div>
<div style="font-size: 12px; opacity: 0.7;">${crypto.name}</div>
</div>
<div style="text-align: right;">
<div class="crypto-price">$${priceDisplay}</div>
<span class="crypto-change ${changeClass}">${changeSign}${(crypto.change * 100).toFixed(2)}%</span>
</div>
</div>
${owned > 0 ? `
<div style="margin: 10px 0; padding: 10px; background: rgba(0,242,254,0.1); border-radius: 5px;">
У вас: ${owned.toFixed(6)} ${symbol}$${ownedValue.toFixed(2)}
</div>
` : ''}
<div class="quick-actions">
<button class="btn btn-buy" onclick="buyCrypto('${symbol}', 100)">Купить на $100</button>
<button class="btn btn-buy" onclick="buyCrypto('${symbol}', 1000)">Купить на $1000</button>
<button class="btn btn-buy" onclick="buyCrypto('${symbol}', 10000)">Купить на $10000</button>
${owned > 0 ? `<button class="btn btn-sell" onclick="sellCrypto('${symbol}', 1)">Продать всё</button>` : ''}
</div>
`;
list.appendChild(div);
}
renderPortfolio();
}
function renderPortfolio() {
const portfolio = document.getElementById('portfolio');
let totalValue = 0;
let hasItems = false;
for (let symbol in myState.portfolio) {
const amount = myState.portfolio[symbol];
if (amount < 0.000001) continue;
hasItems = true;
const value = amount * cryptos[symbol].price;
totalValue += value;
const div = document.createElement('div');
div.className = 'crypto-item';
div.innerHTML = `
<div class="crypto-header">
<div>
<div class="crypto-name">${symbol}</div>
<div style="font-size: 13px;">${amount.toFixed(6)} шт.</div>
</div>
<div style="text-align: right;">
<div class="crypto-price">$${value.toFixed(2)}</div>
</div>
</div>
<button class="btn btn-sell" style="width: 100%; margin: 10px 0 0 0;"
onclick="sellCrypto('${symbol}', 1)">
Продать за $${value.toFixed(2)}
</button>
`;
portfolio.appendChild(div);
}
if (!hasItems) {
portfolio.innerHTML = '<div style="text-align:center;opacity:0.5;padding:30px">Портфель пуст<br>Купите криптовалюту!</div>';
}
document.getElementById('cryptoValue').textContent = '$' + Math.floor(totalValue).toLocaleString();
}
function buyCrypto(symbol, amountUsd) {
if (myState.cash < amountUsd) {
log(`❌ Недостаточно денег! Есть $${Math.floor(myState.cash)}, нужно $${amountUsd}`, 'error');
return;
}
const price = cryptos[symbol].price;
const amount = amountUsd / price;
myState.cash -= amountUsd;
myState.portfolio[symbol] = (myState.portfolio[symbol] || 0) + amount;
log(`✅ Куплено ${amount.toFixed(6)} ${symbol} за $${amountUsd}`, 'success');
saveState();
renderCrypto();
updateUI();
}
function sellCrypto(symbol, fraction) {
const amount = myState.portfolio[symbol];
if (!amount || amount < 0.000001) {
log(`У вас нет ${symbol}`, 'error');
return;
}
const sellAmount = amount * fraction;
const revenue = sellAmount * cryptos[symbol].price;
myState.cash += revenue;
myState.portfolio[symbol] -= sellAmount;
if (myState.portfolio[symbol] < 0.000001) {
delete myState.portfolio[symbol];
}
log(`✅ Продано ${sellAmount.toFixed(6)} ${symbol} за $${revenue.toFixed(2)}`, 'success');
saveState();
renderCrypto();
updateUI();
}
// ═══════════════════════════════════════════════════════════
// ПЕРЕВОДЫ
// ═══════════════════════════════════════════════════════════
async function transferFrom(source) {
if (!api) {
log('❌ API недоступен для переводов', 'error');
return;
}
const input = document.getElementById(source === 'clicker' ? 'fromClicker' : 'fromInvestor');
const amount = parseFloat(input.value);
if (!amount || amount <= 0) {
log('❌ Введите корректную сумму', 'error');
return;
}
try {
let sourceTable, sourceSave, sourceKey;
if (source === 'clicker') {
sourceTable = CLICKER_TABLE;
sourceSave = CLICKER_SAVE;
sourceKey = 'game_state';
} else {
sourceTable = INVESTOR_TABLE;
sourceSave = INVESTOR_SAVE;
sourceKey = 'state';
}
const result = await api.selectData(sourceTable, { key: sourceKey }, sourceSave, {}, source);
if (!result.data || result.data.length === 0) {
log('❌ Источник не найден', 'error');
return;
}
const state = JSON.parse(result.data[0].value);
const available = source === 'clicker' ? state.score : state.capital;
if (available < amount) {
log(`❌ Недостаточно в источнике (есть ${Math.floor(available)}, нужно ${amount})`, 'error');
return;
}
if (source === 'clicker') {
state.score -= amount;
} else {
state.capital -= amount;
}
await api.deleteData(sourceTable, { key: sourceKey }, sourceSave, false, source);
await api.insertData(sourceTable, {
key: sourceKey,
value: JSON.stringify(state),
updated_at: new Date().toISOString()
}, sourceSave, source);
myState.cash += amount;
log(`✅ Получено $${amount.toLocaleString()} из ${source}`, 'success');
input.value = '';
saveState();
updateUI();
refreshAvailable();
} catch (e) {
log('❌ Ошибка перевода: ' + e.message, 'error');
}
}
async function transferTo(target) {
if (!api) {
log('❌ API недоступен для переводов', 'error');
return;
}
const input = document.getElementById(target === 'clicker' ? 'toClicker' : 'toInvestor');
const amount = parseFloat(input.value);
if (!amount || amount <= 0) {
log('❌ Введите корректную сумму', 'error');
return;
}
if (myState.cash < amount) {
log(`❌ Недостаточно средств (есть $${Math.floor(myState.cash)}, нужно $${amount})`, 'error');
return;
}
try {
let targetTable, targetSave, targetKey;
if (target === 'clicker') {
targetTable = CLICKER_TABLE;
targetSave = CLICKER_SAVE;
targetKey = 'game_state';
} else {
targetTable = INVESTOR_TABLE;
targetSave = INVESTOR_SAVE;
targetKey = 'state';
}
const result = await api.selectData(targetTable, { key: targetKey }, targetSave, {}, target);
if (!result.data || result.data.length === 0) {
log('❌ Цель не найдена', 'error');
return;
}
const state = JSON.parse(result.data[0].value);
if (target === 'clicker') {
state.score += amount;
} else {
state.capital += amount;
}
await api.deleteData(targetTable, { key: targetKey }, targetSave, false, target);
await api.insertData(targetTable, {
key: targetKey,
value: JSON.stringify(state),
updated_at: new Date().toISOString()
}, targetSave, target);
myState.cash -= amount;
log(`✅ Отправлено $${amount.toLocaleString()} в ${target}`, 'success');
input.value = '';
saveState();
updateUI();
} catch (e) {
log('❌ Ошибка: ' + e.message, 'error');
}
}
async function refreshAvailable() {
if (!api) return;
try {
const clickerResult = await api.selectData(CLICKER_TABLE, { key: 'game_state' }, CLICKER_SAVE, {}, 'clicker');
if (clickerResult.data && clickerResult.data.length > 0) {
const state = JSON.parse(clickerResult.data[0].value);
document.getElementById('clickerAvailable').textContent = Math.floor(state.score || 0).toLocaleString();
}
const investorResult = await api.selectData(INVESTOR_TABLE, { key: 'state' }, INVESTOR_SAVE, {}, 'investor');
if (investorResult.data && investorResult.data.length > 0) {
const state = JSON.parse(investorResult.data[0].value);
document.getElementById('investorAvailable').textContent = Math.floor(state.capital || 0).toLocaleString();
}
} catch (e) {
console.error(e);
}
}
// ═══════════════════════════════════════════════════════════
// СТРОИТЕЛЬСТВО ДОМА
// ═══════════════════════════════════════════════════════════
function renderHouse() {
const container = document.getElementById('constructionStages');
container.innerHTML = '';
houseStages.forEach((stage, index) => {
const isCompleted = myState.house.stage > index;
const isActive = myState.house.stage === index;
const progress = isActive ? myState.house.progress : (isCompleted ? 100 : 0);
const canAfford = myState.cash >= stage.cost;
const div = document.createElement('div');
div.className = `construction-stage ${isCompleted ? 'completed' : ''} ${isActive ? 'active' : ''}`;
let html = `
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; flex-wrap: wrap; gap: 10px;">
<div>
<div style="font-weight: bold; font-size: 18px;">${index + 1}. ${stage.name}</div>
<div style="font-size: 13px; opacity: 0.8; margin-top: 5px;">
💰 Стоимость: $${stage.cost.toLocaleString()} | ⏱️ Время: ${stage.time}с
</div>
</div>
${isActive && !isCompleted ? `
<button class="btn btn-action" onclick="buildStage()"
${!canAfford && progress === 0 ? 'disabled' : ''}
style="${!canAfford && progress === 0 ? 'opacity: 0.5;' : ''}">
${progress > 0 ? '⚡ Продолжить' : '🏗️ Начать строить'}
</button>
` : ''}
${isCompleted ? '<div style="color: #4caf50; font-weight: bold; font-size: 20px;">✅ Готово</div>' : ''}
</div>
`;
if (isActive) {
html += `
<div class="progress-bar">
<div class="progress-fill" style="width: ${progress}%">${Math.floor(progress)}%</div>
</div>
`;
}
div.innerHTML = html;
container.appendChild(div);
});
if (myState.house.stage >= houseStages.length) {
const div = document.createElement('div');
div.className = 'construction-stage completed';
div.innerHTML = '<div style="text-align: center; font-size: 20px; color: #4caf50;">🎉 Дом полностью построен!</div>';
container.appendChild(div);
}
}
function buildStage() {
if (myState.house.stage >= houseStages.length) {
log('🏠 Дом уже построен!', 'info');
return;
}
const stage = houseStages[myState.house.stage];
if (myState.house.progress === 0) {
if (myState.cash < stage.cost) {
log(`❌ Недостаточно денег! Есть $${Math.floor(myState.cash)}, нужно $${stage.cost}`, 'error');
return;
}
myState.cash -= stage.cost;
log(`💵 Списано $${stage.cost.toLocaleString()} за ${stage.name}`, 'info');
}
myState.house.progress += 10;
if (myState.house.progress >= 100) {
myState.house.stage++;
myState.house.progress = 0;
log(`${stage.name} построен!`, 'success');
} else {
log(`🏗️ ${stage.name}: ${Math.floor(myState.house.progress)}%`, 'info');
}
saveState();
renderHouse();
updateUI();
}
// ═══════════════════════════════════════════════════════════
// БИЗНЕСЫ
// ═══════════════════════════════════════════════════════════
function renderBusinesses() {
const container = document.getElementById('businessList');
container.innerHTML = '';
businesses.forEach(biz => {
const owned = myState.businesses[biz.id];
const canAfford = myState.cash >= biz.cost;
const div = document.createElement('div');
div.className = `shop-item ${owned ? 'owned' : ''}`;
let html = `
<div class="item-info">
<div class="item-name">${biz.name}</div>
<div class="item-desc">
💰 Цена: $${biz.cost.toLocaleString()}<br>
📈 Доход: $${biz.income}/сек
${owned ? `<br>👥 Работников: ${myState.businesses[biz.id].workers} | ⚡ Эффективность: x${myState.businesses[biz.id].efficiency.toFixed(1)}` : ''}
</div>
</div>
<div class="item-price">$${biz.cost.toLocaleString()}</div>
`;
if (owned) {
html += `<button class="btn btn-action" onclick="manageBusiness('${biz.id}')">⚙️ Управление</button>`;
} else {
html += `<button class="btn btn-buy" onclick="buyBusiness('${biz.id}')" ${!canAfford ? 'disabled' : ''}>🛒 Купить</button>`;
}
div.innerHTML = html;
container.appendChild(div);
});
}
function buyBusiness(id) {
const biz = businesses.find(b => b.id === id);
if (myState.cash < biz.cost) {
log(`❌ Недостаточно денег! Есть $${Math.floor(myState.cash)}, нужно $${biz.cost}`, 'error');
return;
}
myState.cash -= biz.cost;
myState.businesses[id] = { workers: 1, efficiency: 1 };
log(`✅ Куплен бизнес: ${biz.name}`, 'success');
saveState();
renderBusinesses();
updateUI();
}
function manageBusiness(id) {
const biz = businesses.find(b => b.id === id);
const data = myState.businesses[id];
const hireCost = Math.floor(biz.cost / 10);
const upgradeCost = Math.floor(biz.cost / 5);
const currentIncome = biz.income * data.workers * data.efficiency;
const action = prompt(
`📊 ${biz.name}\n\n` +
`📈 Текущий доход: $${currentIncome.toFixed(0)}/сек\n` +
`👥 Работников: ${data.workers}\n` +
`⚡ Эффективность: x${data.efficiency.toFixed(1)}\n\n` +
`Выберите действие:\n` +
`1️⃣ Нанять работника ($${hireCost.toLocaleString()})\n` +
`2️⃣ Уволить работника\n` +
`3️⃣ Улучшить эффективность x1.5 ($${upgradeCost.toLocaleString()})`
);
if (action === '1') {
if (myState.cash < hireCost) {
log(`❌ Недостаточно денег для найма ($${hireCost})`, 'error');
return;
}
myState.cash -= hireCost;
data.workers++;
log(`✅ Нанят работник в ${biz.name}`, 'success');
} else if (action === '2') {
if (data.workers > 1) {
data.workers--;
log(`👋 Работник уволен из ${biz.name}`, 'info');
} else {
log('❌ Нельзя уволить последнего работника', 'error');
}
} else if (action === '3') {
if (myState.cash < upgradeCost) {
log(`❌ Недостаточно денег для улучшения ($${upgradeCost})`, 'error');
return;
}
myState.cash -= upgradeCost;
data.efficiency *= 1.5;
log(`✅ Эффективность ${biz.name} улучшена до x${data.efficiency.toFixed(1)}`, 'success');
}
saveState();
renderBusinesses();
updateUI();
}
// ═══════════════════════════════════════════════════════════
// РОСКОШЬ
// ═══════════════════════════════════════════════════════════
function renderLuxury() {
const container = document.getElementById('luxuryList');
container.innerHTML = '';
luxuryItems.forEach(item => {
const owned = myState.luxury[item.id];
const canAfford = myState.cash >= item.cost;
const div = document.createElement('div');
div.className = `shop-item ${owned ? 'owned' : ''}`;
let html = `
<div class="item-info">
<div class="item-name">${item.name}</div>
<div class="item-desc">${item.desc}</div>
</div>
<div class="item-price">$${item.cost.toLocaleString()}</div>
`;
if (owned) {
html += '<button class="btn btn-action" disabled>✅ Куплено</button>';
} else {
html += `<button class="btn btn-buy" onclick="buyLuxury('${item.id}')" ${!canAfford ? 'disabled' : ''}>🛒 Купить</button>`;
}
div.innerHTML = html;
container.appendChild(div);
});
}
function buyLuxury(id) {
const item = luxuryItems.find(i => i.id === id);
if (myState.luxury[id]) {
log('❌ У вас уже есть этот предмет', 'error');
return;
}
if (myState.cash < item.cost) {
log(`❌ Недостаточно денег! Есть $${Math.floor(myState.cash)}, нужно $${item.cost}`, 'error');
return;
}
myState.cash -= item.cost;
myState.luxury[id] = true;
log(`✅ Куплено: ${item.name}`, 'success');
saveState();
renderLuxury();
updateUI();
}
// ═══════════════════════════════════════════════════════════
// ИГРОВОЙ ЦИКЛ
// ═══════════════════════════════════════════════════════════
function tick() {
let totalIncome = 0;
for (let id in myState.businesses) {
const biz = businesses.find(b => b.id === id);
const data = myState.businesses[id];
const income = biz.income * data.workers * data.efficiency;
totalIncome += income;
}
if (totalIncome > 0) {
myState.cash += totalIncome;
}
if (myState.house.stage < houseStages.length && myState.house.progress > 0) {
const stage = houseStages[myState.house.stage];
myState.house.progress += (100 / stage.time);
if (myState.house.progress >= 100) {
myState.house.stage++;
myState.house.progress = 0;
log(`${stage.name} завершён!`, 'success');
renderHouse();
}
}
updateUI();
}
function updateUI() {
document.getElementById('cash').textContent = '$' + Math.floor(myState.cash).toLocaleString();
document.getElementById('cashForHouse').textContent = Math.floor(myState.cash).toLocaleString();
document.getElementById('cashForBusiness').textContent = Math.floor(myState.cash).toLocaleString();
document.getElementById('cashForLuxury').textContent = Math.floor(myState.cash).toLocaleString();
document.getElementById('myCash1').textContent = Math.floor(myState.cash).toLocaleString();
document.getElementById('myCash2').textContent = Math.floor(myState.cash).toLocaleString();
let totalIncome = 0;
for (let id in myState.businesses) {
const biz = businesses.find(b => b.id === id);
const data = myState.businesses[id];
totalIncome += biz.income * data.workers * data.efficiency;
}
document.getElementById('businessIncome').textContent = '$' + Math.floor(totalIncome).toLocaleString() + '/сек';
const housePercent = Math.floor((myState.house.stage / houseStages.length) * 100);
document.getElementById('houseLevel').textContent = housePercent + '%';
}
// ═══════════════════════════════════════════════════════════
// ВКЛАДКИ
// ═══════════════════════════════════════════════════════════
document.querySelectorAll('.tab').forEach(tab => {
tab.onclick = () => {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
tab.classList.add('active');
document.getElementById(tab.dataset.tab).classList.add('active');
if (tab.dataset.tab === 'transfer') refreshAvailable();
};
});
// Запуск игры
updateUI();
</script>
</body>
</html>