762 lines
27 KiB
JavaScript
762 lines
27 KiB
JavaScript
const WebSocket = require('ws')
|
|
const { v4: uuidv4 } = require('uuid')
|
|
const puzzles = require('./puzzles.json')
|
|
const names = require('./assignedNames.json')
|
|
const crypto = require('crypto')
|
|
|
|
const wss = new WebSocket.Server({ port: 8080 })
|
|
const rooms = {} // Stores rooms and their clients
|
|
|
|
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
|
|
] //represents wheel in wheel of fortune game.
|
|
// const wheel = ['spin again','Bankrupt','lose a turn']
|
|
function removeProp(obj, prop) {
|
|
obj = JSON.parse(JSON.stringify(obj))
|
|
if(!Array.isArray(prop)){
|
|
if (!Object.hasOwn(obj,prop)) {
|
|
console.error(`the property '${prop}' you are trying to remove doesn't exist on this object!`)
|
|
} else {
|
|
delete obj[prop]
|
|
}
|
|
}
|
|
else {
|
|
for (let i = 0; i < prop.length; i++) {
|
|
const key = prop[i]
|
|
if (!Object.hasOwn(obj, key)) {
|
|
console.error(`The property '${key}' you are trying to remove doesn't exist on this object!`)
|
|
} else {
|
|
delete obj[key]
|
|
}
|
|
}
|
|
}
|
|
return obj
|
|
}
|
|
//make sure this is run so puzzles are easier to contruct
|
|
function checkPuzzleData(puzzles) {
|
|
for(let puzzle in puzzles) {
|
|
let i = puzzle
|
|
puzzle = puzzles[puzzle]
|
|
if (Array.isArray(puzzle.answer)) {
|
|
if (puzzle.answer.length != 4) {
|
|
throw new Error(`
|
|
puzzle at index ${i} doesn't have the correct amount of slots defined. The proper amount of slots is 4, and you have ${puzzle.answer.length}. use "" to denote an empty line.
|
|
`)
|
|
}
|
|
for (let p of puzzle.answer){
|
|
if (p.length > 12) {
|
|
throw new Error(`${p} (${p.length} characters long) is longer than 12 characters. only twelve characters allowed per line`)
|
|
}
|
|
}
|
|
}
|
|
else if (typeof puzzle.answer === 'string') {
|
|
if (puzzle.answer.length > 12) {
|
|
throw new Error(`${puzzle.answer} (${puzzle.answer.length} characters long) is longer than 12 characters. only twelve characters allowed per line`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function getRandomValue(array) {
|
|
const randomIndex = crypto.randomInt(0, array.length)
|
|
return array[randomIndex]
|
|
}
|
|
function shuffleArray(array) {
|
|
for (let i = array.length - 1; i > 0; i--) {
|
|
// Generate a cryptographically secure random index
|
|
const j = crypto.randomInt(0, i + 1);
|
|
// Swap elements
|
|
[array[i], array[j]] = [array[j], array[i]]
|
|
}
|
|
return array
|
|
}
|
|
|
|
const DefaultNames = shuffleArray(names)
|
|
let NameIndex = 0
|
|
function getRandomName() {
|
|
if (NameIndex >= DefaultNames.length) {
|
|
NameIndex = 0
|
|
}else{
|
|
NameIndex++
|
|
}
|
|
//since we've incremented the index but not returned the value,
|
|
//calculate the index again
|
|
return NameIndex === 0 ? DefaultNames[NameIndex] : DefaultNames[NameIndex-1]
|
|
}
|
|
|
|
|
|
|
|
function loadCurrentPuzzle(gameStateObject) {
|
|
console.log(gameStateObject)
|
|
let letterArray
|
|
//make sure we only show whats given in the letterArray, and empty values for everything else
|
|
function processPuzzle(letterArray,given) {
|
|
let puzzleArray = []
|
|
for (let letter of letterArray) {
|
|
if (letter === ' ') {
|
|
puzzleArray.push(' ')
|
|
continue
|
|
}
|
|
let letterFound = false
|
|
for (let g of given ) {
|
|
if(g.toUpperCase() === letter.toUpperCase()) {
|
|
puzzleArray.push(g)
|
|
letterFound = true
|
|
}
|
|
}
|
|
if (!letterFound) {puzzleArray.push('')}
|
|
}
|
|
console.log('in loadCurrentPuzzle: ',puzzleArray)
|
|
return puzzleArray
|
|
}
|
|
|
|
//calculate the puzzle for now we'll just go through however many iterations there are
|
|
//for each slot
|
|
if (Array.isArray(gameStateObject.puzzles[gameStateObject.puzzleLevel].answer)) {
|
|
let formattedPuzzle = []
|
|
for (let i of gameStateObject.puzzles[gameStateObject.puzzleLevel].answer) {
|
|
letterArray = i.split('')
|
|
const given = gameStateObject.puzzles[gameStateObject.puzzleLevel].given
|
|
formattedPuzzle.push(processPuzzle(letterArray,given))
|
|
}
|
|
return {
|
|
'answer':gameStateObject.puzzles[gameStateObject.puzzleLevel].answer,
|
|
'given':gameStateObject.puzzles[gameStateObject.puzzleLevel].given,
|
|
'category':gameStateObject.puzzles[gameStateObject.puzzleLevel].category,
|
|
'puzzle':formattedPuzzle,
|
|
}
|
|
}
|
|
else {
|
|
letterArray = gameStateObject.puzzles[gameStateObject.puzzleLevel].answer.split('')
|
|
const given = gameStateObject.puzzles[gameStateObject.puzzleLevel].given
|
|
gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle = processPuzzle(letterArray, given)
|
|
//console.log(gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle);
|
|
|
|
return {
|
|
'answer':gameStateObject.puzzles[gameStateObject.puzzleLevel].answer,
|
|
'given':gameStateObject.puzzles[gameStateObject.puzzleLevel].given,
|
|
'category':gameStateObject.puzzles[gameStateObject.puzzleLevel].category,
|
|
'puzzle':gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle,
|
|
}
|
|
}
|
|
}
|
|
|
|
function checkGuess(letter,gameStateObject) {
|
|
console.log('checkguess: ',gameStateObject,letter)
|
|
const currentPuzzle = gameStateObject.puzzles[gameStateObject.puzzleLevel]
|
|
|
|
console.log(currentPuzzle)
|
|
if (typeof currentPuzzle.answer === 'string') {
|
|
//check if user already guessed one of teh given letters
|
|
if (currentPuzzle.puzzle.find((x)=> x.toUpperCase() === letter.toUpperCase())) {
|
|
console.log('user gave a guess on a given letter...')
|
|
return false
|
|
}
|
|
//first create an indexed hashMap
|
|
let charArray = currentPuzzle.answer.split('')
|
|
let matches = []
|
|
for(const c in charArray) {
|
|
const char = charArray[c]
|
|
if (letter.toUpperCase() === char.toUpperCase()) {
|
|
matches.push({id:c,letter:char})
|
|
}
|
|
}
|
|
|
|
//if there are any matches write them to the gameState Object
|
|
if (!matches.length) {
|
|
return false
|
|
}
|
|
else {
|
|
for(const m of matches) {
|
|
gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle[m.id] = m.letter
|
|
}
|
|
//make sure to check if a win occurred
|
|
// convert the answer string into an array of characters
|
|
let checkAnswer = currentPuzzle.answer.split('')
|
|
if (currentPuzzle.puzzle.every((val,i)=> val.toUpperCase() === checkAnswer[i].toUpperCase())) {
|
|
return 'puzzleSolved'
|
|
}
|
|
console.log('matches found!!!', gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle)
|
|
return [true, matches.length]
|
|
}
|
|
}
|
|
else if (Array.isArray(currentPuzzle.answer)) {
|
|
console.log(currentPuzzle.puzzle.flat())
|
|
if (currentPuzzle.puzzle.flat().find((x)=> x.toUpperCase() === letter.toUpperCase())) {
|
|
console.log('user gave a guess on a given letter...')
|
|
return false
|
|
}
|
|
//check each row of letters for the answer
|
|
let matches = []
|
|
console.log(currentPuzzle.answer)
|
|
for (let r in currentPuzzle.answer) {
|
|
let row = currentPuzzle.answer[r]
|
|
//first create an indexed hashMap
|
|
let charArray = row.split('')
|
|
|
|
for(const c in charArray) {
|
|
const char = charArray[c]
|
|
if (letter.toUpperCase() === char.toUpperCase()) {
|
|
matches.push({id:c,row_index:r,letter:char})
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
console.log(matches)
|
|
//if there are any matches write them to the gameState Object
|
|
if (!matches.length) {
|
|
return false
|
|
} else {
|
|
for (let i in matches) {
|
|
let match = matches[i]
|
|
gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle[match.row_index][match.id] = match.letter
|
|
}
|
|
console.log(gameStateObject.puzzles[gameStateObject.puzzleLevel].answer)
|
|
//make sure to check if a win occurred
|
|
// convert the answer string into an array of characters
|
|
console.log(gameStateObject.puzzles[gameStateObject.puzzleLevel].answer.flat())
|
|
let checkAnswer = gameStateObject.puzzles[gameStateObject.puzzleLevel].answer.flat().filter((letter) => letter != ' ').join('')
|
|
let puzzleToCheck = gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle.flat().filter((letter) => letter != ' ').join('')
|
|
console.log(checkAnswer,puzzleToCheck)
|
|
if (gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle.flat().every((val,i)=> val.toUpperCase() === checkAnswer[i].toUpperCase())) {
|
|
return 'puzzleSolved'
|
|
}
|
|
console.log('matches found!!!', gameStateObject.puzzles[gameStateObject.puzzleLevel].puzzle)
|
|
return [true, matches.length]
|
|
}
|
|
|
|
}
|
|
else console.error('invalid input to checkGuess() function',currentPuzzle.puzzle[0])
|
|
|
|
}
|
|
function checkWinState(gameStateobject,ws){
|
|
if (gameStateobject.puzzleLevel === gameStateobject.puzzles.length) {
|
|
console.log('announcing winner')
|
|
announceWinner(gameStateobject,ws)
|
|
return true
|
|
}
|
|
else return false
|
|
}
|
|
function checkSolvePuzzleGuess(guess,ws) {
|
|
const room = rooms[ws.roomCode]
|
|
console.log(room.gameState)
|
|
//check if the answer is an array first as we have to parse it differently if thats the case.
|
|
if (!Array.isArray(room.gameState.puzzles[room.gameState.puzzleLevel].answer)) {
|
|
if (guess.toUpperCase() != room.gameState.puzzles[room.gameState.puzzleLevel].answer.toUpperCase()) {
|
|
console.log(guess.toUpperCase(),room.gameState.puzzles[room.gameState.puzzleLevel].answer.toUpperCase())
|
|
//guess is wrong change turns
|
|
console.log('guess is incorrect')
|
|
const currentUserTurn = room.gameState.players[room.gameState.turn]
|
|
room.clients.forEach((client) => {
|
|
client.send(JSON.stringify({
|
|
type:'incorrect_puzzle_guess',
|
|
message: `${currentUserTurn.name} guessed the puzzle incorrectly`
|
|
}))
|
|
})
|
|
changeTurn(room.gameState,ws)
|
|
}
|
|
else {
|
|
console.log('guess i correct')
|
|
//guessed correctly reward the user
|
|
rewardUser(room.gameState,ws,true)
|
|
if (!checkWinState(room.gameState,ws)) {
|
|
console.log('winstate should be false here 214')
|
|
loadNewPuzzle(room.gameState,ws)
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//its probably an array
|
|
console.log(room.gameState.puzzles[room.gameState.puzzleLevel])
|
|
let answer = room.gameState.puzzles[room.gameState.puzzleLevel].answer
|
|
//convert and filter the answer into a string that can be compared seperated by spaces
|
|
answer = answer.filter(word => word != '').join(' ')
|
|
if (answer.toUpperCase() != guess.toUpperCase()) {
|
|
console.log('wrong',guess.toUpperCase(),answer.toUpperCase())
|
|
//wrong
|
|
changeTurn(room.gameState,ws)
|
|
}
|
|
else {
|
|
//correct
|
|
console.log('correct.',guess.toUpperCase(),answer.toUpperCase())
|
|
rewardUser(room.gameState,ws,true)
|
|
if (!checkWinState(room.gameState,ws)) {
|
|
console.log('winstate should be false here 236')
|
|
loadNewPuzzle(room.gameState,ws)
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
function announceWinner(gameStateObject,ws) {
|
|
//find user with highest points.
|
|
//announce to the world.
|
|
const room = rooms[ws.roomCode]
|
|
let winner = gameStateObject.players.sort((a,b)=> b.points - a.points)
|
|
console.log(winner)
|
|
//needs to be tested on multiple winners and decide ties.
|
|
room.clients.forEach((client) => {
|
|
client.send(JSON.stringify({
|
|
type:'game_over',
|
|
winner: `${winner[0].name} wins with ${winner[0].points} points!!!`,
|
|
playerStats: room.gameState.players
|
|
}))
|
|
})
|
|
console.log('winner', winner)
|
|
}
|
|
//these functions must have both gameStateobject and websockets initialized before use!!!
|
|
function rewardUser(gameStateObject,ws,puzzleSolved = false) {
|
|
const roomCode = ws.roomCode
|
|
const room = rooms[ws.roomCode]
|
|
//find the current player
|
|
let currentUserTurn = gameStateObject.players[gameStateObject.turn]
|
|
currentUserTurn.wins++
|
|
gameStateObject.puzzleLevel++
|
|
if (puzzleSolved) {
|
|
currentUserTurn.points = currentUserTurn.points*3
|
|
console.log(currentUserTurn.points)
|
|
}
|
|
|
|
room.clients.forEach((client) => {
|
|
client.send(JSON.stringify({
|
|
type: 'puzzle_solved', roomCode,
|
|
user: currentUserTurn.name
|
|
}))
|
|
})
|
|
}
|
|
function changeTurn(gameStateObject,ws) {
|
|
console.log('change turn hit')
|
|
const room = rooms[ws.roomCode]
|
|
|
|
if (gameStateObject.turn >= gameStateObject.players.length - 1) {
|
|
gameStateObject.turn = 0
|
|
}
|
|
else gameStateObject.turn++
|
|
|
|
//alert next user of their turn
|
|
room.clients.forEach((client) => {
|
|
client.send(JSON.stringify(
|
|
{ type: 'next_turn',
|
|
user:room.gameState.players[room.gameState.turn].name,
|
|
}))
|
|
})
|
|
}
|
|
function loadNewPuzzle(gameStateObject,ws) {
|
|
console.log('loadNewPuzzle called.')
|
|
const room = rooms[ws.roomCode]
|
|
const newPuzzle = loadCurrentPuzzle(gameStateObject)
|
|
room.gameState.turnState = 'spin'
|
|
room.gameState.puzzles[room.gameState.puzzleLevel] = newPuzzle
|
|
room.gameState.levelsRemaining = (room.gameState.puzzles.length - 1 - room.gameState.puzzleLevel)
|
|
|
|
room.clients.forEach((client) => {
|
|
client.send(JSON.stringify({
|
|
type: 'new_puzzle',
|
|
roomCode: ws.roomCode,
|
|
// eslint-disable-next-line no-undef
|
|
puzzle: removeProp(newPuzzle,'answer'),
|
|
turn:room.gameState.turn,
|
|
turnState:room.gameState.turnState,
|
|
playerStats: room.gameState.players
|
|
}))
|
|
})
|
|
}
|
|
// function checkIfClientExistsInRoom(ws,room) {
|
|
// console.log(room,ws)
|
|
// //assumes ws message is sent and room code exists
|
|
// for(const c in room.clients) {
|
|
// const client = room.clients[c]
|
|
// if (ws.identifierToken === client.identifierToken && ws.roomCode === client.roomCode) {
|
|
// return true
|
|
// }
|
|
// }
|
|
// return false
|
|
// }
|
|
|
|
checkPuzzleData(puzzles)
|
|
|
|
// server.js
|
|
wss.on('connection', (ws) => {
|
|
ws.on('message', (message) => {
|
|
const data = JSON.parse(message)
|
|
|
|
if (data.type === 'change_name') {
|
|
const roomCode = ws.roomCode
|
|
const room = rooms[ws.roomCode]
|
|
console.log(room)
|
|
console.log(room.leader.name,room.clients[0].name)
|
|
console.log(room.clients.filter((i)=> i.name === data.deadName))
|
|
console.log(ws.name,ws.identifierToken)
|
|
//change names in all places.
|
|
//if the leader wants to change their name account for this
|
|
//note this could probably be exploited oneday and break the game but whatever
|
|
//since there is no logging in of users this is bound to happen.
|
|
|
|
//find the names by index using the identifier Token which i user can't easily create
|
|
//a name collision.
|
|
const nameToChange = room.clients.findIndex((i)=> i.identifierToken === ws.identifierToken )
|
|
console.log(nameToChange)
|
|
if (nameToChange != -1) {
|
|
room.clients[nameToChange].name = data.newName
|
|
}
|
|
if (room.leader.identifierToken === room.clients[nameToChange].identifierToken) {
|
|
room.leader.name = data.newName
|
|
}
|
|
//if the user is not the leader, in theory just change the name on the clients list
|
|
//not sure if it exists in other places but test and see
|
|
|
|
//send name changed event to client, similar to joined room but it will work when people are in game.
|
|
ws.send(JSON.stringify({
|
|
type: 'changed_name_you', roomCode,
|
|
isLeader: rooms[roomCode].leader === ws,
|
|
you:data.newName
|
|
}))
|
|
room.clients.forEach((client) => {
|
|
client.send(JSON.stringify({
|
|
type: 'changed_name', roomCode,
|
|
isLeader: room.leader === ws ,
|
|
clients: room.clients.map((i)=> i.name),
|
|
leaderName:room.leader.name
|
|
}))
|
|
})
|
|
|
|
}
|
|
|
|
if (data.type === 'solve_puzzle') {
|
|
console.log('solve_puzzle hit')
|
|
const room = rooms[ws.roomCode]
|
|
if(data.guess == '') return
|
|
if (room.gameState.turnState === null) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'the game hasn\'t started yet!' }))
|
|
return
|
|
}else if (ws.identifierToken !== room.gameState.players[room.gameState.turn].id) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'its not your turn to guess!' }))
|
|
return
|
|
}
|
|
checkSolvePuzzleGuess(data.guess, ws)
|
|
}
|
|
if (data.type === 'confirm_id') {
|
|
const room = rooms[ws.roomCode]
|
|
const currentUserTurn = room.gameState.players[room.gameState.turn]
|
|
room.gameState.turnState = 'spin'
|
|
if (ws.name === currentUserTurn.name && ws.identifierToken === currentUserTurn.id) {
|
|
ws.send(JSON.stringify({ type:'next_turn_confirmed', turnState:room.gameState.turnState}))
|
|
}
|
|
// } else ws.send(JSON.stringify({ type:'next_turn_denied'}))
|
|
room.clients.forEach((client) => {
|
|
if (client.name !== currentUserTurn.name && ws.identifierToken === currentUserTurn.id) {
|
|
console.log(client)
|
|
client.send(JSON.stringify({ type:'next_turn_denied'}))
|
|
}
|
|
})
|
|
}
|
|
if (data.type === 'create_room') {
|
|
const roomCode = uuidv4().slice(0, 5)
|
|
ws.name = getRandomName()
|
|
ws.identifierToken = uuidv4().slice(0, 5)
|
|
rooms[roomCode] = {
|
|
clients: [ws],
|
|
leader: ws,
|
|
gameState: {
|
|
started:false,
|
|
puzzles:shuffleArray(puzzles),
|
|
puzzleLevel:0,
|
|
turn:0,
|
|
turnState:null,
|
|
players:[]
|
|
},
|
|
}
|
|
console.log(rooms[roomCode].clients[0].name)
|
|
ws.roomCode = roomCode
|
|
ws.send(JSON.stringify({ type: 'room_created', roomCode,
|
|
isLeader: true,
|
|
leaderName: ws.name,
|
|
clients: rooms[roomCode].clients.map((i)=> i.name),
|
|
you:ws.name
|
|
}))
|
|
}
|
|
|
|
if (data.type === 'join_room') {
|
|
const { roomCode } = data
|
|
console.log(rooms)
|
|
console.log(rooms[roomCode],roomCode)
|
|
|
|
if (!rooms[roomCode]) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'Room not found' }))
|
|
}
|
|
else if (rooms[roomCode].gameState.started) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'Game has already Started!!!' }))
|
|
}
|
|
else {
|
|
ws.name = getRandomName()
|
|
ws.identifierToken = uuidv4().slice(0, 5)
|
|
rooms[roomCode].clients.push(ws)
|
|
ws.roomCode = roomCode
|
|
const room = rooms[ws.roomCode]
|
|
ws.send(JSON.stringify({
|
|
type: 'joined_room_you', roomCode,
|
|
isLeader: rooms[roomCode].leader === ws,
|
|
you:ws.name
|
|
}))
|
|
|
|
room.clients.forEach((client) => {
|
|
client.send(JSON.stringify({
|
|
type: 'joined_room', roomCode,
|
|
isLeader: rooms[roomCode].leader === ws ,
|
|
clients: rooms[roomCode].clients.map((i)=> i.name),
|
|
leaderName:rooms[roomCode].leader.name
|
|
}))
|
|
})
|
|
|
|
|
|
}
|
|
//console.log('clients: ',rooms[roomCode].leader.name);
|
|
}
|
|
|
|
if (data.type === 'start_game') {
|
|
|
|
const room = rooms[ws.roomCode]
|
|
if (room.gameState.started) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'game has already been started' }))
|
|
}
|
|
const currentPuzzle = loadCurrentPuzzle(room.gameState)
|
|
// const clientPuzzle = Object.entries(currentPuzzle).filter(([key])=> key != 'answer')
|
|
|
|
room.gameState.started = true
|
|
|
|
room.gameState.turnState = 'spin'
|
|
room.gameState.puzzles[room.gameState.puzzleLevel] = currentPuzzle
|
|
room.gameState.levelsRemaining = (room.gameState.puzzles.length - 1 - room.gameState.puzzleLevel)
|
|
room.clients.forEach((client)=>{
|
|
room.gameState.players.push({
|
|
id:client.identifierToken,
|
|
name:client.name,
|
|
points:0,
|
|
wins:0
|
|
})
|
|
})
|
|
//console.log(room.gameState)
|
|
console.log('game started for:',room)
|
|
let selectedPlayer = room.clients.find((x)=>x.identifierToken === room.gameState.players[room.gameState.turn].id)
|
|
if (selectedPlayer) {
|
|
console.log('hit')
|
|
selectedPlayer.send(JSON.stringify({type:'your_turn'}))
|
|
}
|
|
if (room && room.leader === ws) {
|
|
room.clients.forEach((client) => {
|
|
console.log(client.identifierToken)
|
|
client.send(JSON.stringify({
|
|
type: 'game_started',
|
|
roomCode: ws.roomCode,
|
|
puzzle: removeProp(currentPuzzle,'answer'),
|
|
turn:room.gameState.turn,
|
|
turnState:room.gameState.turnState,
|
|
playerStats:room.gameState.players.map(({id,...rest}) => rest)
|
|
}))
|
|
|
|
})
|
|
} else {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'Only the leader can start the game' }))
|
|
}
|
|
}
|
|
|
|
if (data.type === 'spin_wheel' || data.type === 'guess_letter') {
|
|
const room = rooms[ws.roomCode]
|
|
|
|
if (room && room.clients.includes(ws)) {
|
|
// Handle spin and guess events
|
|
|
|
if (data.type === 'spin_wheel') {
|
|
if (room.gameState.turnState === null) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'the game hasn\'t started yet!' }))
|
|
return
|
|
}else if (ws.identifierToken !== room.gameState.players[room.gameState.turn].id) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'its not your turn to spin!' }))
|
|
return
|
|
}
|
|
// Simulate a wheel spin result and update room state
|
|
let spinResult = getRandomValue(wheel)
|
|
|
|
if (typeof spinResult !== 'string') {
|
|
room.gameState.turnState = 'guess'
|
|
room.gameState.players = room.gameState.players.map((player) => {
|
|
return player.id === ws.identifierToken ? {...player, points:parseInt(player.points)+parseInt(spinResult)} : player
|
|
})
|
|
}
|
|
if (spinResult === 'Bankrupt') {
|
|
room.gameState.players = room.gameState.players.map((player) => {
|
|
return player.id === ws.identifierToken ? {...player, points:0} : player
|
|
})
|
|
}
|
|
if (spinResult === 'spin again') {
|
|
console.log('in spin again')
|
|
room.gameState.turnState = 'spin'
|
|
room.gameState.players = room.gameState.players.map((player) => {
|
|
return player.id === ws.identifierToken ? {...player, points:player.points + 0, condition:'spin again'} : player
|
|
})
|
|
|
|
}
|
|
if (spinResult === 'lose a turn') {
|
|
console.log('lose a turn here ig...?')
|
|
changeTurn(room.gameState,ws)
|
|
}
|
|
|
|
room.gameState.players = room.gameState.players.map((player) => {
|
|
return player.id === ws.identifierToken ? {...player, points:player.points + 0} : player
|
|
})
|
|
console.log('players', room.gameState.players)
|
|
room.gameState.spinResult = spinResult
|
|
|
|
console.log('spin_result',room.gameState)
|
|
room.clients.forEach((client) =>
|
|
client.send(JSON.stringify({
|
|
type: 'spin_result', spinResult,
|
|
player: room.gameState.players[room.gameState.turn],
|
|
turnState: room.gameState.turnState,
|
|
playerStats:room.gameState.players.map(({id,...rest}) => rest)
|
|
}))
|
|
)
|
|
}
|
|
|
|
if (data.type === 'guess_letter') {
|
|
if (room.gameState.turnState === null) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'the game hasn\'t started yet!' }))
|
|
return
|
|
}else if (ws.identifierToken !== room.gameState.players[room.gameState.turn].id) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'its not your turn to guess!' }))
|
|
return
|
|
}
|
|
room.gameState.turnState = 'guess'
|
|
const { letter } = data
|
|
// Handle guess logic (e.g., check if the letter is in the puzzle)
|
|
|
|
const guessResult = checkGuess(letter,room.gameState)
|
|
|
|
if (!guessResult) {
|
|
room.gameState.turnState = 'spin'
|
|
//if the player guesses incorrectly, and theres more than one player,
|
|
//its the next players turn
|
|
room.clients.forEach((client) => {
|
|
client.send(JSON.stringify(
|
|
{ type: 'guess_result', letter,
|
|
correct: guessResult,
|
|
player: room.gameState.players[room.gameState.turn],
|
|
turnState:room.gameState.turnState,
|
|
playerStats:room.gameState.players.map(({id,...rest}) => rest)
|
|
}))
|
|
})
|
|
|
|
changeTurn(room.gameState,ws)
|
|
|
|
}
|
|
else if(guessResult === 'puzzleSolved') {
|
|
rewardUser(room.gameState,ws)
|
|
if (!checkWinState(room.gameState,ws)) {
|
|
console.log('winstate should be false here 583')
|
|
loadNewPuzzle(room.gameState,ws)
|
|
}
|
|
}
|
|
else {
|
|
//the player guessed correctly and its still their turn
|
|
room.gameState.turnState = 'spin'
|
|
console.log('correct guess!!',room.gameState.turn)
|
|
room.clients.forEach((client) =>
|
|
client.send(JSON.stringify(
|
|
{ type: 'guess_result', letter,
|
|
correct: guessResult,
|
|
player: room.gameState.players[room.gameState.turn],
|
|
puzzle: room.gameState.puzzles[room.gameState.puzzleLevel],
|
|
turn:room.gameState.turn,
|
|
turnState:room.gameState.turnState,
|
|
playerStats:room.gameState.players.map(({id,...rest}) => rest)
|
|
}))
|
|
)
|
|
}
|
|
|
|
}
|
|
} else {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'You are not in this room' }))
|
|
}
|
|
}
|
|
})
|
|
function moveToNextPlayer(room,playerLeaving) {
|
|
console.log(room)
|
|
let gameStateObject = room.gameState
|
|
console.log('moveToNextPlayer', gameStateObject)
|
|
//we need to take note of where teh players are before the leaving happens.
|
|
for (let p in gameStateObject.players) {
|
|
if (gameStateObject.players[p].id == playerLeaving.id) {
|
|
|
|
if (gameStateObject.players[p+1] === undefined) {
|
|
gameStateObject.turn = 0
|
|
}
|
|
else gameStateObject.turn = p
|
|
break
|
|
}
|
|
}
|
|
gameStateObject.players = gameStateObject.players.filter((client)=> client.id != ws.identifierToken)
|
|
|
|
//alert next user of their turn
|
|
room.clients.forEach((client) => {
|
|
client.send(JSON.stringify(
|
|
{ type: 'next_turn',
|
|
user:room.gameState.players[room.gameState.turn].name,
|
|
}))
|
|
})
|
|
//set the game index and initiate a turn;
|
|
}
|
|
ws.on('close', () => {
|
|
if (ws.roomCode && rooms[ws.roomCode]) {
|
|
|
|
console.log('closing')
|
|
const room = rooms[ws.roomCode]
|
|
const roomCode = ws.roomCode
|
|
|
|
const playerLeaving = room.gameState.players.filter((client) => client.id == ws.identifierToken)[0]
|
|
|
|
console.log(playerLeaving)
|
|
room.clients = room.clients.filter((client) => client !== ws)
|
|
|
|
room.gameState.turnState = 'spin'
|
|
//if the client who's leaving isn't taking their turn, use the changeTurnFunction
|
|
moveToNextPlayer(room,playerLeaving)
|
|
|
|
|
|
|
|
|
|
console.log('in close',room.gameState.players)
|
|
//decide who's turn it is based on who leaves...
|
|
if (room.leader === ws && room.clients.length > 0) {
|
|
console.log('closing within room leader')
|
|
room.leader = room.clients[0]
|
|
room.leader.send(JSON.stringify({
|
|
type: 'new_leader',
|
|
leaderName:rooms[roomCode].leader.name,
|
|
playerStats:room.gameState.players.map(({id,...rest}) => rest),
|
|
turn:room.gameState.turn,
|
|
turnState:room.gameState.turnState
|
|
}))
|
|
|
|
}
|
|
room.clients.forEach((client) =>
|
|
client.send(JSON.stringify({
|
|
type: 'joined_room', roomCode,
|
|
isLeader: rooms[roomCode].leader === ws ,
|
|
playerStats:room.gameState.players.map(({id,...rest}) => rest),
|
|
leaderName:rooms[roomCode].leader.name,
|
|
}))
|
|
)
|
|
if (room.clients.length === 0) delete rooms[ws.roomCode]
|
|
}
|
|
})
|
|
})
|
|
|
|
|
|
console.log('WebSocket server is running on ws://localhost:8080')
|
|
|
|
module.exports = {
|
|
DefaultNames,NameIndex,
|
|
getRandomName,getRandomValue,shuffleArray
|
|
} |