From 445ec0aeb161d0b2b08a11658079760c74aff349 Mon Sep 17 00:00:00 2001 From: KoDer Date: Sat, 20 Jun 2026 00:56:35 +0700 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20kripto-magnat.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kripto-magnat.html | 647 ++++++++++++++++++++++++--------------------- 1 file changed, 349 insertions(+), 298 deletions(-) diff --git a/kripto-magnat.html b/kripto-magnat.html index daad20e..aa0e4e9 100644 --- a/kripto-magnat.html +++ b/kripto-magnat.html @@ -14,31 +14,25 @@ .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; - text-shadow: 0 0 30px rgba(79, 172, 254, 0.5); } + -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; - backdrop-filter: blur(10px); border: 1px solid rgba(79, 172, 254, 0.2); } .status-item { text-align: center; } - .status-label { font-size: 11px; opacity: 0.7; text-transform: uppercase; letter-spacing: 1px; } + .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; - background: rgba(0,0,0,0.3); padding: 10px; border-radius: 12px; - flex-wrap: wrap; - } + .tabs { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; } .tab { - flex: 1; min-width: 100px; padding: 12px; border: none; border-radius: 8px; - background: rgba(255,255,255,0.05); color: #fff; cursor: pointer; + 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); } - .tab:hover:not(.active) { background: rgba(255,255,255,0.1); } + .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; } @@ -48,97 +42,82 @@ .card { background: rgba(0,0,0,0.4); border-radius: 12px; - padding: 20px; backdrop-filter: blur(10px); - border: 1px solid rgba(255,255,255,0.1); + 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.05); + border: 1px solid rgba(255,255,255,0.1); } .crypto-header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 10px; - } - .crypto-name { font-weight: bold; font-size: 16px; } - .crypto-price { color: #00f2fe; font-size: 18px; font-weight: bold; } - .crypto-change { font-size: 14px; margin-left: 10px; } - .crypto-change.up { color: #4caf50; } - .crypto-change.down { color: #f44336; } - .crypto-buttons { - display: flex; gap: 5px; flex-wrap: wrap; - } - .crypto-buttons button { - flex: 1; min-width: 60px; padding: 8px 5px; + 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: 8px 16px; border: none; border-radius: 6px; - font-size: 13px; font-weight: bold; cursor: pointer; - color: white; transition: all 0.2s; + 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:not(:disabled) { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.3); } - .btn:disabled { opacity: 0.4; cursor: not-allowed; } + .btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.3); } + .btn:active { transform: translateY(0); } - .quick-buy { - display: flex; gap: 5px; margin-top: 10px; - } - .quick-buy button { - flex: 1; padding: 6px; font-size: 11px; - background: rgba(76, 175, 80, 0.3); - } + .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: 30px; overflow: hidden; margin: 10px 0; - position: relative; + 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: 14px; + 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.05); - flex-wrap: wrap; gap: 10px; + 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: 150px; } + .item-info { flex: 1; min-width: 200px; } .item-name { font-weight: bold; font-size: 16px; margin-bottom: 5px; } - .item-desc { font-size: 12px; opacity: 0.7; } - .item-price { color: #ffd700; font-weight: bold; margin-right: 15px; } + .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.5); border-radius: 8px; - padding: 10px; margin-top: 20px; font-family: monospace; - font-size: 12px; max-height: 200px; overflow-y: auto; + 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: 3px; padding: 2px 0; } + .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; left: 10px; - padding: 8px 16px; border-radius: 20px; - font-size: 12px; background: rgba(0, 0, 0, 0.7); + 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; } - .api-status.error { color: #f44336; } - .api-status.fallback { color: #ff9800; } + .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); @@ -146,31 +125,40 @@ } .transfer-section h3 { color: #ff9800; margin-bottom: 10px; font-size: 16px; } .transfer-input { - width: 100%; padding: 10px; border: 1px solid rgba(255,255,255,0.2); - border-radius: 6px; background: rgba(0,0,0,0.3); color: #fff; + 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: 15px; - border-radius: 8px; margin-bottom: 10px; - border: 1px solid rgba(255,255,255,0.05); + 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.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: 10px; right: 10px; - padding: 8px 16px; border-radius: 20px; - font-size: 12px; background: rgba(76, 175, 80, 0.9); - color: white; z-index: 1000; - opacity: 0; transition: opacity 0.3s; + 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; + } -
⏳ API: ожидание...
+
⏳ Загрузка...
💾 Сохранено!
@@ -186,7 +174,7 @@
$0
-
🏢 Бизнесы
+
🏢 Доход
$0/сек
@@ -224,32 +212,30 @@

📥 Ввод средств

Из Кликера

-

- Доступно: 0 -

- - +
Доступно: $0
+ +

Из Инвестора

-

- Доступно: 0 -

- - +
Доступно: $0
+ +

📤 Вывод средств

В Кликер

- - +
Ваш баланс: $0
+ +

В Инвестор

- - +
Ваш баланс: $0
+ +
@@ -259,6 +245,7 @@

🏗️ Строительство дома

+
Ваши деньги: $10000
@@ -267,6 +254,7 @@

🏢 Мои бизнесы

+
Ваши деньги: $10000
@@ -275,6 +263,7 @@

💎 Предметы роскоши

+
Ваши деньги: $10000
@@ -292,7 +281,7 @@ const INVESTOR_TABLE = 'investor_data'; const MY_SAVE = 'crypto_save'; const MY_TABLE = 'crypto_data'; - const LOCAL_STORAGE_KEY = 'cryptoTycoonSave'; + const LOCAL_STORAGE_KEY = 'cryptoTycoonSave_v2'; let myState = { cash: 10000, @@ -319,11 +308,11 @@ ]; 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 } + { 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 = [ @@ -358,22 +347,9 @@ } // ═══════════════════════════════════════════════════════════ - // API ИНИЦИАЛИЗАЦИЯ + // ИНИЦИАЛИЗАЦИЯ // ═══════════════════════════════════════════════════════════ - 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, 'Данные крипто-магната'); - log('Таблица создана', 'success'); - } catch (e) { - log('Таблица уже существует'); - } - + async function initGame() { await loadState(); updateUI(); renderCrypto(); @@ -384,27 +360,36 @@ 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('⚠️ API недоступен, используем localStorage', 'fallback'); - log('API недоступен, работаем с localStorage', 'warning'); - loadState(); - updateUI(); - renderCrypto(); - renderHouse(); - renderBusinesses(); - renderLuxury(); - - setInterval(tick, 1000); - setInterval(updateCryptoPrices, 3000); - setInterval(saveState, 10000); + updateStatus('⚠️ localStorage', 'fallback'); + log('Работаем с localStorage', 'warning'); + initGame(); } - }, 5000); + }, 3000); // ═══════════════════════════════════════════════════════════ - // СОХРАНЕНИЕ / ЗАГРУЗКА (С FALLBACK НА LOCALSTORAGE!) + // СОХРАНЕНИЕ / ЗАГРУЗКА // ═══════════════════════════════════════════════════════════ async function loadState() { if (api) { @@ -412,22 +397,21 @@ 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'); + log('Загружено из API', 'success'); return; } } catch (e) { - log('Ошибка загрузки API: ' + e.message, 'error'); + log('API ошибка: ' + e.message, 'error'); } } - // Fallback на localStorage const saved = localStorage.getItem(LOCAL_STORAGE_KEY); if (saved) { try { myState = JSON.parse(saved); - log('Состояние загружено из localStorage', 'success'); + log('Загружено из localStorage', 'success'); } catch (e) { - log('Ошибка загрузки localStorage', 'error'); + log('Ошибка загрузки', 'error'); } } } @@ -444,34 +428,16 @@ showSaveIndicator(); return; } catch (e) { - log('Ошибка сохранения API: ' + e.message, 'error'); + log('API save error', 'error'); } } - // Fallback на localStorage - try { - localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(myState)); - showSaveIndicator(); - } catch (e) { - log('Ошибка сохранения localStorage: ' + e.message, 'error'); - } + localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(myState)); + showSaveIndicator(); } - // Автосохранение при закрытии страницы - window.addEventListener('beforeunload', () => { - if (api) { - navigator.sendBeacon('/api/save', JSON.stringify({ - table: MY_TABLE, - key: 'state', - value: JSON.stringify(myState), - save: MY_SAVE - })); - } - localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(myState)); - }); - // ═══════════════════════════════════════════════════════════ - // КРИПТО БИРЖА (ИСПРАВЛЕНО!) + // КРИПТО БИРЖА // ═══════════════════════════════════════════════════════════ function updateCryptoPrices() { for (let symbol in cryptos) { @@ -487,39 +453,41 @@ 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 canBuy = myState.cash >= crypto.price; - const hasPortfolio = myState.portfolio[symbol] > 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; - list.innerHTML += ` -
-
-
-
${symbol}
-
${crypto.name}
-
-
- $${crypto.price < 1 ? crypto.price.toFixed(6) : crypto.price.toFixed(2)} - ${changeSign}${(crypto.change * 100).toFixed(2)}% -
+ const div = document.createElement('div'); + div.className = 'crypto-item'; + div.innerHTML = ` +
+
+
${symbol}
+
${crypto.name}
-
- - - - +
+
$${priceDisplay}
+ ${changeSign}${(crypto.change * 100).toFixed(2)}%
- ${hasPortfolio ? ` -
- У вас: ${myState.portfolio[symbol].toFixed(6)} ${symbol} - (≈$${(myState.portfolio[symbol] * crypto.price).toFixed(2)}) -
- ` : ''} +
+ ${owned > 0 ? ` +
+ У вас: ${owned.toFixed(6)} ${symbol} ≈ $${ownedValue.toFixed(2)} +
+ ` : ''} +
+ + + + ${owned > 0 ? `` : ''}
`; + list.appendChild(div); } renderPortfolio(); } @@ -527,71 +495,78 @@ function renderPortfolio() { const portfolio = document.getElementById('portfolio'); let totalValue = 0; - let html = ''; + 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; - html += ` -
-
+ + const div = document.createElement('div'); + div.className = 'crypto-item'; + div.innerHTML = ` +
+
${symbol}
+
${amount.toFixed(6)} шт.
+
+
$${value.toFixed(2)}
-
- ${amount.toFixed(6)} ${symbol} -
-
+ `; + portfolio.appendChild(div); } - if (html === '') { - html = '
Портфель пуст
'; + + if (!hasItems) { + portfolio.innerHTML = '
Портфель пуст
Купите криптовалюту!
'; } - portfolio.innerHTML = html; + document.getElementById('cryptoValue').textContent = '$' + Math.floor(totalValue).toLocaleString(); } - // Покупка на указанную сумму в долларах (ИСПРАВЛЕНО!) - function buyCryptoAmount(symbol, amountUsd) { - const price = cryptos[symbol].price; + function buyCrypto(symbol, amountUsd) { if (myState.cash < amountUsd) { - log(`Недостаточно средств (есть $${Math.floor(myState.cash)}, нужно $${amountUsd})`, 'error'); + 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'); + + log(`✅ Куплено ${amount.toFixed(6)} ${symbol} за $${amountUsd}`, 'success'); saveState(); renderCrypto(); updateUI(); } - // Покупка на процент от денег - function buyCryptoPercent(symbol, percent) { - const amountUsd = myState.cash * percent; - buyCryptoAmount(symbol, amountUsd); - } - - // Продажа процента от криптовалюты - function sellCryptoPercent(symbol, percent) { - if (!myState.portfolio[symbol] || myState.portfolio[symbol] < 0.000001) { - log(`У вас нет ${symbol}`, 'error'); + function sellCrypto(symbol, fraction) { + const amount = myState.portfolio[symbol]; + if (!amount || amount < 0.000001) { + log(`❌ У вас нет ${symbol}`, 'error'); return; } - const amount = myState.portfolio[symbol] * percent; - const revenue = amount * cryptos[symbol].price; + + const sellAmount = amount * fraction; + const revenue = sellAmount * cryptos[symbol].price; + myState.cash += revenue; - myState.portfolio[symbol] -= amount; + myState.portfolio[symbol] -= sellAmount; + if (myState.portfolio[symbol] < 0.000001) { delete myState.portfolio[symbol]; } - log(`Продано ${amount.toFixed(6)} ${symbol} за $${revenue.toFixed(2)}`, 'success'); + + log(`✅ Продано ${sellAmount.toFixed(6)} ${symbol} за $${revenue.toFixed(2)}`, 'success'); saveState(); renderCrypto(); updateUI(); @@ -601,10 +576,18 @@ // ПЕРЕВОДЫ // ═══════════════════════════════════════════════════════════ async function transferFrom(source) { - if (!api) { log('API недоступен для переводов', 'error'); return; } + 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; } + + if (!amount || amount <= 0) { + log('❌ Введите корректную сумму', 'error'); + return; + } try { let sourceTable, sourceSave, sourceKey; @@ -620,7 +603,7 @@ const result = await api.selectData(sourceTable, { key: sourceKey }, sourceSave, {}, source); if (!result.data || result.data.length === 0) { - log('Источник не найден', 'error'); + log('❌ Источник не найден', 'error'); return; } @@ -628,7 +611,7 @@ const available = source === 'clicker' ? state.score : state.capital; if (available < amount) { - log(`Недостаточно средств в источнике (есть ${Math.floor(available)}, нужно ${amount})`, 'error'); + log(`❌ Недостаточно в источнике (есть ${Math.floor(available)}, нужно ${amount})`, 'error'); return; } @@ -646,23 +629,32 @@ }, sourceSave, source); myState.cash += amount; - log(`Получено $${amount.toLocaleString()} из ${source}`, 'success'); + log(`✅ Получено $${amount.toLocaleString()} из ${source}`, 'success'); input.value = ''; saveState(); updateUI(); - await refreshAvailable(); + refreshAvailable(); } catch (e) { - log('Ошибка перевода: ' + e.message, 'error'); + log('❌ Ошибка перевода: ' + e.message, 'error'); } } async function transferTo(target) { - if (!api) { log('API недоступен для переводов', 'error'); return; } + 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 (!amount || amount <= 0) { + log('❌ Введите корректную сумму', 'error'); + return; + } + if (myState.cash < amount) { - log(`Недостаточно средств (есть $${Math.floor(myState.cash)}, нужно $${amount})`, 'error'); + log(`❌ Недостаточно средств (есть $${Math.floor(myState.cash)}, нужно $${amount})`, 'error'); return; } @@ -680,7 +672,7 @@ const result = await api.selectData(targetTable, { key: targetKey }, targetSave, {}, target); if (!result.data || result.data.length === 0) { - log('Цель не найдена', 'error'); + log('❌ Цель не найдена', 'error'); return; } @@ -699,12 +691,12 @@ }, targetSave, target); myState.cash -= amount; - log(`Отправлено $${amount.toLocaleString()} в ${target}`, 'success'); + log(`✅ Отправлено $${amount.toLocaleString()} в ${target}`, 'success'); input.value = ''; saveState(); updateUI(); } catch (e) { - log('Ошибка перевода: ' + e.message, 'error'); + log('❌ Ошибка: ' + e.message, 'error'); } } @@ -723,7 +715,7 @@ document.getElementById('investorAvailable').textContent = Math.floor(state.capital || 0).toLocaleString(); } } catch (e) { - log('Ошибка обновления: ' + e.message, 'error'); + console.error(e); } } @@ -733,55 +725,82 @@ 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; - container.innerHTML += ` -
-
-
-
${stage.name}
-
Стоимость: $${stage.cost.toLocaleString()} | Время: ${stage.time}с
+ const div = document.createElement('div'); + div.className = `construction-stage ${isCompleted ? 'completed' : ''} ${isActive ? 'active' : ''}`; + + let html = ` +
+
+
${index + 1}. ${stage.name}
+
+ 💰 Стоимость: $${stage.cost.toLocaleString()} | ⏱️ Время: ${stage.time}с
- ${isActive && !isCompleted ? ` - - ` : ''} - ${isCompleted ? '
✓ Готово
' : ''}
- ${isActive ? ` -
-
${Math.floor(progress)}%
-
+ ${isActive && !isCompleted ? ` + ` : ''} + ${isCompleted ? '
✅ Готово
' : ''}
`; + + if (isActive) { + html += ` +
+
${Math.floor(progress)}%
+
+ `; + } + + div.innerHTML = html; + container.appendChild(div); }); + + if (myState.house.stage >= houseStages.length) { + const div = document.createElement('div'); + div.className = 'construction-stage completed'; + div.innerHTML = '
🎉 Дом полностью построен!
'; + container.appendChild(div); + } } function buildStage() { if (myState.house.stage >= houseStages.length) { - log('Дом полностью построен!', 'success'); + log('🏠 Дом уже построен!', 'info'); return; } + const stage = houseStages[myState.house.stage]; - if (myState.cash < stage.cost) { - log(`Недостаточно средств (есть $${Math.floor(myState.cash)}, нужно $${stage.cost})`, 'error'); - return; - } + 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'); + log(`✅ ${stage.name} построен!`, 'success'); + } else { + log(`🏗️ ${stage.name}: ${Math.floor(myState.house.progress)}%`, 'info'); } + saveState(); renderHouse(); updateUI(); @@ -793,37 +812,49 @@ function renderBusinesses() { const container = document.getElementById('businessList'); container.innerHTML = ''; + businesses.forEach(biz => { const owned = myState.businesses[biz.id]; - container.innerHTML += ` -
-
-
${biz.name}
-
- Доход: $${biz.income}/сек - ${owned ? ` | Работников: ${myState.businesses[biz.id].workers} | Эффективность: x${myState.businesses[biz.id].efficiency.toFixed(1)}` : ''} -
+ const canAfford = myState.cash >= biz.cost; + + const div = document.createElement('div'); + div.className = `shop-item ${owned ? 'owned' : ''}`; + + let html = ` +
+
${biz.name}
+
+ 💰 Цена: $${biz.cost.toLocaleString()}
+ 📈 Доход: $${biz.income}/сек + ${owned ? `
👥 Работников: ${myState.businesses[biz.id].workers} | ⚡ Эффективность: x${myState.businesses[biz.id].efficiency.toFixed(1)}` : ''}
-
$${biz.cost.toLocaleString()}
-
+
$${biz.cost.toLocaleString()}
`; + + if (owned) { + html += ``; + } else { + html += ``; + } + + 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'); + log(`❌ Недостаточно денег! Есть $${Math.floor(myState.cash)}, нужно $${biz.cost}`, 'error'); return; } + myState.cash -= biz.cost; myState.businesses[id] = { workers: 1, efficiency: 1 }; - log(`Куплен бизнес: ${biz.name}`, 'success'); + + log(`✅ Куплен бизнес: ${biz.name}`, 'success'); saveState(); renderBusinesses(); updateUI(); @@ -831,46 +862,47 @@ 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` + - `Текущее состояние:\n` + - `- Работников: ${myState.businesses[id].workers}\n` + - `- Эффективность: x${myState.businesses[id].efficiency.toFixed(1)}\n` + - `- Доход: $${Math.floor(biz.income * myState.businesses[id].workers * myState.businesses[id].efficiency)}/сек\n\n` + - `Действия:\n` + - `1. Нанять работника ($${hireCost.toLocaleString()})\n` + - `2. Уволить работника (бесплатно)\n` + - `3. Улучшить эффективность x1.5 ($${upgradeCost.toLocaleString()})\n\n` + - `Введите номер действия:` + `📊 ${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; + if (myState.cash < hireCost) { + log(`❌ Недостаточно денег для найма ($${hireCost})`, 'error'); + return; } myState.cash -= hireCost; - myState.businesses[id].workers++; - log(`Нанят работник в ${biz.name}`, 'success'); + data.workers++; + log(`✅ Нанят работник в ${biz.name}`, 'success'); } else if (action === '2') { - if (myState.businesses[id].workers > 1) { - myState.businesses[id].workers--; - log(`Работник уволен из ${biz.name}`, 'info'); + if (data.workers > 1) { + data.workers--; + log(`👋 Работник уволен из ${biz.name}`, 'info'); } else { - log('Нельзя уволить последнего работника', 'error'); + log('❌ Нельзя уволить последнего работника', 'error'); } } else if (action === '3') { - if (myState.cash < upgradeCost) { - log(`Недостаточно средств (нужно $${upgradeCost})`, 'error'); - return; + if (myState.cash < upgradeCost) { + log(`❌ Недостаточно денег для улучшения ($${upgradeCost})`, 'error'); + return; } myState.cash -= upgradeCost; - myState.businesses[id].efficiency *= 1.5; - log(`Улучшена эффективность ${biz.name} до x${myState.businesses[id].efficiency.toFixed(1)}`, 'success'); + data.efficiency *= 1.5; + log(`✅ Эффективность ${biz.name} улучшена до x${data.efficiency.toFixed(1)}`, 'success'); } + saveState(); renderBusinesses(); updateUI(); @@ -882,38 +914,50 @@ function renderLuxury() { const container = document.getElementById('luxuryList'); container.innerHTML = ''; + luxuryItems.forEach(item => { const owned = myState.luxury[item.id]; - container.innerHTML += ` -
-
-
${item.name}
-
${item.desc}
-
-
$${item.cost.toLocaleString()}
- + const canAfford = myState.cash >= item.cost; + + const div = document.createElement('div'); + div.className = `shop-item ${owned ? 'owned' : ''}`; + + let html = ` +
+
${item.name}
+
${item.desc}
+
$${item.cost.toLocaleString()}
`; + + if (owned) { + html += ''; + } else { + html += ``; + } + + div.innerHTML = html; + container.appendChild(div); }); } function buyLuxury(id) { const item = luxuryItems.find(i => i.id === id); + if (myState.luxury[id]) { - log('У вас уже есть этот предмет', 'error'); + log('❌ У вас уже есть этот предмет', 'error'); return; } + if (myState.cash < item.cost) { - log(`Недостаточно средств (есть $${Math.floor(myState.cash)}, нужно $${item.cost})`, 'error'); + log(`❌ Недостаточно денег! Есть $${Math.floor(myState.cash)}, нужно $${item.cost}`, 'error'); return; } + myState.cash -= item.cost; myState.luxury[id] = true; - log(`Куплено: ${item.name}`, 'success'); + + log(`✅ Куплено: ${item.name}`, 'success'); saveState(); renderLuxury(); updateUI(); @@ -930,6 +974,7 @@ const income = biz.income * data.workers * data.efficiency; totalIncome += income; } + if (totalIncome > 0) { myState.cash += totalIncome; } @@ -940,7 +985,7 @@ if (myState.house.progress >= 100) { myState.house.stage++; myState.house.progress = 0; - log(`Этап "${stage.name}" завершён!`, 'success'); + log(`✅ ${stage.name} завершён!`, 'success'); renderHouse(); } } @@ -950,6 +995,11 @@ 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) { @@ -976,6 +1026,7 @@ }; }); + // Запуск игры updateUI();