mapguesser/public/static/js/game.js

755 lines
28 KiB
JavaScript
Raw Normal View History

'use strict';
(function () {
var Game = {
NUMBER_OF_ROUNDS: 5,
MAX_SCORE: 1000,
mapBounds: null,
multi: { token: null, owner: false },
rounds: [],
scoreSum: 0,
panoId: null,
pov: null,
panorama: null,
2020-05-22 23:23:54 +02:00
map: null,
guessMarker: null,
adaptGuess: false,
googleLink: null,
MultiConnector: {
connection: null,
reconnectCounter: 0,
connect: function () {
if (Game.MultiConnector.connection && Game.MultiConnector.connection.readyState !== WebSocket.CLOSED) {
return;
}
Game.MultiConnector.connection = new WebSocket((MapGuesser.isSecure ? 'wss' : 'ws') + '://' + multiUrl);
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 } }));
};
Game.MultiConnector.connection.onclose = Game.MultiConnector.noConnection;
Game.MultiConnector.connection.onerror = function (event) {
console.error('WebSocket error in Game.MultiConnector:', event);
};
Game.MultiConnector.connection.onmessage = function (message) {
var json;
try {
json = JSON.parse(message.data);
} catch (e) {
console.error('Cannot parse message!');
console.error(message.data);
return;
}
switch (json.type) {
case 'initialize':
Game.MultiConnector.initialize(json.data);
break;
case 'member_joined':
Game.MultiConnector.memberJoined(json.data);
break;
case 'new_round':
Game.MultiConnector.newRound(json.data);
break;
case 'results':
//TODO
break;
case 'guess':
//TODO
break;
}
};
},
noConnection: function () {
if (Game.MultiConnector.reconnectCounter === 2) {
console.error('Could not reconnect WebSocket for Game.MultiConnector...')
}
setTimeout(function () {
Game.MultiConnector.reconnectCounter++;
console.log('Reconnecting WebSocket for Game.MultiConnector... ' + Game.MultiConnector.reconnectCounter);
Game.MultiConnector.connect();
}, 1000 + Math.min(Game.MultiConnector.reconnectCounter * 500, 9000));
},
initialize: function (data) {
if (data.history) {
for (var i = 0; i < data.history.length; ++i) {
var round = data.history[i];
Game.rounds.push({ position: round.position, guessPosition: round.guessPosition, realMarker: null, guessMarker: null, line: null });
Game.addRealGuessPair(round.position, round.guessPosition, true);
Game.scoreSum += round.score;
}
document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS);
document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE);
}
if (data.place) {
Game.panoId = data.place.panoId;
Game.pov = data.place.pov;
document.getElementById('multi').style.visibility = 'hidden';
document.getElementById('panoCover').style.visibility = 'hidden';
Game.startNewRound();
}
document.getElementById('loading').style.visibility = 'hidden';
var div = document.getElementById('players');
for (var i = 0; i < data.members.length; ++i) {
var member = data.members[i];
var p = document.createElement('p');
p.innerHTML = member.userName + (member.me ? ' (me)' : '');
div.appendChild(p);
}
},
memberJoined: function (data) {
var div = document.getElementById('players');
var p = document.createElement('p');
p.innerHTML = data.userName;
div.appendChild(p);
},
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.addRealGuessPair(data.result.position, null);
}
Game.panoId = data.place.panoId;
Game.pov = data.place.pov;
document.getElementById('multi').style.visibility = 'hidden';
Game.resetRound();
Game.startNewRound();
}
},
prepare: function () {
var data = new FormData();
var userNames;
if (roomId) {
var userNames = localStorage.userNames ? JSON.parse(localStorage.userNames) : {};
if (!userNames.hasOwnProperty(roomId)) {
userNames[roomId] = prompt('Your name: ');
localStorage.userNames = JSON.stringify(userNames);
}
data.append('userName', userNames[roomId]);
}
document.getElementById('loading').style.visibility = 'visible';
var url = roomId ? '/multiGame/' + roomId + '/prepare.json' : '/game/' + mapId + '/prepare.json';
MapGuesser.httpRequest('POST', url, function () {
document.getElementById('loading').style.visibility = 'hidden';
2021-03-21 14:37:08 +01:00
if (this.response.error) {
Game.handleErrorResponse(this.response.error);
return;
}
document.getElementById('mapName').innerHTML = this.response.mapName;
Game.mapBounds = this.response.bounds;
Game.initialize();
if (roomId) {
Game.multi.token = this.response.token;
Game.multi.owner = this.response.owner;
document.getElementById('multi').style.visibility = 'visible';
if (Game.multi.owner) {
document.getElementById('startMultiGameButton').style.display = 'block';
}
document.getElementById('loading').style.visibility = 'visible';
Game.MultiConnector.connect();
}
}, data);
},
initialize: function () {
document.getElementById('panoCover').style.visibility = 'visible';
document.getElementById('currentRound').innerHTML = '1/' + String(Game.NUMBER_OF_ROUNDS);
document.getElementById('currentScoreSum').innerHTML = '0/0';
Game.map.setOptions({
draggableCursor: 'crosshair'
});
Game.map.fitBounds(Game.mapBounds);
if (roomId) {
// if it is multiplayer mode, data is sent via WS
return;
}
document.getElementById('loading').style.visibility = 'visible';
MapGuesser.httpRequest('POST', '/game/' + mapId + '/initialData.json', function () {
document.getElementById('loading').style.visibility = 'hidden';
document.getElementById('panoCover').style.visibility = 'hidden';
if (this.response.error) {
2021-03-21 14:37:08 +01:00
Game.handleErrorResponse(this.response.error);
return;
}
Game.panoId = this.response.place.panoId;
Game.pov = this.response.place.pov;
if (this.response.history) {
for (var i = 0; i < this.response.history.length; ++i) {
var round = this.response.history[i];
Game.rounds.push({ position: round.position, guessPosition: round.guessPosition, realMarker: null, guessMarker: null, line: null });
Game.addRealGuessPair(round.position, round.guessPosition, true);
Game.scoreSum += round.score;
}
document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS);
document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE);
}
Game.startNewRound();
});
},
reset: function () {
if (Game.guessMarker) {
Game.guessMarker.setMap(null);
Game.guessMarker = null;
}
for (var i = 0; i < Game.rounds.length; ++i) {
var round = Game.rounds[i];
if (round.realMarker) {
round.realMarker.setMap(null);
}
if (round.guessMarker) {
round.guessMarker.setMap(null);
round.line.setMap(null);
}
}
Game.rounds = [];
Game.scoreSum = 0;
var distanceInfo = document.getElementById('distanceInfo');
distanceInfo.children[0].style.display = null;
distanceInfo.children[1].style.display = null;
var scoreInfo = document.getElementById('scoreInfo');
scoreInfo.children[0].style.display = null;
scoreInfo.children[1].style.display = null;
document.getElementById('continueButton').style.display = null;
document.getElementById('startNewGameButton').style.display = null;
document.getElementById('showGuessButton').style.visibility = null;
document.getElementById('guess').style.visibility = null;
document.getElementById('guess').classList.remove('result');
Game.initialize();
},
resetRound: function () {
document.getElementById('scoreBar').style.width = null;
if (Game.rounds.length > 0) {
var lastRound = Game.rounds[Game.rounds.length - 1];
lastRound.realMarker.setVisible(false);
if (lastRound.guessMarker) {
lastRound.guessMarker.setVisible(false);
lastRound.line.setVisible(false);
}
}
document.getElementById('panoCover').style.visibility = 'hidden';
document.getElementById('showGuessButton').style.visibility = null;
document.getElementById('guess').style.visibility = null;
document.getElementById('guess').classList.remove('result')
Game.map.setOptions({
draggableCursor: 'crosshair'
});
Game.map.fitBounds(Game.mapBounds);
if (roomId) {
// if it is multiplayer mode, data is sent via WS
return;
}
Game.startNewRound();
},
startNewRound: function () {
Game.rounds.push({ position: null, guessPosition: null, realMarker: null, guessMarker: null, line: null });
document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS);
Game.loadPano(Game.panoId, Game.pov);
},
handleErrorResponse: function (error) {
2021-03-21 14:37:08 +01:00
switch (error) {
case 'no_session_found':
MapGuesser.showModalWithContent('Error', 'Your session is invalid!', [{
type: 'button',
classNames: [],
text: 'Restart game',
onclick: function () {
window.location.reload();
}
}]);
break;
case 'game_already_started':
MapGuesser.showModalWithContent('Error', 'This game is already started, you cannot join.');
break;
default:
MapGuesser.showModalWithContent('Error', 'Error code: \'' + error + '\'');
break
}
},
loadPano: function (panoId, pov) {
if (Game.adaptGuess) {
document.getElementById('guess').classList.add('adapt');
}
Game.panorama.setPov({ heading: pov.heading, pitch: pov.pitch });
Game.panorama.setZoom(pov.zoom);
Game.panorama.setPano(panoId);
},
evaluateGuess: function () {
if (!Game.guessMarker) {
return;
}
var guessPosition = Game.guessMarker.getPosition().toJSON();
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();
data.append('lat', String(guessPosition.lat));
data.append('lng', String(guessPosition.lng));
2021-03-21 14:37:08 +01:00
document.getElementById('loading').style.visibility = 'visible';
var url = roomId ? '/multiGame/' + roomId + '/guess.json' : '/game/' + mapId + '/guess.json';
MapGuesser.httpRequest('POST', url, function () {
2021-03-21 14:37:08 +01:00
document.getElementById('loading').style.visibility = 'hidden';
if (this.response.error) {
Game.handleErrorResponse(this.response.error);
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);
Game.rounds[Game.rounds.length - 1].position = this.response.result.position;
Game.addRealGuessPair(this.response.result.position, guessPosition);
var resultBounds = new google.maps.LatLngBounds();
resultBounds.extend(this.response.result.position);
resultBounds.extend(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';
}
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);
},
addRealGuessPair: function (position, guessPosition, hidden) {
var round = Game.rounds[Game.rounds.length - 1];
round.realMarker = new google.maps.Marker({
map: Game.map,
visible: !hidden,
position: position,
2020-05-22 23:55:09 +02:00
title: 'Open in Google Maps',
zIndex: Game.rounds.length * 2,
clickable: true,
2020-06-01 20:41:46 +02:00
draggable: false,
icon: {
url: STATIC_ROOT + '/img/markers/marker-green.svg?rev=' + REVISION,
2020-06-01 20:41:46 +02:00
size: new google.maps.Size(24, 32),
scaledSize: new google.maps.Size(24, 32),
anchor: new google.maps.Point(12, 32)
},
});
round.realMarker.addListener('click', function () {
window.open('https://www.google.com/maps/search/?api=1&query=' + this.getPosition().toUrlValue(), '_blank');
});
if (!guessPosition) {
return;
}
round.guessMarker = new google.maps.Marker({
map: Game.map,
visible: !hidden,
position: guessPosition,
zIndex: Game.rounds.length,
clickable: false,
draggable: false,
2020-06-01 20:41:46 +02:00
icon: {
url: STATIC_ROOT + '/img/markers/marker-gray-empty.svg?rev=' + REVISION,
2020-06-01 20:41:46 +02:00
size: new google.maps.Size(24, 32),
scaledSize: new google.maps.Size(24, 32),
anchor: new google.maps.Point(12, 32),
labelOrigin: new google.maps.Point(12, 14)
},
label: {
color: '#ffffff',
fontFamily: 'Roboto',
2020-06-01 20:41:46 +02:00
fontSize: '16px',
fontWeight: '500',
text: '?'
}
});
round.line = new google.maps.Polyline({
map: Game.map,
visible: !hidden,
path: [
position,
guessPosition
],
geodesic: true,
strokeOpacity: 0,
icons: [{
icon: {
path: 'M 0,-1 0,1',
strokeOpacity: 1,
strokeWeight: 2,
scale: 2
},
offset: '0',
repeat: '10px'
}],
clickable: false,
draggable: false,
editable: false
});
},
calculateScoreBarProperties: function (score, maxScore) {
var percent = Math.floor((score / maxScore) * 100);
var color;
if (percent >= 90) {
color = '#11ca00';
} else if (percent >= 10) {
color = '#ea9000';
} else {
color = '#ca1100';
}
return { width: percent + '%', backgroundColor: color };
},
showSummary: function () {
var distanceInfo = document.getElementById('distanceInfo');
distanceInfo.children[0].style.display = 'none';
distanceInfo.children[1].style.display = 'block';
var scoreInfo = document.getElementById('scoreInfo');
scoreInfo.children[0].style.display = 'none';
scoreInfo.children[1].style.display = 'block';
document.getElementById('showSummaryButton').style.display = null;
2021-03-20 22:24:47 +01:00
if (!roomId || Game.multi.owner) {
document.getElementById('startNewGameButton').style.display = 'block';
}
var resultBounds = new google.maps.LatLngBounds();
for (var i = 0; i < Game.rounds.length; ++i) {
var round = Game.rounds[i];
2020-06-01 20:41:46 +02:00
round.realMarker.setIcon({
url: STATIC_ROOT + '/img/markers/marker-green-empty.svg?rev=' + REVISION,
2020-06-01 20:41:46 +02:00
size: new google.maps.Size(24, 32),
scaledSize: new google.maps.Size(24, 32),
anchor: new google.maps.Point(12, 32),
labelOrigin: new google.maps.Point(12, 14)
});
round.realMarker.setLabel({
2020-06-01 20:41:46 +02:00
color: '#285624',
fontFamily: 'Roboto',
fontSize: '16px',
fontWeight: '500',
text: String(i + 1)
});
round.realMarker.setVisible(true);
if (round.guessMarker) {
round.guessMarker.setVisible(true);
round.line.setVisible(true);
}
resultBounds.extend(round.position);
if (round.guessMarker) {
resultBounds.extend(round.guessPosition);
}
}
Game.map.fitBounds(resultBounds);
document.getElementById('scoreSum').innerHTML = String(Game.scoreSum);
var scoreBarProperties = Game.calculateScoreBarProperties(Game.scoreSum, Game.NUMBER_OF_ROUNDS * Game.MAX_SCORE);
var scoreBar = document.getElementById('scoreBar');
scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor;
scoreBar.style.width = scoreBarProperties.width;
},
rewriteGoogleLink: function () {
if (!Game.googleLink) {
var anchors = document.getElementById('panorama').getElementsByTagName('a');
for (var i = 0; i < anchors.length; i++) {
var a = anchors[i];
if (a.href.indexOf('maps.google.com/maps') !== -1) {
Game.googleLink = a;
break;
}
}
}
setTimeout(function () {
if (Game.googleLink) {
Game.googleLink.title = 'Google Maps';
Game.googleLink.href = 'https://maps.google.com/maps';
}
}, 1);
}
};
var Util = {
printDistanceForHuman: function (distance) {
if (distance < 1000) {
return Number.parseFloat(distance).toFixed(0) + ' m';
} else if (distance < 10000) {
return Number.parseFloat(distance / 1000).toFixed(2) + ' km';
} else if (distance < 100000) {
return Number.parseFloat(distance / 1000).toFixed(1) + ' km';
} else {
return Number.parseFloat(distance / 1000).toFixed(0) + ' km';
}
}
};
MapGuesser.sessionAvailableHooks.reinitializeGame = Game.prepare;
if (!('ontouchstart' in document.documentElement)) {
Game.adaptGuess = true;
}
Game.map = new google.maps.Map(document.getElementById('map'), {
disableDefaultUI: true,
clickableIcons: false,
draggingCursor: 'grabbing'
});
Game.map.addListener('click', function (e) {
if (Game.rounds[Game.rounds.length - 1].guessPosition) {
return;
}
if (Game.guessMarker) {
Game.guessMarker.setPosition(e.latLng);
return;
}
Game.guessMarker = new google.maps.Marker({
map: Game.map,
position: e.latLng,
clickable: false,
draggable: true,
2020-06-01 20:41:46 +02:00
icon: {
url: STATIC_ROOT + '/img/markers/marker-gray-empty.svg?rev=' + REVISION,
2020-06-01 20:41:46 +02:00
size: new google.maps.Size(24, 32),
scaledSize: new google.maps.Size(24, 32),
anchor: new google.maps.Point(12, 32),
labelOrigin: new google.maps.Point(12, 14)
},
label: {
color: '#ffffff',
fontFamily: 'Roboto',
2020-06-01 20:41:46 +02:00
fontSize: '16px',
fontWeight: '500',
text: '?'
}
});
document.getElementById('guessButton').disabled = false;
});
Game.panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), {
disableDefaultUI: true,
linksControl: true,
showRoadLabels: false,
motionTracking: false
});
Game.panorama.addListener('position_changed', function () {
Game.rewriteGoogleLink();
});
Game.panorama.addListener('pov_changed', function () {
Game.rewriteGoogleLink();
});
if (COOKIES_CONSENT) {
Game.prepare();
}
document.getElementById('showGuessButton').onclick = function () {
this.style.visibility = 'hidden';
document.getElementById('guess').style.visibility = 'visible';
}
document.getElementById('closeGuessButton').onclick = function () {
document.getElementById('showGuessButton').style.visibility = null;
document.getElementById('guess').style.visibility = null;
}
document.getElementById('guessButton').onclick = function () {
Game.evaluateGuess();
}
document.getElementById('continueButton').onclick = function () {
if (roomId) {
if (!Game.multi.owner) {
return;
}
document.getElementById('loading').style.visibility = 'visible';
MapGuesser.httpRequest('POST', '/multiGame/' + roomId + '/nextRound.json', function () {
document.getElementById('loading').style.visibility = 'hidden';
2021-03-21 14:37:08 +01:00
if (this.response.error) {
Game.handleErrorResponse(this.response.error);
return;
}
});
} else {
Game.resetRound();
}
}
document.getElementById('showSummaryButton').onclick = function () {
Game.showSummary();
}
document.getElementById('startNewGameButton').onclick = function () {
if (roomId) {
if (!Game.multi.owner) {
return;
}
document.getElementById('loading').style.visibility = 'visible';
MapGuesser.httpRequest('POST', '/multiGame/' + roomId + '/initialData.json', function () {
document.getElementById('loading').style.visibility = 'hidden';
2021-03-21 14:37:08 +01:00
if (this.response.error) {
Game.handleErrorResponse(this.response.error);
return;
}
});
} else {
Game.reset();
}
}
document.getElementById('startMultiGameButton').onclick = function () {
if (!roomId || !Game.multi.owner) {
return;
}
document.getElementById('multi').style.visibility = 'hidden';
2021-03-21 14:37:08 +01:00
document.getElementById('loading').style.visibility = 'visible';
MapGuesser.httpRequest('POST', '/multiGame/' + roomId + '/initialData.json', function () {
document.getElementById('loading').style.visibility = 'hidden';
2021-03-21 14:37:08 +01:00
if (this.response.error) {
Game.handleErrorResponse(this.response.error);
return;
}
});
}
})();