Добавить kripto-magnat.html
This commit is contained in:
@@ -0,0 +1,820 @@
|
||||
<!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;
|
||||
text-shadow: 0 0 30px rgba(79, 172, 254, 0.5); }
|
||||
|
||||
.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-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;
|
||||
}
|
||||
.tab {
|
||||
flex: 1; padding: 12px; border: none; border-radius: 8px;
|
||||
background: rgba(255,255,255,0.05); 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-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; backdrop-filter: blur(10px);
|
||||
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;
|
||||
display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 10px;
|
||||
align-items: center; border: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
.crypto-name { font-weight: bold; font-size: 16px; }
|
||||
.crypto-price { color: #00f2fe; font-size: 18px; font-weight: bold; }
|
||||
.crypto-change { font-size: 14px; }
|
||||
.crypto-change.up { color: #4caf50; }
|
||||
.crypto-change.down { color: #f44336; }
|
||||
|
||||
.btn {
|
||||
padding: 8px 16px; border: none; border-radius: 6px;
|
||||
font-size: 13px; font-weight: bold; cursor: pointer;
|
||||
color: white; transition: all 0.2s;
|
||||
}
|
||||
.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; }
|
||||
|
||||
.progress-bar {
|
||||
background: rgba(255,255,255,0.1); border-radius: 10px;
|
||||
height: 30px; overflow: hidden; margin: 10px 0;
|
||||
position: relative;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
.shop-item.owned { border-color: #4caf50; background: rgba(76, 175, 80, 0.1); }
|
||||
.item-info { flex: 1; }
|
||||
.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; }
|
||||
|
||||
.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;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
.log-entry { margin-bottom: 3px; padding: 2px 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);
|
||||
color: #aaa; z-index: 1000;
|
||||
}
|
||||
.api-status.connected { color: #4caf50; }
|
||||
.api-status.error { color: #f44336; }
|
||||
|
||||
.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: 10px; border: 1px solid rgba(255,255,255,0.2);
|
||||
border-radius: 6px; background: rgba(0,0,0,0.3); color: #fff;
|
||||
margin-bottom: 10px; font-size: 14px;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
.construction-stage.completed { border-color: #4caf50; background: rgba(76, 175, 80, 0.1); }
|
||||
.construction-stage.active { border-color: #00f2fe; background: rgba(0, 242, 254, 0.1); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="api-status" id="apiStatus">⏳ API: ожидание...</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">0</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>
|
||||
<p style="font-size: 13px; opacity: 0.7; margin-bottom: 10px;">
|
||||
Доступно: <span id="clickerAvailable">0</span>
|
||||
</p>
|
||||
<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>
|
||||
<p style="font-size: 13px; opacity: 0.7; margin-bottom: 10px;">
|
||||
Доступно: <span id="investorAvailable">0</span>
|
||||
</p>
|
||||
<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>
|
||||
<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>
|
||||
<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 id="constructionStages"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- БИЗНЕСЫ -->
|
||||
<div class="tab-content" id="business">
|
||||
<div class="card">
|
||||
<h2>🏢 Мои бизнесы</h2>
|
||||
<div id="businessList"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- РОСКОШЬ -->
|
||||
<div class="tab-content" id="luxury">
|
||||
<div class="card">
|
||||
<h2>💎 Предметы роскоши</h2>
|
||||
<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';
|
||||
|
||||
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');
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 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('Таблица уже существует');
|
||||
}
|
||||
|
||||
await loadState();
|
||||
updateUI();
|
||||
renderCrypto();
|
||||
renderHouse();
|
||||
renderBusinesses();
|
||||
renderLuxury();
|
||||
|
||||
setInterval(tick, 1000);
|
||||
setInterval(updateCryptoPrices, 3000);
|
||||
setInterval(saveState, 10000);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (!api) {
|
||||
updateStatus('✗ API недоступен', 'error');
|
||||
log('API недоступен', 'error');
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// СОХРАНЕНИЕ / ЗАГРУЗКА
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
async function loadState() {
|
||||
if (!api) return;
|
||||
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('Состояние загружено', 'success');
|
||||
}
|
||||
} catch (e) {
|
||||
log('Ошибка загрузки: ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function saveState() {
|
||||
if (!api) return;
|
||||
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);
|
||||
} catch (e) {
|
||||
log('Ошибка сохранения: ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// КРИПТО БИРЖА
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
function updateCryptoPrices() {
|
||||
for (let symbol in cryptos) {
|
||||
const change = (Math.random() - 0.5) * 0.1;
|
||||
cryptos[symbol].change = change;
|
||||
cryptos[symbol].price *= (1 + change);
|
||||
}
|
||||
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 ? '+' : '';
|
||||
list.innerHTML += `
|
||||
<div class="crypto-item">
|
||||
<div>
|
||||
<div class="crypto-name">${symbol}</div>
|
||||
<div style="font-size: 12px; opacity: 0.7;">${crypto.name}</div>
|
||||
</div>
|
||||
<div class="crypto-price">$${crypto.price.toFixed(2)}</div>
|
||||
<div class="crypto-change ${changeClass}">${changeSign}${(crypto.change * 100).toFixed(2)}%</div>
|
||||
<div>
|
||||
<button class="btn btn-buy" onclick="buyCrypto('${symbol}')">Купить</button>
|
||||
<button class="btn btn-sell" onclick="sellCrypto('${symbol}')" ${myState.portfolio[symbol] ? '' : 'disabled'}>Продать</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
renderPortfolio();
|
||||
}
|
||||
|
||||
function renderPortfolio() {
|
||||
const portfolio = document.getElementById('portfolio');
|
||||
let totalValue = 0;
|
||||
let html = '';
|
||||
for (let symbol in myState.portfolio) {
|
||||
const amount = myState.portfolio[symbol];
|
||||
const value = amount * cryptos[symbol].price;
|
||||
totalValue += value;
|
||||
html += `
|
||||
<div class="crypto-item">
|
||||
<div class="crypto-name">${symbol}</div>
|
||||
<div>${amount.toFixed(6)}</div>
|
||||
<div class="crypto-price">$${value.toFixed(2)}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
if (html === '') {
|
||||
html = '<div style="text-align:center;opacity:0.5;padding:20px">Портфель пуст</div>';
|
||||
}
|
||||
portfolio.innerHTML = html;
|
||||
document.getElementById('cryptoValue').textContent = '$' + Math.floor(totalValue).toLocaleString();
|
||||
}
|
||||
|
||||
function buyCrypto(symbol) {
|
||||
const amount = parseFloat(prompt(`Сколько ${symbol} купить? (цена: $${cryptos[symbol].price.toFixed(2)})`));
|
||||
if (!amount || amount <= 0) return;
|
||||
const cost = amount * cryptos[symbol].price;
|
||||
if (myState.cash < cost) {
|
||||
log('Недостаточно средств', 'error');
|
||||
return;
|
||||
}
|
||||
myState.cash -= cost;
|
||||
myState.portfolio[symbol] = (myState.portfolio[symbol] || 0) + amount;
|
||||
log(`Куплено ${amount} ${symbol} за $${cost.toFixed(2)}`, 'success');
|
||||
renderCrypto();
|
||||
updateUI();
|
||||
}
|
||||
|
||||
function sellCrypto(symbol) {
|
||||
const amount = parseFloat(prompt(`Сколько ${symbol} продать? (есть: ${myState.portfolio[symbol]})`));
|
||||
if (!amount || amount <= 0) return;
|
||||
if (myState.portfolio[symbol] < amount) {
|
||||
log('Недостаточно криптовалюты', 'error');
|
||||
return;
|
||||
}
|
||||
const revenue = amount * cryptos[symbol].price;
|
||||
myState.cash += revenue;
|
||||
myState.portfolio[symbol] -= amount;
|
||||
if (myState.portfolio[symbol] < 0.000001) delete myState.portfolio[symbol];
|
||||
log(`Продано ${amount} ${symbol} за $${revenue.toFixed(2)}`, 'success');
|
||||
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('Недостаточно средств в источнике', '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} из ${source}`, 'success');
|
||||
input.value = '';
|
||||
updateUI();
|
||||
await 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('Недостаточно средств', '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} в ${target}`, 'success');
|
||||
input.value = '';
|
||||
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) {
|
||||
log('Ошибка обновления: ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// СТРОИТЕЛЬСТВО ДОМА
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
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);
|
||||
|
||||
container.innerHTML += `
|
||||
<div class="construction-stage ${isCompleted ? 'completed' : ''} ${isActive ? 'active' : ''}">
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
|
||||
<div>
|
||||
<div style="font-weight: bold; font-size: 16px;">${stage.name}</div>
|
||||
<div style="font-size: 12px; opacity: 0.7;">Стоимость: $${stage.cost.toLocaleString()} | Время: ${stage.time}с</div>
|
||||
</div>
|
||||
${isActive ? `<button class="btn btn-action" onclick="buildStage()">Строить</button>` : ''}
|
||||
${isCompleted ? '<div style="color: #4caf50; font-weight: bold;">✓ Готово</div>' : ''}
|
||||
</div>
|
||||
${isActive ? `
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" style="width: ${progress}%">${Math.floor(progress)}%</div>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
function buildStage() {
|
||||
if (myState.house.stage >= houseStages.length) {
|
||||
log('Дом полностью построен!', 'success');
|
||||
return;
|
||||
}
|
||||
const stage = houseStages[myState.house.stage];
|
||||
if (myState.cash < stage.cost) {
|
||||
log('Недостаточно средств', 'error');
|
||||
return;
|
||||
}
|
||||
myState.cash -= stage.cost;
|
||||
myState.house.progress = 0;
|
||||
log(`Начато строительство: ${stage.name}`, 'success');
|
||||
renderHouse();
|
||||
updateUI();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// БИЗНЕСЫ
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
function renderBusinesses() {
|
||||
const container = document.getElementById('businessList');
|
||||
container.innerHTML = '';
|
||||
businesses.forEach(biz => {
|
||||
const owned = myState.businesses[biz.id];
|
||||
container.innerHTML += `
|
||||
<div class="shop-item ${owned ? 'owned' : ''}">
|
||||
<div class="item-info">
|
||||
<div class="item-name">${biz.name}</div>
|
||||
<div class="item-desc">Доход: $${biz.income}/сек</div>
|
||||
</div>
|
||||
<div class="item-price">$${biz.cost.toLocaleString()}</div>
|
||||
<button class="btn ${owned ? 'btn-action' : 'btn-buy'}"
|
||||
onclick="${owned ? 'manageBusiness' : 'buyBusiness'}('${biz.id}')"
|
||||
${!owned && myState.cash < biz.cost ? 'disabled' : ''}>
|
||||
${owned ? '⚙️ Управление' : '🛒 Купить'}
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
function buyBusiness(id) {
|
||||
const biz = businesses.find(b => b.id === id);
|
||||
if (myState.cash < biz.cost) {
|
||||
log('Недостаточно средств', 'error');
|
||||
return;
|
||||
}
|
||||
myState.cash -= biz.cost;
|
||||
myState.businesses[id] = { workers: 1, efficiency: 1 };
|
||||
log(`Куплен бизнес: ${biz.name}`, 'success');
|
||||
renderBusinesses();
|
||||
updateUI();
|
||||
}
|
||||
|
||||
function manageBusiness(id) {
|
||||
const biz = businesses.find(b => b.id === id);
|
||||
const action = prompt(`Управление: ${biz.name}\n1. Нанять работника ($${biz.cost / 10})\n2. Уволить работника\n3. Улучшить эффективность ($${biz.cost / 5})\nВведите номер:`);
|
||||
|
||||
if (action === '1') {
|
||||
const cost = biz.cost / 10;
|
||||
if (myState.cash < cost) { log('Недостаточно средств', 'error'); return; }
|
||||
myState.cash -= cost;
|
||||
myState.businesses[id].workers++;
|
||||
log(`Нанят работник в ${biz.name}`, 'success');
|
||||
} else if (action === '2') {
|
||||
if (myState.businesses[id].workers > 1) {
|
||||
myState.businesses[id].workers--;
|
||||
log(`Работник уволен из ${biz.name}`, 'info');
|
||||
} else {
|
||||
log('Нельзя уволить последнего работника', 'error');
|
||||
}
|
||||
} else if (action === '3') {
|
||||
const cost = biz.cost / 5;
|
||||
if (myState.cash < cost) { log('Недостаточно средств', 'error'); return; }
|
||||
myState.cash -= cost;
|
||||
myState.businesses[id].efficiency *= 1.5;
|
||||
log(`Улучшена эффективность ${biz.name}`, 'success');
|
||||
}
|
||||
renderBusinesses();
|
||||
updateUI();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// РОСКОШЬ
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
function renderLuxury() {
|
||||
const container = document.getElementById('luxuryList');
|
||||
container.innerHTML = '';
|
||||
luxuryItems.forEach(item => {
|
||||
const owned = myState.luxury[item.id];
|
||||
container.innerHTML += `
|
||||
<div class="shop-item ${owned ? 'owned' : ''}">
|
||||
<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>
|
||||
<button class="btn ${owned ? 'btn-action' : 'btn-buy'}"
|
||||
onclick="buyLuxury('${item.id}')"
|
||||
${owned || myState.cash < item.cost ? 'disabled' : ''}>
|
||||
${owned ? '✓ Куплено' : '🛒 Купить'}
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
function buyLuxury(id) {
|
||||
const item = luxuryItems.find(i => i.id === id);
|
||||
if (myState.cash < item.cost) {
|
||||
log('Недостаточно средств', 'error');
|
||||
return;
|
||||
}
|
||||
myState.cash -= item.cost;
|
||||
myState.luxury[id] = true;
|
||||
log(`Куплено: ${item.name}`, 'success');
|
||||
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();
|
||||
|
||||
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>
|
||||
Reference in New Issue
Block a user