hytopia
Version:
The HYTOPIA SDK makes it easy for developers to create massively multiplayer games using JavaScript or TypeScript.
270 lines (227 loc) • 7.16 kB
HTML
<!-- Scripts -->
<script>
// State
let queueCount = 0;
let gameState = '';
let gameStartTime = 0;
// Helper functions
function formatTime(seconds) {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
}
function updateGameStatus(status) {
const gameStatusElements = document.getElementsByClassName('game-status');
for (let i = 0; i < gameStatusElements.length; i++) {
gameStatusElements[i].innerText = status;
}
}
function updatePlayerList(players) {
const playerListElement = document.getElementsByClassName('player-list')[0];
playerListElement.innerHTML = '';
players.forEach(player => {
const playerEntry = document.createElement('div');
playerEntry.classList.add('player-entry');
playerEntry.innerText = player;
playerListElement.appendChild(playerEntry);
});
}
// Event handlers
hytopia.onData(data => {
console.log(data);
if (data.queueCount !== undefined) {
queueCount = data.queueCount;
const joinNpcQueuedPlayerCount = document.getElementById('join-npc-queued-player-count');
if (joinNpcQueuedPlayerCount) {
joinNpcQueuedPlayerCount.innerText = data.queueCount;
}
const queuedPlayerCount = document.getElementById('queued-player-count');
if (queuedPlayerCount) {
queuedPlayerCount.innerText = data.queueCount;
}
}
if (data.gameState !== undefined) {
gameState = data.gameState;
if (gameState === 'awaitingPlayers') {
document.getElementById('game-board-awaiting-players-info').style.display = 'block';
document.getElementById('game-board-active-game-info').style.display = 'none';
updateGameStatus('AWAITING PLAYERS...');
}
if (gameState === 'starting') {
const gameCountdownStartTime = data.gameCountdownStartTime;
const countdownSeconds = data.countdown;
let timeLeft = countdownSeconds;
const updateCountdown = () => {
if (timeLeft > 0 && gameState === 'starting') {
const now = Date.now();
timeLeft = Math.max(0, Math.ceil((gameCountdownStartTime + (countdownSeconds * 1000) - now) / 1000));
updateGameStatus(`GAME STARTING IN ${timeLeft}...`);
setTimeout(updateCountdown, 1000);
}
};
updateCountdown();
}
if (gameState === 'inProgress') {
document.getElementById('game-board-awaiting-players-info').style.display = 'none';
document.getElementById('game-board-active-game-info').style.display = 'block';
updateGameStatus('GAME IN PROGRESS');
}
}
if (data.gameLevel !== undefined) {
document.getElementById('game-board-level').innerText = data.gameLevel;
}
if (data.gameStartTime !== undefined) {
gameStartTime = data.gameStartTime;
const updateElapsedTime = () => {
const now = Date.now();
const timeElapsed = Math.floor((now - gameStartTime) / 1000);
document.getElementById('elapsed-time').innerText = formatTime(timeElapsed);
if (gameState === 'inProgress') {
setTimeout(updateElapsedTime, 1000);
}
};
updateElapsedTime();
}
if (data.playersRemaining !== undefined) {
document.getElementById('players-remaining').innerText = data.playersRemaining.length;
updatePlayerList(data.playersRemaining);
}
});
hytopia.registerSceneUITemplate('join-npc-ui', () => {
const joinNpcUi = document.getElementById('join-npc-ui').content.cloneNode(true);
joinNpcUi.getElementById('join-npc-queued-player-count').innerText = queueCount;
return joinNpcUi;
});
hytopia.registerSceneUITemplate('player-queued', () => {
return document.getElementById('player-queued').content.cloneNode(true);
});
</script>
<!-- Game UI -->
<template id="join-npc-ui">
<div class="join-message-box">
<h2>Join Game</h2>
<p>Come close to queue up for the next game!</p>
<p class="player-count">PLAYERS QUEUED: <span id="join-npc-queued-player-count">0</span></p>
<p class="game-status">AWAITING PLAYERS...</p>
<div class="arrow"></div>
</div>
</template>
<template id="player-queued">
<div class="queued-message-box">
<p class="queued-text">READY</p>
<div class="arrow"></div>
</div>
</template>
<div class="game-board">
<h2>Hole In The Wall</h2>
<div id="game-board-awaiting-players-info">
<p class="elapsed-time">Queued Players: <span id="queued-player-count">0</span></p>
<p class="game-status">AWAITING PLAYERS...</p>
</div>
<div id="game-board-active-game-info">
<p class="level">Level: <span id="game-board-level">1</span></p>
<p class="elapsed-time">Elapsed Time: <span id="elapsed-time">00:00</span></p>
<p class="players-remaining">Players remaining: <span id="players-remaining">10</span></p>
<div class="player-list"></div>
</div>
</div>
<!-- Styles -->
<style>
/* Global styles */
* {
font-family: Arial, sans-serif;
user-select: none;
}
/* Game board styles */
.game-board {
position: fixed;
top: 20px;
right: 20px;
background: rgba(0,0,0,0.8);
padding: 15px 20px;
border-radius: 12px;
width: 200px;
}
.game-board h2 {
color: white;
margin: 0 0 10px 0;
font-size: 24px;
text-align: center;
}
#game-board-active-game-info {
display: none;
}
.level {
color: #4ade80;
margin: 0 0 5px 0;
text-align: center;
font-weight: bold;
}
.elapsed-time {
color: #60a5fa;
margin: 0 0 5px 0;
text-align: center;
font-weight: bold;
}
.players-remaining, #game-board-awaiting-players-info {
color: #ff4444;
margin: 0 0 15px 0;
text-align: center;
font-weight: bold;
}
.player-list {
max-height: 220px;
overflow-y: scroll;
}
.player-entry {
color: white;
padding: 5px 10px;
margin: 5px 0;
background: rgba(255,255,255,0.1);
border-radius: 4px;
}
.join-message-box, .queued-message-box {
background: rgba(0,0,0,0.8);
padding: 15px 20px 10px 20px;
border-radius: 12px;
position: relative;
max-width: 250px;
text-align: center;
}
.join-message-box h2 {
color: white;
margin: 0 0 10px 0;
font-size: 24px;
text-align: center;
}
.join-message-box p {
color: white;
margin: 0 0 15px 0;
text-align: center;
}
.join-message-box .player-count {
color: #4ade80;
font-weight: bold;
}
.join-message-box .game-status {
color: #ef4444;
font-weight: bold;
}
.queued-message-box .queued-text {
color: #4ade80;
font-size: 24px;
font-weight: bold;
margin: 0;
}
.join-message-box .arrow {
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid rgba(0,0,0,0.8);
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
}
</style>