Всем привет это снова я ShadowWisp давно не виделись у меня вышло обновление и
Я вышел на новый уровень ведь теперь я не фоткаю комп а делаю скрины прямо на нём! А ещё
Я наконец начал делать нормальные фотографии для поста ладно хватит рассказывать про себя поговорим про игру
Во первых я наконец сделал анимацию раньше я не мог понять как но 2 дня и я всё понял но увы видео вроде плохо работают но

Забыл сказать тут пока только 2 кадра но они работаю
В тт возможно скоро сниму видео я не уверен пока ладно хватит про тт я переделал меню

Как то плохо получилось сфоткать
Так же под прошлым постом мне сказал делать уроки сам Рикардио также они победили в голосовании поэтому скоро возможно будет 1 урок
Кстати помните в описании я сказал про праздники? Так вот я сделал анимацию но это мини праздник а большой
Я попал в топ 500 блогов! Спасибо всем кто меня поддержал я очень благодарен.
Скажу почему меня не было во первых я был у бабушки на несколько дней но там я тоже работал рисовал анимацию и тайлмап но он пока не закончен
Можете глянуть кстати моё первое видео в тт а ещё я хотел отправить фото этого поста в полный размер

Для первого фото я считаю это прикольно
Кстати я передумал насчёт названия имба от нуба звучит ну не особо по кубически поэтому предлагайте свои варианты в комментариях
Еле вспомнил про планы на будущее начнём 1 сделать нормальный первый уровень а не просто сцена с невидимыми стенами
2 поменять персонажа который стоит в меню 3 сделать больше кадров что б выглядело по живее 4 придумать название игры
Ну и 5 доделать тайл мап

Старое меню
какая версия меню лучше?2
Всем привет с вами снова я ShadowWisp👋👋👋👋👋 и сразу хочу сказать что я создал тт канал под этим ником
В нём тоже буду что то публиковать но это также будет здесь но там возможно будут видео
Я решил сделать состава что и когда идёт:
1 приветствие 1-4
2 меню 5-8 блок
3 новые текстуры 9-14
4 скрипты 15-17
5 уровень 1 18-24
6 персонаж 25-28
7 планы и прощание 29-32
И так покажу вам саму игру и заодно буду всё объяснять вам

Вот оно меню!
Ну я сделал так сказать обычное меню для своей игры, потом его надо будет переделать но это в будущем
Кстати его я добавил постепенно сначала трава что бы её трогать с небом и иконкой этого поста
Потом добавил текстур новых кстати сейчас покажу это и поставил их также переместил иконку поста

Солнце

Дерево

Листья

Персонаж
Вот вроде все персонажи теперь сразу прошу извините если буду пистаь с ошибками просто у меня глаз почему то опух и мне закапали теперь плохо видно
А потом я просто забуду про пост как два дня уже это делал и переходим дальше к скриптам я решил их отправить

Меню

Персонаж
Вот новые и старые скрипты если что я потом может объясню если надо что за что отвечает
А теперь покажу первый уровень там ещё ничего толком нет но я создал карту это уже круто

Вот он

Он двинулся?

Э куда!?

О он вернулся

Надо было с этого начинать
Так вот это весь первый уровень а ещё я хотел кое что рассказать но понял что это входит в персонаж
Ну а теперь собственно персонаж и всё про него 1 он умеет ходить и прыгать 2
За ним ходит камера и я сделал так что бы она не уходила куда не надо
3 шестым будет его фото 4 у него нет анимации

