📄 Source: about.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require_once __DIR__ . '/vendor/autoload.php';
use xPaw\MinecraftPing;
use xPaw\MinecraftPingException;
$server_ip = '127.0.0.1';
$server_port = 25565;
// --- Password for Dev Panel ---
define('DEV_PASSWORD', 'pswMaster0140'); // CHANGE THIS!
// --- Handle Manual Commands ---
$command_result = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['fog_command'])) {
$command = $_POST['fog_command'];
$command_file = '/mnt/1tb_ssd/NeoSV1/fog_command.txt';
$valid_commands = ['rise', 'fall', 'reset', 'clear', 'status'];
if (in_array($command, $valid_commands)) {
file_put_contents($command_file, $command);
$command_result = "✅ Command '$command' sent to fog controller!";
} else {
$command_result = "❌ Invalid command: $command";
}
}
// Handle config updates
if (isset($_POST['update_config'])) {
$config_file = '/mnt/1tb_ssd/NeoSV1/fog_config.json';
$config = [
'INITIAL_GRACE_HOURS' => intval($_POST['grace_hours'] ?? 24),
'WARNING_MIN_HOURS' => intval($_POST['warning_min'] ?? 1),
'WARNING_MAX_HOURS' => intval($_POST['warning_max'] ?? 6),
'RISING_MIN_HOURS' => intval($_POST['rising_min'] ?? 0),
'RISING_MAX_HOURS' => intval($_POST['rising_max'] ?? 1),
'PEAK_MIN_HOURS' => intval($_POST['peak_min'] ?? 4),
'PEAK_MAX_HOURS' => intval($_POST['peak_max'] ?? 12),
'FALLING_MIN_HOURS' => intval($_POST['falling_min'] ?? 0),
'FALLING_MAX_HOURS' => intval($_POST['falling_max'] ?? 1),
'END_HOURS' => intval($_POST['end_hours'] ?? 2),
'FOG_START_HEIGHT' => intval($_POST['start_height'] ?? -65),
'FOG_PEAK_HEIGHT_MIN' => intval($_POST['peak_min_height'] ?? 150),
'FOG_PEAK_HEIGHT_MAX' => intval($_POST['peak_max_height'] ?? 200),
'FOG_FALL_HEIGHT' => intval($_POST['fall_height'] ?? -64),
];
file_put_contents($config_file, json_encode($config, JSON_PRETTY_PRINT));
$command_result = "✅ Config updated! Changes will apply on next fog cycle.";
}
}
// --- Load Config ---
function getFogConfig() {
$config_file = '/mnt/1tb_ssd/NeoSV1/fog_config.json';
if (file_exists($config_file)) {
$data = json_decode(file_get_contents($config_file), true);
if ($data) return $data;
}
return [
'INITIAL_GRACE_HOURS' => 24,
'WARNING_MIN_HOURS' => 1,
'WARNING_MAX_HOURS' => 6,
'RISING_MIN_HOURS' => 0,
'RISING_MAX_HOURS' => 1,
'PEAK_MIN_HOURS' => 4,
'PEAK_MAX_HOURS' => 12,
'FALLING_MIN_HOURS' => 0,
'FALLING_MAX_HOURS' => 1,
'END_HOURS' => 2,
'FOG_START_HEIGHT' => -65,
'FOG_PEAK_HEIGHT_MIN' => 150,
'FOG_PEAK_HEIGHT_MAX' => 200,
'FOG_FALL_HEIGHT' => -64,
];
}
$config = getFogConfig();
// --- Fog Status ---
function getFogStatusFromJson() {
$json_file = __DIR__ . '/fog_status.json';
if (file_exists($json_file)) {
$data = json_decode(file_get_contents($json_file), true);
if ($data) return $data;
}
return [
'status' => '🌤️ Fog is down',
'current_height' => 0,
'next_event' => '',
'last_updated' => 'Never'
];
}
// Get server status
function getServerStatus() {
global $server_ip, $server_port;
try {
$Query = new MinecraftPing($server_ip, $server_port, 3);
$info = $Query->Query();
$Query->Close();
return [
'online' => true,
'players' => $info['players']['online'] ?? 0,
'max_players' => $info['players']['max'] ?? 0,
'version' => $info['version']['name'] ?? '1.21.1',
'players_list' => $info['players']['sample'] ?? []
];
} catch (MinecraftPingException $e) {
return [
'online' => false,
'players' => 0,
'max_players' => 0,
'version' => 'Offline',
'players_list' => []
];
}
}
$server = getServerStatus();
$fog = getFogStatusFromJson();
$last_updated = $fog['last_updated'] ?? 'Never';
if ($last_updated !== 'Never') {
try {
$dt = new DateTime($last_updated);
$last_updated = $dt->format('Y-m-d H:i:s');
} catch (Exception $e) {}
}
$is_fog_active = strpos($fog['status'], '🌫️') !== false;
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CreateCraft - About</title>
<link rel="stylesheet" href="style.css">
<style>
.server-status-line {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin-bottom: 25px;
padding: 10px;
}
.status-dot {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
}
.status-dot.online {
background-color: #4caf50;
animation: pulse 1.5s ease-in-out infinite;
}
.status-dot.offline {
background-color: #f44336;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(0.85); }
}
.status-title {
font-size: 18px;
font-weight: bold;
color: #5a9e4e;
}
.status-title.offline {
color: #f44336;
}
.fog-status-line {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin: 15px 0 10px 0;
padding: 12px;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
flex-wrap: wrap;
cursor: default;
}
.fog-icon {
font-size: 28px;
cursor: pointer;
user-select: none;
transition: transform 0.3s;
display: inline-block;
}
.fog-icon:hover {
transform: scale(1.15);
}
.fog-icon.active {
animation: fogFloat 3s ease-in-out infinite;
}
@keyframes fogFloat {
0%, 100% { transform: translateY(0px) scale(1); }
50% { transform: translateY(-5px) scale(1.05); }
}
.fog-label {
font-size: 16px;
font-weight: 500;
color: #ddd;
}
.fog-status-text {
font-size: 18px;
font-weight: bold;
color: #ff9800;
}
.fog-next-event {
font-size: 14px;
opacity: 0.7;
margin-left: 8px;
}
.fog-height {
font-size: 14px;
opacity: 0.7;
text-align: center;
padding: 4px 0;
}
.fog-last-update {
font-size: 12px;
opacity: 0.4;
text-align: center;
margin-top: 2px;
}
.fog-controls {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.fog-btn {
padding: 6px 16px;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
color: white;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
}
.fog-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.fog-btn:active {
transform: translateY(0px);
}
.fog-btn-rise { background: rgba(255, 152, 0, 0.7); border-color: #ff9800; }
.fog-btn-rise:hover { background: #ff9800; }
.fog-btn-fall { background: rgba(33, 150, 243, 0.7); border-color: #2196f3; }
.fog-btn-fall:hover { background: #2196f3; }
.fog-btn-reset { background: rgba(244, 67, 54, 0.7); border-color: #f44336; }
.fog-btn-reset:hover { background: #f44336; }
.fog-btn-clear { background: rgba(76, 175, 80, 0.7); border-color: #4caf50; }
.fog-btn-clear:hover { background: #4caf50; }
.fog-btn-status { background: rgba(156, 39, 176, 0.7); border-color: #9c27b0; }
.fog-btn-status:hover { background: #9c27b0; }
.command-result {
text-align: center;
padding: 6px 12px;
margin-top: 8px;
border-radius: 6px;
font-size: 13px;
background: rgba(0,0,0,0.3);
color: #8f8;
}
.command-result.error {
color: #f88;
}
.config-form {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px 20px;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid rgba(255,255,255,0.1);
}
.config-group {
display: flex;
flex-direction: column;
gap: 2px;
}
.config-group label {
font-size: 12px;
color: #aaa;
}
.config-group input {
padding: 4px 8px;
border-radius: 4px;
border: 1px solid rgba(255,255,255,0.15);
background: rgba(0,0,0,0.3);
color: white;
font-size: 13px;
width: 100%;
box-sizing: border-box;
}
.config-group input:focus {
outline: none;
border-color: #ff9800;
}
.config-submit {
grid-column: 1 / -1;
padding: 6px;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
color: white;
background: rgba(76, 175, 80, 0.7);
border: 1px solid #4caf50;
transition: all 0.3s;
margin-top: 4px;
}
.config-submit:hover {
background: #4caf50;
transform: translateY(-2px);
}
.player-stats {
text-align: center;
padding: 15px;
margin: 10px 0;
}
.player-number {
font-size: 56px;
font-weight: bold;
color: #5a9e4e;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
line-height: 1;
}
.player-max {
font-size: 16px;
opacity: 0.8;
margin-top: 5px;
}
.version-line {
text-align: center;
padding: 8px;
margin: 10px 0;
font-size: 13px;
opacity: 0.7;
border-top: 1px solid rgba(255,255,255,0.1);
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.players-section {
margin-top: 20px;
}
.players-toggle {
cursor: pointer;
color: #5a9e4e;
text-align: center;
padding: 10px;
background: rgba(90, 158, 78, 0.15);
border-radius: 8px;
transition: background 0.3s;
}
.players-toggle:hover {
background: rgba(90, 158, 78, 0.25);
}
.players-container {
margin-top: 15px;
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
}
.player-badge {
background: rgba(90, 158, 78, 0.85);
color: white;
padding: 6px 14px;
border-radius: 20px;
font-size: 13px;
font-weight: 500;
backdrop-filter: blur(4px);
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}
.no-players-msg {
text-align: center;
padding: 15px;
color: rgba(255,255,255,0.6);
font-style: italic;
}
.quick-links-nav {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 15px;
justify-content: center;
}
.quick-links-nav a {
color: white;
text-decoration: none;
display: inline-block;
padding: 8px 20px;
background: rgba(90, 158, 78, 0.8);
border-radius: 8px;
transition: all 0.3s ease;
font-weight: 500;
}
.quick-links-nav a:hover {
background: rgba(90, 158, 78, 1);
transform: translateY(-2px);
}
/* Password Modal */
.password-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(8px);
z-index: 1000;
justify-content: center;
align-items: center;
}
.password-overlay.visible {
display: flex;
}
.password-modal {
background: rgba(30, 30, 40, 0.95);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 16px;
padding: 30px 40px;
max-width: 400px;
width: 90%;
box-shadow: 0 20px 60px rgba(0,0,0,0.8);
}
.password-modal h2 {
color: #ff9;
text-align: center;
margin-bottom: 8px;
}
.password-modal p {
color: #aaa;
text-align: center;
font-size: 14px;
margin-bottom: 20px;
}
.password-modal input {
width: 100%;
padding: 10px 14px;
border-radius: 8px;
border: 1px solid rgba(255,255,255,0.15);
background: rgba(0,0,0,0.3);
color: white;
font-size: 16px;
box-sizing: border-box;
text-align: center;
letter-spacing: 4px;
}
.password-modal input:focus {
outline: none;
border-color: #ff9800;
}
.password-modal .error-msg {
color: #f88;
font-size: 13px;
text-align: center;
margin-top: 10px;
min-height: 20px;
}
.password-modal .btn-row {
display: flex;
gap: 10px;
margin-top: 16px;
justify-content: center;
}
.password-modal .btn-row button {
padding: 8px 30px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
}
.password-modal .btn-submit {
background: #ff9800;
color: white;
}
.password-modal .btn-submit:hover {
background: #e68900;
transform: scale(1.02);
}
.password-modal .btn-cancel {
background: rgba(255,255,255,0.1);
color: #aaa;
}
.password-modal .btn-cancel:hover {
background: rgba(255,255,255,0.2);
}
.dev-panel {
display: none;
margin-top: 15px;
padding: 15px;
background: rgba(0, 0, 0, 0.5);
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
font-family: 'Courier New', monospace;
font-size: 13px;
color: #8f8;
overflow-x: auto;
white-space: pre-wrap;
word-break: break-all;
}
.dev-panel.visible {
display: block;
}
.dev-hint {
font-size: 11px;
opacity: 0.15;
text-align: center;
margin-top: 4px;
cursor: default;
user-select: none;
}
.dev-hint:hover {
opacity: 0.5;
}
@media (max-width: 768px) {
.player-number {
font-size: 44px;
}
.status-title {
font-size: 16px;
}
.player-badge {
padding: 5px 12px;
font-size: 12px;
}
.fog-status-line {
flex-wrap: wrap;
}
.fog-controls {
gap: 5px;
}
.fog-btn {
padding: 5px 12px;
font-size: 12px;
}
.dev-panel {
font-size: 11px;
}
.config-form {
grid-template-columns: 1fr;
}
.password-modal {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="video-background">
<video autoplay muted loop playsinline id="bgVideo">
<source src="videos/timelapse.mp4" type="video/mp4">
Your browser does not support HTML5 video.
</video>
</div>
<!-- Password Modal -->
<div class="password-overlay" id="passwordOverlay">
<div class="password-modal">
<h2>🔐 Developer Access</h2>
<p>Enter the password to access fog controls</p>
<input type="password" id="passwordInput" placeholder="Enter password..." autocomplete="off">
<div class="error-msg" id="passwordError"></div>
<div class="btn-row">
<button class="btn-cancel" onclick="closePasswordModal()">Cancel</button>
<button class="btn-submit" onclick="verifyPassword()">Unlock</button>
</div>
</div>
</div>
<div class="content">
<header>
<h1>CreateCraft: <strong>About</strong></h1>
<p><a href="demo.php">DEMO</a></p>
</header>
<main>
<div class="card">
<div class="server-status-line">
<div class="status-dot <?php echo $server['online'] ? 'online' : 'offline'; ?>"></div>
<div class="status-title <?php echo $server['online'] ? 'online' : 'offline'; ?>">
CreateCraft is <?php echo $server['online'] ? 'Online' : 'Offline'; ?>
</div>
</div>
<div class="fog-status-line" id="fogStatusLine">
<span class="fog-icon <?php echo $is_fog_active ? 'active' : ''; ?>" id="fogIcon" title="">❕</span>
<span class="fog-label">Status:</span>
<span class="fog-status-text" id="fogStatusText"><?php echo htmlspecialchars($fog['status']); ?></span>
<?php if (!empty($fog['next_event'])): ?>
<span class="fog-next-event" id="fogNextEvent">⏳ <?php echo htmlspecialchars($fog['next_event']); ?></span>
<?php endif; ?>
</div>
<div class="fog-height" id="fogHeight">Fog height: <?php echo $fog['current_height'] ?? 0; ?> blocks</div>
<div class="fog-last-update" id="fogUpdate">Last updated: <?php echo $last_updated; ?></div>
<!-- Dev Panel (password protected) -->
<div class="dev-panel" id="devPanel">
<div style="margin-bottom: 10px; color: #ff9; font-weight: bold;">🔧 Developer Controls</div>
<div class="fog-controls">
<form method="POST" style="display: contents;">
<button type="submit" name="fog_command" value="rise" class="fog-btn fog-btn-rise">🌫️ Rise</button>
<button type="submit" name="fog_command" value="fall" class="fog-btn fog-btn-fall">🌤️ Fall</button>
<button type="submit" name="fog_command" value="clear" class="fog-btn fog-btn-clear">✨ Clear</button>
<button type="submit" name="fog_command" value="reset" class="fog-btn fog-btn-reset">🔄 Reset</button>
<button type="submit" name="fog_command" value="status" class="fog-btn fog-btn-status">📊 Status</button>
</form>
</div>
<?php if ($command_result): ?>
<div class="command-result <?php echo strpos($command_result, '✅') !== false ? '' : 'error'; ?>">
<?php echo $command_result; ?>
</div>
<?php endif; ?>
<!-- Config Form -->
<div style="margin-top: 12px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 12px;">
<div style="color: #ff9; font-weight: bold; margin-bottom: 8px;">⚙️ Fog Config</div>
<form method="POST" style="margin: 0;">
<div class="config-form">
<div class="config-group">
<label>Grace Hours</label>
<input type="number" name="grace_hours" value="<?php echo $config['INITIAL_GRACE_HOURS']; ?>" min="0">
</div>
<div class="config-group">
<label>End Hours</label>
<input type="number" name="end_hours" value="<?php echo $config['END_HOURS']; ?>" min="0">
</div>
<div class="config-group">
<label>Warning (min-max)</label>
<div style="display:flex; gap:4px;">
<input type="number" name="warning_min" value="<?php echo $config['WARNING_MIN_HOURS']; ?>" min="0" style="width:50%;">
<input type="number" name="warning_max" value="<?php echo $config['WARNING_MAX_HOURS']; ?>" min="0" style="width:50%;">
</div>
</div>
<div class="config-group">
<label>Rising (min-max)</label>
<div style="display:flex; gap:4px;">
<input type="number" name="rising_min" value="<?php echo $config['RISING_MIN_HOURS']; ?>" min="0" style="width:50%;">
<input type="number" name="rising_max" value="<?php echo $config['RISING_MAX_HOURS']; ?>" min="0" style="width:50%;">
</div>
</div>
<div class="config-group">
<label>Peak (min-max)</label>
<div style="display:flex; gap:4px;">
<input type="number" name="peak_min" value="<?php echo $config['PEAK_MIN_HOURS']; ?>" min="0" style="width:50%;">
<input type="number" name="peak_max" value="<?php echo $config['PEAK_MAX_HOURS']; ?>" min="0" style="width:50%;">
</div>
</div>
<div class="config-group">
<label>Falling (min-max)</label>
<div style="display:flex; gap:4px;">
<input type="number" name="falling_min" value="<?php echo $config['FALLING_MIN_HOURS']; ?>" min="0" style="width:50%;">
<input type="number" name="falling_max" value="<?php echo $config['FALLING_MAX_HOURS']; ?>" min="0" style="width:50%;">
</div>
</div>
<div class="config-group">
<label>Start Height</label>
<input type="number" name="start_height" value="<?php echo $config['FOG_START_HEIGHT']; ?>">
</div>
<div class="config-group">
<label>Fall Height</label>
<input type="number" name="fall_height" value="<?php echo $config['FOG_FALL_HEIGHT']; ?>">
</div>
<div class="config-group">
<label>Peak Height (min-max)</label>
<div style="display:flex; gap:4px;">
<input type="number" name="peak_min_height" value="<?php echo $config['FOG_PEAK_HEIGHT_MIN']; ?>" style="width:50%;">
<input type="number" name="peak_max_height" value="<?php echo $config['FOG_PEAK_HEIGHT_MAX']; ?>" style="width:50%;">
</div>
</div>
<button type="submit" name="update_config" class="config-submit">💾 Save Config</button>
</div>
</form>
</div>
<div style="margin-top: 12px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 12px;">
<span style="color: #ff9;">📊 Full Fog Data</span>
<?php echo "\n" . json_encode($fog, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); ?>
</div>
</div>
<!-- <div class="dev-hint" id="devHint">🔧 double-click the fog icon</div> -->
<?php if ($server['online']): ?>
<div class="player-stats">
<div class="player-number" id="playerCount"><?php echo $server['players']; ?></div>
<div class="player-max">Players Online / <?php echo $server['max_players']; ?></div>
</div>
<div class="version-line">
Running: <?php echo htmlspecialchars($server['version']); ?>
</div>
<div class="players-section">
<div class="players-toggle" onclick="togglePlayers()">
📋 <?php echo !empty($server['players_list']) ? count($server['players_list']) . ' player' . (count($server['players_list']) != 1 ? 's' : '') . ' online' : 'No players online'; ?>
</div>
<div id="playersContainer" class="players-container" style="<?php echo !empty($server['players_list']) ? 'display: flex;' : 'display: none;'; ?>">
<?php foreach ($server['players_list'] as $player): ?>
<span class="player-badge"><?php echo htmlspecialchars($player['name']); ?></span>
<?php endforeach; ?>
<?php if (empty($server['players_list'])): ?>
<div class="no-players-msg">Be the first to join!</div>
<?php endif; ?>
</div>
</div>
<?php else: ?>
<div class="no-players-msg">Server is offline. Start it to see player information!</div>
<?php endif; ?>
</div>
<div class="card">
<h2>Quick Links</h2>
<div class="quick-links-nav">
<a href="index.php">Home</a>
<a href="factions.php">Factions</a>
<a href="about.php">About</a>
<a href="https://discord.com/invite/959SAvcCpp" target="_blank">Discord</a>
</div>
</div>
</main>
<footer>
<p>© <?php echo date('Y'); ?> CreateCraft SI | Built with PHP | Server status updates every 5 seconds</p>
</footer>
</div>
<script>
let lastPlayerCount = <?php echo $server['players']; ?>;
let devPanelVisible = false;
let passwordAttempts = 0;
const MAX_ATTEMPTS = 5;
const CORRECT_PASSWORD = '<?php echo DEV_PASSWORD; ?>';
function togglePlayers() {
const container = document.getElementById('playersContainer');
if (container.style.display === 'none' || container.style.display === '') {
container.style.display = 'flex';
} else {
container.style.display = 'none';
}
}
// --- Password Modal ---
function showPasswordModal() {
if (passwordAttempts >= MAX_ATTEMPTS) {
document.getElementById('passwordError').textContent = '⚠️ Too many failed attempts. Refresh the page to try again.';
return;
}
document.getElementById('passwordOverlay').classList.add('visible');
document.getElementById('passwordInput').value = '';
document.getElementById('passwordInput').focus();
document.getElementById('passwordError').textContent = '';
}
function closePasswordModal() {
document.getElementById('passwordOverlay').classList.remove('visible');
}
function verifyPassword() {
const input = document.getElementById('passwordInput').value;
const error = document.getElementById('passwordError');
if (input === CORRECT_PASSWORD) {
// Correct password — show dev panel
closePasswordModal();
const panel = document.getElementById('devPanel');
const hint = document.getElementById('devHint');
devPanelVisible = true;
panel.classList.add('visible');
hint.textContent = '🔧 double-click to hide';
error.textContent = '';
passwordAttempts = 0;
} else {
// Wrong password
passwordAttempts++;
const remaining = MAX_ATTEMPTS - passwordAttempts;
if (remaining <= 0) {
error.textContent = '⚠️ Too many failed attempts. Refresh the page to try again.';
document.getElementById('passwordInput').disabled = true;
} else {
error.textContent = `❌ Wrong password. ${remaining} attempt(s) remaining.`;
document.getElementById('passwordInput').value = '';
document.getElementById('passwordInput').focus();
}
}
}
// Enter key in password field
document.getElementById('passwordInput').addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
verifyPassword();
}
});
// Double-click fog icon → show password modal
document.getElementById('fogIcon').addEventListener('dblclick', function(e) {
if (devPanelVisible) {
// If panel is visible, hide it
const panel = document.getElementById('devPanel');
const hint = document.getElementById('devHint');
devPanelVisible = false;
panel.classList.remove('visible');
hint.textContent = '🔧 double-click the fog icon';
} else {
// Show password modal
showPasswordModal();
}
});
// Click outside modal to close
document.getElementById('passwordOverlay').addEventListener('click', function(e) {
if (e.target === this) {
closePasswordModal();
}
});
function updateServerStatus() {
fetch(window.location.href + '?ajax=1')
.then(response => response.text())
.then(html => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const newCountElem = doc.querySelector('.player-number');
if (newCountElem) {
const newCount = parseInt(newCountElem.textContent);
const countElem = document.getElementById('playerCount');
if (countElem && !isNaN(newCount) && newCount !== lastPlayerCount) {
countElem.style.transform = 'scale(1.1)';
setTimeout(() => {
countElem.style.transform = 'scale(1)';
}, 200);
countElem.textContent = newCount;
lastPlayerCount = newCount;
}
}
const newPlayersContainer = doc.querySelector('.players-container');
const newPlayersToggle = doc.querySelector('.players-toggle');
if (newPlayersContainer && newPlayersToggle) {
const currentContainer = document.getElementById('playersContainer');
const currentToggle = document.querySelector('.players-toggle');
if (currentContainer && currentToggle) {
currentContainer.innerHTML = newPlayersContainer.innerHTML;
currentToggle.textContent = newPlayersToggle.textContent;
const hasPlayers = newPlayersContainer.innerHTML.includes('player-badge');
if (hasPlayers && currentContainer.style.display === 'none') {
currentContainer.style.display = 'flex';
} else if (!hasPlayers && currentContainer.style.display !== 'none') {
currentContainer.style.display = 'none';
}
}
}
const newFogStatus = doc.querySelector('.fog-status-text');
const newFogNext = doc.querySelector('.fog-next-event');
const newFogHeight = doc.querySelector('.fog-height');
const newFogUpdate = doc.querySelector('.fog-last-update');
const newFogIcon = doc.querySelector('.fog-icon');
if (newFogStatus) {
const currentFogStatus = document.getElementById('fogStatusText');
if (currentFogStatus) currentFogStatus.textContent = newFogStatus.textContent;
}
if (newFogNext) {
const currentFogNext = document.getElementById('fogNextEvent');
if (currentFogNext) {
currentFogNext.textContent = newFogNext.textContent;
currentFogNext.style.display = 'inline';
}
} else {
const currentFogNext = document.getElementById('fogNextEvent');
if (currentFogNext) currentFogNext.style.display = 'none';
}
if (newFogHeight) {
const currentFogHeight = document.getElementById('fogHeight');
if (currentFogHeight) currentFogHeight.textContent = newFogHeight.textContent;
}
if (newFogUpdate) {
const currentFogUpdate = document.getElementById('fogUpdate');
if (currentFogUpdate) currentFogUpdate.textContent = newFogUpdate.textContent;
}
if (newFogIcon) {
const currentFogIcon = document.getElementById('fogIcon');
if (currentFogIcon) {
currentFogIcon.textContent = newFogIcon.textContent;
const isActive = newFogIcon.textContent.includes('🌫️');
if (isActive) {
currentFogIcon.classList.add('active');
} else {
currentFogIcon.classList.remove('active');
}
}
}
})
.catch(err => console.log('Update failed:', err));
}
setInterval(updateServerStatus, 5000);
document.querySelectorAll('.fog-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
const command = this.value;
if (command === 'reset' || command === 'clear') {
if (!confirm(`⚠️ Are you sure you want to ${command} the fog?`)) {
e.preventDefault();
}
}
});
});
</script>
</body>
</html>
← Back