Spaces:
Running
Running
| // Инициализация игры | |
| class RealityOS { | |
| constructor() { | |
| this.player = { | |
| name: "Влад", | |
| age: 20, | |
| profession: "Системный инженер АПС и СОУЭ", | |
| location: "Москва, Дорогобужская 9", | |
| financialGoal: 1000000, | |
| currentPortfolio: 4726, | |
| currentIncome: 165000, | |
| credits: 300000, | |
| characteristics: { | |
| sw: 450, // Сила воли | |
| hp: 750, // Энергия | |
| disc: 600, // Дисциплина | |
| fg: 350, // Финансовая грамотность | |
| tech: 550, // Техническая экспертиза | |
| sys: 500, // Системное мышление | |
| ldr: 250, // Лидерство | |
| str: 400 // Стрессоустойчивость | |
| }, | |
| levels: { | |
| engineering: { | |
| level: 4, | |
| exp: 650000, | |
| maxExp: 1000000, | |
| title: "Бригадир монтажников" | |
| }, | |
| investing: { | |
| level: 1, | |
| exp: 4726, | |
| maxExp: 150000, | |
| title: "Финансовый новичок" | |
| }, | |
| personal: { | |
| level: 2, | |
| exp: 112500, | |
| maxExp: 250000, | |
| title: "Самопознающий" | |
| } | |
| } | |
| }; | |
| this.init(); | |
| } | |
| init() { | |
| this.renderStats(); | |
| this.renderLevels(); | |
| this.initCharts(); | |
| this.setupEventListeners(); | |
| } | |
| renderStats() { | |
| // Обновляем характеристики | |
| const stats = this.player.characteristics; | |
| document.querySelector('.stat-value:nth-child(1)').textContent = `${stats.sw}/1000`; | |
| document.querySelector('.progress-fill:nth-child(1)').style.width = `${stats.sw/10}%`; | |
| document.querySelector('.stat-value:nth-child(2)').textContent = `${stats.hp}/1000`; | |
| document.querySelector('.progress-fill:nth-child(2)').style.width = `${stats.hp/10}%`; | |
| document.querySelector('.stat-value:nth-child(3)').textContent = `${stats.disc}/1000`; | |
| document.querySelector('.progress-fill:nth-child(3)').style.width = `${stats.disc/10}%`; | |
| document.querySelector('.stat-value:nth-child(4)').textContent = `${stats.fg}/1000`; | |
| document.querySelector('.progress-fill:nth-child(4)').style.width = `${stats.fg/10}%`; | |
| document.querySelector('.stat-value:nth-child(5)').textContent = `${stats.tech}/1000`; | |
| document.querySelector('.progress-fill:nth-child(5)').style.width = `${stats.tech/10}%`; | |
| document.querySelector('.stat-value:nth-child(6)').textContent = `${stats.sys}/1000`; | |
| document.querySelector('.progress-fill:nth-child(6)').style.width = `${stats.sys/10}%`; | |
| document.querySelector('.stat-value:nth-child(7)').textContent = `${stats.ldr}/1000`; | |
| document.querySelector('.progress-fill:nth-child(7)').style.width = `${stats.ldr/10}%`; | |
| document.querySelector('.stat-value:nth-child(8)').textContent = `${stats.str}/1000`; | |
| document.querySelector('.progress-fill:nth-child(8)').style.width = `${stats.str/10}%`; | |
| // Обновляем финансовый прогресс | |
| const progressPercent = (this.player.currentPortfolio / this.player.financialGoal) * 100; | |
| document.querySelector('.financial-stats span:first-child').textContent = `Текущий портфель: ${this.player.currentPortfolio.toLocaleString()} ₽`; | |
| document.querySelector('.financial-stats span:last-child').textContent = `Цель: ${this.player.financialGoal.toLocaleString()} ₽`; | |
| document.querySelector('.financial-goal .progress-fill').style.width = `${progressPercent}%`; | |
| } | |
| renderLevels() { | |
| // Обновляем уровни | |
| const engLevel = this.player.levels.engineering; | |
| const invLevel = this.player.levels.investing; | |
| const perLevel = this.player.levels.personal; | |
| document.querySelectorAll('.level-card')[0].innerHTML = ` | |
| <h4>Инженерная ветка</h4> | |
| <p>Уровень ${engLevel.level}: ${engLevel.title}</p> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" style="width: ${(engLevel.exp/engLevel.maxExp)*100}%"></div> | |
| </div> | |
| <span>${engLevel.exp.toLocaleString()}/${engLevel.maxExp.toLocaleString()} EXP</span> | |
| `; | |
| document.querySelectorAll('.level-card')[1].innerHTML = ` | |
| <h4>Инвесторская ветка</h4> | |
| <p>Уровень ${invLevel.level}: ${invLevel.title}</p> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" style="width: ${(invLevel.exp/invLevel.maxExp)*100}%"></div> | |
| </div> | |
| <span>${invLevel.exp.toLocaleString()}/${invLevel.maxExp.toLocaleString()} EXP</span> | |
| `; | |
| document.querySelectorAll('.level-card')[2].innerHTML = ` | |
| <h4>Личностная ветка</h4> | |
| <p>Уровень ${perLevel.level}: ${perLevel.title}</p> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" style="width: ${(perLevel.exp/perLevel.maxExp)*100}%"></div> | |
| </div> | |
| <span>${perLevel.exp.toLocaleString()}/${perLevel.maxExp.toLocaleString()} EXP</span> | |
| `; | |
| } | |
| initCharts() { | |
| const ctx = document.getElementById('progressChart').getContext('2d'); | |
| this.chart = new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'], | |
| datasets: [{ | |
| label: 'Прогресс по характеристикам', | |
| data: [ | |
| this.player.characteristics.sw, | |
| this.player.characteristics.hp, | |
| this.player.characteristics.disc, | |
| this.player.characteristics.fg, | |
| this.player.characteristics.tech, | |
| this.player.characteristics.sys, | |
| this.player.characteristics.ldr | |
| ], | |
| borderColor: '#3498db', | |
| backgroundColor: 'rgba(52, 152, 219, 0.1)', | |
| tension: 0.4, | |
| fill: true | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| max: 1000 | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| setupEventListeners() { | |
| // Кнопки быстрого ввода | |
| document.querySelectorAll('.action-btn').forEach(button => { | |
| button.addEventListener('click', (e) => { | |
| const action = e.target.textContent; | |
| this.handleQuickAction(action); | |
| }); | |
| }); | |
| // Форма ручного ввода | |
| document.querySelector('.manual-input').addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| this.handleManualInput(); | |
| }); | |
| // Вкладки отчетов | |
| document.querySelectorAll('.tab-btn').forEach(button => { | |
| button.addEventListener('click', (e) => { | |
| document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); | |
| e.target.classList.add('active'); | |
| this.updateReportContent(e.target.textContent); | |
| }); | |
| }); | |
| // Кнопки футера | |
| document.querySelectorAll('.footer-btn').forEach(button => { | |
| button.addEventListener('click', (e) => { | |
| const action = e.target.textContent.trim(); | |
| this.handleFooterAction(action); | |
| }); | |
| }); | |
| } | |
| handleQuickAction(action) { | |
| let expGain = 0; | |
| let characteristic = ''; | |
| switch(action) { | |
| case '💼 Работа': | |
| expGain = 50; | |
| characteristic = 'tech'; | |
| break; | |
| case '💰 Инвестиции': | |
| expGain = 30; | |
| characteristic = 'fg'; | |
| break; | |
| case '🧘 Тренировка': | |
| expGain = 40; | |
| characteristic = 'hp'; | |
| break; | |
| case '📚 Обучение': | |
| expGain = 35; | |
| characteristic = 'sys'; | |
| break; | |
| case '📝 Медитация': | |
| expGain = 25; | |
| characteristic = 'sw'; | |
| break; | |
| case '🏖️ Отдых': | |
| expGain = 20; | |
| characteristic = 'hp'; | |
| break; | |
| } | |
| if (characteristic) { | |
| this.player.characteristics[characteristic] += expGain; | |
| if (this.player.characteristics[characteristic] > 1000) { | |
| this.player.characteristics[characteristic] = 1000; | |
| } | |
| // Также добавляем EXP в соответствующую ветку | |
| if (characteristic === 'tech') { | |
| this.player.levels.engineering.exp += expGain * 100; | |
| if (this.player.levels.engineering.exp >= this.player.levels.engineering.maxExp) { | |
| this.levelUp('engineering'); | |
| } | |
| } else if (characteristic === 'fg') { | |
| this.player.levels.investing.exp += expGain * 100; | |
| if (this.player.levels.investing.exp >= this.player.levels.investing.maxExp) { | |
| this.levelUp('investing'); | |
| } | |
| } else { | |
| this.player.levels.personal.exp += expGain * 100; | |
| if (this.player.levels.personal.exp >= this.player.levels.personal.maxExp) { | |
| this.levelUp('personal'); | |
| } | |
| } | |
| this.renderStats(); | |
| this.renderLevels(); | |
| this.showNotification(`Получено ${expGain} EXP за ${action}`); | |
| } | |
| } | |
| handleManualInput() { | |
| const activityType = document.getElementById('activity-type').value; | |
| const description = document.getElementById('activity-description').value; | |
| const expAmount = parseInt(document.getElementById('exp-amount').value) || 0; | |
| if (!description || expAmount <= 0) { | |
| this.showNotification('Пожалуйста, заполните все поля корректно', 'error'); | |
| return; | |
| } | |
| // Добавляем EXP в зависимости от типа активности | |
| switch(activityType) { | |
| case 'career': | |
| this.player.levels.engineering.exp += expAmount; | |
| if (this.player.levels.engineering.exp >= this.player.levels.engineering.maxExp) { | |
| this.levelUp('engineering'); | |
| } | |
| break; | |
| case 'finance': | |
| this.player.levels.investing.exp += expAmount; | |
| if (this.player.levels.investing.exp >= this.player.levels.investing.maxExp) { | |
| this.levelUp('investing'); | |
| } | |
| this.player.currentPortfolio += expAmount; // Упрощение для демонстрации | |
| break; | |
| case 'personal': | |
| this.player.levels.personal.exp += expAmount; | |
| if (this.player.levels.personal.exp >= this.player.levels.personal.maxExp) { | |
| this.levelUp('personal'); | |
| } | |
| break; | |
| } | |
| this.renderStats(); | |
| this.renderLevels(); | |
| this.showNotification(`Добавлено ${expAmount} EXP: ${description}`); | |
| // Очищаем форму | |
| document.getElementById('activity-description').value = ''; | |
| document.getElementById('exp-amount').value = ''; | |
| } | |
| levelUp(branch) { | |
| this.player.levels[branch].level++; | |
| this.player.levels[branch].exp = 0; | |
| // Увеличиваем максимальный EXP для следующего уровня | |
| const nextLevel = this.player.levels[branch].level; | |
| switch(branch) { | |
| case 'engineering': | |
| this.player.levels[branch].maxExp = nextLevel < 15 ? Math.pow(2, nextLevel) * 50000 : 180000000; | |
| this.player.levels[branch].title = this.getEngineeringTitle(nextLevel); | |
| break; | |
| case 'investing': | |
| this.player.levels[branch].maxExp = nextLevel < 12 ? nextLevel * 150000 : 50000000; | |
| this.player.levels[branch].title = this.getInvestingTitle(nextLevel); | |
| break; | |
| case 'personal': | |
| this.player.levels[branch].maxExp = nextLevel < 10 ? nextLevel * 100000 : 16000000; | |
| this.player.levels[branch].title = this.getPersonalTitle(nextLevel); | |
| break; | |
| } | |
| this.showNotification(`🎉 Повышение уровня! Теперь вы: ${this.player.levels[branch].title}`, 'success'); | |
| } | |
| getEngineeringTitle(level) { | |
| const titles = [ | |
| "", "Монтажник-стажер", "Монтажник", "Старший монтажник", | |
| "Бригадир монтажников", "Младший наладчик", "Наладчик", | |
| "Старший наладчик", "Инженер-проектировщик", "Старший инженер-проектировщик", | |
| "Lead инженер", "Руководитель проектов", "Начальник отдела", | |
| "Технический директор", "Директор по развитию", "Основатель технологической компании" | |
| ]; | |
| return titles[level] || "Неизвестный уровень"; | |
| } | |
| getInvestingTitle(level) { | |
| const titles = [ | |
| "", "Финансовый новичок", "Начинающий инвестор", "Активный инвестор", | |
| "Опытный инвестор", "Специалист по ВДО", "Портфельный менеджер", | |
| "Профессиональный инвестор", "Инвестиционный аналитик", "Старший инвестор", | |
| "Инвестиционный стратег", "Управляющий активами", "Капитан рынка" | |
| ]; | |
| return titles[level] || "Неизвестный уровень"; | |
| } | |
| getPersonalTitle(level) { | |
| const titles = [ | |
| "", "Искатель", "Самопознающий", "Практик", "Осознанный", | |
| "Дисциплинированный", "Сбалансированный", "Мастер привычек", | |
| "Лидер", "Мудрец", "Просветленный" | |
| ]; | |
| return titles[level] || "Неизвестный уровень"; | |
| } | |
| updateReportContent(period) { | |
| const reportContent = document.querySelector('.report-content'); | |
| switch(period) { | |
| case 'Ежедневно': | |
| reportContent.innerHTML = ` | |
| <h3>Ежедневная статистика</h3> | |
| <ul> | |
| <li>💼 Работа: 8 часов</li> | |
| <li>💰 Инвестиции: 30 минут</li> | |
| <li>🧘 Тренировка: 1 час</li> | |
| <li>📚 Обучение: 2 часа</li> | |
| </ul> | |
| `; | |
| break; | |
| case 'Еженедельно': | |
| reportContent.innerHTML = ` | |
| <h3>Еженедельный аудит</h3> | |
| <ul> | |
| <li>📈 Прогресс по всем веткам: +2.5%</li> | |
| <li>💰 Финансовый портфель: +1,200 ₽</li> | |
| <li>🎯 Выполнено 4 из 5 недельных квестов</li> | |
| <li>🏆 Получено 2 достижения</li> | |
| </ul> | |
| `; | |
| break; | |
| case 'Ежемесячно': | |
| reportContent.innerHTML = ` | |
| <h3>Ежемесячный отчет</h3> | |
| <ul> | |
| <li>📊 Общий прогресс: +12%</li> | |
| <li>💰 Доход: 165,000 ₽</li> | |
| <li>📈 Инвестиционная доходность: 1.8%</li> | |
| <li>🎯 Достигнуты 2 цели из 5</li> | |
| </ul> | |
| `; | |
| break; | |
| case 'Годовые цели': | |
| reportContent.innerHTML = ` | |
| <h3>Годовые цели</h3> | |
| <ul> | |
| <li>🚀 Достичь уровня 6 в инженерной ветке</li> | |
| <li>💰 Накопить 150,000 ₽ в портфеле</li> | |
| <li>🧠 Повысить все характеристики до 600+</li> | |
| <li>🏆 Получить 10 достижений</li> | |
| </ul> | |
| `; | |
| break; | |
| } | |
| } | |
| handleFooterAction(action) { | |
| switch(action) { | |
| case '💾 Сохранить': | |
| this.saveGame(); | |
| break; | |
| case '📤 Экспорт данных': | |
| this.exportData(); | |
| break; | |
| case '📥 Импорт данных': | |
| this.importData(); | |
| break; | |
| } | |
| } | |
| saveGame() { | |
| localStorage.setItem('realityOS_save', JSON.stringify(this.player)); | |
| this.showNotification('Игра сохранена успешно!', 'success'); | |
| } | |
| exportData() { | |
| const dataStr = JSON.stringify(this.player); | |
| const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); | |
| const exportFileDefaultName = 'reality_os_save.json'; | |
| const linkElement = document.createElement('a'); | |
| linkElement.setAttribute('href', dataUri); | |
| linkElement.setAttribute('download', exportFileDefaultName); | |
| linkElement.click(); | |
| this.showNotification('Данные экспортированы!', 'success'); | |
| } | |
| importData() { | |
| const input = document.createElement('input'); | |
| input.type = 'file'; | |
| input.accept = '.json'; | |
| input.onchange = e => { | |
| const file = e.target.files[0]; | |
| const reader = new FileReader(); | |
| reader.onload = event => { | |
| try { | |
| const data = JSON.parse(event.target.result); | |
| this.player = data; | |
| this.renderStats(); | |
| this.renderLevels(); | |
| this.showNotification('Данные импортированы успешно!', 'success'); | |
| } catch (error) { | |
| this.showNotification('Ошибка при импорте данных', 'error'); | |
| } | |
| }; | |
| reader.readAsText(file); | |
| }; | |
| input.click(); | |
| } | |
| showNotification(message, type = 'info') { | |
| // Создаем элемент уведомления | |
| const notification = document.createElement('div'); | |
| notification.className = `notification ${type}`; | |
| notification.textContent = message; | |
| // Стили для уведомления | |
| Object.assign(notification.style, { | |
| position: 'fixed', | |
| top: '20px', | |
| right: '20px', | |
| padding: '15px 25px', | |
| borderRadius: '8px', | |
| color: 'white', | |
| fontWeight: 'bold', | |
| zIndex: '1000', | |
| boxShadow: '0 5px 15px rgba(0,0,0,0.3)', | |
| transform: 'translateX(100%)', | |
| transition: 'transform 0.3s ease' | |
| }); | |
| // Цвета для разных типов уведомлений | |
| if (type === 'success') { | |
| notification.style.background = 'linear-gradient(135deg, #2ecc71, #27ae60)'; | |
| } else if (type === 'error') { | |
| notification.style.background = 'linear-gradient(135deg, #e74c3c, #c0392b)'; | |
| } else { | |
| notification.style.background = 'linear-gradient(135deg, #3498db, #2980b9)'; | |
| } | |
| document.body.appendChild(notification); | |
| // Анимация появления | |
| setTimeout(() => { | |
| notification.style.transform = 'translateX(0)'; | |
| }, 100); | |
| // Удаление через 3 секунды | |
| setTimeout(() => { | |
| notification.style.transform = 'translateX(100%)'; | |
| setTimeout(() => { | |
| document.body.removeChild(notification); | |
| }, 300); | |
| }, 3000); | |
| } | |
| } | |
| // Инициализация игры при загрузке страницы | |
| document.addEventListener('DOMContentLoaded', () => { | |
| window.game = new RealityOS(); | |
| }); |