6
Наконец я закончил с персонажем но . не знаю он не крутой по моеиу поэтому наверно что нибудь сделаю
Ну и вот мои планы наконц я до них добрался их очень много
1 переделать меню 2 переделать чуть гг 3 сделать первый уровень первым уровнем
4 сделать второй уровень 5 сделать магазин и валюту это будут имбурики тепо рубль от имба от нуба
6 Сделать или попытаться сделать скины 7сделать анимацию персонажа 8 возможно с позволения от sasha.go тоже делать уроки но для своей версии и от вашего хотения
Фух вроде всё впервые делаю такой длинный пост в целых 32 блока! Но ладно я вроде всем поделился и могу попрощаться что же всем пока и удачи вам!! 👋👋
мне делать уроки для вас?2
Какая-то нездоровая тематика с вашим программированием и играми пошла..так почему бы к нему не присоединиться?
Да. Такое резкое заявление не может остаться не замеченным и грубо так говоря неоставленным без вашего внимания. Good day to all and take it easy, as they say! By Mentos!
Под этим спойлером ниже, скрывается HTML-код (открытый браузерный код, который самый простой в освоении на данный момент). НЕ ОТКРЫВАЙТЕ ЭТОТ КОД, ПРОСТО ТАК, ЧТОБЫ НЕ ЛИСТАТЬ ВНИЗ ОЧЕНЬ МНОГО ТЕКСТА.
КОД:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, maximum-scale=1.5">
<title>Formula Manager 2026 – F1 & Formula E</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
body { background: linear-gradient(135deg, #0a0f1e 0%, #1a1f2e 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 20px; }
.app-container { max-width: 960px; width: 100%; }
.main-menu, .game-container { background: rgba(20, 25, 40, 0.95); backdrop-filter: blur(20px); border-radius: 32px; padding: 24px; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.8); color: #e0e0e0; margin-bottom: 20px; }
.main-menu h2 { text-align: center; margin-bottom: 24px; font-size: 2rem; }
.menu-buttons { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; }
.menu-btn { border: none; color: white; padding: 20px 30px; border-radius: 24px; font-size: 1.5rem; font-weight: 700; cursor: pointer; transition: 0.2s; width: 280px; text-align: center; }
.menu-btn.f1 { background: #e10600; } .menu-btn.f1:hover { background: #ff1a1a; }
.menu-btn.fe { background: #0066cc; } .menu-btn.fe:hover { background: #0080ff; }
.game-wrapper { display: none; }
.game-wrapper.active { display: block; }
.back-btn { border: none; color: white; padding: 10px 18px; border-radius: 30px; font-weight: 600; cursor: pointer; margin-bottom: 16px; }
.back-btn.f1 { background: #e10600; } .back-btn.fe { background: #0066cc; }
.game-container h1 { font-size: 2rem; font-weight: 700; text-align: center; margin-bottom: 8px; }
.game-container .subtitle { text-align: center; font-size: 0.9rem; color: #8a8f9a; margin-bottom: 20px; border-bottom: 1px solid #2a2f3a; padding-bottom: 16px; }
.single-video { text-align: center; margin-bottom: 20px; }
.single-video iframe { border-radius: 16px; max-width: 100%; display: none; }
.video-link { display: inline-flex; align-items: center; justify-content: center; gap: 8px; background: #e10600; color: white; text-decoration: none; font-weight: 600; padding: 10px 24px; border-radius: 30px; font-size: 0.95rem; transition: background 0.2s; }
.video-link:hover { background: #ff1a1a; }
.team-selector { background: #0d1117; border-radius: 20px; padding: 20px; margin-bottom: 20px; border: 1px solid #2a2f3a; }
select { width: 100%; padding: 12px; background: #161b25; border: 1px solid #2a2f3a; color: white; border-radius: 12px; font-size: 1rem; margin-bottom: 10px; }
.team-preview { background: #161b25; border-radius: 16px; padding: 16px; margin-top: 12px; border: 1px solid #2a2f3a; display: none; }
.team-preview.active { display: block; }
.stats-panel { display: flex; justify-content: space-between; background: #0d1117; border-radius: 20px; padding: 16px 20px; margin-bottom: 20px; border: 1px solid #2a2f3a; flex-wrap: wrap; }
.stat { text-align: center; min-width: 80px; }
.stat-value { font-size: 1.8rem; font-weight: 700; color: #fff; }
.stat-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.5px; color: #8a8f9a; }
.money { color: #f0b90b; } .money.negative { color: #ff6b6b; }
.tabs { display: flex; gap: 8px; margin-bottom: 20px; flex-wrap: wrap; }
.tab { flex: 1; background: #161b25; border: 1px solid #2a2f3a; color: #8a8f9a; padding: 12px 8px; border-radius: 16px; font-weight: 600; font-size: 0.9rem; cursor: pointer; transition: all 0.2s; text-align: center; min-width: 70px; }
.tab.active { color: white; }
.view { display: none; animation: fade 0.2s ease; } .view.active { display: block; }
@keyframes fade { from { opacity: 0.3; } to { opacity: 1; } }
.card { background: #161b25; border-radius: 20px; padding: 18px; margin-bottom: 16px; border: 1px solid #2a2f3a; }
.card h3 { font-size: 1.1rem; margin-bottom: 14px; display: flex; align-items: center; gap: 8px; }
.pilot-row, .upgrade-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
.pilot-name { font-weight: 700; } .pilot-skill { font-size: 0.85rem; color: #a0a5b0; }
button { background: #2a2f3a; border: none; color: white; padding: 10px 18px; border-radius: 30px; font-weight: 600; font-size: 0.85rem; cursor: pointer; transition: 0.2s; border: 1px solid #3a3f4a; }
button:hover { background: #3a3f4a; } button.primary { font-weight: 700; } button:disabled { opacity: 0.4; pointer-events: none; }
.progress-bg { height: 8px; background: #0d1117; border-radius: 10px; margin: 6px 0 12px; overflow: hidden; }
.progress-fill { height: 100%; width: 0%; border-radius: 10px; transition: width 0.3s; }
.race-log { background: #0d1117; border-radius: 16px; padding: 12px; max-height: 250px; overflow-y: auto; font-size: 0.85rem; color: #b0b5c0; margin: 12px 0; }
table { width: 100%; border-collapse: collapse; margin: 10px 0; font-size: 0.85rem; }
th, td { padding: 6px 8px; border-bottom: 1px solid #2a2f3a; text-align: left; } th { color: #8a8f9a; font-weight: 600; }
.race-info { font-size: 0.9rem; color: #a0a5b0; margin-top: 6px; }
.button-group { display: flex; gap: 10px; margin-top: 12px; } .button-group button { flex: 1; }
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); justify-content: center; align-items: center; z-index: 100; }
.modal.active { display: flex; }
.modal-content { background: #1a1f2e; border-radius: 24px; padding: 20px; max-width: 700px; width: 90%; max-height: 80vh; overflow-y: auto; color: #e0e0e0; border: 1px solid #2a2f3a; }
.modal-content h2 { margin-bottom: 12px; }
.modal-content button { margin-top: 12px; }
.f1-theme .tab.active { background: #e10600; border-color: #e10600; }
.f1-theme button.primary { background: #e10600; border-color: #e10600; }
.f1-theme .progress-fill { background: #e10600; }
.f1-theme h1 { background: linear-gradient(to right, #e10600, #ff6b6b); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.fe-theme .tab.active { background: #0066cc; border-color: #0066cc; }
.fe-theme button.primary { background: #0066cc; border-color: #0066cc; }
.fe-theme .progress-fill { background: #0066cc; }
.fe-theme h1 { background: linear-gradient(to right, #0066cc, #00aaff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
</style>
</head>
<body>
<div class="app-container" id="appContainer">
<div id="mainMenu" class="main-menu">
<h2>🏁 Formula Manager 2026</h2>
<div class="menu-buttons">
<button class="menu-btn f1" id="playF1Btn">🏎️ F1 Manager 2026</button>
<button class="menu-btn fe" id="playFEBtn">⚡ Formula E 2025/26</button>
</div>
</div>
<div id="f1Container" class="game-wrapper">
<button class="back-btn f1" id="backFromF1">↩️ Главное меню</button>
<div id="f1Root" class="f1-theme game-container"></div>
</div>
<div id="feContainer" class="game-wrapper">
<button class="back-btn fe" id="backFromFE">↩️ Главное меню</button>
<div id="feRoot" class="fe-theme game-container"></div>
</div>
</div>
<script>
(function() {
function hideAllGames() {
document.getElementById('f1Container').classList.remove('active');
document.getElementById('feContainer').classList.remove('active');
document.getElementById('mainMenu').style.display = 'block';
}
class RacingGame {
constructor(rootId, theme, data) {
this.root = document.getElementById(rootId);
this.theme = theme;
this.data = data;
this.state = {
playerTeamIndex: -1,
isSpectator: true,
teams: JSON.parse(JSON.stringify(data.teams)),
budget: 0,
points: 0,
currentRace: 1,
upgradeCost: 8,
raceInProgress: false,
standings: [],
driverStandings: [],
raceHistory: [],
qualifyingGrid: []
};
this.init();
}
init() {
this.renderStaticHTML();
this.attachEvents();
this.resetStandings();
this.updateUI();
}
renderStaticHTML() {
this.root.innerHTML = `
<h1>${this.data.title}</h1>
<div class="subtitle">${this.data.subtitle}</div>
${this.theme === 'f1' ? `
<div class="single-video">
<iframe src="https://vkvideo.ru/video_ext.php?oid=-90428826&id=456246874&hd=2" width="100%" height="300" frameborder="0" allow="autoplay; encrypted-media; fullscreen; picture-in-picture" allowfullscreen onload="this.style.display='block'; this.nextElementSibling.style.display='none';" onerror="this.style.display='none'; this.nextElementSibling.style.display='inline-flex';"></iframe>
<a class="video-link" href="https://vkvideo.ru/video-90428826_456246874" target="_blank" style="display: inline-flex;">▶️ Открыть видео ВК</a>
</div>` : ''}
<div class="team-selector" id="teamSelectPanel">
<label style="color:#a0a5b0; margin-bottom:6px; display:block;">Выберите команду или режим наблюдателя:</label>
<select id="teamSelect"><option value="">-- Выберите команду --</option></select>
<div id="teamPreview" class="team-preview">
<h3 style="margin:0 0 12px; font-size:1rem;" id="previewConstructor"></h3>
<div class="pilot-row"><span class="pilot-name" id="previewPilot1"></span><span class="pilot-skill" id="previewPilot1Skill"></span></div>
<div class="pilot-row"><span class="pilot-name" id="previewPilot2"></span><span class="pilot-skill" id="previewPilot2Skill"></span></div>
<div id="previewCarStats" style="margin: 10px 0;"></div>
<div style="font-weight:600; margin-top:8px;">Бюджет: <span id="previewBudget" class="money"></span> млн $</div>
</div>
<div class="button-group">
<button id="startCareerBtn" class="primary">🚀 Начать карьеру</button>
<button id="spectatorBtn" class="primary" style="background:#4a4f5a; border-color:#4a4f5a;">👁️ Режим наблюдателя</button>
</div>
</div>
<div id="gameUI" style="display:none;">
<div class="stats-panel" id="spectatorPanel" style="display: flex;">
<div class="stat"><span class="stat-value" id="raceDisplay">1/${this.data.totalRaces}</span><div class="stat-label">Этап</div></div>
<div class="stat"><span class="stat-value" style="color:${this.theme === 'f1' ? '#e10600' : '#0066cc'};">👁️</span><div class="stat-label">Наблюдатель</div></div>
</div>
<div class="stats-panel" id="managerPanel" style="display: none;">
<div class="stat"><span class="stat-value money" id="budgetDisplay">0</span><div class="stat-label">Млн $</div></div>
<div class="stat"><span class="stat-value" id="pointsDisplay">0</span><div class="stat-label">Очки команды</div></div>
<div class="stat"><span class="stat-value" id="raceDisplay2">1/${this.data.totalRaces}</span><div class="stat-label">Этап</div></div>
<div class="stat"><span class="stat-value" id="teamNameDisplay">-</span><div class="stat-label">Команда</div></div>
</div>
<div class="tabs" id="tabsContainer"></div>
<div id="teamView" class="view">
<div class="card"><h3>🏆 Пилоты</h3><div id="pilot1Info" class="pilot-row"></div><div id="pilot2Info" class="pilot-row"></div><div style="margin-top:12px; font-size:0.85rem; color:#8a8f9a;">Свободные агенты (3 млн за контракт)</div></div>
<div class="card"><h3>⚙️ Характеристики машины</h3><div id="carStats"></div></div>
</div>
<div id="upgradesView" class="view">
<div class="card"><h3>📈 Улучшения (8 млн $)</h3><div id="upgradeList"></div></div>
</div>
<div id="raceView" class="view">
<div class="card">
<h3>📍 <span id="grandPrixName"></span></h3>
<div class="race-info"><div>🏟️ <span id="trackName"></span></div><div>📅 <span id="raceDate"></span></div><div>🔄 Кругов: <span id="raceLaps"></span></div></div>
<button id="startRaceBtn" class="primary" style="width:100%; margin-top:16px;">🚦 Старт уик-энда</button>
<div id="raceProgress" style="display:none; margin-top:16px;"><div class="progress-bg"><div id="raceProgressBar" class="progress-fill" style="width:0%"></div></div><div id="raceStatusText" class="stat-label" style="text-align:center;">Квалификация...</div></div>
<div id="raceLog" class="race-log" style="display:none;"></div>
<div id="raceResult" style="margin-top:12px;"></div>
</div>
</div>
<div id="standingsView" class="view"><div class="card"><h3>🏆 Кубок конструкторов</h3><div id="standingsTable"></div></div></div>
<div id="driversView" class="view"><div class="card"><h3>👤 Личный зачёт пилотов</h3><div id="driversTable"></div></div></div>
<div id="historyView" class="view">
<div class="card"><h3>📋 Результаты гонок</h3><select id="historySelect" style="margin-bottom:15px;"><option value="">-- Выберите этап --</option></select><div id="historyTable"></div></div>
</div>
<div id="newsView" class="view">
<div class="card">
<h3>📰 Новости</h3>
<button id="refreshNewsBtn" class="primary" style="margin-bottom:12px;">🔄 Обновить новости</button>
<div id="newsFeed"></div>
</div>
</div>
</div>
<div id="statsModal" class="modal">
<div class="modal-content">
<h2>🏁 <span id="modalRaceName"></span></h2>
<div id="modalTable"></div>
<button id="closeModalBtn" class="primary">Закрыть</button>
</div>
</div>
`;
const select = this.root.querySelector('#teamSelect');
this.data.teams.forEach((team, idx) => {
const opt = document.createElement('option');
opt.value = idx; opt.textContent = team.name;
select.appendChild(opt);
});
}
attachEvents() {
const root = this.root;
root.querySelector('#teamSelect').addEventListener('change', (e) => this.showTeamPreview(parseInt(e.target.value)));
root.querySelector('#startCareerBtn').addEventListener('click', () => {
const idx = parseInt(root.querySelector('#teamSelect').value);
if (isNaN(idx) || idx < 0) { alert('Выберите команду!'); return; }
this.setMode(true, idx);
});
root.querySelector('#spectatorBtn').addEventListener('click', () => this.setMode(false));
root.querySelector('#startRaceBtn').addEventListener('click', () => this.simulateRace());
root.querySelector('#closeModalBtn').addEventListener('click', () => root.querySelector('#statsModal').classList.remove('active'));
root.querySelector('#historySelect').addEventListener('change', (e) => this.showHistoryTable(e.target.value));
root.querySelector('#refreshNewsBtn').addEventListener('click', () => this.generateNews());
root.addEventListener('click', (e) => {
if (e.target.classList.contains('hire-btn')) this.hirePilot(parseInt(e.target.dataset.pilotIndex));
if (e.target.classList.contains('upgrade-btn')) this.applyUpgrade(e.target.dataset.key);
});
window.addEventListener('click', (e) => {
if (e.target === root.querySelector('#statsModal')) root.querySelector('#statsModal').classList.remove('active');
});
}
generateNews() {
const newsDiv = this.root.querySelector('#newsFeed');
const templates = [
"{driver} из команды {team} одержал победу в последнем Гран-при!",
"Слухи: {driver} может перейти в {team2} в следующем сезоне.",
"Обзор: {team} представила обновлённую аэродинамику на этапе в {location}.",
"Контракт: {driver} продлил соглашение с {team} на 2 года.",
"Технический сбой: у {driver} отказал двигатель на прогревочном круге.",
"Погода внесла коррективы: гонка в {location} прошла под дождём.",
"Интервью: {driver} заявил, что машина стала быстрее после обновлений.",
"{team} объявила о партнёрстве с новым спонсором.",
"Столкновение: {driver} и {driver2} сошли с трассы на первом круге.",
"Квалификация: {driver} завоевал поул-позицию в {grandPrix}."
];
const drivers = this.state.teams.flatMap(t => t.drivers.map(d => d.name));
const teams = this.state.teams.map(t => t.name);
const locations = this.data.schedule.map(s => s.location);
let html = '';
for (let i = 0; i < 5; i++) {
const template = templates[Math.floor(Math.random() * templates.length)];
let news = template
.replace(/{driver}/g, () => drivers[Math.floor(Math.random() * drivers.length)])
.replace(/{driver2}/g, () => drivers[Math.floor(Math.random() * drivers.length)])
.replace(/{team}/g, () => teams[Math.floor(Math.random() * teams.length)])
.replace(/{team2}/g, () => teams[Math.floor(Math.random() * teams.length)])
.replace(/{location}/g, () => locations[Math.floor(Math.random() * locations.length)])
.replace(/{grandPrix}/g, () => this.data.schedule[Math.floor(Math.random() * this.data.schedule.length)].grandPrix);
html += `<div class="card" style="margin-bottom:8px; padding:10px;">${news}</div>`;
}
newsDiv.innerHTML = html;
}
showTeamPreview(index) {
const previewDiv = this.root.querySelector('#teamPreview');
if (isNaN(index) || index < 0) { previewDiv.classList.remove('active'); return; }
const team = this.data.teams[index];
this.root.querySelector('#previewConstructor').textContent = team.constructor;
this.root.querySelector('#previewPilot1').textContent = team.drivers[0].name;
this.root.querySelector('#previewPilot1Skill').textContent = `Мастерство: ${team.drivers[0].skill}`;
this.root.querySelector('#previewPilot2').textContent = team.drivers[1].name;
this.root.querySelector('#previewPilot2Skill').textContent = `Мастерство: ${team.drivers[1].skill}`;
const carDiv = this.root.querySelector('#previewCarStats');
carDiv.innerHTML = '';
['Аэродинамика','Двигатель','Шасси','Надёжность'].forEach((label,i) => {
const keys = ['aero','engine','chassis','reliability'];
const val = team.car[keys[i]];
carDiv.innerHTML += `<div style="margin-bottom:6px;"><div style="display:flex;justify-content:space-between;"><span>${label}</span><span>${val}</span></div><div class="progress-bg"><div class="progress-fill" style="width:${val}%;"></div></div></div>`;
});
this.root.querySelector('#previewBudget').textContent = team.budget;
previewDiv.classList.add('active');
}
resetStandings() {
this.state.standings = this.state.teams.map(t => ({ teamName: t.name, points: 0 }));
this.state.driverStandings = [];
this.state.teams.forEach(t => t.drivers.forEach(d => this.state.driverStandings.push({ name: d.name, teamName: t.name, points: 0 })));
this.state.raceHistory = [];
this.state.qualifyingGrid = [];
}
setMode(careerMode, teamIndex = -1) {
this.state.isSpectator = !careerMode;
this.state.playerTeamIndex = careerMode ? teamIndex : -1;
this.state.budget = careerMode ? this.data.teams[teamIndex].budget : 0;
this.state.points = 0;
this.state.currentRace = 1;
this.resetStandings();
this.root.querySelector('#teamSelectPanel').style.display = 'none';
this.root.querySelector('#gameUI').style.display = 'block';
if (careerMode) {
this.root.querySelector('#managerPanel').style.display = 'flex';
this.root.querySelector('#spectatorPanel').style.display = 'none';
this.root.querySelector('#teamNameDisplay').textContent = this.data.teams[teamIndex].constructor;
this.root.querySelector('#tabsContainer').innerHTML = `
<div class="tab active" data-view="team">👥 Команда</div>
<div class="tab" data-view="upgrades">🔧 Разработка</div>
<div class="tab" data-view="race">🏁 Гонка</div>
<div class="tab" data-view="standings">📊 Команды</div>
<div class="tab" data-view="drivers">👤 Пилоты</div>
<div class="tab" data-view="history">📋 Гонки</div>
<div class="tab" data-view="news">📰 Новости</div>
`;
this.showView('team');
} else {
this.root.querySelector('#managerPanel').style.display = 'none';
this.root.querySelector('#spectatorPanel').style.display = 'flex';
this.root.querySelector('#teamView').style.display = 'none';
this.root.querySelector('#upgradesView').style.display = 'none';
this.root.querySelector('#tabsContainer').innerHTML = `
<div class="tab active" data-view="race">🏁 Гонка</div>
<div class="tab" data-view="standings">📊 Команды</div>
<div class="tab" data-view="drivers">👤 Пилоты</div>
<div class="tab" data-view="history">📋 Гонки</div>
<div class="tab" data-view="news">📰 Новости</div>
`;
this.showView('race');
}
this.attachTabEvents();
this.updateUI();
}
attachTabEvents() {
this.root.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
this.root.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
this.showView(tab.dataset.view);
});
});
}
showView(viewName) {
const views = this.root.querySelectorAll('.view');
views.forEach(v => v.classList.remove('active'));
const target = this.root.querySelector(`#${viewName}View`);
if (target) {
target.classList.add('active');
if (viewName === 'history') this.updateHistoryView();
if (viewName === 'news') this.generateNews();
}
}
updateUI() {
const root = this.root;
root.querySelector('#raceDisplay').textContent = `${this.state.currentRace}/${this.data.totalRaces}`;
const rd2 = root.querySelector('#raceDisplay2');
if (rd2) rd2.textContent = `${this.state.currentRace}/${this.data.totalRaces}`;
if (this.state.currentRace <= this.data.totalRaces) {
const gp = this.data.schedule[this.state.currentRace-1];
root.querySelector('#grandPrixName').textContent = gp.grandPrix;
root.querySelector('#trackName').textContent = gp.track ? `${gp.track} (${gp.location})` : gp.location;
root.querySelector('#raceDate').textContent = gp.date || '';
root.querySelector('#raceLaps').textContent = gp.laps;
}
if (!this.state.isSpectator && this.state.playerTeamIndex !== -1) {
const team = this.state.teams[this.state.playerTeamIndex];
root.querySelector('#budgetDisplay').textContent = this.state.budget;
root.querySelector('#pointsDisplay').textContent = this.state.points;
this.renderPilot('pilot1Info', team.drivers[0], 0);
this.renderPilot('pilot2Info', team.drivers[1], 1);
const carDiv = root.querySelector('#carStats');
carDiv.innerHTML = '';
['Аэродинамика','Двигатель','Шасси','Надёжность'].forEach((label,i) => {
const keys = ['aero','engine','chassis','reliability'];
const val = team.car[keys[i]];
carDiv.innerHTML += `<div style="margin-bottom:10px;"><div style="display:flex;justify-content:space-between;"><span>${label}</span><span>${val}</span></div><div class="progress-bg"><div class="progress-fill" style="width:${val}%;"></div></div></div>`;
});
const upDiv = root.querySelector('#upgradeList');
upDiv.innerHTML = '';
['Аэродинамика','Двигатель','Шасси','Надёжность'].forEach((label,i) => {
const keys = ['aero','engine','chassis','reliability'];
const cur = team.car[keys[i]];
const can = this.state.budget >= this.state.upgradeCost && cur < 99;
upDiv.innerHTML += `<div class="upgrade-row"><span>${label} <span class="upgrade-stat">${cur}</span> → ${Math.min(cur+7,99)}</span><button class="upgrade-btn" data-key="${keys[i]}" ${can?'':'disabled'}>+7 (8 млн)</button></div>`;
});
}
const btn = root.querySelector('#startRaceBtn');
if (this.state.raceInProgress) { btn.disabled = true; btn.textContent = '🏎️ Гонка идёт...'; }
else if (this.state.currentRace > this.data.totalRaces) { btn.disabled = true; btn.textContent = '🏆 Сезон завершён'; }
else { btn.disabled = false; btn.textContent = '🚦 Старт уик-энда'; }
this.renderStandings();
this.renderDriverStandings();
if (root.querySelector('#historyView').classList.contains('active')) this.updateHistoryView();
}
renderPilot(id, pilot, idx) {
const el = this.root.querySelector(`#${id}`);
if (!el) return;
el.innerHTML = `<div><div class="pilot-name">${pilot.name}</div><div class="pilot-skill">Мастерство: ${pilot.skill}</div></div><button class="hire-btn" data-pilot-index="${idx}" ${this.state.budget<3?'disabled':''}>🔄 Нанять (3 млн)</button>`;
}
renderStandings() {
const sorted = [...this.state.standings].sort((a,b) => b.points - a.points);
let html = '<table><tr><th>#</th><th>Команда</th><th>Очки</th></tr>';
sorted.forEach((s,i) => html += `<tr><td>${i+1}</td><td>${s.teamName}</td><td>${s.points}</td></tr>`);
this.root.querySelector('#standingsTable').innerHTML = html + '</table>';
}
renderDriverStandings() {
const sorted = [...this.state.driverStandings].sort((a,b) => b.points - a.points);
let html = '<table><tr><th>#</th><th>Пилот</th><th>Команда</th><th>Очки</th></tr>';
sorted.forEach((d,i) => html += `<tr><td>${i+1}</td><td>${d.name}</td><td>${d.teamName}</td><td>${d.points}</td></tr>`);
this.root.querySelector('#driversTable').innerHTML = html + '</table>';
}
updateHistoryView() {
const select = this.root.querySelector('#historySelect');
const tableDiv = this.root.querySelector('#historyTable');
select.innerHTML = '<option value="">-- Выберите этап --</option>';
this.state.raceHistory.forEach((race, index) => {
const option = document.createElement('option');
option.value = index; option.textContent = race.grandPrix;
select.appendChild(option);
});
if (this.state.raceHistory.length > 0) {
select.value = this.state.raceHistory.length - 1;
this.showHistoryTable(this.state.raceHistory.length - 1);
} else {
tableDiv.innerHTML = '<p style="color:#8a8f9a;">Нет завершённых гонок.</p>';
}
}
showHistoryTable(index) {
const tableDiv = this.root.querySelector('#historyTable');
if (this.state.raceHistory[index]) {
const race = this.state.raceHistory[index];
let html = `<p style="margin-bottom:8px; font-weight:600;">🏁 ${race.grandPrix}</p>`;
html += `<table><tr><th>#</th><th>Пилот</th><th>Команда</th><th>Старт</th><th>Финиш</th></tr>`;
race.results.forEach((r, i) => {
const start = r.startPosition || '–';
const finish = r.dnf ? '💔 DNF' : `P${r.position}`;
html += `<tr><td>${i+1}</td><td>${r.name}</td><td>${r.team}</td><td>${start}</td><td>${finish}</td></tr>`;
});
html += '</table>';
tableDiv.innerHTML = html;
}
}
runQualifying() {
const drivers = [];
this.state.teams.forEach(team => {
team.drivers.forEach(driver => {
drivers.push({ name: driver.name, skill: driver.skill, team: team.name, car: team.car });
});
});
const getPace = d => {
const carAvg = (d.car.aero + d.car.engine + d.car.chassis) / 3;
return d.skill * 0.65 + carAvg * 0.35;
};
const attempt = d => getPace(d) + (Math.random() * 1.2 - 0.6);
const q1 = drivers.map(d => ({ ...d, time: attempt(d) })).sort((a,b) => b.time - a.time);
const q1Eliminated = q1.slice(17);
const q2Participants = q1.slice(0, 17);
const q2 = q2Participants.map(d => ({ ...d, time: attempt(d) })).sort((a,b) => b.time - a.time);
const q2Eliminated = q2.slice(11);
const q3Participants = q2.slice(0, 11);
const q3 = q3Participants.map(d => ({ ...d, time: attempt(d) })).sort((a,b) => b.time - a.time);
const grid = [];
q3.forEach((d, i) => grid.push({ name: d.name, team: d.team, gridPos: i+1 }));
q2Eliminated.forEach((d, i) => grid.push({ name: d.name, team: d.team, gridPos: 12 + i }));
q1Eliminated.forEach((d, i) => grid.push({ name: d.name, team: d.team, gridPos: 18 + i }));
return grid;
}
simulateRace() {
if (this.state.raceInProgress || this.state.currentRace > this.data.totalRaces) return;
this.state.raceInProgress = true;
this.updateUI();
const root = this.root;
const logEl = root.querySelector('#raceLog');
const bar = root.querySelector('#raceProgressBar');
const status = root.querySelector('#raceStatusText');
root.querySelector('#raceProgress').style.display = 'block';
logEl.style.display = 'block';
logEl.innerHTML = '';
root.querySelector('#raceResult').innerHTML = '';
bar.style.width = '0%';
status.textContent = 'Квалификация...';
const log = msg => {
const div = document.createElement('div');
div.textContent = '🔹 ' + msg;
logEl.appendChild(div);
logEl.scrollTop = logEl.scrollHeight;
};
const grid = this.runQualifying();
this.state.qualifyingGrid = grid;
log('📋 Стартовая решётка:');
grid.forEach(d => log(`${d.gridPos}. ${d.name} (${d.team})`));
const allDrivers = grid.map(g => {
const team = this.state.teams.find(t => t.name === g.team);
const driver = team.drivers.find(d => d.name === g.name);
return {
name: g.name, skill: driver?.skill || 80,
teamName: g.team,
teamIdx: this.state.teams.findIndex(t => t.name === g.team),
car: team?.car,
startPosition: g.gridPos,
position: 0, dnf: false
};
});
allDrivers.forEach(d => d.power = d.skill * 0.55 + (d.car.aero + d.car.engine + d.car.chassis)/3 * 0.45);
const totalLaps = this.data.schedule[this.state.currentRace-1].laps;
let lap = 0;
log(`🚥 ${this.data.schedule[this.state.currentRace-1].grandPrix} стартовал! (${totalLaps} кругов)`);
const interval = setInterval(() => {
lap++;
bar.style.width = (lap/totalLaps*100)+'%';
status.textContent = `Круг ${lap}/${totalLaps}`;
allDrivers.forEach(d => {
if (d.dnf) return;
if (Math.random() < Math.max(0, (100 - d.car.reliability)/100) * 0.025) {
d.dnf = true;
log(`💥 ${d.name} (${d.teamName}) сошёл!`);
}
});
const active = allDrivers.filter(d => !d.dnf);
active.sort((a,b) => (b.power + Math.random()*10) - (a.power + Math.random()*10));
active.forEach((d, idx) => d.position = idx+1);
if (lap >= totalLaps) {
clearInterval(interval);
this.finishRace(allDrivers, log);
}
}, 120);
}
finishRace(allDrivers, log) {
this.state.raceInProgress = false;
this.root.querySelector('#raceStatusText').textContent = '🏁 Финиш!';
const raceResultData = allDrivers.map(d => ({
name: d.name, team: d.teamName,
startPosition: d.startPosition,
position: d.position, dnf: d.dnf
})).sort((a,b) => a.position - b.position);
this.state.raceHistory.push({
grandPrix: this.data.schedule[this.state.currentRace-1].grandPrix,
results: raceResultData
});
const pointsTable = [25,18,15,12,10,8,6,4,2,1];
const teamPts = {};
allDrivers.forEach(d => { if (!d.dnf && d.position <= 10) teamPts[d.teamIdx] = (teamPts[d.teamIdx]||0) + pointsTable[d.position-1]; });
for (const [idx, pts] of Object.entries(teamPts)) {
const i = parseInt(idx);
const stand = this.state.standings.find(s => s.teamName === this.state.teams[i].name);
if (stand) stand.points += pts;
if (!this.state.isSpectator && i === this.state.playerTeamIndex) this.state.points += pts;
}
allDrivers.forEach(d => {
if (!d.dnf && d.position <= 10) {
const entry = this.state.driverStandings.find(ds => ds.name === d.name && ds.teamName === d.teamName);
if (entry) entry.points += pointsTable[d.position-1];
else this.state.driverStandings.push({ name: d.name, teamName: d.teamName, points: pointsTable[d.position-1] });
}
});
if (!this.state.isSpectator && this.state.playerTeamIndex !== -1) {
const myDrivers = allDrivers.filter(d => d.teamIdx === this.state.playerTeamIndex);
let prize = 5; myDrivers.forEach(d => { if (!d.dnf && d.position <= 10) prize += (11 - d.position); });
this.state.budget += prize; this.state.budget -= this.state.teams[this.state.playerTeamIndex].drivers.length * 1;
log(`💰 Призовые: +${prize} млн | Зарплаты: -${this.state.teams[this.state.playerTeamIndex].drivers.length} млн`);
}
this.state.currentRace++;
this.updateUI();
this.showStatsModal(this.state.qualifyingGrid, raceResultData);
}
showStatsModal(grid, raceResults) {
const root = this.root;
root.querySelector('#modalRaceName').textContent = this.data.schedule[this.state.currentRace-2]?.grandPrix || 'Последняя гонка';
let html = '<h3>📋 Стартовая решётка</h3><table><tr><th>#</th><th>Пилот</th><th>Команда</th></tr>';
grid.forEach(d => html += `<tr><td>${d.gridPos}</td><td>${d.name}</td><td>${d.team}</td></tr>`);
html += '</table><br><h3>🏁 Результаты гонки</h3>';
html += '<table><tr><th>#</th><th>Пилот</th><th>Команда</th><th>Старт</th><th>Финиш</th></tr>';
raceResults.forEach((r, i) => {
const start = r.startPosition || '–';
const finish = r.dnf ? '💔 DNF' : `P${r.position}`;
html += `<tr><td>${i+1}</td><td>${r.name}</td><td>${r.team}</td><td>${start}</td><td>${finish}</td></tr>`;
});
html += '</table>';
root.querySelector('#modalTable').innerHTML = html;
root.querySelector('#statsModal').classList.add('active');
}
hirePilot(index) {
if (this.state.raceInProgress || this.state.budget < 3 || this.state.playerTeamIndex === -1 || this.state.isSpectator) return;
const newDriver = this.data.freeAgents[Math.floor(Math.random()*this.data.freeAgents.length)];
this.state.budget -= 3;
const team = this.state.teams[this.state.playerTeamIndex];
const oldName = team.drivers[index].name;
this.state.driverStandings = this.state.driverStandings.filter(d => !(d.name === oldName && d.teamName === team.name));
this.state.driverStandings.push({ name: newDriver.name, teamName: team.name, points: 0 });
team.drivers[index] = { name: newDriver.name, skill: newDriver.skill };
this.updateUI();
}
applyUpgrade(key) {
if (this.state.raceInProgress || this.state.budget < this.state.upgradeCost || this.state.playerTeamIndex === -1 || this.state.isSpectator) return;
const team = this.state.teams[this.state.playerTeamIndex];
if (team.car[key] >= 99) return;
this.state.budget -= this.state.upgradeCost;
team.car[key] = Math.min(99, team.car[key]+7);
this.updateUI();
}
}
const f1Data = {
title: '🏎️ F1 MANAGER 2026',
subtitle: 'Классическая симуляция · Gucci Lamborghini · Календарь 2026',
totalRaces: 24,
teams: [
{ name: "BWT Alpine F1 Team", constructor: "Alpine", drivers: [{ name: "Пьер Гасли", skill: 87 }, { name: "Франко Колапинто", skill: 84 }], car: { aero: 80, engine: 78, chassis: 77, reliability: 80 }, budget: 52 },
{ name: "Aston Martin Aramco Honda", constructor: "Aston Martin", drivers: [{ name: "Фернандо Алонсо", skill: 88 }, { name: "Лэнс Стролл", skill: 79 }], car: { aero: 70, engine: 72, chassis: 70, reliability: 72 }, budget: 45 },
{ name: "Audi Revolut F1 Team", constructor: "Audi", drivers: [{ name: "Габриэль Бортолето", skill: 83 }, { name: "Нико Хюлькенберг", skill: 86 }], car: { aero: 72, engine: 70, chassis: 70, reliability: 74 }, budget: 46 },
{ name: "Cadillac Formula 1 Team", constructor: "Cadillac", drivers: [{ name: "Серхио Перес", skill: 84 }, { name: "Валттери Боттас", skill: 86 }], car: { aero: 71, engine: 73, chassis: 70, reliability: 73 }, budget: 44 },
{ name: "Scuderia Ferrari HP", constructor: "Ferrari", drivers: [{ name: "Шарль Леклер", skill: 91 }, { name: "Льюис Хэмилтон", skill: 94 }], car: { aero: 93, engine: 92, chassis: 91, reliability: 88 }, budget: 65 },
{ name: "TGR Haas F1 Team", constructor: "Haas", drivers: [{ name: "Эстебан Окон", skill: 81 }, { name: "Оливер Берман", skill: 79 }], car: { aero: 70, engine: 72, chassis: 68, reliability: 73 }, budget: 42 },
{ name: "McLaren Mastercard Formula 1 Team", constructor: "McLaren", drivers: [{ name: "Ландо Норрис", skill: 92 }, { name: "Оскар Пиастри", skill: 90 }], car: { aero: 91, engine: 90, chassis: 90, reliability: 86 }, budget: 62 },
{ name: "Mercedes-AMG Petronas F1 Team", constructor: "Mercedes", drivers: [{ name: "Джордж Расселл", skill: 93 }, { name: "Кими Антонелли", skill: 90 }], car: { aero: 94, engine: 94, chassis: 93, reliability: 90 }, budget: 68 },
{ name: "Gucci Lamborghini F1 Team", constructor: "Lamborghini", drivers: [{ name: "Лиам Лоусон", skill: 85 }, { name: "Никола Цолов", skill: 81 }], car: { aero: 79, engine: 77, chassis: 76, reliability: 79 }, budget: 50 },
{ name: "Oracle Red Bull Racing", constructor: "Red Bull Racing", drivers: [{ name: "Макс Ферстаппен", skill: 95 }, { name: "Изак Хаджар", skill: 88 }], car: { aero: 90, engine: 91, chassis: 89, reliability: 87 }, budget: 64 },
{ name: "Atlassian Williams F1 Team", constructor: "Williams", drivers: [{ name: "Александр Албон", skill: 82 }, { name: "Карлос Сайнс-мл.", skill: 83 }], car: { aero: 73, engine: 74, chassis: 72, reliability: 75 }, budget: 44 }
],
schedule: [
{ country: "🇦🇺", grandPrix: "Гран-при Австралии", location: "Мельбурн", track: "Альберт-Парк", date: "16 марта", laps: 58 },
{ country: "🇨🇳", grandPrix: "Гран-при Китая", location: "Шанхай", track: "Международный автодром Шанхая", date: "23 марта", laps: 56 },
{ country: "🇯🇵", grandPrix: "Гран-при Японии", location: "Судзука", track: "Судзука", date: "6 апреля", laps: 53 },
{ country: "🇧🇭", grandPrix: "Гран-при Бахрейна", location: "Сахир", track: "Международный автодром Бахрейна", date: "13 апреля", laps: 57 },
{ country: "🇸🇦", grandPrix: "Гран-при Саудовской Аравии", location: "Джидда", track: "Городская трасса Джидда", date: "20 апреля", laps: 50 },
{ country: "🇺🇸", grandPrix: "Гран-при Майами", location: "Майами", track: "Международный автодром Майами", date: "4 мая", laps: 57 },
{ country: "🇮🇹", grandPrix: "Гран-при Эмилии-Романьи", location: "Имола", track: "Автодром Энцо и Дино Феррари", date: "18 мая", laps: 63 },
{ country: "🇲🇨", grandPrix: "Гран-при Монако", location: "Монте-Карло", track: "Городская трасса Монте-Карло", date: "25 мая", laps: 78 },
{ country: "🇪🇸", grandPrix: "Гран-при Испании", location: "Монмело", track: "Барселона-Каталунья", date: "1 июня", laps: 66 },
{ country: "🇨🇦", grandPrix: "Гран-при Канады", location: "Монреаль", track: "Автодром имени Жиля Вильнёва", date: "15 июня", laps: 70 },
{ country: "🇦🇹", grandPrix: "Гран-при Австрии", location: "Шпильберг", track: "Ред Булл Ринг", date: "29 июня", laps: 71 },
{ country: "🇬🇧", grandPrix: "Гран-при Великобритании", location: "Силверстон", track: "Сильверстоун", date: "6 июля", laps: 52 },
{ country: "🇧🇪", grandPrix: "Гран-при Бельгии", location: "Спа", track: "Спа-Франкоршам", date: "27 июля", laps: 44 },
{ country: "🇭🇺", grandPrix: "Гран-при Венгрии", location: "Будапешт", track: "Хунгароринг", date: "3 августа", laps: 70 },
{ country: "🇳🇱", grandPrix: "Гран-при Нидерландов", location: "Зандворт", track: "Зандворт", date: "31 августа", laps: 72 },
{ country: "🇮🇹", grandPrix: "Гран-при Италии", location: "Монца", track: "Монца", date: "7 сентября", laps: 53 },
{ country: "🇦🇿", grandPrix: "Гран-при Азербайджана", location: "Баку", track: "Городская трасса Баку", date: "21 сентября", laps: 51 },
{ country: "🇸🇬", grandPrix: "Гран-при Сингапура", location: "Сингапур", track: "Марина-Бей", date: "5 октября", laps: 62 },
{ country: "🇺🇸", grandPrix: "Гран-при США", location: "Остин", track: "Трасса Америк", date: "19 октября", laps: 56 },
{ country: "🇲🇽", grandPrix: "Гран-при Мехико", location: "Мехико", track: "Автодром имени братьев Родригес", date: "26 октября", laps: 71 },
{ country: "🇧🇷", grandPrix: "Гран-при Сан-Паулу", location: "Сан-Паулу", track: "Интерлагос", date: "9 ноября", laps: 71 },
{ country: "🇺🇸", grandPrix: "Гран-при Лас-Вегаса", location: "Лас-Вегас", track: "Городская трасса Лас-Вегас", date: "22 ноября", laps: 50 },
{ country: "🇶🇦", grandPrix: "Гран-при Катара", location: "Лусаил", track: "Международный автодром Лусаил", date: "30 ноября", laps: 57 },
{ country: "🇦🇪", grandPrix: "Гран-при Абу-Даби", location: "Абу-Даби", track: "Яс Марина", date: "7 декабря", laps: 58 }
],
freeAgents: [
{ name: "Даниэль Риккардо", skill: 80 }, { name: "Мик Шумахер", skill: 77 },
{ name: "Роберт Шварцман", skill: 76 }, { name: "Тео Пуршер", skill: 75 }, { name: "Джек Дуэн", skill: 74 }
]
};
const feData = {
title: '⚡ FORMULA E MANAGER 2025/26',
subtitle: 'Синие тона · Реальные команды и пилоты · 16 этапов',
totalRaces: 16,
teams: [
{ name: "Jaguar TCS Racing", constructor: "Jaguar", drivers: [{ name: "Митч Эванс", skill: 95 }, { name: "Антониу Феликс да Кошта", skill: 92 }], car: { aero: 90, engine: 92, chassis: 91, reliability: 88 }, budget: 58 },
{ name: "Porsche Formula E Team", constructor: "Porsche", drivers: [{ name: "Паскаль Верляйн", skill: 96 }, { name: "Нико Мюллер", skill: 90 }], car: { aero: 91, engine: 93, chassis: 92, reliability: 87 }, budget: 60 },
{ name: "Mahindra Racing", constructor: "Mahindra", drivers: [{ name: "Эдоардо Мортара", skill: 89 }, { name: "Ник де Врис", skill: 90 }], car: { aero: 88, engine: 87, chassis: 89, reliability: 86 }, budget: 55 },
{ name: "Andretti Formula E", constructor: "Andretti", drivers: [{ name: "Джейк Деннис", skill: 91 }, { name: "Фелипе Другович", skill: 89 }], car: { aero: 89, engine: 88, chassis: 88, reliability: 85 }, budget: 56 },
{ name: "Nissan Formula E Team", constructor: "Nissan", drivers: [{ name: "Оливер Роуленд", skill: 92 }, { name: "Норман Нато", skill: 78 }], car: { aero: 87, engine: 89, chassis: 86, reliability: 84 }, budget: 54 },
{ name: "Envision Racing", constructor: "Envision", drivers: [{ name: "Себастьен Буэми", skill: 86 }, { name: "Джоэль Эрикссон", skill: 82 }], car: { aero: 82, engine: 83, chassis: 81, reliability: 82 }, budget: 50 },
{ name: "Citroën Racing", constructor: "Citroën", drivers: [{ name: "Ник Кэссиди", skill: 84 }, { name: "Жан-Эрик Вернь", skill: 84 }], car: { aero: 84, engine: 85, chassis: 84, reliability: 83 }, budget: 52 },
{ name: "Cupra Kiro", constructor: "Cupra Kiro", drivers: [{ name: "Хосеп-Мария Марти", skill: 83 }, { name: "Дэн Тиктум", skill: 75 }], car: { aero: 79, engine: 80, chassis: 78, reliability: 79 }, budget: 48 },
{ name: "DS Penske", constructor: "DS Penske", drivers: [{ name: "Тейлор Барнард", skill: 75 }, { name: "Максимилиан Гюнтер", skill: 75 }], car: { aero: 75, engine: 76, chassis: 74, reliability: 77 }, budget: 46 },
{ name: "Lola Yamaha ABT Formula E Team", constructor: "Lola Yamaha ABT", drivers: [{ name: "Лукас ди Грасси", skill: 78 }, { name: "Зейн Мэлони", skill: 72 }], car: { aero: 74, engine: 78, chassis: 73, reliability: 76 }, budget: 45 }
],
schedule: [
{ country: "🇲🇽", grandPrix: "Mexico City E-Prix", location: "Мехико", laps: 36 },
{ country: "🇸🇦", grandPrix: "Diriyah E-Prix", location: "Джидда", laps: 37 },
{ country: "🇧🇷", grandPrix: "São Paulo E-Prix", location: "Сан-Паулу", laps: 34 },
{ country: "🇩🇪", grandPrix: "Berlin E-Prix", location: "Берлин", laps: 40 },
{ country: "🇲🇨", grandPrix: "Monaco E-Prix", location: "Монте-Карло", laps: 29 },
{ country: "🇨🇳", grandPrix: "Shanghai E-Prix", location: "Шанхай", laps: 32 },
{ country: "🇮🇹", grandPrix: "Rome E-Prix", location: "Рим", laps: 27 },
{ country: "🇬🇧", grandPrix: "London E-Prix", location: "Лондон", laps: 35 },
{ country: "🇺🇸", grandPrix: "New York E-Prix", location: "Нью-Йорк", laps: 38 },
{ country: "🇫🇷", grandPrix: "Paris E-Prix", location: "Париж", laps: 31 },
{ country: "🇰🇷", grandPrix: "Seoul E-Prix", location: "Сеул", laps: 33 },
{ country: "🇮🇩", grandPrix: "Jakarta E-Prix", location: "Джакарта", laps: 30 },
{ country: "🇿🇦", grandPrix: "Cape Town E-Prix", location: "Кейптаун", laps: 32 },
{ country: "🇦🇷", grandPrix: "Buenos Aires E-Prix", location: "Буэнос-Айрес", laps: 34 },
{ country: "🇯🇵", grandPrix: "Tokyo E-Prix", location: "Токио", laps: 35 },
{ country: "🇦🇺", grandPrix: "Sydney E-Prix", location: "Сидней", laps: 33 }
],
freeAgents: [
{ name: "Стоффель Вандорн", skill: 82 }, { name: "Робин Фряйнс", skill: 79 },
{ name: "Андре Лоттерер", skill: 77 }
]
};
let f1Game, feGame;
document.getElementById('playF1Btn').addEventListener('click', () => {
document.getElementById('mainMenu').style.display = 'none';
document.getElementById('f1Container').classList.add('active');
if (!f1Game) f1Game = new RacingGame('f1Root', 'f1', f1Data);
});
document.getElementById('playFEBtn').addEventListener('click', () => {
document.getElementById('mainMenu').style.display = 'none';
document.getElementById('feContainer').classList.add('active');
if (!feGame) feGame = new RacingGame('feRoot', 'fe', feData);
});
document.getElementById('backFromF1').addEventListener('click', hideAllGames);
document.getElementById('backFromFE').addEventListener('click', hideAllGames);
})();
</script>
</body>
</html>
Инструкция по открытию игры:

Также я жду фидбека по поводу самой игры и возможным предложениям по самой игре. Буду рад услышать.
Десяток скриншотов по самой игре:









Прошло приличное количества времени с момента заморозки стратегической игры и перед фактическим началом я бы хотел сделать парочку релизов, задать пару вопросов и освежить память для себя и игроков
1. Игроки:
ССРВ (Вландия) - Mofistus
США (Арказия) - 13th Raven Gendarme
Деерская Республика - аболтус
Тропико - Селектронски
Дальноруссия - Не Топлес
Отмечаю вас, чтобы напомнить и заодно уточню (напишите в лс или в коментариях) хотите ли вы продолжить игру?
Если вы хотите отказаться, то можете это сделать и я заменю вас другим игроком (Если же те, кто следит за игрой, желают быть своего рода в "резерве" - вы сможете получить роль или занять место отказавшегося
2. Платформа:
Хотел уточнить у игроков, где проводить игру. Telegram был бы удобнее и практичнее для ведения игры, однако данный вопрос оставлю на усмотрение участников, я лишь высказал рекомендации.
3. Механики
Вероятно многие забыли основные концепции и механики игры вы можете освежить память здесь:
Что же касается новых механик и определенной переработки стратегии то вот, что было сделано за прошедшее время:
1. Была изменена и отредактирована глобальная карта, где были добавлены ранее не вписанные в лор страны.
2. Переработка системы решений. Теперь помимо прежний концепции "Событие - Варианты действий" Будут те событии, где игрок сможет определенно импровизировать, а не полагаться на заранее подготовленные ответы.
3. "Энциклопедия" и расширенная статистика. Сам я планировал создание более творческой и свободной стратегии, где игроки помимо строгих игровых механик могут уточнить у меня информацию, запросить отчет о состоянии дел в конкретных отраслях и данные о других странах. Теперь ивенты у вас могут быть выделены* некоторые термины и слова, о которых вы можете узнать больше, также участник может запросить у меня информацию о "любой" интересующей его теме в рамках игрового процесса.

Обновленная карта
ИТОГИ
Формально игра началась, но пока я жду ответа участников о их готовности и желании продолжить

Где продолжаем игру?13

Этот парень родился спортсменом

Жиртресса

Русалка уже не та

НеТопицца

МужЖен герой молний

Мистер и миссис смит (д)

Крош с жирафом внизу

Под конец крыша слетела у всех
Из других режимов не выйдет, я сохраняла как гифки( тут такое хрен добавишь
Мы можем играть еще (от 4ех человек до 10 человек)!
Кто угодно может присоединиться и играть вместе с нами. Теперь мы можем выбрать день и время (первоначально суббота в 20:00 по МСК)
Мы созвонимся теперь не в зум, а в гуглмит
ТАКЖЕ
Мы можем собираться играть в разные игры или даже смотреть фильмы, сериалы и обсуждать после просмотра (можно выбрать время пораньше, чтоб до ночи не сидеть)
Пишите, что бы вы хотели в первую очередь. И что по времени и субботе?

Можно и групповой созвон устроить! Го играть и веселиться вместе в рисовательно-писательную игру!
Чтобы участвовать - напиши в комментах "участвую". Ссылки пришлю в лс, скорее всего, за мин10
Сама игра
МАКСИМУМ 10 ЧЕЛОВЕК.
Пойдешь?9
1. Игровая экономика Роблокса построена так, что ребёнку нажать «купить» проще, чем родителю объяснить, зачем это надо. Виртуальная валюта Robux, скины и «коробочки-сюрпризы» стимулируют импульсивные покупки — у детей нет экономического понимания расходов, но есть доступная кнопка.

Это не просто трата денег — исследователи и журналисты сравнивают с практиками, которые учат детей тратить без осознания.
2. Большая часть контента создаётся игроками/разработчиками. Чтобы всё отфильтровать — нужна армия модераторов и умные алгоритмы. На практике автоматические фильтры ошибаются: либо пропускают опасное (педофильский контент, сцены насилия).

Итог — дети могут наткнуться на неприемлемое, а создатели — потерять аккаунты без понятного объяснения.
3. Роблокс оказался в поле зрения властей и юристов: есть иски о сборе данных детей без согласия родителей, иски по вопросам безопасности и даже правовые претензии от штатов. Когда платформа, ориентированная на детей, регулярно попадает в юридические сводки — это знак, что что-то системно не работает.

4. Внутренняя социальная среда игровых пространств — как в любой большой сети: от буллинга до мошенничества (аккаунты, перепродажа, подставные конкурсы). Для детей это особенно рискованно: доверчивость + доступность чата/встреч = уязвимость к манипуляциям и мошенникам.

Журналисты и исследователи неоднократно фиксировали такие случаи и описывали «тревожный разрыв» между веселым фасадом платформы и реальным опытом детей.
Собираемая игровыми платформами аналитика — это важно для бизнеса, но опасно для детей. Против Роблокса поданы иски, где говорится о скрытом отслеживании и коммерческом использовании детских данных без прозрачного согласия родителей. Это не «теория заговоров», а юридические претензии, которые требуют ответа.
Вывод: Играйте в шашки ру и будет вам счастье.
Рады блокировке роблокс? 10