feature/MAPG-205-set-timeout-in-multiplayer-rooms #15
@ -3,6 +3,11 @@
|
||||
process.title = 'mapguesser-multi';
|
||||
|
||||
class MultiGame {
|
||||
static ROUND_TIMEOUT_DEFAULT = 120000;
|
||||
static ROUND_TIMEOUT_MINIMUM = 15000;
|
||||
static ROUND_TIMEOUT_DIVIDER = 2;
|
||||
static ROUND_TIMEOUT_OFFSET = 500;
|
||||
|
||||
constructor() {
|
||||
this.rooms = new Map();
|
||||
}
|
||||
@ -67,7 +72,13 @@ class MultiGame {
|
||||
|
||||
var rounds = [];
|
||||
places.forEach(function (place) {
|
||||
rounds.push({ place: place, results: new Map() })
|
||||
rounds.push({
|
||||
place: place,
|
||||
results: new Map(),
|
||||
timeout: MultiGame.ROUND_TIMEOUT_DEFAULT,
|
||||
timeoutStarted: null,
|
||||
timeoutHandler: null
|
||||
})
|
||||
});
|
||||
|
||||
room.rounds = rounds;
|
||||
@ -86,9 +97,24 @@ class MultiGame {
|
||||
room.updated = new Date();
|
||||
|
||||
var round = room.rounds[room.currentRound];
|
||||
|
||||
clearTimeout(round.timeoutHandler);
|
||||
round.timeout = round.timeout - (new Date() - round.timeoutStarted);
|
||||
if (round.timeout > MultiGame.ROUND_TIMEOUT_DIVIDER * MultiGame.ROUND_TIMEOUT_MINIMUM) {
|
||||
round.timeout = round.timeout / MultiGame.ROUND_TIMEOUT_DIVIDER;
|
||||
} else if (round.timeout > MultiGame.ROUND_TIMEOUT_MINIMUM) {
|
||||
round.timeout = MultiGame.ROUND_TIMEOUT_MINIMUM;
|
||||
}
|
||||
round.timeoutStarted = new Date();
|
||||
var self = this;
|
||||
round.timeoutHandler = setTimeout(function () {
|
||||
self._endRoundTimeout(room, round);
|
||||
}, round.timeout + MultiGame.ROUND_TIMEOUT_OFFSET);
|
||||
|
||||
var member = room.members.get(token);
|
||||
var allResults = this._collectResultsInRound(room, round);
|
||||
|
||||
this._broadcastTimeout(room, round);
|
||||
this._broadcastGuess(room, member.userName, guessPosition, distance, score);
|
||||
|
||||
round.results.set(token, { guessPosition: guessPosition, distance: distance, score: score });
|
||||
@ -110,12 +136,15 @@ class MultiGame {
|
||||
|
||||
var round = room.rounds[room.currentRound];
|
||||
|
||||
round.timeoutStarted = new Date();
|
||||
var self = this;
|
||||
round.timeoutHandler = setTimeout(function () {
|
||||
self._endRoundTimeout(room, round);
|
||||
}, round.timeout + MultiGame.ROUND_TIMEOUT_OFFSET);
|
||||
|
||||
var data = {};
|
||||
data.place = { panoId: round.place.panoId, pov: round.place.pov };
|
||||
|
||||
if (room.currentRound > 0) {
|
||||
data.result = { position: room.rounds[room.currentRound - 1].place.position };
|
||||
}
|
||||
data.timeout = round.timeout;
|
||||
|
||||
var self = this;
|
||||
room.members.forEach(function (member) {
|
||||
@ -123,11 +152,28 @@ class MultiGame {
|
||||
});
|
||||
}
|
||||
|
||||
_endRoundTimeout(room, round) {
|
||||
clearTimeout(round.timeoutHandler);
|
||||
|
||||
var data = { position: round.place.position, allResults: this._collectResultsInRound(room, round) };
|
||||
var self = this;
|
||||
room.members.forEach(function (member, token) {
|
||||
if (round.results.has(token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._sendToMember(member, 'end_round', data);
|
||||
});
|
||||
}
|
||||
|
||||
_sendInitialData(room, member, token) {
|
||||
var data = {};
|
||||
|
||||
if (room.currentRound >= 0) {
|
||||
data.place = room.rounds[room.currentRound].place;
|
||||
var round = room.rounds[room.currentRound];
|
||||
|
||||
data.place = round.place;
|
||||
data.timeout = round.timeout - (new Date() - round.timeoutStarted);
|
||||
}
|
||||
|
||||
data.history = [];
|
||||
@ -175,6 +221,13 @@ class MultiGame {
|
||||
return results;
|
||||
}
|
||||
|
||||
_broadcastTimeout(room, round) {
|
||||
var self = this;
|
||||
room.members.forEach(function (member) {
|
||||
self._sendToMember(member, 'timeout_changed', { timeout: round.timeout });
|
||||
});
|
||||
}
|
||||
|
||||
_broadcastGuess(room, userName, guessPosition, distance, score) {
|
||||
var data = { userName: userName, guessPosition: guessPosition, distance: distance, score: score };
|
||||
var round = room.rounds[room.currentRound];
|
||||
|
@ -79,7 +79,7 @@
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
#distanceInfo>p:nth-child(2), #scoreInfo>p:nth-child(2) {
|
||||
#distanceInfo>p:nth-child(2), #distanceInfo>p:nth-child(3), #scoreInfo>p:nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -111,6 +111,44 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#countdown {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
padding: 0 8px;
|
||||
background-color: #eeeeee;
|
||||
border: solid 1px #555555;
|
||||
border-radius: 3px;
|
||||
opacity: 0.95;
|
||||
z-index: 5;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#countdown.yellow {
|
||||
background-color: #f7c789;
|
||||
border: solid 1px #e8a349;
|
||||
}
|
||||
|
||||
#countdown.red {
|
||||
background-color: #f7a5a5;
|
||||
border: solid 1px #aa5e5e;
|
||||
}
|
||||
|
||||
#countdown p {
|
||||
font-size: 16px;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
#countdown.yellow p {
|
||||
color: #9c4308;
|
||||
}
|
||||
|
||||
#countdown.red p {
|
||||
color: #701919;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 599px) {
|
||||
#mapName {
|
||||
display: none;
|
||||
|
@ -17,6 +17,9 @@
|
||||
adaptGuess: false,
|
||||
googleLink: null,
|
||||
|
||||
timeoutEnd: null,
|
||||
countdownHandler: null,
|
||||
|
||||
MultiConnector: {
|
||||
connection: null,
|
||||
reconnectCounter: 0,
|
||||
@ -30,9 +33,7 @@
|
||||
|
||||
Game.MultiConnector.connection.onopen = function () {
|
||||
document.getElementById('loading').style.visibility = 'hidden';
|
||||
|
||||
Game.MultiConnector.reconnectCounter = 0;
|
||||
|
||||
Game.MultiConnector.connection.send(JSON.stringify({ func: 'connect_to_room', args: { roomId: roomId, token: Game.multi.token } }));
|
||||
};
|
||||
|
||||
@ -69,6 +70,14 @@
|
||||
case 'guess':
|
||||
Game.MultiConnector.guess(json.data);
|
||||
break;
|
||||
|
||||
case 'timeout_changed':
|
||||
Game.MultiConnector.timeoutChanged(json.data);
|
||||
break;
|
||||
|
||||
case 'end_round':
|
||||
Game.MultiConnector.endRound(json.data);
|
||||
break;
|
||||
}
|
||||
};
|
||||
},
|
||||
@ -107,6 +116,10 @@
|
||||
document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE);
|
||||
}
|
||||
|
||||
if (data.timeout) {
|
||||
Game.startCountdown(data.timeout);
|
||||
}
|
||||
|
||||
if (data.place) {
|
||||
Game.panoId = data.place.panoId;
|
||||
Game.pov = data.place.pov;
|
||||
@ -139,27 +152,18 @@
|
||||
},
|
||||
|
||||
newRound: function (data) {
|
||||
//TODO: workaround until results are not sent
|
||||
if (Game.adaptGuess) {
|
||||
document.getElementById('guess').classList.remove('adapt');
|
||||
}
|
||||
|
||||
if (Game.rounds.length === Game.NUMBER_OF_ROUNDS) {
|
||||
Game.reset();
|
||||
}
|
||||
|
||||
// if player didn't guess - TODO: show everything on a map
|
||||
if (data.result && Game.rounds.length > 0 && !Game.rounds[Game.rounds.length - 1].position) {
|
||||
Game.rounds[Game.rounds.length - 1].position = data.result.position;
|
||||
Game.addPositionToResultMap();
|
||||
}
|
||||
|
||||
Game.panoId = data.place.panoId;
|
||||
Game.pov = data.place.pov;
|
||||
|
||||
document.getElementById('multi').style.visibility = 'hidden';
|
||||
Game.resetRound();
|
||||
Game.startNewRound();
|
||||
|
||||
Game.startCountdown(data.timeout);
|
||||
},
|
||||
|
||||
guess: function (data) {
|
||||
@ -169,6 +173,26 @@
|
||||
resultBounds.extend(data.guessPosition);
|
||||
|
||||
Game.map.fitBounds(resultBounds);
|
||||
},
|
||||
|
||||
timeoutChanged: function (data) {
|
||||
Game.startCountdown(data.timeout);
|
||||
},
|
||||
|
||||
endRound: function (data) {
|
||||
// TODO: refactor - it is necessary for mobile
|
||||
if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') {
|
||||
document.getElementById('showGuessButton').click();
|
||||
}
|
||||
|
||||
document.getElementById('guessButton').disabled = true;
|
||||
document.getElementById('panoCover').style.visibility = 'visible';
|
||||
|
||||
Game.showResults(data.position, null, { distance: NaN, score: 0 }, data.allResults);
|
||||
|
||||
if (!Game.multi.owner) {
|
||||
document.getElementById('continueButton').style.display = 'none';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -289,6 +313,7 @@
|
||||
var distanceInfo = document.getElementById('distanceInfo');
|
||||
distanceInfo.children[0].style.display = null;
|
||||
distanceInfo.children[1].style.display = null;
|
||||
distanceInfo.children[2].style.display = null;
|
||||
var scoreInfo = document.getElementById('scoreInfo');
|
||||
scoreInfo.children[0].style.display = null;
|
||||
scoreInfo.children[1].style.display = null;
|
||||
@ -379,6 +404,68 @@
|
||||
Game.panorama.setPano(panoId);
|
||||
},
|
||||
|
||||
showResults: function (position, guessPosition, result, allResults) {
|
||||
if (Game.adaptGuess) {
|
||||
document.getElementById('guess').classList.remove('adapt');
|
||||
}
|
||||
|
||||
if (Game.guessMarker) {
|
||||
Game.guessMarker.setMap(null);
|
||||
Game.guessMarker = null;
|
||||
}
|
||||
|
||||
document.getElementById('guess').classList.add('result');
|
||||
|
||||
Game.scoreSum += result.score;
|
||||
document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE);
|
||||
|
||||
var resultBounds = new google.maps.LatLngBounds();
|
||||
|
||||
Game.rounds[Game.rounds.length - 1].position = position;
|
||||
Game.addPositionToResultMap();
|
||||
resultBounds.extend(position);
|
||||
|
||||
if (guessPosition) {
|
||||
Game.addGuessPositionToResultMap(guessPosition);
|
||||
resultBounds.extend(guessPosition);
|
||||
}
|
||||
|
||||
if (allResults) {
|
||||
for (var i = 0; i < allResults.length; ++i) {
|
||||
var currentResult = allResults[i];
|
||||
Game.addGuessPositionToResultMap(currentResult.guessPosition, currentResult);
|
||||
resultBounds.extend(currentResult.guessPosition);
|
||||
}
|
||||
}
|
||||
|
||||
Game.map.setOptions({
|
||||
draggableCursor: 'grab'
|
||||
});
|
||||
Game.map.fitBounds(resultBounds);
|
||||
|
||||
var distanceInfo = document.getElementById('distanceInfo');
|
||||
if (Number.isNaN(result.distance)) {
|
||||
distanceInfo.children[0].style.display = 'none';
|
||||
distanceInfo.children[1].style.display = 'block';
|
||||
} else {
|
||||
distanceInfo.children[0].style.display = 'block';
|
||||
distanceInfo.children[1].style.display = 'none';
|
||||
document.getElementById('distance').innerHTML = Util.printDistanceForHuman(result.distance);
|
||||
}
|
||||
|
||||
document.getElementById('score').innerHTML = result.score;
|
||||
|
||||
var scoreBarProperties = Game.calculateScoreBarProperties(result.score, Game.MAX_SCORE);
|
||||
var scoreBar = document.getElementById('scoreBar');
|
||||
scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor;
|
||||
scoreBar.style.width = scoreBarProperties.width;
|
||||
|
||||
if (Game.rounds.length === Game.NUMBER_OF_ROUNDS) {
|
||||
document.getElementById('continueButton').style.display = 'none';
|
||||
document.getElementById('showSummaryButton').style.display = 'block';
|
||||
}
|
||||
},
|
||||
|
||||
guess: function () {
|
||||
if (!Game.guessMarker) {
|
||||
return;
|
||||
@ -388,9 +475,6 @@
|
||||
Game.rounds[Game.rounds.length - 1].guessPosition = guessPosition;
|
||||
|
||||
document.getElementById('guessButton').disabled = true;
|
||||
if (Game.adaptGuess) {
|
||||
document.getElementById('guess').classList.remove('adapt');
|
||||
}
|
||||
document.getElementById('panoCover').style.visibility = 'visible';
|
||||
|
||||
var data = new FormData();
|
||||
@ -407,58 +491,15 @@
|
||||
return;
|
||||
}
|
||||
|
||||
Game.guessMarker.setMap(null);
|
||||
Game.guessMarker = null;
|
||||
|
||||
document.getElementById('guess').classList.add('result');
|
||||
|
||||
Game.scoreSum += this.response.result.score;
|
||||
document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE);
|
||||
|
||||
var resultBounds = new google.maps.LatLngBounds();
|
||||
|
||||
Game.rounds[Game.rounds.length - 1].position = this.response.position;
|
||||
Game.addPositionToResultMap();
|
||||
resultBounds.extend(this.response.position);
|
||||
Game.addGuessPositionToResultMap(guessPosition);
|
||||
resultBounds.extend(guessPosition);
|
||||
|
||||
if (roomId) {
|
||||
for (var i = 0; i < this.response.allResults.length; ++i) {
|
||||
var result = this.response.allResults[i];
|
||||
Game.addGuessPositionToResultMap(result.guessPosition, result);
|
||||
resultBounds.extend(result.guessPosition);
|
||||
}
|
||||
}
|
||||
|
||||
Game.map.setOptions({
|
||||
draggableCursor: 'grab'
|
||||
});
|
||||
Game.map.fitBounds(resultBounds);
|
||||
|
||||
document.getElementById('distance').innerHTML = Util.printDistanceForHuman(this.response.result.distance);
|
||||
document.getElementById('score').innerHTML = this.response.result.score;
|
||||
|
||||
var scoreBarProperties = Game.calculateScoreBarProperties(this.response.result.score, Game.MAX_SCORE);
|
||||
var scoreBar = document.getElementById('scoreBar');
|
||||
scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor;
|
||||
scoreBar.style.width = scoreBarProperties.width;
|
||||
|
||||
if (Game.rounds.length === Game.NUMBER_OF_ROUNDS) {
|
||||
document.getElementById('continueButton').style.display = 'none';
|
||||
document.getElementById('showSummaryButton').style.display = 'block';
|
||||
}
|
||||
Game.showResults(this.response.position, guessPosition, this.response.result, this.response.allResults);
|
||||
|
||||
if (this.response.place) {
|
||||
Game.panoId = this.response.place.panoId;
|
||||
Game.pov = this.response.place.pov;
|
||||
} else {
|
||||
if (!Game.multi.owner) {
|
||||
//TODO: "waiting for" disabled button
|
||||
document.getElementById('continueButton').style.display = 'none';
|
||||
}
|
||||
Game.panoId = null;
|
||||
Game.pov = null;
|
||||
}
|
||||
}, data);
|
||||
},
|
||||
@ -575,7 +616,8 @@
|
||||
showSummary: function () {
|
||||
var distanceInfo = document.getElementById('distanceInfo');
|
||||
distanceInfo.children[0].style.display = 'none';
|
||||
distanceInfo.children[1].style.display = 'block';
|
||||
distanceInfo.children[1].style.display = 'none';
|
||||
distanceInfo.children[2].style.display = 'block';
|
||||
var scoreInfo = document.getElementById('scoreInfo');
|
||||
scoreInfo.children[0].style.display = 'none';
|
||||
scoreInfo.children[1].style.display = 'block';
|
||||
@ -645,6 +687,47 @@
|
||||
Game.googleLink.href = 'https://maps.google.com/maps';
|
||||
}
|
||||
}, 1);
|
||||
},
|
||||
|
||||
startCountdown: function (timeout) {
|
||||
if (Game.countdownHandler) {
|
||||
clearInterval(Game.countdownHandler);
|
||||
}
|
||||
|
||||
Game.timeoutEnd = new Date(new Date().getTime() + timeout);
|
||||
Game.countdownElement = document.getElementById('countdown');
|
||||
Game.countdownTimeElement = document.getElementById('countdownTime');
|
||||
|
||||
Game.setCountdownTime(Math.round(timeout / 1000));
|
||||
|
||||
Game.countdownHandler = setInterval(function () {
|
||||
var timeLeft = Math.round((Game.timeoutEnd - new Date()) / 1000);
|
||||
|
||||
Game.setCountdownTime(timeLeft);
|
||||
|
||||
if (timeLeft <= 0) {
|
||||
document.getElementById('panoCover').style.visibility = 'visible';
|
||||
clearInterval(Game.countdownHandler);
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
setCountdownTime: function (time) {
|
||||
if (time <= 0) {
|
||||
Game.countdownElement.style.visibility = 'hidden';
|
||||
return;
|
||||
}
|
||||
|
||||
if (time <= 15) {
|
||||
Game.countdownElement.className = 'red';
|
||||
} else if (time <= 30) {
|
||||
Game.countdownElement.className = 'yellow';
|
||||
} else {
|
||||
Game.countdownElement.className = '';
|
||||
}
|
||||
|
||||
Game.countdownElement.style.visibility = 'visible';
|
||||
Game.countdownTimeElement.innerHTML = time;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -21,6 +21,9 @@
|
||||
@endsection
|
||||
|
||||
@section(main)
|
||||
<div id="countdown">
|
||||
<p id="countdownTime" class="mono bold"></p>
|
||||
</div>
|
||||
<div id="panoCover"></div>
|
||||
<div id="panorama"></div>
|
||||
<div id="showGuessButtonContainer">
|
||||
@ -37,6 +40,7 @@
|
||||
<div id="resultInfo">
|
||||
<div id="distanceInfo">
|
||||
<p>You were <span id="distance" class="bold"></span> close.</p>
|
||||
<p>You didn't guess in this round.</p>
|
||||
<p class="bold">Game finished.</p>
|
||||
</div>
|
||||
<div id="scoreInfo">
|
||||
|
Loading…
Reference in New Issue
Block a user