2024-11-19 12:16:26 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
<title>Multiplayer Game</title>
|
2024-11-28 15:08:38 +00:00
|
|
|
<link rel="stylesheet" type="text/css" href="mflx.min.css">
|
2024-12-10 00:17:36 +00:00
|
|
|
<style type="text/css">
|
|
|
|
.text-with-shadow {
|
|
|
|
text-shadow: -1px -1px 1px #000, 1px -1px 1px #000,
|
|
|
|
-1px 1px 1px #000, 1px 1px 1px #000; color: white;
|
|
|
|
}
|
2024-12-15 03:45:58 +00:00
|
|
|
.div-shadow {
|
|
|
|
box-shadow: rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dialog styling */
|
|
|
|
dialog {
|
|
|
|
border: none;
|
|
|
|
border-radius: 8px;
|
|
|
|
padding: 20px;
|
|
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
|
|
font-family: Arial, sans-serif;
|
|
|
|
max-width: 300px;
|
|
|
|
text-align: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
dialog::backdrop {
|
|
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
dialog h2 {
|
|
|
|
margin-top: 0;
|
|
|
|
font-size: 18px;
|
|
|
|
}
|
|
|
|
.nameInput {
|
|
|
|
text-align: center;
|
|
|
|
}
|
2024-12-10 00:17:36 +00:00
|
|
|
</style>
|
2024-11-19 12:16:26 +00:00
|
|
|
</head>
|
|
|
|
<body>
|
2024-12-15 03:45:58 +00:00
|
|
|
<!-- Dialog -->
|
|
|
|
<dialog class="nameDialog">
|
2024-12-16 00:41:54 +00:00
|
|
|
<div class="flx(column) space-evenly">
|
|
|
|
<h2>Change Your Name?</h2>
|
|
|
|
<label for="nameInput">Name:</label>
|
|
|
|
<input type="text" class="nameInput is-round" style="padding: .25rem; ">
|
|
|
|
<div class="mt-4 flx(wrap) space-evenly">
|
|
|
|
<button class="saveButton btn is-success is-round mr-1 div-shadow">save</button>
|
|
|
|
<button class="closeDialogButton btn is-error is-round ml-1 div-shadow">close</button>
|
|
|
|
</div>
|
2024-12-15 03:45:58 +00:00
|
|
|
<div>
|
|
|
|
</dialog>
|
|
|
|
|
2024-11-19 12:16:26 +00:00
|
|
|
<button id="create-room">Create Room</button>
|
|
|
|
<input id="join-code" placeholder="Enter room code">
|
|
|
|
<button id="join-room">Join Room</button>
|
2024-12-15 03:45:58 +00:00
|
|
|
|
|
|
|
<div class="flx(wrap) mb-2 players"></div>
|
|
|
|
|
2024-11-19 12:16:26 +00:00
|
|
|
<div id="status"></div>
|
|
|
|
<button id="start-game" style="display: none;">Start Game</button>
|
|
|
|
<button id="spin-wheel" style="display: none;">Spin Wheel</button>
|
|
|
|
<input id="guess-letter" placeholder="Guess a letter" style="display: none;">
|
|
|
|
<button id="guess-button" style="display: none;">Guess</button>
|
2024-11-30 01:05:50 +00:00
|
|
|
<div style="display:relative;">
|
|
|
|
<span style="position: absolute; top: 0; left:50%">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>arrow-down-bold</title><path d="M9,4H15V12H19.84L12,19.84L4.16,12H9V4Z" /></svg>
|
|
|
|
</span>
|
|
|
|
<canvas id="wheelCanvas" width="500" height="500"></canvas>
|
|
|
|
</div>
|
2024-11-19 12:16:26 +00:00
|
|
|
<script>
|
2024-12-10 00:17:36 +00:00
|
|
|
//================general Purpose functions=========================================//
|
|
|
|
function cryptoSecureShuffle(array) {
|
|
|
|
const shuffledArray = [...array]; // Create a copy to avoid mutating the original array
|
|
|
|
const n = shuffledArray.length;
|
|
|
|
|
|
|
|
// Fisher-Yates shuffle
|
|
|
|
for (let i = n - 1; i > 0; i--) {
|
|
|
|
// Generate a cryptographically secure random index
|
|
|
|
let randIndex;
|
|
|
|
do {
|
|
|
|
// Generate a random value in the range [0, 2^32)
|
|
|
|
const randomBuffer = new Uint32Array(1);
|
|
|
|
window.crypto.getRandomValues(randomBuffer);
|
|
|
|
randIndex = randomBuffer[0] >>> 0; // Convert to a non-negative integer
|
|
|
|
} while (randIndex >= Math.floor(2 ** 32 / (i + 1)) * (i + 1)); // Avoid bias
|
|
|
|
|
|
|
|
// Compute the unbiased index
|
|
|
|
randIndex %= (i + 1);
|
|
|
|
|
|
|
|
// Swap the elements
|
|
|
|
[shuffledArray[i], shuffledArray[randIndex]] = [shuffledArray[randIndex], shuffledArray[i]];
|
|
|
|
}
|
|
|
|
|
|
|
|
return shuffledArray;
|
|
|
|
}
|
|
|
|
//====================making Name Tags/ assigning colors to names================//
|
|
|
|
let nameColors = [
|
|
|
|
'#ff0000','#ff8700','#ffd300','#deff0a','#a1ff0a',
|
|
|
|
'#0aff99','#0aefff','#147df5','#580aff','#be0aff'
|
|
|
|
]
|
|
|
|
nameColors = cryptoSecureShuffle(nameColors);
|
|
|
|
function assignColors(names, colors) {
|
|
|
|
let result = [];
|
|
|
|
let n = 0; // Keep track of name index
|
|
|
|
let c = 0; // Keep track of color index
|
|
|
|
|
|
|
|
if (names.length > colors.length) {
|
|
|
|
// Assign colors cyclically
|
|
|
|
while (n < names.length) {
|
|
|
|
result.push({ name: names[n], color: colors[c] });
|
|
|
|
n++;
|
|
|
|
c = (c + 1) % colors.length; // Cycle through the colors
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Assign colors directly, keeping track of indices
|
|
|
|
while (n < names.length) {
|
|
|
|
result.push({ name: names[n], color: colors[c] });
|
|
|
|
n++;
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
}
|
2024-11-30 01:05:50 +00:00
|
|
|
|
2024-12-10 00:17:36 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
//=========================================wheel==========================================//
|
2024-11-30 01:05:50 +00:00
|
|
|
const wheel = [
|
|
|
|
'lose a turn', 800, 350, 450, 700, 300, 600, 5000,
|
|
|
|
300, 600, 300, 500, 800, 550, 400, 300, 900, 500, 'spin again',
|
|
|
|
900, 'Bankrupt', 600, 400, 300
|
|
|
|
];
|
2024-12-03 12:54:13 +00:00
|
|
|
const wheelSpinSpeedData = {
|
|
|
|
'0':0.2523,//lose a turn
|
|
|
|
'1':.2497,//800
|
|
|
|
'2':.2471,//350
|
|
|
|
'3':.2445,//450
|
|
|
|
'4':.2418,//450
|
|
|
|
'5':.2392,//300
|
|
|
|
'6':.2366,//600
|
|
|
|
'7':.2339,//5000
|
|
|
|
'8':.2313,//300
|
|
|
|
'9':.2287,//600
|
|
|
|
'10':.2261,//300
|
|
|
|
'11':.2235,//500
|
|
|
|
|
|
|
|
'12':.2208,//800
|
|
|
|
'13':.2182,//550
|
|
|
|
'14':.2156,//400
|
|
|
|
'15':.213,//300
|
|
|
|
'16':.2104,//900
|
|
|
|
'17':.2078,//500
|
|
|
|
'18':.2052,//spin again
|
|
|
|
'19':.2026,//900
|
|
|
|
'20':.1999,//Bankrupt
|
|
|
|
'21':.1973,//600
|
|
|
|
'22':.1947,//400
|
|
|
|
'23':.1921//300
|
|
|
|
};
|
2024-11-30 01:05:50 +00:00
|
|
|
const canvas = document.getElementById('wheelCanvas');
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
const radius = canvas.width / 2;
|
|
|
|
|
2024-11-30 19:16:48 +00:00
|
|
|
let rotation = 0 - (.283*6); //sets the zero index on top
|
2024-11-30 01:05:50 +00:00
|
|
|
let spinning = false;
|
|
|
|
let spinSpeed = 0;
|
|
|
|
let img = new Image();
|
|
|
|
img.src = "arrow-down-bold.png";
|
|
|
|
img.onload = function() {
|
|
|
|
ctx.drawImage(img, 200, -50, 100,100);
|
|
|
|
}
|
|
|
|
// Draw the wheel
|
|
|
|
function drawWheel(drawResult = false) {
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
|
|
|
|
|
|
ctx.save();
|
|
|
|
|
|
|
|
|
|
|
|
ctx.translate(radius, radius);
|
|
|
|
ctx.rotate(rotation);
|
|
|
|
|
|
|
|
const anglePerSegment = (2 * Math.PI) / wheel.length;
|
|
|
|
|
|
|
|
wheel.forEach((value, i) => {
|
|
|
|
const startAngle = i * anglePerSegment;
|
|
|
|
const endAngle = startAngle + anglePerSegment;
|
|
|
|
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.moveTo(0, 0);
|
|
|
|
ctx.arc(0, 0, radius, startAngle, endAngle);
|
|
|
|
ctx.closePath();
|
|
|
|
|
|
|
|
ctx.fillStyle = i % 2 === 0 ? '#ffdd99' : '#ffeecc';
|
|
|
|
ctx.fill();
|
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
// Add text
|
|
|
|
ctx.save();
|
|
|
|
ctx.translate(
|
|
|
|
Math.cos(startAngle + anglePerSegment / 2) * radius * 0.7,
|
|
|
|
Math.sin(startAngle + anglePerSegment / 2) * radius * 0.7
|
|
|
|
);
|
|
|
|
ctx.rotate(startAngle + anglePerSegment / 2 + Math.PI / 2 - 1.5);
|
|
|
|
ctx.textAlign = 'center';
|
|
|
|
ctx.fillStyle = '#000';
|
|
|
|
ctx.font = '12px Arial';
|
|
|
|
ctx.fillText(String(i+","+value), 0, 0);
|
|
|
|
|
|
|
|
ctx.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
ctx.restore();
|
|
|
|
ctx.drawImage(img, 200, -50, 100,100);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start spinning
|
|
|
|
function startSpin(targetIndex) {
|
|
|
|
console.log('targetIndex: ',targetIndex);
|
2024-12-03 12:54:13 +00:00
|
|
|
let resultingSpeed = wheelSpinSpeedData[targetIndex];
|
|
|
|
console.log('resultingSpeed: ', resultingSpeed);
|
2024-11-30 01:05:50 +00:00
|
|
|
if (spinning) return;
|
2024-11-30 19:16:48 +00:00
|
|
|
rotation = 0 - (.283*6)
|
2024-11-30 01:05:50 +00:00
|
|
|
spinning = true;
|
|
|
|
|
|
|
|
const totalSpins = Math.floor(Math.random() * 3) + 3; // 3-6 full spins
|
|
|
|
const anglePerSegment = (2 * Math.PI) / wheel.length;
|
|
|
|
const targetAngle = targetIndex * anglePerSegment;
|
|
|
|
console.log(anglePerSegment);
|
|
|
|
|
|
|
|
let currentRotation = rotation; // Start from the current rotation
|
2024-12-03 12:54:13 +00:00
|
|
|
spinSpeed = wheelSpinSpeedData[targetIndex]; // Initial speed
|
2024-11-30 01:05:50 +00:00
|
|
|
|
|
|
|
function animate() {
|
2024-11-30 19:16:48 +00:00
|
|
|
if ( spinSpeed < 0.001) {
|
2024-11-30 01:05:50 +00:00
|
|
|
spinning = false;
|
|
|
|
console.log(`Landed on: ${wheel[targetIndex]}`);
|
|
|
|
return;
|
|
|
|
}
|
2024-11-30 19:16:48 +00:00
|
|
|
// console.log('currentRotation: ',currentRotation);
|
|
|
|
// console.log('spinSpeed: ', spinSpeed);
|
2024-11-30 01:05:50 +00:00
|
|
|
currentRotation += spinSpeed;
|
|
|
|
spinSpeed *= 0.99; // Gradual slowdown
|
2024-11-30 19:16:48 +00:00
|
|
|
|
|
|
|
|
2024-11-30 01:05:50 +00:00
|
|
|
rotation = currentRotation ;
|
|
|
|
drawWheel();
|
|
|
|
requestAnimationFrame(animate);
|
|
|
|
}
|
|
|
|
|
|
|
|
animate();
|
|
|
|
}
|
|
|
|
// Simulate WebSocket server response
|
|
|
|
// function simulateServerResponse() {
|
|
|
|
// const targetIndex = Math.floor(Math.random() * wheel.length); // Random result
|
|
|
|
// startSpin(targetIndex);
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Initial draw
|
|
|
|
drawWheel();
|
|
|
|
|
|
|
|
// Trigger spin after 2 seconds
|
|
|
|
//setTimeout(simulateServerResponse, 2000);
|
|
|
|
|
2024-12-10 00:17:36 +00:00
|
|
|
//==================================end of Wheel=====================================//
|
|
|
|
function drawPlayers(message) {
|
2024-12-15 03:45:58 +00:00
|
|
|
console.log(message);
|
2024-12-10 00:17:36 +00:00
|
|
|
let players = document.querySelector('.players');
|
|
|
|
//assignColors(names, colors) {
|
|
|
|
let processedNametags = assignColors(message.clients,nameColors);
|
|
|
|
const playersHtml = processedNametags.map(client => {
|
2024-12-15 03:45:58 +00:00
|
|
|
if(client.name != window.you) {
|
|
|
|
if (client.name === message.leaderName) {
|
|
|
|
return `<span class='mr-2 btn is-round text-with-shadow div-shadow' style='background-color:${client.color};'>⭐ ${client.name} ⭐</span>`; // Highlight the leader with stars
|
|
|
|
} else {
|
|
|
|
return `<span class='mr-2 btn is-round text-with-shadow div-shadow' style='background-color:${client.color};'>${client.name}</span>`;
|
|
|
|
}
|
2024-12-10 00:17:36 +00:00
|
|
|
}
|
2024-12-15 03:45:58 +00:00
|
|
|
else if (client.name === message.leaderName) {
|
|
|
|
return `<span class='mr-2 btn is-round text-with-shadow div-shadow' style='background-color:${client.color};' onclick="changeName()">⭐ ${client.name} ⭐ (you)</span>`; // Highlight the leader with stars
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return `<span class='mr-2 btn is-round text-with-shadow div-shadow' style='background-color:${client.color};' onclick="changeName()">${client.name} (you)</span>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-12-10 00:17:36 +00:00
|
|
|
}).join(""); // Combine all elements into a single string
|
|
|
|
|
|
|
|
players.innerHTML = playersHtml;
|
|
|
|
}
|
2024-12-15 03:45:58 +00:00
|
|
|
|
|
|
|
function changeName() {
|
|
|
|
console.log('change name hit.');
|
|
|
|
const nameDialog = document.querySelector('.nameDialog');
|
|
|
|
const nameInput = document.querySelector('.nameInput');
|
|
|
|
const saveButton = document.querySelector('.saveButton');
|
|
|
|
const closeDialogButton = document.querySelector('.closeDialogButton');
|
|
|
|
nameDialog.showModal();
|
|
|
|
nameInput.value = window.you;
|
|
|
|
closeDialogButton.addEventListener('click',() => {nameDialog.close()});
|
|
|
|
saveButton.addEventListener('click',() => {
|
|
|
|
socket.send(JSON.stringify({ type: "change_name", deadName:window.you, newName:nameInput.value }));
|
|
|
|
console.log('you changed your name.');
|
|
|
|
|
|
|
|
nameDialog.close();
|
|
|
|
});
|
|
|
|
}
|
2024-11-19 12:16:26 +00:00
|
|
|
const socket = new WebSocket("ws://localhost:8080");
|
|
|
|
|
|
|
|
document.getElementById("create-room").onclick = () => {
|
|
|
|
socket.send(JSON.stringify({ type: "create_room" }));
|
|
|
|
};
|
|
|
|
|
|
|
|
document.getElementById("join-room").onclick = () => {
|
|
|
|
const roomCode = document.getElementById("join-code").value;
|
|
|
|
socket.send(JSON.stringify({ type: "join_room", roomCode }));
|
|
|
|
};
|
|
|
|
|
|
|
|
document.getElementById("start-game").onclick = () => {
|
|
|
|
socket.send(JSON.stringify({ type: "start_game" }));
|
|
|
|
};
|
|
|
|
|
|
|
|
document.getElementById("spin-wheel").onclick = () => {
|
|
|
|
socket.send(JSON.stringify({ type: "spin_wheel" }));
|
|
|
|
};
|
|
|
|
|
|
|
|
document.getElementById("guess-button").onclick = () => {
|
|
|
|
const letter = document.getElementById("guess-letter").value;
|
|
|
|
socket.send(JSON.stringify({ type: "guess_letter", letter }));
|
|
|
|
document.getElementById("guess-letter").value = ""; // Clear input
|
|
|
|
};
|
|
|
|
|
2024-12-10 00:17:36 +00:00
|
|
|
|
|
|
|
|
2024-11-19 12:16:26 +00:00
|
|
|
socket.onmessage = (event) => {
|
|
|
|
const message = JSON.parse(event.data);
|
|
|
|
|
|
|
|
if (message.type === "room_created") {
|
2024-12-15 03:45:58 +00:00
|
|
|
window.you = message.you;
|
2024-11-19 12:16:26 +00:00
|
|
|
document.getElementById("status").innerText = `Room created! Code: ${message.roomCode}`;
|
2024-12-10 00:17:36 +00:00
|
|
|
drawPlayers(message);
|
2024-11-19 12:16:26 +00:00
|
|
|
if (message.isLeader) {
|
|
|
|
document.getElementById("start-game").style.display = "inline";
|
|
|
|
}
|
|
|
|
}
|
2024-12-16 00:41:54 +00:00
|
|
|
if (message.type === "changed_name_you") {
|
|
|
|
window.you = message.you;
|
|
|
|
console.log('server has changed your name!');
|
|
|
|
}
|
|
|
|
if (message.type === "changed_name") {
|
|
|
|
drawPlayers(message);
|
|
|
|
console.log('server has changed your name!');
|
|
|
|
}
|
2024-12-15 03:45:58 +00:00
|
|
|
if (message.type === "joined_room_you") {
|
|
|
|
window.you = message.you;
|
|
|
|
}
|
2024-11-19 12:16:26 +00:00
|
|
|
if (message.type === "joined_room") {
|
2024-12-15 03:45:58 +00:00
|
|
|
|
2024-12-10 00:17:36 +00:00
|
|
|
drawPlayers(message);
|
2024-11-19 12:16:26 +00:00
|
|
|
const status = message.isLeader ? "You are the party leader!" : "You joined the room!";
|
2024-12-10 00:17:36 +00:00
|
|
|
// document.querySelector('.players').innerHtml += `<span>${}<span>`
|
|
|
|
|
2024-11-19 12:16:26 +00:00
|
|
|
document.getElementById("status").innerText = `Room Code: ${message.roomCode} - ${status}`;
|
|
|
|
if (message.isLeader) {
|
|
|
|
document.getElementById("start-game").style.display = "inline";
|
|
|
|
document.getElementById("spin-wheel").style.display = "inline";
|
|
|
|
document.getElementById("guess-letter").style.display = "inline";
|
|
|
|
document.getElementById("guess-button").style.display = "inline";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message.type === "game_started") {
|
2024-12-16 00:41:54 +00:00
|
|
|
console.log(message);
|
2024-12-10 00:17:36 +00:00
|
|
|
document.getElementById("status").innerText = "The game has started!";
|
|
|
|
document.getElementById("start-game").style.display = "none";
|
|
|
|
document.getElementById("start-game").style.display = "inline";
|
2024-11-28 15:08:38 +00:00
|
|
|
document.getElementById("spin-wheel").style.display = "inline";
|
|
|
|
document.getElementById("guess-letter").style.display = "inline";
|
|
|
|
document.getElementById("guess-button").style.display = "inline";
|
2024-11-19 12:16:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (message.type === "spin_result") {
|
2024-11-30 01:05:50 +00:00
|
|
|
console.log('spin result recieved');
|
2024-11-19 12:16:26 +00:00
|
|
|
document.getElementById("status").innerText = `Spin result: ${message.spinResult} points (${message.player})`;
|
2024-11-30 01:05:50 +00:00
|
|
|
//find target index
|
|
|
|
let targetIndexArr = wheel.entries();
|
|
|
|
let filterResultsArr = [];
|
|
|
|
for(let x of targetIndexArr){
|
|
|
|
if (x[1] == message.spinResult) {
|
|
|
|
filterResultsArr.push(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
console.log(filterResultsArr[0][0]);
|
|
|
|
//if there is multiple entries where the spin exists pick a random index.
|
|
|
|
if(filterResultsArr.length > 1) {
|
|
|
|
startSpin(filterResultsArr[Math.floor(Math.random() * filterResultsArr.length)][0]);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
startSpin(filterResultsArr[0]);
|
|
|
|
}
|
|
|
|
|
2024-11-19 12:16:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (message.type === "guess_result") {
|
|
|
|
const outcome = message.correct ? "correct" : "incorrect";
|
|
|
|
document.getElementById("status").innerText = `Guess '${message.letter}' was ${outcome} (${message.player})`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message.type === "new_leader") {
|
2024-12-10 00:17:36 +00:00
|
|
|
drawPlayers(message);
|
2024-11-19 12:16:26 +00:00
|
|
|
document.getElementById("status").innerText = "You are now the party leader!";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message.type === "error") {
|
|
|
|
alert(message.message);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
</script>
|
2024-11-30 01:05:50 +00:00
|
|
|
|
|
|
|
|
2024-11-19 12:16:26 +00:00
|
|
|
</body>
|
|
|
|
</html>
|