Kapitán - Kormidlo
?
QR Kód s aktuální adresou
Stanoviště: Kapitán
Integrity:
100
%
Odchylka:
0
°
Úkol:
Udržuj horizont v rovině! Pokud bude odchylka příliš velká, loď se poškodí.
Zapnout Gyroskop
Manuální kormidlo:
0°
Vycentrovat kormidlo
Upravit obsah stránky
{% extends "templates/base.html" %} {% set game_name = "Kapitán - Kormidlo" %} {% block content %} <style> /* Stylování umělého horizontu */ .horizon-wrapper { display: flex; justify-content: center; align-items: center; margin: 20px 0; } .horizon-container { width: 300px; height: 300px; border-radius: 50%; border: 8px solid #2c3e50; box-shadow: 0 0 20px rgba(0,0,0,0.5); position: relative; overflow: hidden; background-color: #2c3e50; } /* Vnitřní rotující disk horizontu */ .horizon-disk { width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; background: linear-gradient(to bottom, #3498db 50%, #8e44ad 50%); transition: transform 0.1s ease-out; transform: rotate(0deg); } /* Rysky horizontu */ .horizon-disk::after { content: ''; position: absolute; top: 50%; left: 25%; width: 50%; height: 2px; background-color: white; } /* Statický zaměřovač uprostřed */ .crosshair { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 150px; height: 40px; border: 3px solid #f1c40f; border-top: none; border-radius: 0 0 10px 10px; z-index: 10; } .crosshair::before { content: ''; position: absolute; top: -10px; left: 50%; transform: translateX(-50%); width: 4px; height: 10px; background-color: #f1c40f; } </style> <div class="container py-3 text-center"> <h2 class="text-primary mb-3"><i class="fa-solid fa-ship"></i> Stanoviště: Kapitán</h2> <!-- Telemetrie čtená ze serveru --> <div class="row mb-3"> <div class="col"> <div class="badge bg-success fs-6 w-100 p-2">Integrity: <span id="uiIntegrity">100</span>%</div> </div> <div class="col"> <div class="badge bg-danger fs-6 w-100 p-2">Odchylka: <span id="uiDeviation">0</span>°</div> </div> </div> <!-- Umělý horizont --> <div class="horizon-wrapper"> <div class="horizon-container"> <div class="horizon-disk" id="horizonDisk"></div> <div class="crosshair"></div> </div> </div> <div class="alert alert-info mt-3"> <strong>Úkol:</strong> Udržuj horizont v rovině! Pokud bude odchylka příliš velká, loď se poškodí. </div> <!-- Ovládání gyroskopu --> <div id="gyroSetup" class="mb-4"> <button class="btn btn-warning btn-lg" onclick="requestGyroPermission()"> <i class="fa-solid fa-mobile-screen"></i> Zapnout Gyroskop </button> </div> <!-- Fallback ovládání pro PC / Bez gyroskopu --> <div class="card bg-light mt-4" id="manualControls"> <div class="card-body"> <label for="manualSteer" class="form-label text-muted"> <i class="fa-solid fa-hand-pointer"></i> Manuální kormidlo: <strong id="sliderValue">0°</strong> </label> <input type="range" class="form-range" min="-45" max="45" value="0" id="manualSteer"> <button class="btn btn-sm btn-outline-secondary mt-2" onclick="resetSlider()">Vycentrovat kormidlo</button> </div> </div> </div> <script> const forrestHubLib = ForrestHubLib.getInstance(true); // Lokální proměnné let rudderInput = 0; let windForce = 0; let targetHeading = 0; let currentTilt = 0; let isGyroActive = false; // --- MANUÁLNÍ OVLÁDÁNÍ (Zabezpečeno proti NaN) --- const manualSteerSlider = document.getElementById('manualSteer'); const sliderValueText = document.getElementById('sliderValue'); manualSteerSlider.addEventListener('input', (e) => { if (!isGyroActive) { let val = parseFloat(e.target.value); rudderInput = isNaN(val) ? 0 : val; sliderValueText.innerText = rudderInput + "°"; } }); function resetSlider() { if (!isGyroActive) { manualSteerSlider.value = 0; rudderInput = 0; sliderValueText.innerText = "0°"; } } // --- GYROSKOP --- function requestGyroPermission() { if (typeof DeviceOrientationEvent !== 'undefined' && typeof DeviceOrientationEvent.requestPermission === 'function') { DeviceOrientationEvent.requestPermission() .then(permissionState => { if (permissionState === 'granted') { activateGyro(); } else { forrestHubLib.uiShowAlert('danger', 'Přístup ke gyroskopu byl zamítnut.'); } }) .catch(console.error); } else { activateGyro(); } } function activateGyro() { window.addEventListener('deviceorientation', handleOrientation); isGyroActive = true; document.getElementById('gyroSetup').style.display = 'none'; document.getElementById('manualControls').style.opacity = '0.5'; // Vizuální utlumení slideru manualSteerSlider.disabled = true; forrestHubLib.uiShowAlert('success', 'Gyroskop aktivován!'); } function handleOrientation(event) { // Ochrana: některé prohlížeče bez gyra pošlou event, kde je gamma null if (event.gamma === null || event.gamma === undefined) return; let gamma = event.gamma; if (gamma > 45) gamma = 45; if (gamma < -45) gamma = -45; rudderInput = gamma; } // --- HERNÍ SMYČKA (30 FPS pro plynulý horizont) --- setInterval(() => { // Bezpečnostní pojistka proti NaN if (isNaN(windForce)) windForce = 0; if (isNaN(targetHeading)) targetHeading = 0; if (isNaN(rudderInput)) rudderInput = 0; currentTilt = windForce + targetHeading + rudderInput; let displayTilt = Math.max(-60, Math.min(60, currentTilt)); // Aplikace rotace document.getElementById('horizonDisk').style.transform = `rotate(${-displayTilt}deg)`; // UI Odchylka let absoluteDeviation = Math.abs(Math.round(currentTilt)); document.getElementById('uiDeviation').innerText = absoluteDeviation; const devUI = document.getElementById('uiDeviation').parentElement; if (absoluteDeviation > 20) { devUI.className = "badge bg-danger fs-6 w-100 p-2"; } else if (absoluteDeviation > 10) { devUI.className = "badge bg-warning text-dark fs-6 w-100 p-2"; } else { devUI.className = "badge bg-success fs-6 w-100 p-2"; } }, 1000 / 30); // --- SYNCHRONIZAČNÍ SMYČKA S DATABÁZÍ (1 FPS) --- setInterval(async () => { // 1. Odeslání aktuální odchylky do DB let deviationPenalty = Math.abs(currentTilt); if (!isNaN(deviationPenalty)) { await forrestHubLib.dbVarSetKey('courseDeviation', deviationPenalty.toFixed(1)); } // 2. Čtení stavu hry od Hosta (pro UI) try { let dbIntegrity = await forrestHubLib.dbVarGetKey('integrity'); if (dbIntegrity !== null && dbIntegrity !== undefined) { document.getElementById('uiIntegrity').innerText = parseFloat(dbIntegrity).toFixed(0); } // 3. Čtení požadavků od Navigátora let dbTargetHeading = await forrestHubLib.dbVarGetKey('targetHeading'); if (dbTargetHeading !== null && dbTargetHeading !== undefined) { let parsedHeading = parseFloat(dbTargetHeading); targetHeading = isNaN(parsedHeading) ? 0 : parsedHeading; } else { targetHeading = 0; } } catch (err) { console.warn("Chyba při čtení z DB: ", err); } // 4. Simulace Větru let windDrift = (Math.random() - 0.5) * 5; windForce += windDrift; if(windForce > 30) windForce = 30; if(windForce < -30) windForce = -30; }, 1000); </script> {% endblock %}