Added leaderboard, music & respawn screen

This commit is contained in:
Thomas
2020-09-03 16:13:04 +02:00
parent 4d542c1091
commit 832e55c0a3
20 changed files with 259 additions and 113 deletions

View File

@@ -1,6 +1,6 @@
<template> <template>
<div id="app"> <div id="app">
<Game v-if="isLoggedIn"/> <Game v-if="isLoggedIn && isSocketConnected"/>
<div v-else class="container mainMenu"> <div v-else class="container mainMenu">
<img src="./assets/logo.png" alt="TronIo logo" width="150px"/> <img src="./assets/logo.png" alt="TronIo logo" width="150px"/>
<h1 class="title">tron.io</h1> <h1 class="title">tron.io</h1>
@@ -12,7 +12,9 @@
<script> <script>
import { send } from './store/socketPlugin' import { send } from './store/socketPlugin'
import { sound } from '@/game/sound.js'
import Game from './components/Game' import Game from './components/Game'
export default { export default {
name: 'App', name: 'App',
components: { components: {
@@ -29,17 +31,20 @@ export default {
return this.$store.state.game.loggedIn return this.$store.state.game.loggedIn
} }
}, },
mounted () {
sound.startBackgroundMusic()
},
methods: { methods: {
loginPrompt () { loginPrompt () {
this.$buefy.dialog.prompt({ this.$buefy.dialog.prompt({
message: 'Choose a name', message: 'Choose a name',
trapFocus: true, trapFocus: true,
inputAttrs: { inputAttrs: {
placeholder: 'pedro', placeholder: 'user',
minlength: 3, minlength: 1,
maxlength: 30 maxlength: 15
}, },
confirmText: 'KK', confirmText: 'Go',
onConfirm: (name) => { onConfirm: (name) => {
localStorage.setItem('name', name) localStorage.setItem('name', name)
this.login(name) this.login(name)

Binary file not shown.

Binary file not shown.

View File

@@ -12,7 +12,7 @@ $blue: #3498db;
$purple: #8e44ad; $purple: #8e44ad;
$red: #e74c3c; $red: #e74c3c;
$white-ter: #ecf0f1; $white-ter: #ecf0f1;
$primary: $turquoise; $primary: $purple;
$yellow-invert: #fff; $yellow-invert: #fff;
$family-sans-serif: "Gravity Regular", "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", $family-sans-serif: "Gravity Regular", "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI",

View File

@@ -1,5 +1,11 @@
<template> <template>
<div class="game"> <div class="game">
<b-modal v-model="playerIsDead">
<h1 class="title">DED</h1>
<h2 class="title">Score: {{player.score}}</h2>
<h3 class="subtitle">Best score: {{player.bestScore}}</h3>
<b-button @click="respawn">Respawn</b-button>
</b-modal>
<canvas <canvas
class="game-canvas" class="game-canvas"
ref="canvas"> ref="canvas">
@@ -9,6 +15,9 @@
<script> <script>
import { send } from '@/store/socketPlugin' import { send } from '@/store/socketPlugin'
import { render } from '@/game/render.js'
import { sound } from '@/game/sound.js'
export default { export default {
name: 'Game', name: 'Game',
data () { data () {
@@ -22,7 +31,6 @@ export default {
y: 0 y: 0
}, },
stats: { stats: {
leaderboard: [],
totalWalls: 0, totalWalls: 0,
lastUpdateTime: 0, lastUpdateTime: 0,
lastFrame: 0 lastFrame: 0
@@ -51,7 +59,7 @@ export default {
if (pastUpdate.players === undefined) return [] if (pastUpdate.players === undefined) return []
if (nextUpdate === undefined) return pastUpdate.players if (nextUpdate === undefined) return pastUpdate.players
return pastUpdate.players return nextUpdate.players
/* /*
const currentTime = Date.now() / 1000 const currentTime = Date.now() / 1000
@@ -61,26 +69,29 @@ export default {
}, },
isPaused () { isPaused () {
return this.$store.state.game.paused return this.$store.state.game.paused
},
leaderboard () {
return this.$store.state.game.leaderboard
},
playerIsDead () {
if (this.player.state === 'DEAD') sound.explosion()
return this.player.state === 'DEAD'
} }
}, },
mounted () { mounted () {
this.canvas.width = window.innerWidth this.setCanvasSize()
this.canvas.height = window.innerHeight
this.canvas.addEventListener('mousemove', this.mouseEvent) this.canvas.addEventListener('mousemove', this.mouseEvent)
this.canvas.addEventListener('touchmove', this.touchEvent) this.canvas.addEventListener('touchmove', this.touchEvent)
this.renderTimer = setInterval(this.render, 1000 / 120) this.canvas.addEventListener('resize', this.setCanvasSize)
// this.renderTimer = setInterval(this.render, 1000 / 120)
this.render()
}, },
methods: { methods: {
render () { render () {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.renderBorders() render.borders(this.context, this.canvas, this.camera, this.settings)
if (!this.players) return if (!this.players) return
this.players.sort((a, b) => {
return b.walls.length - a.walls.length
})
this.stats.leaderboard = []
this.stats.totalWalls = 0 this.stats.totalWalls = 0
this.players.forEach(player => { this.players.forEach(player => {
@@ -88,78 +99,25 @@ export default {
this.camera.x = player.x this.camera.x = player.x
this.camera.y = player.y this.camera.y = player.y
} }
this.renderPlayer(player)
this.renderWalls(player) if (player.state === 'DEAD') this.context.globalAlpha = 0.1
this.stats.leaderboard.push(player.name + ' - ' + player.walls.length) render.walls(this.context, this.canvas, this.camera, this.settings, player)
render.player(this.context, this.canvas, this.camera, this.settings, player)
if (player.state === 'DEAD') this.context.globalAlpha = 1
this.stats.totalWalls += player.walls.length this.stats.totalWalls += player.walls.length
}) })
this.renderLeaderboard() render.leaderboard(this.context, this.canvas, this.leaderboard)
this.renderMouse() render.mouse(this.context, this.mouse, this.player)
render.debug(this.context, this.camera, this.mouse, this.canvas, this.stats)
this.renderDebug()
this.stats.lastFrame = performance.now() this.stats.lastFrame = performance.now()
},
renderBorders () {
this.context.strokeStyle = 'white'
this.context.lineWidth = 1
this.context.strokeRect(this.canvas.width / 2 - this.camera.x, this.canvas.height / 2 - this.camera.y, this.settings.arenaSize, this.settings.arenaSize)
},
renderPlayer (player) {
this.context.save()
const canvasX = this.canvas.width / 2 + player.x - this.camera.x const nextUpdate = this.$store.state.game.updates[1]
const canvasY = this.canvas.height / 2 + player.y - this.camera.y if (nextUpdate !== undefined) this.stats.lastUpdateTime = nextUpdate.time
this.context.translate(canvasX, canvasY) requestAnimationFrame(this.render, this.canvas)
this.context.rotate(player.angle)
this.context.fillStyle = player.color
this.context.fillRect(-this.settings.playerSize, -this.settings.playerSize, this.settings.playerSize * 2, this.settings.playerSize * 2)
this.context.translate(0, 0)
this.context.restore()
},
renderWalls (player) {
this.context.beginPath()
this.context.lineWidth = this.settings.wallSize
this.context.strokeStyle = player.color
player.walls.forEach(wall => {
const canvasX = this.canvas.width / 2 + wall.x - this.camera.x
const canvasY = this.canvas.height / 2 + wall.y - this.camera.y
this.context.lineTo(canvasX, canvasY)
})
this.context.lineTo(this.canvas.width / 2 + player.x - this.camera.x, this.canvas.height / 2 + player.y - this.camera.y)
this.context.stroke()
},
renderDebug () {
const canvasX = this.canvas.width / 2
const canvasY = this.canvas.height / 2
this.context.fillStyle = 'white'
this.context.textAlign = 'start'
this.context.fillText('camera x: ' + this.camera.x + ' y:' + this.camera.y, 10, 12)
this.context.fillText('mouse x: ' + this.mouse.x + ' y:' + this.mouse.y, 10, 24)
this.context.fillText('canvasX: ' + canvasX + ' canvasY: ' + canvasY, 10, 36)
this.context.fillText('Total walls: ' + this.stats.totalWalls, 10, 48)
this.context.fillText('Last update: ' + this.stats.lastUpdateTime, 10, 60)
const fps = 1000 / (performance.now() - this.stats.lastFrame)
this.context.fillText('FPS: ' + fps, 10, 72)
},
renderMouse () {
this.context.beginPath()
this.context.arc(this.mouse.x, this.mouse.y, 25, 0, 2 * Math.PI, false)
this.context.lineWidth = 1
this.context.strokeStyle = this.player.color
this.context.stroke()
},
renderLeaderboard () {
this.context.fillStyle = 'white'
this.context.textAlign = 'end'
this.context.fillText('Leaderboard: ', this.canvas.width - 50, 10)
for (var i = 0; i < this.stats.leaderboard.length; i++) {
this.context.fillText(this.players[i].name + ' - ' + this.players[i].walls.length, this.canvas.width - 50, 15 + (i + 1) * 10)
}
}, },
mouseEvent (event) { mouseEvent (event) {
var rect = this.canvas.getBoundingClientRect() var rect = this.canvas.getBoundingClientRect()
@@ -211,6 +169,13 @@ export default {
player.targetAngle = pastPlayer.targetAngle + (nextPlayer.targetAngle - pastPlayer.targetAngle) * dt player.targetAngle = pastPlayer.targetAngle + (nextPlayer.targetAngle - pastPlayer.targetAngle) * dt
return player return player
},
setCanvasSize () {
this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
},
respawn () {
send({ type: 'respawn' })
} }
} }
} }

86
client/src/game/render.js Normal file
View File

@@ -0,0 +1,86 @@
export const render = {
borders (context, canvas, camera, settings) {
context.strokeStyle = 'white'
// context.lineWidth = 1
// context.shadowBlur = 20
context.shadowColor = 'white'
context.strokeRect(canvas.width / 2 - camera.x, canvas.height / 2 - camera.y, settings.arenaSize, settings.arenaSize)
},
player (context, canvas, camera, settings, player) {
const canvasX = canvas.width / 2 + player.x - camera.x
const canvasY = canvas.height / 2 + player.y - camera.y
context.save()
context.translate(canvasX, canvasY)
context.rotate(player.angle)
context.fillStyle = player.color
// context.shadowBlur = 10
// context.shadowColor = player.color
context.fillRect(-settings.playerSize, -settings.playerSize, settings.playerSize * 2, settings.playerSize * 2)
context.translate(0, 0)
context.restore()
context.fillStyle = 'white'
context.textAlign = 'center'
context.fillText(player.name, canvasX, canvasY - settings.playerSize - 5)
},
walls (context, canvas, camera, settings, player) {
context.beginPath()
context.lineWidth = settings.wallSize
context.strokeStyle = player.color
// context.shadowBlur = 0
// context.shadowColor = player.color
player.walls.forEach(wall => {
const canvasX = canvas.width / 2 + wall.x - camera.x
const canvasY = canvas.height / 2 + wall.y - camera.y
context.lineTo(canvasX, canvasY)
})
context.lineTo(canvas.width / 2 + player.x - camera.x, canvas.height / 2 + player.y - camera.y)
context.stroke()
},
debug (context, camera, mouse, canvas, stats) {
const canvasX = canvas.width / 2
const canvasY = canvas.height / 2
context.fillStyle = 'white'
context.textAlign = 'start'
context.fillText('camera x: ' + camera.x + ' y:' + camera.y, 10, 12)
context.fillText('mouse x: ' + mouse.x + ' y:' + mouse.y, 10, 24)
context.fillText('canvasX: ' + canvasX + ' canvasY: ' + canvasY, 10, 36)
context.fillText('Total walls: ' + stats.totalWalls, 10, 48)
context.fillText('Server update: ' + stats.lastUpdateTime, 10, 60)
const fps = 1000 / (performance.now() - stats.lastFrame)
context.fillText('FPS: ' + fps, 10, 72)
},
mouse (context, mouse, player) {
context.beginPath()
context.arc(mouse.x, mouse.y, 25, 0, 2 * Math.PI, false)
context.lineWidth = 1
context.strokeStyle = player.color
context.stroke()
},
leaderboard (context, canvas, leaderboard) {
context.fillStyle = 'white'
context.textAlign = 'end'
context.fillText('Leaderboard: ', canvas.width - 50, 10)
var i = 1
leaderboard.forEach(player => {
context.fillStyle = player.color
context.fillText(player.name + ' - ' + player.score + ' (' + player.bestScore + ')', canvas.width - 50, 15 + i * 10)
i++
})
},
dead (context, canvas, player) {
const canvasX = canvas.width / 2
const canvasY = canvas.height / 2
context.fillStyle = 'white'
context.textAlign = 'center'
context.fillText('YO DED', canvasX, canvasY - 50)
context.fillText('Score: ' + player.score, canvasX, canvasY)
context.fillText('Best score so far ' + player.bestScore, canvasX, canvasY + 25)
context.fillText('Tap to respawn', canvasX, canvasY + 50)
}
}

23
client/src/game/sound.js Normal file
View File

@@ -0,0 +1,23 @@
const music = new Audio(require('@/assets/sound/revengeOfCats.mp3'))
// const motor = new Audio(require('@/assets/sound/motor.mp3'))
const explosion = new Audio(require('@/assets/sound/explosion.mp3'))
export const sound = {
startBackgroundMusic () {
music.loop = true
music.play()
},
stopBackgroundMusic () {
music.pause()
},
startMotor () {
motor.loop = true
motor.play()
},
stopMotor () {
motor.pause()
},
explosion () {
explosion.play()
}
}

View File

@@ -5,7 +5,7 @@ import Buefy from 'buefy'
import './registerServiceWorker' import './registerServiceWorker'
// import 'buefy/dist/buefy.css' // import 'buefy/dist/buefy.css'
import './assets/style.scss' import './assets/style/style.scss'
Vue.config.productionTip = false Vue.config.productionTip = false
Vue.use(Buefy) Vue.use(Buefy)

View File

@@ -4,8 +4,10 @@ const state = {
y: 100, y: 100,
angle: 0, angle: 0,
targetAngle: 0, targetAngle: 0,
color: 'red' color: 'red',
state: 'DEAD'
}, },
leaderboard: [],
updates: [], updates: [],
settings: { settings: {
playerSize: 10, playerSize: 10,
@@ -21,19 +23,11 @@ const state = {
} }
const getters = { const getters = {
// Interpolation position des joueurs
currentPlayers: state => {
if (state.updates.length < 2) return state.updates[0].players
else {
const dt = state.updates[0].time - state.updates[1].time
return state.updates[1].players.map(player => interpolatePlayer(player, dt))
}
}
} }
const actions = { const actions = {
socketConnected ({ commit }) { socketConnected ({ commit }, connected) {
commit('SET_CONNECTED') commit('SET_CONNECTED', connected)
commit('CLEAR_UPDATE') commit('CLEAR_UPDATE')
}, },
login ({ commit }, player) { login ({ commit }, player) {
@@ -45,10 +39,15 @@ const actions = {
}, },
update ({ commit }, update) { update ({ commit }, update) {
commit('ADD_UPDATE', update) commit('ADD_UPDATE', update)
commit('SET_LEADERBOARD', update.players)
}, },
dead ({ commit }, player) { dead ({ commit }, player) {
commit('SET_PLAYER', player) commit('SET_PLAYER', player)
commit('CLEAR_UPDATE') // commit('CLEAR_UPDATE')
},
spawn ({ commit }, player) {
commit('SET_PLAYER', player)
// commit('CLEAR_UPDATE')
}, },
error ({ commit }, error) { error ({ commit }, error) {
alert('Error: ' + error) alert('Error: ' + error)
@@ -71,8 +70,8 @@ const mutations = {
SET_PAUSE (state) { SET_PAUSE (state) {
state.paused = !state.paused state.paused = !state.paused
}, },
SET_CONNECTED (state) { SET_CONNECTED (state, connected) {
state.socketConnected = !state.socketConnected state.socketConnected = connected
}, },
ADD_UPDATE (state, update) { ADD_UPDATE (state, update) {
if (state.updates.length > 2) { if (state.updates.length > 2) {
@@ -83,11 +82,23 @@ const mutations = {
}, },
CLEAR_UPDATE (state) { CLEAR_UPDATE (state) {
state.updates = [] state.updates = []
} },
} SET_LEADERBOARD (state, players) {
state.leaderboard = []
function interpolatePlayer (player, dt) { players.forEach(player => {
return player state.leaderboard.push({
name: player.name,
score: player.score,
bestScore: player.bestScore,
color: player.color
})
})
state.leaderboard.sort((a, b) => {
return b.score - a.score
})
}
} }
export default { export default {

View File

@@ -1,17 +1,18 @@
import { ToastProgrammatic as Toast } from 'buefy' import { ToastProgrammatic as Toast } from 'buefy'
// const connection = new WebSocket('ws://localhost:8181/socket') const connection = new WebSocket('ws://localhost:8181/socket')
const connection = new WebSocket('wss://tronio.gltronic.ovh/socket') // const connection = new WebSocket('wss://tronio.gltronic.ovh/socket')
export default function createSocketPlugin () { export default function createSocketPlugin () {
return store => { return store => {
connection.onopen = function () { connection.onopen = function () {
console.log('[WS] connected') console.log('[WS] connected')
store.dispatch('game/socketConnected') store.dispatch('game/socketConnected', true)
} }
connection.onclose = function () { connection.onclose = function () {
console.log('[WS] closed') console.log('[WS] closed')
store.dispatch('game/socketConnected', false)
} }
connection.onerror = function (error) { connection.onerror = function (error) {
@@ -41,6 +42,9 @@ export default function createSocketPlugin () {
case 'gamePlayerDead': case 'gamePlayerDead':
store.dispatch('game/dead', data.message) store.dispatch('game/dead', data.message)
break break
case 'gamePlayerSpawn':
store.dispatch('game/spawn', data.message)
break
default: default:
break break
} }

View File

@@ -17,6 +17,7 @@ import org.springframework.web.socket.WebSocketSession;
import gltronic.tronio.model.Game; import gltronic.tronio.model.Game;
import gltronic.tronio.model.GameUpdate; import gltronic.tronio.model.GameUpdate;
import gltronic.tronio.model.Player; import gltronic.tronio.model.Player;
import gltronic.tronio.model.PlayerState;
import gltronic.tronio.model.Wall; import gltronic.tronio.model.Wall;
@Service @Service
@@ -33,6 +34,7 @@ public class GameManager implements IGameManager {
Player player = initPlayer(new Player()); Player player = initPlayer(new Player());
player.setName(name); player.setName(name);
player.setBestScore(0);
game.getSessions().put(session.getId(), session); game.getSessions().put(session.getId(), session);
game.getPlayers().put(session.getId(), player); game.getPlayers().put(session.getId(), player);
@@ -58,7 +60,7 @@ public class GameManager implements IGameManager {
} }
@Override @Override
@Scheduled(fixedDelay = 1000 / 120) @Scheduled(fixedDelay = 1000 / 60)
public void step() throws InterruptedException, IOException { public void step() throws InterruptedException, IOException {
Instant currentTime = Instant.now(); Instant currentTime = Instant.now();
Duration durationSinceLastUpdate = Duration.between(game.getLastUpdate(), currentTime); Duration durationSinceLastUpdate = Duration.between(game.getLastUpdate(), currentTime);
@@ -66,6 +68,8 @@ public class GameManager implements IGameManager {
// CHECK OUT OF BORDERS & COLISIONS // CHECK OUT OF BORDERS & COLISIONS
game.getPlayers().forEach((id, player) -> { game.getPlayers().forEach((id, player) -> {
if (!player.getState().equals(PlayerState.ALIVE)) return;
// OUT OF BORDERS // OUT OF BORDERS
if (player.getX() - game.getSettings().getPlayerSize() < 0 if (player.getX() - game.getSettings().getPlayerSize() < 0
|| player.getX() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize() || player.getX() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize()
@@ -76,20 +80,31 @@ public class GameManager implements IGameManager {
return; return;
} }
/*
// BORDER WRAP
if (player.getX() - game.getSettings().getPlayerSize() < 0) player.setX(game.getSettings().getPlayerSize());
else if (player.getX() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize()) player.setX(0);
else if (player.getY() - game.getSettings().getPlayerSize() < 0) player.setY(game.getSettings().getPlayerSize());
else if (player.getY() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize()) player.setY(0);
*/
// COLLISIONS // COLLISIONS
game.getPlayers().forEach((id2, player2) -> { game.getPlayers().forEach((id2, player2) -> {
if (!player2.getState().equals(PlayerState.ALIVE)) return;
for (var i = 0; i < player2.getWalls().size() - 2; i++) { for (var i = 0; i < player2.getWalls().size() - 2; i++) {
// Pour evité la collision avec un mur venant d'être placé par le même joueur // Pour evité la collision avec un mur venant d'être placé par le même joueur
if (id.equals(id2) && i >= player2.getWalls().size() - 4) break; if (id.equals(id2) && i >= player2.getWalls().size() - 2) break;
Wall wallA = player2.getWalls().get(i); Wall wallA = player2.getWalls().get(i);
Wall wallB = player2.getWalls().get(i + 1); Wall wallB = player2.getWalls().get(i + 1);
if (isCloseToWall(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(), player.getY(), if (isCloseToWall(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(), player.getY(),
game.getSettings().getPlayerSize())) { game.getSettings().getPlayerSize())) {
System.out.println("[GAME] close to wall"); System.out.println("[GAME] " + player.getName() + " close to wall");
if (this.isCrossingLine(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(), if (this.isCrossingLine(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(),
player.getY(), game.getSettings().getPlayerSize())) { player.getY(), game.getSettings().getPlayerSize())) {
System.out.println("[GAME] touch da wall"); System.out.println("[GAME] " + player.getName() + " killed by " + player2.getName());
if (!player.equals(player2)) player2.setScore(player.getScore() + 300);
killPlayer(id); killPlayer(id);
return; return;
} }
@@ -100,6 +115,8 @@ public class GameManager implements IGameManager {
// ADD WALLS & MOVE PLAYER // ADD WALLS & MOVE PLAYER
game.getPlayers().forEach((id, player) -> { game.getPlayers().forEach((id, player) -> {
if (!player.getState().equals(PlayerState.ALIVE)) return;
// On cherche le nombre de pas pour atteindre les 60 up/s // On cherche le nombre de pas pour atteindre les 60 up/s
long tickToSimulate = ( durationSinceLastUpdate.toMillis() * 120 ) / 1000; long tickToSimulate = ( durationSinceLastUpdate.toMillis() * 120 ) / 1000;
@@ -108,6 +125,7 @@ public class GameManager implements IGameManager {
player.setLastWall(player.getLastWall() + 1); player.setLastWall(player.getLastWall() + 1);
if (player.getLastWall() > game.getSettings().getWallUpdate()) { if (player.getLastWall() > game.getSettings().getWallUpdate()) {
player.getWalls().add(new Wall(player.getX(), player.getY())); player.getWalls().add(new Wall(player.getX(), player.getY()));
player.setScore(player.getScore() + 1);
player.setLastWall(0); player.setLastWall(0);
} }
@@ -129,11 +147,13 @@ public class GameManager implements IGameManager {
private void killPlayer(String id) { private void killPlayer(String id) {
Player player = game.getPlayers().get(id); Player player = game.getPlayers().get(id);
initPlayer(player);
if (player.getBestScore() < player.getScore()) player.setBestScore(player.getScore());
player.setState(PlayerState.DEAD);
System.out.println("[GAME] Player " + player.getName() + " (" + id + ") is dead | status: " + player.getX() + " " + player.getY() + " " + player.getColor()); System.out.println("[GAME] Player " + player.getName() + " (" + id + ") is dead | status: " + player.getX() + " " + player.getY() + " " + player.getColor());
try { try {
SocketUtils.sendObject(game.getSessions().get(id), "gamePlayerDead", SocketUtils.sendObject(game.getSessions().get(id), "gamePlayerDead", new ObjectMapper().writeValueAsString(player));
new ObjectMapper().writeValueAsString(player));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -166,7 +186,22 @@ public class GameManager implements IGameManager {
player.setColor(String.format("#%06x", rand.nextInt(0xffffff + 1))); player.setColor(String.format("#%06x", rand.nextInt(0xffffff + 1)));
player.setX(x); player.setX(x);
player.setY(y); player.setY(y);
player.setScore(0);
player.setState(PlayerState.ALIVE);
return player; return player;
} }
@Override
public void respawn(WebSocketSession session) throws InterruptedException, IOException {
Player player = game.getPlayers().get(session.getId());
initPlayer(player);
player.setState(PlayerState.ALIVE);
try {
SocketUtils.sendObject(session, "gamePlayerSpawn", new ObjectMapper().writeValueAsString(player));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
} }

View File

@@ -10,6 +10,7 @@ public interface IGameManager {
void login(WebSocketSession session, String name) throws InterruptedException, IOException; void login(WebSocketSession session, String name) throws InterruptedException, IOException;
void leave(WebSocketSession session) throws InterruptedException, IOException; void leave(WebSocketSession session) throws InterruptedException, IOException;
void updatePlayer(WebSocketSession session, Player player) throws InterruptedException, IOException; void updatePlayer(WebSocketSession session, Player player) throws InterruptedException, IOException;
void respawn(WebSocketSession session) throws InterruptedException, IOException;
void step() throws InterruptedException, IOException; void step() throws InterruptedException, IOException;
} }

View File

@@ -16,6 +16,7 @@ import lombok.Setter;
public class Game { public class Game {
private Map<String, Player> players; private Map<String, Player> players;
private Map<String, WebSocketSession> sessions; private Map<String, WebSocketSession> sessions;
private Map<String, Integer> leaderboard;
private GameSettings settings; private GameSettings settings;
private boolean updateNeeded; private boolean updateNeeded;
private Instant lastUpdate; private Instant lastUpdate;
@@ -25,6 +26,7 @@ public class Game {
this.lastUpdate = Instant.now(); this.lastUpdate = Instant.now();
this.players = new HashMap<>(); this.players = new HashMap<>();
this.sessions = new HashMap<>(); this.sessions = new HashMap<>();
this.leaderboard = new HashMap<>();
this.settings = new GameSettings(); this.settings = new GameSettings();
} }
} }

View File

@@ -16,9 +16,9 @@ public class GameSettings {
public GameSettings () { public GameSettings () {
this.arenaSize = 1000; this.arenaSize = 1000;
this.playerSize = 10; this.playerSize = 10;
this.playerSpeed = 2; this.playerSpeed = 2.3;
this.playerTurnSpeed = 10; this.playerTurnSpeed = 10;
this.wallSize = 8; this.wallSize = 8;
this.wallUpdate = 5; this.wallUpdate = 20;
} }
} }

View File

@@ -17,6 +17,12 @@ public class Player {
private String color; private String color;
private int score;
private int bestScore;
private PlayerState state;
private double x; private double x;
private double y; private double y;

View File

@@ -0,0 +1,5 @@
package gltronic.tronio.model;
public enum PlayerState {
ALIVE, DEAD, PAUSED
}

View File

@@ -35,6 +35,9 @@ public class SocketHandler extends TextWebSocketHandler {
case "login": case "login":
gameManager.login(session, rootObject.getString("message")); gameManager.login(session, rootObject.getString("message"));
break; break;
case "respawn":
gameManager.respawn(session);
break;
case "update": case "update":
Player player = new ObjectMapper().readValue(rootObject.get("message").toString(), Player.class); Player player = new ObjectMapper().readValue(rootObject.get("message").toString(), Player.class);
gameManager.updatePlayer(session, player); gameManager.updatePlayer(session, player);