Добавить investor.html

This commit is contained in:
2026-06-16 15:26:19 +03:00
parent b086c2b2a1
commit 7ad228c670
+402
View File
@@ -0,0 +1,402 @@
<!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, #0f3443 0%, #34e89e 100%);
color: #fff; min-height: 100vh; padding: 20px;
}
.container { max-width: 1000px; margin: 0 auto; }
h1 { text-align: center; margin-bottom: 30px; font-size: 36px; text-shadow: 0 2px 10px rgba(0,0,0,0.3); }
.status-bar {
background: rgba(0,0,0,0.3); border-radius: 12px;
padding: 20px; margin-bottom: 20px;
display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px;
backdrop-filter: blur(10px);
}
.status-item { text-align: center; }
.status-label { font-size: 12px; opacity: 0.7; text-transform: uppercase; letter-spacing: 1px; }
.status-value { font-size: 28px; font-weight: bold; color: #ffd700; margin-top: 5px; }
.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.3); border-radius: 12px;
padding: 25px; backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.1);
}
.card h2 { margin-bottom: 15px; color: #ffd700; font-size: 20px; }
.clicker-info {
background: rgba(255,215,0,0.1); border: 1px solid rgba(255,215,0,0.3);
border-radius: 8px; padding: 15px; margin-bottom: 15px;
}
.clicker-info .score { font-size: 32px; color: #ffd700; font-weight: bold; }
.clicker-info .label { font-size: 12px; opacity: 0.7; margin-bottom: 5px; }
.invest-btn, .action-btn {
width: 100%; padding: 12px; border: none; border-radius: 8px;
font-size: 14px; font-weight: bold; cursor: pointer;
margin-bottom: 10px; color: white; transition: all 0.2s;
}
.invest-btn { background: linear-gradient(135deg, #f093fb, #f5576c); }
.invest-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(245,87,108,0.4); }
.invest-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.action-btn { background: linear-gradient(135deg, #667eea, #764ba2); }
.action-btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(102,126,234,0.4); }
.history { max-height: 300px; overflow-y: auto; }
.history-item {
background: rgba(255,255,255,0.05); padding: 10px;
border-radius: 6px; margin-bottom: 8px; font-size: 13px;
display: flex; justify-content: space-between;
border: 1px solid rgba(255,255,255,0.05);
}
.history-item .amount { font-weight: bold; }
.history-item .amount.positive { color: #4caf50; }
.history-item .amount.negative { color: #f44336; }
.log {
background: rgba(0,0,0,0.4); border-radius: 8px;
padding: 10px; margin-top: 15px; font-family: monospace;
font-size: 12px; max-height: 150px; 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; }
.api-status {
position: fixed; top: 10px; left: 10px;
padding: 8px 16px; border-radius: 20px;
font-size: 12px; background: rgba(0, 0, 0, 0.7);
color: #aaa; z-index: 1000;
}
.api-status.connected { color: #4caf50; }
.api-status.error { color: #f44336; }
</style>
</head>
<body>
<div class="api-status" id="apiStatus">⏳ API: ожидание...</div>
<div class="container">
<h1>💰 Инвестор</h1>
<div class="status-bar">
<div class="status-item">
<div class="status-label">Ваш капитал</div>
<div class="status-value" id="capital">0</div>
</div>
<div class="status-item">
<div class="status-label">Доход/сек</div>
<div class="status-value" id="income">0</div>
</div>
<div class="status-item">
<div class="status-label">Инвестировано</div>
<div class="status-value" id="invested">0</div>
</div>
</div>
<div class="grid">
<div class="card">
<h2>📊 Состояние кликера</h2>
<div class="clicker-info">
<div class="label">Счёт игрока в Кликере:</div>
<div class="score" id="clickerScore"></div>
</div>
<button class="action-btn" id="refreshBtn">🔄 Обновить данные кликера</button>
<button class="action-btn" id="boostBtn">🚀 Бонус +1000 к кликеру (стоит 500)</button>
<button class="action-btn" id="manageBtn">⚙️ Управление разрешениями</button>
</div>
<div class="card">
<h2>💼 Инвестиции</h2>
<p style="margin-bottom: 15px; opacity: 0.8; font-size: 14px;">
Инвестируйте капитал — получайте 1% дохода в секунду
</p>
<button class="invest-btn" data-amount="100">Инвестировать 100</button>
<button class="invest-btn" data-amount="500">Инвестировать 500</button>
<button class="invest-btn" data-amount="1000">Инвестировать 1000</button>
<button class="invest-btn" data-amount="5000">Инвестировать 5000</button>
</div>
</div>
<div class="card" style="margin-top: 20px;">
<h2>📜 История операций</h2>
<div class="history" id="history"></div>
</div>
<div class="log" id="log"></div>
</div>
<script>
// ═══════════════════════════════════════════════════════════
// КОНФИГУРАЦИЯ
// ═══════════════════════════════════════════════════════════
const CLICKER_SAVE = 'main_save'; // Save кликера (чужой!)
const CLICKER_TABLE = 'game_save'; // Таблица кликера
const MY_SAVE = 'investor_save'; // Наш save
const MY_TABLE = 'investor_data'; // Наша таблица данных
const HISTORY_TABLE = 'history'; // Наша таблица истории
let myState = {
capital: 1000,
invested: 0,
income_per_sec: 0
};
let clickerScore = null;
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);
}
}
function updateUI() {
document.getElementById('capital').textContent = Math.floor(myState.capital).toLocaleString();
document.getElementById('income').textContent = myState.income_per_sec.toLocaleString();
document.getElementById('invested').textContent = Math.floor(myState.invested).toLocaleString();
document.getElementById('clickerScore').textContent =
clickerScore !== null ? Math.floor(clickerScore).toLocaleString() : '—';
}
// ═══════════════════════════════════════════════════════════
// ИНИЦИАЛИЗАЦИЯ 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('Таблица данных уже существует');
}
try {
await api.createTable(HISTORY_TABLE, {
'type': 'TEXT', 'amount': 'NUMBER', 'timestamp': 'TEXT'
}, MY_SAVE, 'История операций');
log('Таблица истории создана', 'success');
} catch (e) {
log('Таблица истории уже существует');
}
await loadState();
await loadHistory();
updateUI();
// Доход каждую секунду
setInterval(tick, 1000);
// Автосохранение
setInterval(saveState, 10000);
});
// Fallback если API не подключился
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');
}
}
async function loadHistory() {
if (!api) return;
try {
const result = await api.selectData(HISTORY_TABLE, {}, MY_SAVE);
renderHistory(result.data || []);
} catch (e) {
log('Ошибка загрузки истории: ' + e.message, 'error');
}
}
function renderHistory(items) {
const el = document.getElementById('history');
if (items.length === 0) {
el.innerHTML = '<div style="text-align:center;opacity:0.5;padding:20px">Нет операций</div>';
return;
}
el.innerHTML = items.slice().reverse().map(item => `
<div class="history-item">
<span>${item.type}</span>
<span class="amount ${item.amount >= 0 ? 'positive' : 'negative'}">
${item.amount > 0 ? '+' : ''}${item.amount}
</span>
</div>
`).join('');
}
async function addHistory(type, amount) {
if (!api) return;
try {
await api.insertData(HISTORY_TABLE, {
type, amount,
timestamp: new Date().toISOString()
}, MY_SAVE);
await loadHistory();
} catch (e) {
log('Ошибка записи истории: ' + e.message, 'error');
}
}
// ═══════════════════════════════════════════════════════════
// ВЗАИМОДЕЙСТВИЕ С КЛИКЕРОМ
// ═══════════════════════════════════════════════════════════
async function refreshClicker() {
if (!api) {
log('API недоступен', 'error');
return;
}
try {
// Читаем save кликера (потребуется разрешение на select в main_save)
const result = await api.selectData(CLICKER_TABLE, { key: 'game_state' }, CLICKER_SAVE);
if (result.data && result.data.length > 0) {
const state = JSON.parse(result.data[0].value);
clickerScore = state.score || 0;
log(`Счёт кликера: ${clickerScore}`, 'success');
} else {
clickerScore = 0;
log('Кликер ещё не сохранял данные', 'info');
}
updateUI();
} catch (e) {
log('Ошибка чтения кликера: ' + e.message, 'error');
}
}
async function boostClicker() {
if (!api) {
log('API недоступен', 'error');
return;
}
if (myState.capital < 500) {
log('Недостаточно капитала (нужно 500)', 'error');
return;
}
try {
// 1. Читаем текущее состояние кликера
const result = await api.selectData(CLICKER_TABLE, { key: 'game_state' }, CLICKER_SAVE);
if (!result.data || result.data.length === 0) {
log('Кликер не найден — сначала запустите его', 'error');
return;
}
const state = JSON.parse(result.data[0].value);
const oldScore = state.score || 0;
state.score = oldScore + 1000;
// 2. Обновляем в save кликера (потребуется разрешение на insert в main_save)
await api.deleteData(CLICKER_TABLE, { key: 'game_state' }, CLICKER_SAVE);
await api.insertData(CLICKER_TABLE, {
key: 'game_state',
value: JSON.stringify(state),
updated_at: new Date().toISOString()
}, CLICKER_SAVE);
// 3. Списываем капитал
myState.capital -= 500;
await addHistory('Бонус кликеру (+1000)', -500);
log(`✅ Кликеру добавлено +1000 (было ${oldScore}, стало ${state.score})`, 'success');
updateUI();
} catch (e) {
log('Ошибка: ' + e.message, 'error');
}
}
// ══════════════════════════════════════════════════════════
// ИНВЕСТИЦИИ
// ═══════════════════════════════════════════════════════════
async function invest(amount) {
if (myState.capital < amount) {
log('Недостаточно капитала', 'error');
return;
}
myState.capital -= amount;
myState.invested += amount;
myState.income_per_sec += Math.floor(amount / 100); // 1% в секунду
await addHistory('Инвестиция', -amount);
log(`Инвестировано ${amount} (+${Math.floor(amount/100)}/сек)`, 'success');
updateUI();
}
async function tick() {
if (myState.income_per_sec > 0) {
myState.capital += myState.income_per_sec;
updateUI();
}
}
// ═══════════════════════════════════════════════════════════
// ОБРАБОТЧИКИ
// ═══════════════════════════════════════════════════════════
document.getElementById('refreshBtn').onclick = refreshClicker;
document.getElementById('boostBtn').onclick = boostClicker;
document.getElementById('manageBtn').onclick = () => {
if (api) api.managePermissions();
else log('API недоступен', 'error');
};
document.querySelectorAll('.invest-btn').forEach(btn => {
btn.onclick = () => invest(parseInt(btn.dataset.amount));
});
updateUI();
</script>
</body>
</html>