Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d30ec3a3a0 | ||
|
|
cc19d454fa
|
||
|
|
241d2f2b30
|
||
|
|
d0751017db
|
||
|
|
6014e4517a | ||
|
|
81a8153866
|
||
|
|
e435ef6be0 | ||
|
|
90528e61e5
|
||
|
|
d02aa4abe0
|
||
|
|
4f45e213c3 | ||
|
|
d877d2fe03
|
||
|
|
66240c2d65 | ||
|
|
0654b65940
|
||
|
|
272ec3568d | ||
|
|
be4a2038e5
|
||
|
|
f305c97513
|
||
|
|
784037de6f | ||
|
|
6f27450423 | ||
|
|
bbaa2fe1eb | ||
|
|
28165d76d3 | ||
|
|
1c1e5f051d | ||
|
|
283c214c50 | ||
|
|
7970927654 | ||
|
|
567602c749 | ||
|
|
a5238234d2 | ||
|
|
c965713c9c | ||
|
|
b00fe4ebe3 | ||
|
|
599d8bf153 | ||
|
|
636c47366a | ||
|
|
4520d11559 | ||
|
|
5f0b639fe7 | ||
|
|
d4a279d2f4 | ||
|
|
aadeda05af | ||
|
|
b78d564f6f | ||
|
|
c2df9b7713 | ||
|
|
bbb66ca979 | ||
|
|
3b98570f6d | ||
|
|
77ff175794 | ||
|
|
ae7fd98256 | ||
|
|
517d758e8e | ||
|
|
2a1b51b014 | ||
|
|
c085a5a1ca | ||
|
|
a7cfc3c19f | ||
|
|
7898c52328 | ||
|
|
62251b1062 | ||
|
|
069c6b37c8 | ||
|
|
30f4b7ad19 | ||
|
|
45ddb7f56a | ||
|
|
7792f1c3ff | ||
|
|
7a1674fdd0 | ||
|
|
44df94eb98 | ||
|
|
00afd65d75 | ||
|
|
21e41b7c36 | ||
|
|
69964acfb2 | ||
|
|
e7869d67f7 | ||
|
|
93f8fc3f34 | ||
|
|
5daed10036 | ||
|
|
d7147b30d6 | ||
|
|
c02f595606 | ||
|
|
99244e93d2 | ||
|
|
313a3568aa | ||
|
|
54bc9c31db
|
No files matched your search
@@ -20,3 +20,6 @@ MULTI_INTERNAL_HOST=multi
|
|||||||
MULTI_INTERNAL_PORT=5000
|
MULTI_INTERNAL_PORT=5000
|
||||||
MULTI_WS_URL=mapguesser-dev.ch:8090
|
MULTI_WS_URL=mapguesser-dev.ch:8090
|
||||||
MULTI_WS_PORT=8090
|
MULTI_WS_PORT=8090
|
||||||
|
ENABLE_GAME_FOR_GUESTS=0
|
||||||
|
RECAPTCHA_SITEKEY=your_recaptcha_sitekey
|
||||||
|
RECAPTCHA_SECRET=your_recaptcha_secret
|
||||||
Vendored
+59
@@ -0,0 +1,59 @@
|
|||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
node {
|
||||||
|
label 'mapguesser'
|
||||||
|
customWorkspace 'workspace/mapguesser'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Install composer') {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
filename 'docker/Dockerfile-test'
|
||||||
|
dir '.'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'composer install'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Unit Testing') {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
filename 'docker/Dockerfile-test'
|
||||||
|
dir '.'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'vendor/bin/phpunit --log-junit unit_test_results.xml --testdox tests'
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
archiveArtifacts 'unit_test_results.xml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Static Code Analysis') {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
filename 'docker/Dockerfile-test'
|
||||||
|
dir '.'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'php -d memory_limit=1G vendor/bin/phpstan analyse -c phpstan.neon --error-format=prettyJson > static_code_analysis_results.json'
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
archiveArtifacts 'static_code_analysis_results.json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# MapGuesser
|
# MapGuesser
|
||||||
|
|
||||||
|
[](https://jenkins.e5tv.hu/job/mapguesser/job/develop/)
|
||||||
|
|
||||||
This is the MapGuesser Application project. This is a game about guessing where you are based on a street view panorama - inspired by existing applications.
|
This is the MapGuesser Application project. This is a game about guessing where you are based on a street view panorama - inspired by existing applications.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
CREATE TABLE `challenges` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`token` int(10) unsigned NOT NULL,
|
||||||
|
`time_limit` int(10) unsigned,
|
||||||
|
`time_limit_type` enum('game', 'round') NOT NULL DEFAULT 'game',
|
||||||
|
`no_move` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`no_pan` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`no_zoom` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE `user_in_challenge` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` int(10) unsigned NOT NULL,
|
||||||
|
`challenge_id` int(10) unsigned NOT NULL,
|
||||||
|
`current_round` smallint(5) signed NOT NULL DEFAULT 0,
|
||||||
|
`time_left` int(10) unsigned,
|
||||||
|
`is_owner` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `user_id` (`user_id`),
|
||||||
|
KEY `challenge_id` (`challenge_id`),
|
||||||
|
CONSTRAINT `user_in_challenge_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
|
||||||
|
CONSTRAINT `user_in_challenge_challenge_id` FOREIGN KEY (`challenge_id`) REFERENCES `challenges` (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE `place_in_challenge` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`place_id` int(10) unsigned NOT NULL,
|
||||||
|
`challenge_id` int(10) unsigned NOT NULL,
|
||||||
|
`round` smallint(5) unsigned NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `place_id` (`place_id`),
|
||||||
|
KEY `challenge_id` (`challenge_id`),
|
||||||
|
CONSTRAINT `place_in_challenge_place_id` FOREIGN KEY (`place_id`) REFERENCES `places` (`id`),
|
||||||
|
CONSTRAINT `place_in_challenge_challenge_id` FOREIGN KEY (`challenge_id`) REFERENCES `challenges` (`id`),
|
||||||
|
CONSTRAINT `unique_order_in_challenge` UNIQUE (`round`, `challenge_id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE `guesses` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` int(10) unsigned NOT NULL,
|
||||||
|
`place_in_challenge_id` int(10) unsigned NOT NULL,
|
||||||
|
`lat` decimal(8,6) NOT NULL,
|
||||||
|
`lng` decimal(9,6) NOT NULL,
|
||||||
|
`score` int(10) NOT NULL,
|
||||||
|
`distance` int(10) NOT NULL,
|
||||||
|
`time_spent` int(10),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `user_id` (`user_id`),
|
||||||
|
KEY `place_in_challenge_id` (`place_in_challenge_id`),
|
||||||
|
CONSTRAINT `guesses_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
|
||||||
|
CONSTRAINT `guesses_place_in_challenge_id` FOREIGN KEY (`place_in_challenge_id`) REFERENCES `place_in_challenge` (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `maps`
|
||||||
|
ADD `unlisted` TINYINT(1) NOT NULL DEFAULT 0;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
FROM php:7.4.7-cli-buster
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y unzip
|
||||||
|
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||||
@@ -15,6 +15,17 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panningBlockerCover {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
opacity: 0;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +33,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 30px;
|
bottom: 30px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
z-index: 2;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
#guess.result {
|
#guess.result {
|
||||||
@@ -153,6 +164,47 @@
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#goToStart {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#highscoresTable {
|
||||||
|
margin: 1em;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#highscoresTable td, #highscoresTable th {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#highscoresTable tr:nth-child(even) {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#highscoresTable tr:hover {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#highscoresTable th {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #e8a349;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#highscoresTable tr.ownPlayer {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 899px) {
|
||||||
|
.hideOnNarrowScreen {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 599px) {
|
@media screen and (max-width: 599px) {
|
||||||
#mapName {
|
#mapName {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ main {
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
p, h1, h2, input, textarea, select, button, a {
|
p, h1, h2, h3, input, textarea, select, button, a, table, label {
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Roboto', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2 {
|
h1, h2, h3 {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,11 @@ h2, header.small h1 {
|
|||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
p, h2 {
|
h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, h2, h3 {
|
||||||
line-height: 150%;
|
line-height: 150%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +241,7 @@ button.green:enabled:hover, button.green:enabled:focus, a.button.green:hover, a.
|
|||||||
background-color: #1b7d31;
|
background-color: #1b7d31;
|
||||||
}
|
}
|
||||||
|
|
||||||
input, select, textarea {
|
input.text, select, textarea {
|
||||||
background-color: #f9fafb;
|
background-color: #f9fafb;
|
||||||
border: solid #c8d2e1 1px;
|
border: solid #c8d2e1 1px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@@ -246,22 +250,26 @@ input, select, textarea {
|
|||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
input, select {
|
input.text, select {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type=checkbox], input[type=radio] {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.big, select.big, textarea.big, div.inputWithButton>input {
|
input.text.big, select.big, textarea.big, div.inputWithButton>input.text {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.big, select.big, div.inputWithButton>input {
|
input.text.big, select.big, div.inputWithButton>input.text {
|
||||||
height: 35px;
|
height: 35px;
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
@@ -276,19 +284,19 @@ input.fullWidth, select.fullWidth, textarea.fullWidth {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:disabled, select:disabled, textarea:disabled {
|
input.text:disabled, select:disabled, textarea:disabled {
|
||||||
background-color: #dfdfdf;
|
background-color: #dfdfdf;
|
||||||
border: solid #dfdfdf 1px;
|
border: solid #dfdfdf 1px;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus, select:focus, textarea:focus {
|
input.text:focus, select:focus, textarea:focus {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: solid #29457f 2px;
|
border: solid #29457f 2px;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus, select:focus {
|
input.text:focus, select:focus {
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,16 +304,16 @@ textarea:focus {
|
|||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.big:focus, select.big:focus {
|
input.text.big:focus, select.big:focus {
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.inputWithButton>input {
|
div.inputWithButton>input.text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 83px 0 6px;
|
padding: 0 83px 0 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.inputWithButton>input:focus {
|
div.inputWithButton>input.text:focus {
|
||||||
padding: 0 82px 0 5px;
|
padding: 0 82px 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ div.mapItem.new {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.mapItem.unlisted {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
div.mapItem>div.title {
|
div.mapItem>div.title {
|
||||||
background-color: #28a745;
|
background-color: #28a745;
|
||||||
color: white;
|
color: white;
|
||||||
@@ -75,6 +79,10 @@ div.mapItem>div.buttonContainer {
|
|||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#timeLimitType {
|
||||||
|
margin-left: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1504px) {
|
@media screen and (min-width: 1504px) {
|
||||||
#mapContainer {
|
#mapContainer {
|
||||||
grid-template-columns: auto auto auto auto;
|
grid-template-columns: auto auto auto auto;
|
||||||
|
|||||||
+339
-60
@@ -1,10 +1,13 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 });
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
var Game = {
|
var Game = {
|
||||||
NUMBER_OF_ROUNDS: 5,
|
NUMBER_OF_ROUNDS: 5,
|
||||||
MAX_SCORE: 1000,
|
MAX_SCORE: 1000,
|
||||||
|
|
||||||
|
type: GameType.SINGLE,
|
||||||
mapBounds: null,
|
mapBounds: null,
|
||||||
multi: { token: null, owner: false },
|
multi: { token: null, owner: false },
|
||||||
rounds: [],
|
rounds: [],
|
||||||
@@ -16,6 +19,8 @@
|
|||||||
guessMarker: null,
|
guessMarker: null,
|
||||||
adaptGuess: false,
|
adaptGuess: false,
|
||||||
googleLink: null,
|
googleLink: null,
|
||||||
|
history: [],
|
||||||
|
restrictions: null,
|
||||||
|
|
||||||
readyToContinue: true,
|
readyToContinue: true,
|
||||||
timeoutEnd: null,
|
timeoutEnd: null,
|
||||||
@@ -211,6 +216,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getGameIdentifier: function () {
|
||||||
|
switch (Game.type) {
|
||||||
|
case GameType.SINGLE:
|
||||||
|
return '/game/' + mapId;
|
||||||
|
case GameType.MULTI:
|
||||||
|
return '/multiGame/' + roomId;
|
||||||
|
case GameType.CHALLENGE:
|
||||||
|
return '/challenge/' + challengeToken;
|
||||||
|
default:
|
||||||
|
return '/game/' + mapId;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
prepare: function () {
|
prepare: function () {
|
||||||
var data = new FormData();
|
var data = new FormData();
|
||||||
var userNames;
|
var userNames;
|
||||||
@@ -226,7 +244,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
var url = roomId ? '/multiGame/' + roomId + '/prepare.json' : '/game/' + mapId + '/prepare.json';
|
var url = Game.getGameIdentifier() + '/prepare.json';
|
||||||
MapGuesser.httpRequest('POST', url, function () {
|
MapGuesser.httpRequest('POST', url, function () {
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
@@ -269,7 +287,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
MapGuesser.httpRequest('POST', '/game/' + mapId + '/initialData.json', function () {
|
MapGuesser.httpRequest('POST', Game.getGameIdentifier() + '/initialData.json', function () {
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
document.getElementById('panoCover').style.visibility = 'hidden';
|
document.getElementById('panoCover').style.visibility = 'hidden';
|
||||||
|
|
||||||
@@ -278,24 +296,199 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game.panoId = this.response.place.panoId;
|
Game.loadHistory(this.response);
|
||||||
Game.pov = this.response.place.pov;
|
|
||||||
|
|
||||||
for (var i = 0; i < this.response.history.length; ++i) {
|
Game.restrictions = this.response.restrictions;
|
||||||
var round = this.response.history[i];
|
Game.displayRestrictions();
|
||||||
Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] });
|
|
||||||
Game.addPositionToResultMap(true);
|
if (this.response.finished) {
|
||||||
Game.addGuessPositionToResultMap(round.result.guessPosition, null, true);
|
|
||||||
Game.scoreSum += round.result.score;
|
Game.transitToResultMap();
|
||||||
|
Game.showSummary();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Game.panoId = this.response.place.panoId;
|
||||||
|
Game.pov = this.response.place.pov;
|
||||||
|
|
||||||
|
Game.startNewRound();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS);
|
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);
|
document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE);
|
||||||
|
|
||||||
Game.startNewRound();
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
enableRestrictions: function () {
|
||||||
|
if (!Game.restrictions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game.panorama.setOptions({
|
||||||
|
clickToGo: !Game.restrictions.noMove,
|
||||||
|
linksControl: !(Game.restrictions.noMove || Game.restrictions.noPan),
|
||||||
|
scrollwheel: !Game.restrictions.noZoom
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Game.restrictions.noPan) {
|
||||||
|
document.getElementById('panningBlockerCover').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game.restrictions.timeLimit) {
|
||||||
|
Game.startCountdown(Game.restrictions.timeLimit, function () {
|
||||||
|
Game.guess();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
displayRestrictions: function () {
|
||||||
|
if (!Game.restrictions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var restrictionsForDisplay = [];
|
||||||
|
if (Game.restrictions.timeLimit) {
|
||||||
|
restrictionsForDisplay.push('time limit per ' + Game.restrictions.timeLimitType);
|
||||||
|
}
|
||||||
|
if (Game.restrictions.noPan) {
|
||||||
|
restrictionsForDisplay.push('no camera change');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (Game.restrictions.noMove) {
|
||||||
|
restrictionsForDisplay.push('no move');
|
||||||
|
}
|
||||||
|
if (Game.restrictions.noZoom) {
|
||||||
|
restrictionsForDisplay.push('no zoom');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restrictionsForDisplay.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create restrictions span for header
|
||||||
|
var restrictions = document.createElement('span');
|
||||||
|
restrictions.setAttribute('id', 'restrictions');
|
||||||
|
restrictions.setAttribute('class', 'hideOnNarrowScreen');
|
||||||
|
var restrictionsTitle = document.createElement('span');
|
||||||
|
restrictionsTitle.setAttribute('class', 'bold');
|
||||||
|
restrictionsTitle.innerText = 'Restrictions: ';
|
||||||
|
var restrictionsList = document.createElement('span');
|
||||||
|
restrictionsList.innerText = restrictionsForDisplay.join(', ');
|
||||||
|
restrictions.appendChild(restrictionsTitle);
|
||||||
|
restrictions.appendChild(restrictionsList);
|
||||||
|
|
||||||
|
var roundContainer = document.getElementById('roundContainer');
|
||||||
|
var header = roundContainer.parentNode;
|
||||||
|
header.insertBefore(restrictions, roundContainer);
|
||||||
|
},
|
||||||
|
|
||||||
|
disableRestrictions: function () {
|
||||||
|
|
||||||
|
Game.panorama.setOptions({
|
||||||
|
clickToGo: true,
|
||||||
|
linksControl: true,
|
||||||
|
scrollwheel: true
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('panningBlockerCover').style.display = null;
|
||||||
|
|
||||||
|
Game.startCountdown(0);
|
||||||
|
Game.timeoutEnd = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
hideRestrictions: function () {
|
||||||
|
var restrictions = document.getElementById('restrictions');
|
||||||
|
if (restrictions) {
|
||||||
|
var header = restrictions.parentNode;
|
||||||
|
header.removeChild(restrictions);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
transitToResultMap: function () {
|
||||||
|
// TODO: refactor - it is necessary for mobile
|
||||||
|
if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') {
|
||||||
|
document.getElementById('showGuessButton').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
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.map.setOptions({
|
||||||
|
draggableCursor: 'grab'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Game.rounds.length === Game.NUMBER_OF_ROUNDS) {
|
||||||
|
document.getElementById('continueButton').style.display = 'none';
|
||||||
|
document.getElementById('showSummaryButton').style.display = 'block';
|
||||||
|
} else if (Game.type == GameType.MULTI) {
|
||||||
|
if (Game.multi.owner) {
|
||||||
|
if (!Game.readyToContinue) {
|
||||||
|
document.getElementById('continueButton').disabled = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById('continueButton').style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadHistory: function (response) {
|
||||||
|
if (!response.history)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Game.history = response.history;
|
||||||
|
|
||||||
|
for (var i = 0; i < Game.rounds.length; ++i) {
|
||||||
|
var round = Game.rounds[i];
|
||||||
|
|
||||||
|
if (round.realMarker) {
|
||||||
|
round.realMarker.setMap(null);
|
||||||
|
}
|
||||||
|
for (var j = 0; j < round.guessMarkers.length; ++j) {
|
||||||
|
var guessMarker = round.guessMarkers[j];
|
||||||
|
guessMarker.marker.setMap(null);
|
||||||
|
guessMarker.line.setMap(null);
|
||||||
|
if (guessMarker.info) {
|
||||||
|
guessMarker.info.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Game.rounds = [];
|
||||||
|
|
||||||
|
Game.scoreSum = 0;
|
||||||
|
for (var i = 0; i < Game.history.length; ++i) {
|
||||||
|
var round = Game.history[i];
|
||||||
|
|
||||||
|
if (round.result) {
|
||||||
|
Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] });
|
||||||
|
Game.addPositionToResultMap(true);
|
||||||
|
if (round.result.guessPosition) {
|
||||||
|
Game.addGuessPositionToResultMap(round.result.guessPosition, round.result, true);
|
||||||
|
}
|
||||||
|
Game.scoreSum += round.result.score;
|
||||||
|
|
||||||
|
|
||||||
|
if (round.allResults !== undefined) {
|
||||||
|
for (var j = 0; j < round.allResults.length; ++j) {
|
||||||
|
var result = round.allResults[j];
|
||||||
|
if (result.guessPosition) {
|
||||||
|
Game.addGuessPositionToResultMap(result.guessPosition, result, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
reset: function () {
|
reset: function () {
|
||||||
if (Game.guessMarker) {
|
if (Game.guessMarker) {
|
||||||
Game.guessMarker.setMap(null);
|
Game.guessMarker.setMap(null);
|
||||||
@@ -325,6 +518,7 @@
|
|||||||
distanceInfo.children[0].style.display = null;
|
distanceInfo.children[0].style.display = null;
|
||||||
distanceInfo.children[1].style.display = null;
|
distanceInfo.children[1].style.display = null;
|
||||||
distanceInfo.children[2].style.display = null;
|
distanceInfo.children[2].style.display = null;
|
||||||
|
document.getElementById('summaryInfo').innerHTML = "Game finished."
|
||||||
var scoreInfo = document.getElementById('scoreInfo');
|
var scoreInfo = document.getElementById('scoreInfo');
|
||||||
scoreInfo.children[0].style.display = null;
|
scoreInfo.children[0].style.display = null;
|
||||||
scoreInfo.children[1].style.display = null;
|
scoreInfo.children[1].style.display = null;
|
||||||
@@ -339,6 +533,13 @@
|
|||||||
// needs to be set visible after the show guess map hid it in mobile view
|
// needs to be set visible after the show guess map hid it in mobile view
|
||||||
document.getElementById("navigation").style.visibility = 'visible';
|
document.getElementById("navigation").style.visibility = 'visible';
|
||||||
|
|
||||||
|
Game.disableRestrictions();
|
||||||
|
Game.hideRestrictions();
|
||||||
|
|
||||||
|
document.getElementById('panningBlockerCover').style.display = null;
|
||||||
|
|
||||||
|
Game.history = [];
|
||||||
|
|
||||||
Game.initialize();
|
Game.initialize();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -391,6 +592,8 @@
|
|||||||
// update the compass
|
// update the compass
|
||||||
const heading = Game.panorama.getPov().heading;
|
const heading = Game.panorama.getPov().heading;
|
||||||
document.getElementById("compass").style.transform = "translateY(-50%) rotate(" + heading + "deg)";
|
document.getElementById("compass").style.transform = "translateY(-50%) rotate(" + heading + "deg)";
|
||||||
|
|
||||||
|
Game.enableRestrictions();
|
||||||
},
|
},
|
||||||
|
|
||||||
handleErrorResponse: function (error) {
|
handleErrorResponse: function (error) {
|
||||||
@@ -411,7 +614,11 @@
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'game_not_found':
|
case 'game_not_found':
|
||||||
MapGuesser.showModalWithContent('Error', 'The game room was not found by this ID. Please check the link.');
|
MapGuesser.showModalWithContent('Error', 'The game was not found by this ID. Please check the link.');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'anonymous_user':
|
||||||
|
MapGuesser.showModalWithContent('Error', 'You have to login to join a challenge!');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -441,7 +648,7 @@
|
|||||||
resultBounds.extend(position);
|
resultBounds.extend(position);
|
||||||
|
|
||||||
if (guessPosition) {
|
if (guessPosition) {
|
||||||
Game.addGuessPositionToResultMap(guessPosition);
|
Game.addGuessPositionToResultMap(guessPosition, result);
|
||||||
resultBounds.extend(guessPosition);
|
resultBounds.extend(guessPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,25 +684,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
showResultMap: function (result, resultBounds) {
|
showResultMap: function (result, resultBounds) {
|
||||||
// TODO: refactor - it is necessary for mobile
|
|
||||||
if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') {
|
|
||||||
document.getElementById('showGuessButton').click();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.adaptGuess) {
|
Game.transitToResultMap();
|
||||||
document.getElementById('guess').classList.remove('adapt');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.guessMarker) {
|
|
||||||
Game.guessMarker.setMap(null);
|
|
||||||
Game.guessMarker = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('guess').classList.add('result');
|
|
||||||
|
|
||||||
Game.map.setOptions({
|
|
||||||
draggableCursor: 'grab'
|
|
||||||
});
|
|
||||||
Game.map.fitBounds(resultBounds);
|
Game.map.fitBounds(resultBounds);
|
||||||
|
|
||||||
var distanceInfo = document.getElementById('distanceInfo');
|
var distanceInfo = document.getElementById('distanceInfo');
|
||||||
@@ -514,38 +705,32 @@
|
|||||||
var scoreBar = document.getElementById('scoreBar');
|
var scoreBar = document.getElementById('scoreBar');
|
||||||
scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor;
|
scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor;
|
||||||
scoreBar.style.width = scoreBarProperties.width;
|
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';
|
|
||||||
} else if (roomId) {
|
|
||||||
if (Game.multi.owner) {
|
|
||||||
if (!Game.readyToContinue) {
|
|
||||||
document.getElementById('continueButton').disabled = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
document.getElementById('continueButton').style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
guess: function () {
|
guess: function () {
|
||||||
if (!Game.guessMarker) {
|
|
||||||
return;
|
var data = new FormData();
|
||||||
|
|
||||||
|
if (Game.timeoutEnd) {
|
||||||
|
var timeLeft = Math.ceil((Game.timeoutEnd - new Date()) / 1000);
|
||||||
|
data.append('timeLeft', timeLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
var guessPosition = Game.guessMarker.getPosition().toJSON();
|
Game.disableRestrictions();
|
||||||
Game.rounds[Game.rounds.length - 1].guessPosition = guessPosition;
|
|
||||||
|
if (Game.guessMarker) {
|
||||||
|
var guessPosition = Game.guessMarker.getPosition().toJSON();
|
||||||
|
Game.rounds[Game.rounds.length - 1].guessPosition = guessPosition;
|
||||||
|
|
||||||
|
data.append('lat', String(guessPosition.lat));
|
||||||
|
data.append('lng', String(guessPosition.lng));
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('guessButton').disabled = true;
|
document.getElementById('guessButton').disabled = true;
|
||||||
document.getElementById('panoCover').style.visibility = 'visible';
|
document.getElementById('panoCover').style.visibility = 'visible';
|
||||||
|
|
||||||
var data = new FormData();
|
|
||||||
data.append('lat', String(guessPosition.lat));
|
|
||||||
data.append('lng', String(guessPosition.lng));
|
|
||||||
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
var url = roomId ? '/multiGame/' + roomId + '/guess.json' : '/game/' + mapId + '/guess.json';
|
var url = Game.getGameIdentifier() + '/guess.json';
|
||||||
|
|
||||||
MapGuesser.httpRequest('POST', url, function () {
|
MapGuesser.httpRequest('POST', url, function () {
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
@@ -554,12 +739,16 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Game.loadHistory(this.response);
|
||||||
|
Game.restrictions = this.response.restrictions;
|
||||||
|
|
||||||
Game.receiveResult(this.response.position, guessPosition, this.response.result, this.response.allResults);
|
Game.receiveResult(this.response.position, guessPosition, this.response.result, this.response.allResults);
|
||||||
|
|
||||||
if (this.response.place) {
|
if (this.response.place) {
|
||||||
Game.panoId = this.response.place.panoId;
|
Game.panoId = this.response.place.panoId;
|
||||||
Game.pov = this.response.place.pov;
|
Game.pov = this.response.place.pov;
|
||||||
}
|
}
|
||||||
|
|
||||||
}, data);
|
}, data);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -593,8 +782,8 @@
|
|||||||
var position = round.position;
|
var position = round.position;
|
||||||
|
|
||||||
var guessMarker = { marker: null, line: null, info: null };
|
var guessMarker = { marker: null, line: null, info: null };
|
||||||
var markerSvg = result ? 'marker-gray-empty.svg' : 'marker-blue-empty.svg';
|
var markerSvg = result && result.userName ? 'marker-gray-empty.svg' : 'marker-blue-empty.svg';
|
||||||
var markerLabel = result ? result.userName.charAt(0).toUpperCase() : '?';
|
var markerLabel = result && result.userName ? result.userName.charAt(0).toUpperCase() : '?';
|
||||||
|
|
||||||
guessMarker.marker = new google.maps.Marker({
|
guessMarker.marker = new google.maps.Marker({
|
||||||
map: Game.map,
|
map: Game.map,
|
||||||
@@ -644,8 +833,9 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
const userName = result.userName ? result.userName : 'me';
|
||||||
guessMarker.info = new google.maps.InfoWindow({
|
guessMarker.info = new google.maps.InfoWindow({
|
||||||
content: '<p class="small bold">' + result.userName + '</p>' +
|
content: '<p class="small bold">' + userName + '</p>' +
|
||||||
'<p class="small">' + Util.printDistanceForHuman(result.distance) + ' | ' + result.score + ' points</p>',
|
'<p class="small">' + Util.printDistanceForHuman(result.distance) + ' | ' + result.score + ' points</p>',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -672,6 +862,36 @@
|
|||||||
return { width: percent + '%', backgroundColor: color };
|
return { width: percent + '%', backgroundColor: color };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
calculateHighScores: function () {
|
||||||
|
|
||||||
|
var highscores = new Map();
|
||||||
|
highscores.set('me', Game.scoreSum);
|
||||||
|
|
||||||
|
// collect the results of users who are through the last round
|
||||||
|
const round = Game.history[Game.history.length - 1];
|
||||||
|
if (round.allResults) {
|
||||||
|
for (const result of round.allResults) {
|
||||||
|
highscores.set(result.userName, result.score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add up scores only for the finishers
|
||||||
|
for (var i = Game.history.length - 2; i >= 0; --i) {
|
||||||
|
const round = Game.history[i];
|
||||||
|
if (round.allResults) {
|
||||||
|
for (const result of round.allResults) {
|
||||||
|
if (highscores.has(result.userName)) {
|
||||||
|
highscores.set(result.userName, highscores.get(result.userName) + result.score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sortedHighscores = Array.from(highscores, ([userName, score]) => ({ 'userName': userName, 'score': score }))
|
||||||
|
.sort(function (resultA, resultB) { return resultB.score - resultA.score });
|
||||||
|
return sortedHighscores;
|
||||||
|
},
|
||||||
|
|
||||||
showSummary: function () {
|
showSummary: function () {
|
||||||
var distanceInfo = document.getElementById('distanceInfo');
|
var distanceInfo = document.getElementById('distanceInfo');
|
||||||
distanceInfo.children[0].style.display = 'none';
|
distanceInfo.children[0].style.display = 'none';
|
||||||
@@ -682,11 +902,13 @@
|
|||||||
scoreInfo.children[1].style.display = 'block';
|
scoreInfo.children[1].style.display = 'block';
|
||||||
document.getElementById('showSummaryButton').style.display = null;
|
document.getElementById('showSummaryButton').style.display = null;
|
||||||
|
|
||||||
if (!roomId || Game.multi.owner) {
|
if (Game.type == GameType.SINGLE || Game.multi.owner) {
|
||||||
document.getElementById('startNewGameButton').style.display = 'block';
|
document.getElementById('startNewGameButton').style.display = 'block';
|
||||||
if (!Game.readyToContinue) {
|
if (!Game.readyToContinue) {
|
||||||
document.getElementById('startNewGameButton').disabled = true;
|
document.getElementById('startNewGameButton').disabled = true;
|
||||||
}
|
}
|
||||||
|
} else if (Game.type == GameType.CHALLENGE) {
|
||||||
|
document.getElementById('goToStart').style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
var resultBounds = new google.maps.LatLngBounds();
|
var resultBounds = new google.maps.LatLngBounds();
|
||||||
@@ -729,6 +951,48 @@
|
|||||||
var scoreBar = document.getElementById('scoreBar');
|
var scoreBar = document.getElementById('scoreBar');
|
||||||
scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor;
|
scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor;
|
||||||
scoreBar.style.width = scoreBarProperties.width;
|
scoreBar.style.width = scoreBarProperties.width;
|
||||||
|
|
||||||
|
Game.showHighscores();
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
showHighscores: function () {
|
||||||
|
|
||||||
|
if (Game.type == GameType.CHALLENGE) {
|
||||||
|
var highscores = this.calculateHighScores();
|
||||||
|
var summaryInfo = document.getElementById('summaryInfo');
|
||||||
|
|
||||||
|
if (highscores.length > 2) {
|
||||||
|
var table = document.getElementById('highscoresTable');
|
||||||
|
for (const result of highscores) {
|
||||||
|
var userName = document.createElement('td');
|
||||||
|
userName.innerHTML = result.userName;
|
||||||
|
var score = document.createElement('td');
|
||||||
|
score.innerHTML = result.score;
|
||||||
|
var line = document.createElement('tr');
|
||||||
|
line.appendChild(userName);
|
||||||
|
line.appendChild(score);
|
||||||
|
table.appendChild(line);
|
||||||
|
|
||||||
|
if (result.userName === 'me') {
|
||||||
|
line.setAttribute('class', 'ownPlayer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MapGuesser.showModal('highscores');
|
||||||
|
} else if (highscores.length == 2) {
|
||||||
|
|
||||||
|
if (highscores[0].userName === 'me') {
|
||||||
|
summaryInfo.innerHTML = 'You won! <span class="hideOnNarrowScreen">' + highscores[1].userName + ' got only ' + highscores[1].score + ' points.</span>';
|
||||||
|
} else {
|
||||||
|
summaryInfo.innerHTML = 'You lost! <span class="hideOnNarrowScreen">' + highscores[0].userName + ' won with ' + highscores[0].score + ' points.</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (highscores.length == 1) {
|
||||||
|
summaryInfo.innerHTML = 'You are the first to finish. <span class="hideOnNarrowScreen">Invite your friends by sending them the link.</span>'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
rewriteGoogleLink: function () {
|
rewriteGoogleLink: function () {
|
||||||
@@ -751,7 +1015,7 @@
|
|||||||
}, 1);
|
}, 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
startCountdown: function (timeout) {
|
startCountdown: function (timeout, timedOutHandler) {
|
||||||
if (Game.countdownHandler) {
|
if (Game.countdownHandler) {
|
||||||
clearInterval(Game.countdownHandler);
|
clearInterval(Game.countdownHandler);
|
||||||
}
|
}
|
||||||
@@ -773,7 +1037,12 @@
|
|||||||
Game.setCountdownTime(timeLeft);
|
Game.setCountdownTime(timeLeft);
|
||||||
|
|
||||||
if (timeLeft <= 0) {
|
if (timeLeft <= 0) {
|
||||||
document.getElementById('panoCover').style.visibility = 'visible';
|
if (typeof timedOutHandler === 'function') {
|
||||||
|
timedOutHandler();
|
||||||
|
} else {
|
||||||
|
document.getElementById('panoCover').style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
|
||||||
clearInterval(Game.countdownHandler);
|
clearInterval(Game.countdownHandler);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@@ -876,6 +1145,12 @@
|
|||||||
document.getElementById("compass").style.transform = "translateY(-50%) rotate(" + heading + "deg)";
|
document.getElementById("compass").style.transform = "translateY(-50%) rotate(" + heading + "deg)";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (roomId !== null) {
|
||||||
|
Game.type = GameType.MULTI;
|
||||||
|
} else if (challengeToken !== null) {
|
||||||
|
Game.type = GameType.CHALLENGE;
|
||||||
|
}
|
||||||
|
|
||||||
if (COOKIES_CONSENT) {
|
if (COOKIES_CONSENT) {
|
||||||
Game.prepare();
|
Game.prepare();
|
||||||
}
|
}
|
||||||
@@ -965,4 +1240,8 @@
|
|||||||
document.getElementById('compassContainer').onclick = function () {
|
document.getElementById('compassContainer').onclick = function () {
|
||||||
Game.panorama.setPov({ heading: 0, pitch: Game.panorama.getPov().pitch });
|
Game.panorama.setPov({ heading: 0, pitch: Game.panorama.getPov().pitch });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.getElementById('closeHighscoresButton').onclick = function () {
|
||||||
|
MapGuesser.hideModal();
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
MapEditor.metadata.name = form.elements.name.value;
|
MapEditor.metadata.name = form.elements.name.value;
|
||||||
MapEditor.metadata.description = form.elements.description.value;
|
MapEditor.metadata.description = form.elements.description.value;
|
||||||
|
MapEditor.metadata.unlisted = form.elements.unlisted.checked;
|
||||||
|
|
||||||
document.getElementById('mapName').innerHTML = form.elements.name.value ? form.elements.name.value : '[unnamed map]';
|
document.getElementById('mapName').innerHTML = form.elements.name.value ? form.elements.name.value : '[unnamed map]';
|
||||||
|
|
||||||
@@ -254,6 +255,9 @@
|
|||||||
if (MapEditor.metadata.description !== null) {
|
if (MapEditor.metadata.description !== null) {
|
||||||
data.append('description', MapEditor.metadata.description);
|
data.append('description', MapEditor.metadata.description);
|
||||||
}
|
}
|
||||||
|
if (MapEditor.metadata.unlisted !== null) {
|
||||||
|
data.append('unlisted', MapEditor.metadata.unlisted);
|
||||||
|
}
|
||||||
|
|
||||||
for (var placeId in MapEditor.added) {
|
for (var placeId in MapEditor.added) {
|
||||||
if (!MapEditor.added.hasOwnProperty(placeId)) {
|
if (!MapEditor.added.hasOwnProperty(placeId)) {
|
||||||
|
|||||||
@@ -65,6 +65,32 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var Util = {
|
||||||
|
printTimeForHuman: function (time) {
|
||||||
|
const minutes = Math.floor(time / 60);
|
||||||
|
const seconds = time % 60;
|
||||||
|
var time_str = '';
|
||||||
|
|
||||||
|
if (minutes == 1) {
|
||||||
|
time_str += '1 minute';
|
||||||
|
} else if (minutes > 1) {
|
||||||
|
time_str += minutes + ' minutes';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minutes > 0 && seconds > 0) {
|
||||||
|
time_str += ' and ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seconds == 1) {
|
||||||
|
time_str += '1 second';
|
||||||
|
} else if (seconds > 1) {
|
||||||
|
time_str += seconds + ' seconds';
|
||||||
|
}
|
||||||
|
|
||||||
|
return time_str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Maps.addStaticMaps();
|
Maps.addStaticMaps();
|
||||||
|
|
||||||
Maps.initializeDescriptionDivs();
|
Maps.initializeDescriptionDivs();
|
||||||
@@ -85,10 +111,42 @@
|
|||||||
window.location.href = '/multiGame/' + this.elements.roomId.value;
|
window.location.href = '/multiGame/' + this.elements.roomId.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
document.getElementById('challengeForm').onsubmit = function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var url = '/challenge/create.json';
|
||||||
|
var formData = new FormData(this);
|
||||||
|
|
||||||
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
|
MapGuesser.httpRequest('POST', url, function() {
|
||||||
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
|
if (this.response.error) {
|
||||||
|
Game.handleErrorResponse(this.response.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = '/challenge/' + this.response.challengeToken;
|
||||||
|
|
||||||
|
}, formData);
|
||||||
|
};
|
||||||
|
|
||||||
document.getElementById('multiButton').onclick = function () {
|
document.getElementById('multiButton').onclick = function () {
|
||||||
MapGuesser.showModal('multi');
|
MapGuesser.showModal('multi');
|
||||||
document.getElementById('createNewRoomButton').href = '/multiGame/new/' + this.dataset.mapId;
|
document.getElementById('createNewRoomButton').href = '/multiGame/new/' + this.dataset.mapId;
|
||||||
document.getElementById('multiForm').elements.roomId.select();
|
document.getElementById('multiForm').elements.roomId.select();
|
||||||
|
document.getElementById('playMode').style.visibility = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.getElementById('challengeButton')) {
|
||||||
|
document.getElementById('challengeButton').onclick = function () {
|
||||||
|
MapGuesser.showModal('challenge');
|
||||||
|
document.getElementById('createNewChallengeButton').href = '/challenge/new/' + this.dataset.mapId;
|
||||||
|
document.getElementById('playMode').style.visibility = 'hidden';
|
||||||
|
|
||||||
|
var timeLimit = document.getElementById('timeLimit').value;
|
||||||
|
document.getElementById('timeLimitLabel').innerText = 'Time limit of ' + Util.printTimeForHuman(timeLimit);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('closePlayModeButton').onclick = function () {
|
document.getElementById('closePlayModeButton').onclick = function () {
|
||||||
@@ -99,6 +157,10 @@
|
|||||||
MapGuesser.hideModal();
|
MapGuesser.hideModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
document.getElementById('closeChallengeButton').onclick = function () {
|
||||||
|
MapGuesser.hideModal();
|
||||||
|
}
|
||||||
|
|
||||||
var buttons = document.getElementById('mapContainer').getElementsByClassName('playButton');
|
var buttons = document.getElementById('mapContainer').getElementsByClassName('playButton');
|
||||||
for (var i = 0; i < buttons.length; i++) {
|
for (var i = 0; i < buttons.length; i++) {
|
||||||
var button = buttons[i];
|
var button = buttons[i];
|
||||||
@@ -107,6 +169,13 @@
|
|||||||
MapGuesser.showModal('playMode');
|
MapGuesser.showModal('playMode');
|
||||||
document.getElementById('singleButton').href = '/game/' + this.dataset.mapId;
|
document.getElementById('singleButton').href = '/game/' + this.dataset.mapId;
|
||||||
document.getElementById('multiButton').dataset.mapId = this.dataset.mapId;
|
document.getElementById('multiButton').dataset.mapId = this.dataset.mapId;
|
||||||
|
document.getElementById('challengeMapId').value = this.dataset.mapId;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.getElementById('timeLimit').oninput = function () {
|
||||||
|
var timeLimit = document.getElementById('timeLimit').value;
|
||||||
|
document.getElementById('timeLimitLabel').innerText = 'Time limit of ' + Util.printTimeForHuman(timeLimit);
|
||||||
|
document.getElementById('timerEnabled').checked = true;
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
@@ -2,20 +2,29 @@
|
|||||||
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Faker\Factory;
|
use Faker\Factory;
|
||||||
|
use MapGuesser\Interfaces\Authorization\ISecured;
|
||||||
use MapGuesser\Interfaces\Request\IRequest;
|
use MapGuesser\Interfaces\Request\IRequest;
|
||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
use MapGuesser\Response\JsonContent;
|
use MapGuesser\Response\JsonContent;
|
||||||
use MapGuesser\Interfaces\Response\IContent;
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
use MapGuesser\Interfaces\Response\IRedirect;
|
use MapGuesser\Interfaces\Response\IRedirect;
|
||||||
use MapGuesser\Multi\MultiConnector;
|
use MapGuesser\Multi\MultiConnector;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
use MapGuesser\PersistentData\Model\MultiRoom;
|
use MapGuesser\PersistentData\Model\MultiRoom;
|
||||||
|
use MapGuesser\PersistentData\Model\PlaceInChallenge;
|
||||||
|
use MapGuesser\PersistentData\Model\UserInChallenge;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
use MapGuesser\Repository\ChallengeRepository;
|
||||||
use MapGuesser\Repository\MapRepository;
|
use MapGuesser\Repository\MapRepository;
|
||||||
use MapGuesser\Repository\MultiRoomRepository;
|
use MapGuesser\Repository\MultiRoomRepository;
|
||||||
|
use MapGuesser\Repository\PlaceRepository;
|
||||||
|
use MapGuesser\Repository\UserInChallengeRepository;
|
||||||
use MapGuesser\Response\Redirect;
|
use MapGuesser\Response\Redirect;
|
||||||
|
|
||||||
class GameController
|
class GameController implements ISecured
|
||||||
{
|
{
|
||||||
|
const NUMBER_OF_ROUNDS = 5;
|
||||||
|
|
||||||
private IRequest $request;
|
private IRequest $request;
|
||||||
|
|
||||||
private PersistentDataManager $pdm;
|
private PersistentDataManager $pdm;
|
||||||
@@ -26,6 +35,12 @@ class GameController
|
|||||||
|
|
||||||
private MapRepository $mapRepository;
|
private MapRepository $mapRepository;
|
||||||
|
|
||||||
|
private PlaceRepository $placeRepository;
|
||||||
|
|
||||||
|
private ChallengeRepository $challengeRepository;
|
||||||
|
|
||||||
|
private UserInChallengeRepository $userInChallengeRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
@@ -33,6 +48,14 @@ class GameController
|
|||||||
$this->multiConnector = new MultiConnector();
|
$this->multiConnector = new MultiConnector();
|
||||||
$this->multiRoomRepository = new MultiRoomRepository();
|
$this->multiRoomRepository = new MultiRoomRepository();
|
||||||
$this->mapRepository = new MapRepository();
|
$this->mapRepository = new MapRepository();
|
||||||
|
$this->placeRepository = new PlaceRepository();
|
||||||
|
$this->challengeRepository = new ChallengeRepository();
|
||||||
|
$this->userInChallengeRepository = new UserInChallengeRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return !empty($_ENV['ENABLE_GAME_FOR_GUESTS']) || $this->request->user() !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getGame(): IContent
|
public function getGame(): IContent
|
||||||
@@ -79,6 +102,75 @@ class GameController
|
|||||||
return new HtmlContent('game', ['roomId' => $roomId]);
|
return new HtmlContent('game', ['roomId' => $roomId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getChallenge(): IContent
|
||||||
|
{
|
||||||
|
$challengeToken = $this->request->query('challengeToken');
|
||||||
|
|
||||||
|
return new HtmlContent('game', ['challengeToken' => $challengeToken]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createNewChallenge(): IContent
|
||||||
|
{
|
||||||
|
// create Challenge
|
||||||
|
do {
|
||||||
|
// initiliaze or if a challenge with the same token already exists
|
||||||
|
$challengeToken = mt_rand();
|
||||||
|
} while ($this->challengeRepository->getByToken($challengeToken));
|
||||||
|
|
||||||
|
$challenge = new Challenge();
|
||||||
|
$challenge->setToken($challengeToken);
|
||||||
|
$challenge->setCreatedDate(new DateTime());
|
||||||
|
|
||||||
|
if ($this->request->post('timerEnabled') !== null && $this->request->post('timeLimit') !== null) {
|
||||||
|
$challenge->setTimeLimit($this->request->post('timeLimit'));
|
||||||
|
}
|
||||||
|
if ($this->request->post('timeLimitType') !== null) {
|
||||||
|
$challenge->setTimeLimitType($this->request->post('timeLimitType'));
|
||||||
|
}
|
||||||
|
if ($this->request->post('noMove') !== null) {
|
||||||
|
$challenge->setNoMove(true);
|
||||||
|
}
|
||||||
|
if ($this->request->post('noPan') !== null) {
|
||||||
|
$challenge->setNoPan(true);
|
||||||
|
}
|
||||||
|
if ($this->request->post('noZoom') !== null) {
|
||||||
|
$challenge->setNoZoom(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pdm->saveToDb($challenge);
|
||||||
|
|
||||||
|
// save owner/creator
|
||||||
|
|
||||||
|
$session = $this->request->session();
|
||||||
|
$userId = $session->get('userId');
|
||||||
|
|
||||||
|
$userInChallenge = new UserInChallenge();
|
||||||
|
$userInChallenge->setUserId($userId);
|
||||||
|
$userInChallenge->setChallenge($challenge);
|
||||||
|
$userInChallenge->setTimeLeft($challenge->getTimeLimit());
|
||||||
|
$userInChallenge->setIsOwner(true);
|
||||||
|
|
||||||
|
$this->pdm->saveToDb($userInChallenge);
|
||||||
|
|
||||||
|
// select places
|
||||||
|
|
||||||
|
$mapId = (int) $this->request->post('mapId');
|
||||||
|
// $map = $this->mapRepository->getById($mapId);
|
||||||
|
|
||||||
|
$places = $this->placeRepository->getRandomNPlaces($mapId, static::NUMBER_OF_ROUNDS, $userId);
|
||||||
|
|
||||||
|
$round = 0;
|
||||||
|
foreach ($places as $place) {
|
||||||
|
$placeInChallenge = new PlaceInChallenge();
|
||||||
|
$placeInChallenge->setPlace($place);
|
||||||
|
$placeInChallenge->setChallenge($challenge);
|
||||||
|
$placeInChallenge->setRound($round++);
|
||||||
|
$this->pdm->saveToDb($placeInChallenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonContent(['challengeToken' => dechex($challengeToken)]);
|
||||||
|
}
|
||||||
|
|
||||||
public function prepareGame(): IContent
|
public function prepareGame(): IContent
|
||||||
{
|
{
|
||||||
$mapId = (int) $this->request->query('mapId');
|
$mapId = (int) $this->request->query('mapId');
|
||||||
@@ -115,7 +207,7 @@ class GameController
|
|||||||
|
|
||||||
$room = $this->multiRoomRepository->getByRoomId($roomId);
|
$room = $this->multiRoomRepository->getByRoomId($roomId);
|
||||||
|
|
||||||
if(!isset($room)) {
|
if (!isset($room)) {
|
||||||
return new JsonContent(['error' => 'game_not_found']);
|
return new JsonContent(['error' => 'game_not_found']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +246,42 @@ class GameController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function prepareChallenge(): IContent
|
||||||
|
{
|
||||||
|
$challengeToken_str = $this->request->query('challengeToken');
|
||||||
|
$session = $this->request->session();
|
||||||
|
$userId = $session->get('userId');
|
||||||
|
|
||||||
|
if (!isset($userId))
|
||||||
|
{
|
||||||
|
return new JsonContent(['error' => 'anonymous_user']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$challenge = $this->challengeRepository->getByTokenStr($challengeToken_str);
|
||||||
|
|
||||||
|
if (!isset($challenge))
|
||||||
|
{
|
||||||
|
return new JsonContent(['error' => 'game_not_found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) {
|
||||||
|
// new player is joining
|
||||||
|
$userInChallenge = new UserInChallenge();
|
||||||
|
$userInChallenge->setUserId($userId);
|
||||||
|
$userInChallenge->setChallenge($challenge);
|
||||||
|
$userInChallenge->setTimeLeft($challenge->getTimeLimit());
|
||||||
|
$this->pdm->saveToDb($userInChallenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
$map = $this->mapRepository->getByChallenge($challenge);
|
||||||
|
|
||||||
|
return new JsonContent([
|
||||||
|
'mapId' => $map->getId(),
|
||||||
|
'mapName' => $map->getName(),
|
||||||
|
'bounds' => $map->getBounds()->toArray()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
private function getMultiToken(string $roomId): string
|
private function getMultiToken(string $roomId): string
|
||||||
{
|
{
|
||||||
$session = $this->request->session();
|
$session = $this->request->session();
|
||||||
|
|||||||
@@ -1,18 +1,31 @@
|
|||||||
<?php namespace MapGuesser\Controller;
|
<?php namespace MapGuesser\Controller;
|
||||||
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
use MapGuesser\Interfaces\Authorization\ISecured;
|
||||||
use MapGuesser\Interfaces\Request\IRequest;
|
use MapGuesser\Interfaces\Request\IRequest;
|
||||||
use MapGuesser\Util\Geo\Position;
|
use MapGuesser\Util\Geo\Position;
|
||||||
use MapGuesser\Response\JsonContent;
|
use MapGuesser\Response\JsonContent;
|
||||||
use MapGuesser\Interfaces\Response\IContent;
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
use MapGuesser\Multi\MultiConnector;
|
use MapGuesser\Multi\MultiConnector;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
|
use MapGuesser\PersistentData\Model\Guess;
|
||||||
|
use MapGuesser\PersistentData\Model\Map;
|
||||||
|
use MapGuesser\PersistentData\Model\Place;
|
||||||
|
use MapGuesser\PersistentData\Model\PlaceInChallenge;
|
||||||
|
use MapGuesser\PersistentData\Model\User;
|
||||||
use MapGuesser\PersistentData\Model\UserPlayedPlace;
|
use MapGuesser\PersistentData\Model\UserPlayedPlace;
|
||||||
|
use MapGuesser\Repository\ChallengeRepository;
|
||||||
|
use MapGuesser\Repository\GuessRepository;
|
||||||
|
use MapGuesser\Repository\MapRepository;
|
||||||
use MapGuesser\Repository\MultiRoomRepository;
|
use MapGuesser\Repository\MultiRoomRepository;
|
||||||
|
use MapGuesser\Repository\PlaceInChallengeRepository;
|
||||||
use MapGuesser\Repository\PlaceRepository;
|
use MapGuesser\Repository\PlaceRepository;
|
||||||
|
use MapGuesser\Repository\UserInChallengeRepository;
|
||||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||||
|
use MapGuesser\Repository\UserRepository;
|
||||||
|
|
||||||
class GameFlowController
|
class GameFlowController implements ISecured
|
||||||
{
|
{
|
||||||
const NUMBER_OF_ROUNDS = 5;
|
const NUMBER_OF_ROUNDS = 5;
|
||||||
const MAX_SCORE = 1000;
|
const MAX_SCORE = 1000;
|
||||||
@@ -27,8 +40,20 @@ class GameFlowController
|
|||||||
|
|
||||||
private PlaceRepository $placeRepository;
|
private PlaceRepository $placeRepository;
|
||||||
|
|
||||||
|
private MapRepository $mapRepository;
|
||||||
|
|
||||||
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||||
|
|
||||||
|
private ChallengeRepository $challengeRepository;
|
||||||
|
|
||||||
|
private UserInChallengeRepository $userInChallengeRepository;
|
||||||
|
|
||||||
|
private PlaceInChallengeRepository $placeInChallengeRepository;
|
||||||
|
|
||||||
|
private GuessRepository $guessRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
@@ -36,7 +61,18 @@ class GameFlowController
|
|||||||
$this->multiConnector = new MultiConnector();
|
$this->multiConnector = new MultiConnector();
|
||||||
$this->multiRoomRepository = new MultiRoomRepository();
|
$this->multiRoomRepository = new MultiRoomRepository();
|
||||||
$this->placeRepository = new PlaceRepository();
|
$this->placeRepository = new PlaceRepository();
|
||||||
|
$this->mapRepository = new MapRepository();
|
||||||
|
$this->userRepository = new UserRepository();
|
||||||
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||||
|
$this->challengeRepository = new ChallengeRepository();
|
||||||
|
$this->userInChallengeRepository = new UserInChallengeRepository();
|
||||||
|
$this->placeInChallengeRepository = new PlaceInChallengeRepository();
|
||||||
|
$this->guessRepository = new GuessRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return !empty($_ENV['ENABLE_GAME_FOR_GUESTS']) || $this->request->user() !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initialData(): IContent
|
public function initialData(): IContent
|
||||||
@@ -115,6 +151,110 @@ class GameFlowController
|
|||||||
return new JsonContent(['ok' => true]);
|
return new JsonContent(['ok' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function prepareChallengeResponse(int $userId, Challenge $challenge, int $currentRound, bool $withHistory = false): array
|
||||||
|
{
|
||||||
|
$currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound);
|
||||||
|
|
||||||
|
// if the last round was played ($currentPlace == null) or history is explicitly requested (for initializing)
|
||||||
|
if (!isset($currentPlace) || $withHistory) {
|
||||||
|
|
||||||
|
$withRelations = [User::class, PlaceInChallenge::class, Place::class];
|
||||||
|
foreach ($this->guessRepository->getAllInChallenge($challenge, $withRelations) as $guess) {
|
||||||
|
$round = $guess->getPlaceInChallenge()->getRound();
|
||||||
|
|
||||||
|
if ($guess->getUser()->getId() === $userId) {
|
||||||
|
$response['history'][$round]['position'] =
|
||||||
|
$guess->getPlaceInChallenge()->getPlace()->getPosition()->toArray();
|
||||||
|
$response['history'][$round]['result'] = [
|
||||||
|
'guessPosition' => $guess->getPosition()->toArray(),
|
||||||
|
'distance' => $guess->getDistance(),
|
||||||
|
'score' => $guess->getScore()
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$response['history'][$round]['allResults'][] = [
|
||||||
|
'userName' => $guess->getUser()->getDisplayName(),
|
||||||
|
'guessPosition' => $guess->getPosition()->toArray(),
|
||||||
|
'distance' => $guess->getDistance(),
|
||||||
|
'score' => $guess->getScore()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setting default values for rounds without guesses (because of timeout)
|
||||||
|
for ($i = 0; $i < $currentRound; ++$i) {
|
||||||
|
if (!isset($response['history'][$i]) || !isset($response['history'][$i]['result'])) {
|
||||||
|
$response['history'][$i]['result'] = [
|
||||||
|
'guessPosition' => null,
|
||||||
|
'distance' => null,
|
||||||
|
'score' => 0
|
||||||
|
];
|
||||||
|
|
||||||
|
$response['history'][$i]['position'] =
|
||||||
|
$this->placeRepository->getByRoundInChallenge($challenge, $i)->getPosition()->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$response['history']['length'] = $currentRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($currentPlace)) { // game finished
|
||||||
|
$response['finished'] = true;
|
||||||
|
} else { // continue game
|
||||||
|
$response['place'] = [
|
||||||
|
'panoId' => $currentPlace->getPanoIdCached(),
|
||||||
|
'pov' => $currentPlace->getPov()->toArray()
|
||||||
|
];
|
||||||
|
|
||||||
|
$prevRound = $currentRound - 1;
|
||||||
|
if ($prevRound >= 0) {
|
||||||
|
foreach ($this->guessRepository->getAllInChallengeByRound($prevRound, $challenge, [User::class]) as $guess) {
|
||||||
|
if ($guess->getUser()->getId() != $userId) {
|
||||||
|
$response['allResults'][] = [
|
||||||
|
'userName' => $guess->getUser()->getDisplayName(),
|
||||||
|
'guessPosition' => $guess->getPosition()->toArray(),
|
||||||
|
'distance' => $guess->getDistance(),
|
||||||
|
'score' => $guess->getScore()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$response['restrictions'] = [
|
||||||
|
'timeLimit' => $challenge->getTimeLimit() * 1000,
|
||||||
|
'timeLimitType' => $challenge->getTimeLimitType(),
|
||||||
|
'noMove' => $challenge->getNoMove(),
|
||||||
|
'noPan' => $challenge->getNoPan(),
|
||||||
|
'noZoom' => $challenge->getNoZoom()
|
||||||
|
];
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function challengeInitialData(): IContent
|
||||||
|
{
|
||||||
|
$session = $this->request->session();
|
||||||
|
$userId = $session->get('userId');
|
||||||
|
$challengeToken_str = $this->request->query('challengeToken');
|
||||||
|
$userInChallenge = $this->userInChallengeRepository->getByUserIdAndToken($userId, $challengeToken_str, [Challenge::class]);
|
||||||
|
|
||||||
|
if (!isset($userInChallenge)) {
|
||||||
|
return new JsonContent(['error' => 'game_not_found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$challenge = $userInChallenge->getChallenge();
|
||||||
|
$currentRound = $userInChallenge->getCurrentRound();
|
||||||
|
|
||||||
|
$response = $this->prepareChallengeResponse($userId, $challenge, $currentRound, true);
|
||||||
|
|
||||||
|
if ($challenge->getTimeLimitType() === 'game' && $challenge->getTimeLimit() !== null && $userInChallenge->getCurrentRound() > 0) {
|
||||||
|
$timeLimit = max(10, $userInChallenge->getTimeLeft());
|
||||||
|
$response['restrictions']['timeLimit'] = $timeLimit * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonContent($response);
|
||||||
|
}
|
||||||
|
|
||||||
public function guess(): IContent
|
public function guess(): IContent
|
||||||
{
|
{
|
||||||
$mapId = (int) $this->request->query('mapId');
|
$mapId = (int) $this->request->query('mapId');
|
||||||
@@ -126,7 +266,7 @@ class GameFlowController
|
|||||||
|
|
||||||
$last = $state['rounds'][$state['currentRound']];
|
$last = $state['rounds'][$state['currentRound']];
|
||||||
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
|
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
|
||||||
$result = $this->evalueteGuess($last['position'], $guessPosition, $state['area']);
|
$result = $this->evaluateGuess($last['position'], $guessPosition, $state['area']);
|
||||||
|
|
||||||
$last['guessPosition'] = $guessPosition;
|
$last['guessPosition'] = $guessPosition;
|
||||||
$last['distance'] = $result['distance'];
|
$last['distance'] = $result['distance'];
|
||||||
@@ -150,21 +290,20 @@ class GameFlowController
|
|||||||
|
|
||||||
$session->set('state', $state);
|
$session->set('state', $state);
|
||||||
|
|
||||||
$this->saveVisit($last);
|
$this->saveVisit($last['placeId']);
|
||||||
|
|
||||||
return new JsonContent($response);
|
return new JsonContent($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the selected place for the round in UserPlayedPlace
|
// save the selected place for the round in UserPlayedPlace
|
||||||
private function saveVisit($last): void
|
private function saveVisit($placeId): void
|
||||||
{
|
{
|
||||||
$session = $this->request->session();
|
$session = $this->request->session();
|
||||||
$userId = $session->get('userId');
|
$userId = $session->get('userId');
|
||||||
|
|
||||||
if(isset($userId)) {
|
if (isset($userId)) {
|
||||||
$placeId = $last['placeId'];
|
|
||||||
$userPlayedPlace = $this->userPlayedPlaceRepository->getByUserIdAndPlaceId($userId, $placeId);
|
$userPlayedPlace = $this->userPlayedPlaceRepository->getByUserIdAndPlaceId($userId, $placeId);
|
||||||
if(!$userPlayedPlace) {
|
if (!$userPlayedPlace) {
|
||||||
$userPlayedPlace = new UserPlayedPlace();
|
$userPlayedPlace = new UserPlayedPlace();
|
||||||
$userPlayedPlace->setUserId($userId);
|
$userPlayedPlace->setUserId($userId);
|
||||||
$userPlayedPlace->setPlaceId($placeId);
|
$userPlayedPlace->setPlaceId($placeId);
|
||||||
@@ -190,7 +329,7 @@ class GameFlowController
|
|||||||
|
|
||||||
$last = $state['rounds'][$state['currentRound']];
|
$last = $state['rounds'][$state['currentRound']];
|
||||||
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
|
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
|
||||||
$result = $this->evalueteGuess($last['position'], $guessPosition, $state['area']);
|
$result = $this->evaluateGuess($last['position'], $guessPosition, $state['area']);
|
||||||
|
|
||||||
$responseFromMulti = $this->multiConnector->sendMessage('guess', [
|
$responseFromMulti = $this->multiConnector->sendMessage('guess', [
|
||||||
'roomId' => $roomId,
|
'roomId' => $roomId,
|
||||||
@@ -213,6 +352,70 @@ class GameFlowController
|
|||||||
return new JsonContent($response);
|
return new JsonContent($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function challengeGuess(): IContent
|
||||||
|
{
|
||||||
|
$session = $this->request->session();
|
||||||
|
$userId = $session->get('userId');
|
||||||
|
$challengeToken_str = $this->request->query('challengeToken');
|
||||||
|
$userInChallenge = $this->userInChallengeRepository->getByUserIdAndToken($userId, $challengeToken_str, [Challenge::class]);
|
||||||
|
|
||||||
|
if (!isset($userInChallenge)) {
|
||||||
|
return new JsonContent(['error' => 'game_not_found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$challenge = $userInChallenge->getChallenge();
|
||||||
|
$currentRound = $userInChallenge->getCurrentRound();
|
||||||
|
$currentPlaceInChallenge = $this->placeInChallengeRepository->getByRoundInChallenge($currentRound, $challenge, [Place::class, Map::class]);
|
||||||
|
$currentPlace = $currentPlaceInChallenge->getPlace();
|
||||||
|
$map = $currentPlace->getMap();
|
||||||
|
|
||||||
|
// creating response
|
||||||
|
$nextRound = $currentRound + 1;
|
||||||
|
$response = $this->prepareChallengeResponse($userId, $challenge, $nextRound);
|
||||||
|
$response['position'] = $currentPlace->getPosition()->toArray();
|
||||||
|
|
||||||
|
if ($this->request->post('lat') && $this->request->post('lng')) {
|
||||||
|
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
|
||||||
|
$result = $this->evaluateGuess($currentPlace->getPosition(), $guessPosition, $map->getArea());
|
||||||
|
|
||||||
|
// save guess
|
||||||
|
$guess = new Guess();
|
||||||
|
$guess->setUserId($userId);
|
||||||
|
$guess->setPlaceInChallenge($currentPlaceInChallenge);
|
||||||
|
$guess->setPosition($guessPosition);
|
||||||
|
$guess->setDistance($result['distance']);
|
||||||
|
$guess->setScore($result['score']);
|
||||||
|
$this->pdm->saveToDb($guess);
|
||||||
|
|
||||||
|
$response['result'] = $result;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// user didn't manage to guess in the round in the given timeframe
|
||||||
|
$response['result'] = ['distance' => null, 'score' => 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// save user relevant state of challenge
|
||||||
|
$userInChallenge->setCurrentRound($nextRound);
|
||||||
|
$timeLeft = $this->request->post('timeLeft');
|
||||||
|
if (isset($timeLeft)) {
|
||||||
|
$userInChallenge->setTimeLeft(intval($timeLeft));
|
||||||
|
}
|
||||||
|
$this->pdm->saveToDb($userInChallenge);
|
||||||
|
|
||||||
|
if ($challenge->getTimeLimitType() === 'game' && isset($timeLeft)) {
|
||||||
|
$timeLimit = max(10, intval($timeLeft));
|
||||||
|
$response['restrictions']['timeLimit'] = $timeLimit * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($response['history'][$currentRound]['allResults'])) {
|
||||||
|
$response['allResults'] = $response['history'][$currentRound]['allResults'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->saveVisit($currentPlace->getId());
|
||||||
|
|
||||||
|
return new JsonContent($response);
|
||||||
|
}
|
||||||
|
|
||||||
public function multiNextRound(): IContent
|
public function multiNextRound(): IContent
|
||||||
{
|
{
|
||||||
$roomId = $this->request->query('roomId');
|
$roomId = $this->request->query('roomId');
|
||||||
@@ -242,7 +445,7 @@ class GameFlowController
|
|||||||
return new JsonContent(['ok' => true]);
|
return new JsonContent(['ok' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function evalueteGuess(Position $realPosition, Position $guessPosition, float $area)
|
private function evaluateGuess(Position $realPosition, Position $guessPosition, float $area)
|
||||||
{
|
{
|
||||||
$distance = $this->calculateDistance($realPosition, $guessPosition);
|
$distance = $this->calculateDistance($realPosition, $guessPosition);
|
||||||
$score = $this->calculateScore($distance, $area);
|
$score = $this->calculateScore($distance, $area);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use MapGuesser\Repository\UserRepository;
|
|||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
use MapGuesser\Response\JsonContent;
|
use MapGuesser\Response\JsonContent;
|
||||||
use MapGuesser\Response\Redirect;
|
use MapGuesser\Response\Redirect;
|
||||||
|
use MapGuesser\Util\CaptchaValidator;
|
||||||
use MapGuesser\Util\JwtParser;
|
use MapGuesser\Util\JwtParser;
|
||||||
|
|
||||||
class LoginController
|
class LoginController
|
||||||
@@ -285,6 +286,18 @@ class LoginController
|
|||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($_ENV['RECAPTCHA_SITEKEY'])) {
|
||||||
|
if (!$this->request->post('g-recaptcha-response')) {
|
||||||
|
return new JsonContent(['error' => ['errorText' => 'Please check "I\'m not a robot" in the reCAPTCHA box!']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$captchaValidator = new CaptchaValidator();
|
||||||
|
$captchaResponse = $captchaValidator->validate($this->request->post('g-recaptcha-response'));
|
||||||
|
if (!$captchaResponse['success']) {
|
||||||
|
return new JsonContent(['error' => ['errorText' => 'reCAPTCHA challenge failed. Please try again!']]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (filter_var($this->request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
|
if (filter_var($this->request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
|
||||||
return new JsonContent(['error' => ['errorText' => 'The given email address is not valid.']]);
|
return new JsonContent(['error' => ['errorText' => 'The given email address is not valid.']]);
|
||||||
}
|
}
|
||||||
@@ -455,6 +468,18 @@ class LoginController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($_ENV['RECAPTCHA_SITEKEY'])) {
|
||||||
|
if (!$this->request->post('g-recaptcha-response')) {
|
||||||
|
return new JsonContent(['error' => ['errorText' => 'Please check "I\'m not a robot" in the reCAPTCHA box!']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$captchaValidator = new CaptchaValidator();
|
||||||
|
$captchaResponse = $captchaValidator->validate($this->request->post('g-recaptcha-response'));
|
||||||
|
if (!$captchaResponse['success']) {
|
||||||
|
return new JsonContent(['error' => ['errorText' => 'reCAPTCHA challenge failed. Please try again!']]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$user = $this->userRepository->getByEmail($this->request->post('email'));
|
$user = $this->userRepository->getByEmail($this->request->post('email'));
|
||||||
|
|
||||||
if ($user === null) {
|
if ($user === null) {
|
||||||
|
|||||||
@@ -5,11 +5,17 @@ use MapGuesser\Interfaces\Authentication\IUser;
|
|||||||
use MapGuesser\Interfaces\Authorization\ISecured;
|
use MapGuesser\Interfaces\Authorization\ISecured;
|
||||||
use MapGuesser\Interfaces\Request\IRequest;
|
use MapGuesser\Interfaces\Request\IRequest;
|
||||||
use MapGuesser\Interfaces\Response\IContent;
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
use MapGuesser\PersistentData\Model\Map;
|
use MapGuesser\PersistentData\Model\Map;
|
||||||
use MapGuesser\PersistentData\Model\Place;
|
use MapGuesser\PersistentData\Model\Place;
|
||||||
|
use MapGuesser\PersistentData\Model\PlaceInChallenge;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
use MapGuesser\Repository\ChallengeRepository;
|
||||||
|
use MapGuesser\Repository\GuessRepository;
|
||||||
use MapGuesser\Repository\MapRepository;
|
use MapGuesser\Repository\MapRepository;
|
||||||
|
use MapGuesser\Repository\PlaceInChallengeRepository;
|
||||||
use MapGuesser\Repository\PlaceRepository;
|
use MapGuesser\Repository\PlaceRepository;
|
||||||
|
use MapGuesser\Repository\UserInChallengeRepository;
|
||||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
use MapGuesser\Response\JsonContent;
|
use MapGuesser\Response\JsonContent;
|
||||||
@@ -30,6 +36,14 @@ class MapAdminController implements ISecured
|
|||||||
|
|
||||||
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||||
|
|
||||||
|
private ChallengeRepository $challengeRepository;
|
||||||
|
|
||||||
|
private GuessRepository $guessRepository;
|
||||||
|
|
||||||
|
private PlaceInChallengeRepository $placeInChallengeRepository;
|
||||||
|
|
||||||
|
private UserInChallengeRepository $userInChallengeRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
@@ -37,6 +51,10 @@ class MapAdminController implements ISecured
|
|||||||
$this->mapRepository = new MapRepository();
|
$this->mapRepository = new MapRepository();
|
||||||
$this->placeRepository = new PlaceRepository();
|
$this->placeRepository = new PlaceRepository();
|
||||||
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||||
|
$this->challengeRepository = new ChallengeRepository();
|
||||||
|
$this->guessRepository = new GuessRepository();
|
||||||
|
$this->placeInChallengeRepository = new PlaceInChallengeRepository();
|
||||||
|
$this->userInChallengeRepository = new UserInChallengeRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
@@ -63,6 +81,7 @@ class MapAdminController implements ISecured
|
|||||||
'mapId' => $mapId,
|
'mapId' => $mapId,
|
||||||
'mapName' => $map->getName(),
|
'mapName' => $map->getName(),
|
||||||
'mapDescription' => str_replace('<br>', "\n", $map->getDescription()),
|
'mapDescription' => str_replace('<br>', "\n", $map->getDescription()),
|
||||||
|
'mapUnlisted' => $map->getUnlisted(),
|
||||||
'bounds' => $map->getBounds()->toArray(),
|
'bounds' => $map->getBounds()->toArray(),
|
||||||
'places' => &$places
|
'places' => &$places
|
||||||
]);
|
]);
|
||||||
@@ -157,6 +176,9 @@ class MapAdminController implements ISecured
|
|||||||
if (isset($_POST['description'])) {
|
if (isset($_POST['description'])) {
|
||||||
$map->setDescription(str_replace(["\n", "\r\n"], '<br>', $_POST['description']));
|
$map->setDescription(str_replace(["\n", "\r\n"], '<br>', $_POST['description']));
|
||||||
}
|
}
|
||||||
|
if (isset($_POST['unlisted'])) {
|
||||||
|
$map->setUnlisted((bool)$_POST['unlisted']);
|
||||||
|
}
|
||||||
|
|
||||||
$this->pdm->saveToDb($map);
|
$this->pdm->saveToDb($map);
|
||||||
|
|
||||||
@@ -188,6 +210,10 @@ class MapAdminController implements ISecured
|
|||||||
$this->pdm->deleteFromDb($userPlayedPlace);
|
$this->pdm->deleteFromDb($userPlayedPlace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($this->challengeRepository->getAllByPlace($place) as $challenge) {
|
||||||
|
$this->deleteChallenge($challenge);
|
||||||
|
}
|
||||||
|
|
||||||
$this->pdm->deleteFromDb($place);
|
$this->pdm->deleteFromDb($place);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +224,23 @@ class MapAdminController implements ISecured
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function deleteChallenge(Challenge $challenge): void
|
||||||
|
{
|
||||||
|
foreach ($this->userInChallengeRepository->getAllByChallenge($challenge) as $userInChallenge) {
|
||||||
|
$this->pdm->deleteFromDb($userInChallenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->guessRepository->getAllInChallenge($challenge, [PlaceInChallenge::class]) as $guess) {
|
||||||
|
$this->pdm->deleteFromDb($guess);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->placeInChallengeRepository->getAllByChallenge($challenge) as $placeInChallenge) {
|
||||||
|
$this->pdm->deleteFromDb($placeInChallenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pdm->deleteFromDb($challenge);
|
||||||
|
}
|
||||||
|
|
||||||
private function calculateMapBounds(Map $map): Bounds
|
private function calculateMapBounds(Map $map): Bounds
|
||||||
{
|
{
|
||||||
$bounds = new Bounds();
|
$bounds = new Bounds();
|
||||||
|
|||||||
@@ -30,12 +30,19 @@ class MapsController
|
|||||||
['maps', 'bound_north_lat'],
|
['maps', 'bound_north_lat'],
|
||||||
['maps', 'bound_east_lng'],
|
['maps', 'bound_east_lng'],
|
||||||
['maps', 'area'],
|
['maps', 'area'],
|
||||||
|
['maps', 'unlisted'],
|
||||||
new RawExpression('COUNT(places.id) AS num_places')
|
new RawExpression('COUNT(places.id) AS num_places')
|
||||||
]);
|
]);
|
||||||
$select->leftJoin('places', ['places', 'map_id'], '=', ['maps', 'id']);
|
$select->leftJoin('places', ['places', 'map_id'], '=', ['maps', 'id']);
|
||||||
$select->groupBy(['maps', 'id']);
|
$select->groupBy(['maps', 'id']);
|
||||||
$select->orderBy('name');
|
$select->orderBy('name');
|
||||||
|
|
||||||
|
$user = $this->request->user();
|
||||||
|
$isAdmin = $user !== null && $user->hasPermission(IUser::PERMISSION_ADMIN);
|
||||||
|
if (!$isAdmin) {
|
||||||
|
$select->where(['maps', 'unlisted'], '=', false);
|
||||||
|
}
|
||||||
|
|
||||||
$result = $select->execute();
|
$result = $select->execute();
|
||||||
|
|
||||||
$maps = [];
|
$maps = [];
|
||||||
@@ -45,10 +52,10 @@ class MapsController
|
|||||||
$maps[] = $map;
|
$maps[] = $map;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->request->user();
|
|
||||||
return new HtmlContent('maps', [
|
return new HtmlContent('maps', [
|
||||||
'maps' => $maps,
|
'maps' => $maps,
|
||||||
'isAdmin' => $user !== null && $user->hasPermission(IUser::PERMISSION_ADMIN)
|
'isLoggedIn' => $user !== null,
|
||||||
|
'isAdmin' => $isAdmin
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ use MapGuesser\Interfaces\Response\IRedirect;
|
|||||||
use MapGuesser\OAuth\GoogleOAuth;
|
use MapGuesser\OAuth\GoogleOAuth;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
use MapGuesser\PersistentData\Model\User;
|
use MapGuesser\PersistentData\Model\User;
|
||||||
|
use MapGuesser\PersistentData\Model\UserInChallenge;
|
||||||
|
use MapGuesser\Repository\GuessRepository;
|
||||||
use MapGuesser\Repository\UserConfirmationRepository;
|
use MapGuesser\Repository\UserConfirmationRepository;
|
||||||
|
use MapGuesser\Repository\UserInChallengeRepository;
|
||||||
use MapGuesser\Repository\UserPasswordResetterRepository;
|
use MapGuesser\Repository\UserPasswordResetterRepository;
|
||||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
@@ -29,6 +32,10 @@ class UserController implements ISecured
|
|||||||
|
|
||||||
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||||
|
|
||||||
|
private UserInChallengeRepository $userInChallengeRepository;
|
||||||
|
|
||||||
|
private GuessRepository $guessRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
@@ -36,6 +43,8 @@ class UserController implements ISecured
|
|||||||
$this->userConfirmationRepository = new UserConfirmationRepository();
|
$this->userConfirmationRepository = new UserConfirmationRepository();
|
||||||
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
||||||
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||||
|
$this->userInChallengeRepository = new UserInChallengeRepository();
|
||||||
|
$this->guessRepository = new GuessRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
@@ -209,6 +218,14 @@ class UserController implements ISecured
|
|||||||
$this->pdm->deleteFromDb($userPlayedPlace);
|
$this->pdm->deleteFromDb($userPlayedPlace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($this->userInChallengeRepository->getAllByUser($user) as $userInChallenge) {
|
||||||
|
$this->pdm->deleteFromDb($userInChallenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->guessRepository->getAllByUser($user) as $guess) {
|
||||||
|
$this->pdm->deleteFromDb($guess);
|
||||||
|
}
|
||||||
|
|
||||||
$this->pdm->deleteFromDb($user);
|
$this->pdm->deleteFromDb($user);
|
||||||
|
|
||||||
\Container::$dbConnection->commit();
|
\Container::$dbConnection->commit();
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ class Select
|
|||||||
$queryString .= ' LIMIT ' . $this->limit[1] . ', ' . $this->limit[0];
|
$queryString .= ' LIMIT ' . $this->limit[1] . ', ' . $this->limit[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->isDerivedTable()) {
|
if ($this->isDerivedTable()) {
|
||||||
$queryString = '(' . $queryString . ') AS ' . $this->tableAliases[Select::DERIVED_TABLE_KEY];
|
$queryString = '(' . $queryString . ') AS ' . $this->tableAliases[Select::DERIVED_TABLE_KEY];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +276,7 @@ class Select
|
|||||||
return [(string) $table, $params];
|
return [(string) $table, $params];
|
||||||
}
|
}
|
||||||
|
|
||||||
if($table instanceof Select)
|
if ($table instanceof Select)
|
||||||
{
|
{
|
||||||
return $table->generateQuery();
|
return $table->generateQuery();
|
||||||
}
|
}
|
||||||
@@ -332,7 +332,7 @@ class Select
|
|||||||
$joinQueries = [];
|
$joinQueries = [];
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
foreach($this->joins as $join) {
|
foreach ($this->joins as $join) {
|
||||||
list($joinQueryFragment, $paramsFragment) = $this->generateTable($join[1], true);
|
list($joinQueryFragment, $paramsFragment) = $this->generateTable($join[1], true);
|
||||||
$joinQueries[] = $join[0] . ' JOIN ' . $joinQueryFragment . ' ON ' . $this->generateColumn($join[2]) . ' ' . $join[3] . ' ' . $this->generateColumn($join[4]);
|
$joinQueries[] = $join[0] . ' JOIN ' . $joinQueryFragment . ' ON ' . $this->generateColumn($join[2]) . ' ' . $join[3] . ' ' . $this->generateColumn($join[4]);
|
||||||
$params = array_merge($params, $paramsFragment);
|
$params = array_merge($params, $paramsFragment);
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<?php namespace MapGuesser\PersistentData\Model;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class Challenge extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'challenges';
|
||||||
|
|
||||||
|
protected static array $fields = ['token', 'time_limit', 'time_limit_type', 'no_move', 'no_pan', 'no_zoom', 'created'];
|
||||||
|
|
||||||
|
protected static array $relations = [];
|
||||||
|
|
||||||
|
private int $token;
|
||||||
|
|
||||||
|
private ?int $timeLimit = null;
|
||||||
|
|
||||||
|
private static array $timeLimitTypes = ['game', 'round'];
|
||||||
|
|
||||||
|
private string $timeLimitType = 'game';
|
||||||
|
|
||||||
|
private bool $noMove = false;
|
||||||
|
|
||||||
|
private bool $noPan = false;
|
||||||
|
|
||||||
|
private bool $noZoom = false;
|
||||||
|
|
||||||
|
private DateTime $created;
|
||||||
|
|
||||||
|
public function setToken(int $token): void
|
||||||
|
{
|
||||||
|
$this->token = $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTimeLimit(?int $timeLimit): void
|
||||||
|
{
|
||||||
|
if (isset($timeLimit)) {
|
||||||
|
$this->timeLimit = $timeLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTimeLimitType(string $timeLimitType): void
|
||||||
|
{
|
||||||
|
if (in_array($timeLimitType, self::$timeLimitTypes)) {
|
||||||
|
$this->timeLimitType = $timeLimitType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setNoMove(bool $noMove): void
|
||||||
|
{
|
||||||
|
$this->noMove = $noMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setNoPan(bool $noPan): void
|
||||||
|
{
|
||||||
|
$this->noPan = $noPan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setNoZoom(bool $noZoom): void
|
||||||
|
{
|
||||||
|
$this->noZoom = $noZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreatedDate(DateTime $created): void
|
||||||
|
{
|
||||||
|
$this->created = $created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreated(string $created): void
|
||||||
|
{
|
||||||
|
$this->created = new DateTime($created);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getToken(): int
|
||||||
|
{
|
||||||
|
return $this->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTimeLimit(): ?int
|
||||||
|
{
|
||||||
|
return $this->timeLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTimeLimitType(): string
|
||||||
|
{
|
||||||
|
return $this->timeLimitType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNoMove(): bool
|
||||||
|
{
|
||||||
|
return $this->noMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNoPan(): bool
|
||||||
|
{
|
||||||
|
return $this->noPan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNoZoom(): bool
|
||||||
|
{
|
||||||
|
return $this->noZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedDate(): DateTime
|
||||||
|
{
|
||||||
|
return $this->created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreated(): string
|
||||||
|
{
|
||||||
|
return $this->created->format('Y-m-d H:i:s');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<?php namespace MapGuesser\PersistentData\Model;
|
||||||
|
|
||||||
|
use MapGuesser\Util\Geo\Position;
|
||||||
|
|
||||||
|
class Guess extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'guesses';
|
||||||
|
|
||||||
|
protected static array $fields = ['user_id', 'place_in_challenge_id', 'lat', 'lng', 'score', 'distance', 'time_spent'];
|
||||||
|
|
||||||
|
protected static array $relations = ['user' => User::class, 'place_in_challenge' => PlaceInChallenge::class];
|
||||||
|
|
||||||
|
private ?User $user = null;
|
||||||
|
|
||||||
|
private ?int $userId = null;
|
||||||
|
|
||||||
|
private ?PlaceInChallenge $placeInChallenge = null;
|
||||||
|
|
||||||
|
private ?int $placeInChallengeId = null;
|
||||||
|
|
||||||
|
private Position $position;
|
||||||
|
|
||||||
|
private int $score = 0;
|
||||||
|
|
||||||
|
private int $distance = 0;
|
||||||
|
|
||||||
|
private int $timeSpent = 0;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->position = new Position(0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(User $user): void
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUserId(int $userId): void
|
||||||
|
{
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlaceInChallenge(PlaceInChallenge $placeInChallenge): void
|
||||||
|
{
|
||||||
|
$this->placeInChallenge = $placeInChallenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlaceInChallengeId(int $placeInChallengeId): void
|
||||||
|
{
|
||||||
|
$this->placeInChallengeId = $placeInChallengeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPosition(Position $position): void
|
||||||
|
{
|
||||||
|
$this->position = $position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLat(float $lat): void
|
||||||
|
{
|
||||||
|
$this->position->setLat($lat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLng(float $lng): void
|
||||||
|
{
|
||||||
|
$this->position->setLng($lng);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setScore(int $score): void
|
||||||
|
{
|
||||||
|
$this->score = $score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDistance(int $distance): void
|
||||||
|
{
|
||||||
|
$this->distance = $distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTimeSpent(int $timeSpent): void
|
||||||
|
{
|
||||||
|
$this->timeSpent = $timeSpent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser(): ?User
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserId(): ?int
|
||||||
|
{
|
||||||
|
return $this->userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceInChallenge(): ?PlaceInChallenge
|
||||||
|
{
|
||||||
|
return $this->placeInChallenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceInChallengeId(): ?int
|
||||||
|
{
|
||||||
|
return $this->placeInChallengeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPosition(): Position
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLat(): float
|
||||||
|
{
|
||||||
|
return $this->position->getLat();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLng(): float
|
||||||
|
{
|
||||||
|
return $this->position->getLng();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScore(): int
|
||||||
|
{
|
||||||
|
return $this->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDistance(): int
|
||||||
|
{
|
||||||
|
return $this->distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTimeSpent(): ?int
|
||||||
|
{
|
||||||
|
return $this->timeSpent;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ class Map extends Model
|
|||||||
{
|
{
|
||||||
protected static string $table = 'maps';
|
protected static string $table = 'maps';
|
||||||
|
|
||||||
protected static array $fields = ['name', 'description', 'bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng', 'area'];
|
protected static array $fields = ['name', 'description', 'bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng', 'area', 'unlisted'];
|
||||||
|
|
||||||
private string $name = '';
|
private string $name = '';
|
||||||
|
|
||||||
@@ -16,6 +16,8 @@ class Map extends Model
|
|||||||
|
|
||||||
private float $area = 0.0;
|
private float $area = 0.0;
|
||||||
|
|
||||||
|
private bool $unlisted = false;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->bounds = Bounds::createDirectly(-90.0, -180.0, 90.0, 180.0);
|
$this->bounds = Bounds::createDirectly(-90.0, -180.0, 90.0, 180.0);
|
||||||
@@ -61,6 +63,11 @@ class Map extends Model
|
|||||||
$this->area = $area;
|
$this->area = $area;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUnlisted(bool $unlisted): void
|
||||||
|
{
|
||||||
|
$this->unlisted = $unlisted;
|
||||||
|
}
|
||||||
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
@@ -100,4 +107,9 @@ class Map extends Model
|
|||||||
{
|
{
|
||||||
return $this->area;
|
return $this->area;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUnlisted(): bool
|
||||||
|
{
|
||||||
|
return $this->unlisted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<?php namespace MapGuesser\PersistentData\Model;
|
||||||
|
|
||||||
|
class PlaceInChallenge extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'place_in_challenge';
|
||||||
|
|
||||||
|
protected static array $fields = ['place_id', 'challenge_id', 'round'];
|
||||||
|
|
||||||
|
protected static array $relations = ['place' => Place::class, 'challenge' => Challenge::class];
|
||||||
|
|
||||||
|
private ?Place $place = null;
|
||||||
|
|
||||||
|
private ?int $placeId = null;
|
||||||
|
|
||||||
|
private ?Challenge $challenge = null;
|
||||||
|
|
||||||
|
private ?int $challengeId = null;
|
||||||
|
|
||||||
|
private int $round;
|
||||||
|
|
||||||
|
public function setPlace(Place $place): void
|
||||||
|
{
|
||||||
|
$this->place = $place;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlaceId(int $placeId): void
|
||||||
|
{
|
||||||
|
$this->placeId = $placeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setChallenge(Challenge $challenge): void
|
||||||
|
{
|
||||||
|
$this->challenge = $challenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setChallengeId(int $challengeId): void
|
||||||
|
{
|
||||||
|
$this->challengeId = $challengeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRound(int $round): void
|
||||||
|
{
|
||||||
|
$this->round = $round;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlace(): ?Place
|
||||||
|
{
|
||||||
|
return $this->place;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceId(): ?int
|
||||||
|
{
|
||||||
|
return $this->placeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChallenge(): ?Challenge
|
||||||
|
{
|
||||||
|
return $this->challenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChallengeId(): ?int
|
||||||
|
{
|
||||||
|
return $this->challengeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRound(): int
|
||||||
|
{
|
||||||
|
return $this->round;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<?php namespace MapGuesser\PersistentData\Model;
|
||||||
|
|
||||||
|
class UserInChallenge extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'user_in_challenge';
|
||||||
|
|
||||||
|
protected static array $fields = ['user_id', 'challenge_id', 'current_round', 'time_left', 'is_owner'];
|
||||||
|
|
||||||
|
protected static array $relations = ['user' => User::class, 'challenge' => Challenge::class];
|
||||||
|
|
||||||
|
private ?User $user = null;
|
||||||
|
|
||||||
|
private ?int $userId = null;
|
||||||
|
|
||||||
|
private ?Challenge $challenge = null;
|
||||||
|
|
||||||
|
private ?int $challengeId = null;
|
||||||
|
|
||||||
|
private int $currentRound = 0;
|
||||||
|
|
||||||
|
private ?int $timeLeft = null;
|
||||||
|
|
||||||
|
private bool $isOwner = false;
|
||||||
|
|
||||||
|
public function setUser(User $user): void
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUserId(int $userId): void
|
||||||
|
{
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setChallenge(Challenge $challenge): void
|
||||||
|
{
|
||||||
|
$this->challenge = $challenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setChallengeId(int $challengeId): void
|
||||||
|
{
|
||||||
|
$this->challengeId = $challengeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurrentRound(int $currentRound): void
|
||||||
|
{
|
||||||
|
$this->currentRound = $currentRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTimeLeft(?int $timeLeft): void
|
||||||
|
{
|
||||||
|
if (isset($timeLeft)) {
|
||||||
|
$this->timeLeft = max(0, $timeLeft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsOwner(bool $isOwner): void
|
||||||
|
{
|
||||||
|
$this->isOwner = $isOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser(): ?User
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserId(): ?int
|
||||||
|
{
|
||||||
|
return $this->userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChallenge(): ?Challenge
|
||||||
|
{
|
||||||
|
return $this->challenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChallengeId(): ?int
|
||||||
|
{
|
||||||
|
return $this->challengeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrentRound(): int
|
||||||
|
{
|
||||||
|
return $this->currentRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTimeLeft(): ?int
|
||||||
|
{
|
||||||
|
return $this->timeLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsOwner(): bool
|
||||||
|
{
|
||||||
|
return $this->isOwner;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,9 +8,9 @@ use MapGuesser\PersistentData\Model\Model;
|
|||||||
|
|
||||||
class PersistentDataManager
|
class PersistentDataManager
|
||||||
{
|
{
|
||||||
public function selectFromDb(Select $select, string $type, bool $withRelations = false)
|
public function selectFromDb(Select $select, string $type, bool $useRelations = false, array $withRelations = [])
|
||||||
{
|
{
|
||||||
$select = $this->createSelect($select, $type, $withRelations);
|
$select = $this->createSelect($select, $type, $useRelations, $withRelations);
|
||||||
|
|
||||||
$data = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
$data = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -19,50 +19,75 @@ class PersistentDataManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
$model = new $type();
|
$model = new $type();
|
||||||
$this->fillWithData($data, $model);
|
$this->fillWithData($data, $model, $withRelations);
|
||||||
|
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function selectMultipleFromDb(Select $select, string $type, bool $withRelations = false): Generator
|
public function selectMultipleFromDb(Select $select, string $type, bool $useRelations = false, array $withRelations = []): Generator
|
||||||
{
|
{
|
||||||
$select = $this->createSelect($select, $type, $withRelations);
|
$select = $this->createSelect($select, $type, $useRelations, $withRelations);
|
||||||
$result = $select->execute();
|
$result = $select->execute();
|
||||||
|
|
||||||
while ($data = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
while ($data = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
||||||
$model = new $type();
|
$model = new $type();
|
||||||
$this->fillWithData($data, $model);
|
$this->fillWithData($data, $model, $withRelations);
|
||||||
|
|
||||||
yield $model;
|
yield $model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function selectFromDbById($id, string $type, bool $withRelations = false)
|
public function selectFromDbById($id, string $type, bool $useRelations = false)
|
||||||
{
|
{
|
||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->whereId($id);
|
$select->whereId($id);
|
||||||
|
|
||||||
return $this->selectFromDb($select, $type, $withRelations);
|
return $this->selectFromDb($select, $type, $useRelations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fillWithData(array $data, Model $model): void
|
public function fillWithData(array &$data, Model $model, array $withRelations = [], ?string $modelKey = null): void
|
||||||
{
|
{
|
||||||
$relations = $model::getRelations();
|
$relations = $model::getRelations();
|
||||||
$relationData = [];
|
if (count($withRelations)) {
|
||||||
|
$relations = array_intersect($relations, $withRelations);
|
||||||
foreach ($data as $key => $value) {
|
|
||||||
if ($this->extractRelationData($key, $value, $relationData, $relations)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$method = 'set' . str_replace('_', '', ucwords($key, '_'));
|
|
||||||
|
|
||||||
if (method_exists($model, $method)) {
|
|
||||||
$model->$method($value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->setRelations($model, $relationData);
|
while (key($data)) {
|
||||||
|
$key = key($data);
|
||||||
|
$value = current($data);
|
||||||
|
$relation = key($relations);
|
||||||
|
|
||||||
|
if (strpos($key, '__') == false) {
|
||||||
|
$method = 'set' . str_replace('_', '', ucwords($key, '_'));
|
||||||
|
|
||||||
|
if (method_exists($model, $method) && isset($value)) {
|
||||||
|
$model->$method($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
next($data);
|
||||||
|
} else if (isset($modelKey) && substr($key, 0, strlen($modelKey . '__')) === $modelKey . '__') {
|
||||||
|
$key = substr($key, strlen($modelKey) + 2);
|
||||||
|
|
||||||
|
$method = 'set' . str_replace('_', '', ucwords($key, '_'));
|
||||||
|
|
||||||
|
if (method_exists($model, $method) && isset($value)) {
|
||||||
|
$model->$method($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
next($data);
|
||||||
|
} else if (substr($key, 0, strlen($relation . '__')) === $relation . '__') {
|
||||||
|
$relationType = current($relations);
|
||||||
|
$relationModel = new $relationType();
|
||||||
|
$this->fillWithData($data, $relationModel, $withRelations, $relation);
|
||||||
|
|
||||||
|
$method = 'set' . str_replace('_', '', ucwords($relation, '_'));
|
||||||
|
$model->$method($relationModel);
|
||||||
|
|
||||||
|
next($relations);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$model->saveSnapshot();
|
$model->saveSnapshot();
|
||||||
}
|
}
|
||||||
@@ -128,35 +153,37 @@ class PersistentDataManager
|
|||||||
$model->resetSnapshot();
|
$model->resetSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createSelect(Select $select, string $type, bool $withRelations = false): Select
|
private function createSelect(Select $select, string $type, bool $useRelations = false, array $withRelations = []): Select
|
||||||
{
|
{
|
||||||
$table = call_user_func([$type, 'getTable']);
|
$table = call_user_func([$type, 'getTable']);
|
||||||
$fields = call_user_func([$type, 'getFields']);
|
$fields = call_user_func([$type, 'getFields']);
|
||||||
|
|
||||||
|
$columns = [];
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$columns[] = [$table, $field];
|
||||||
|
}
|
||||||
|
|
||||||
$select->from($table);
|
$select->from($table);
|
||||||
|
|
||||||
//TODO: only with some relations?
|
if ($useRelations) {
|
||||||
if ($withRelations) {
|
|
||||||
$relations = call_user_func([$type, 'getRelations']);
|
$relations = call_user_func([$type, 'getRelations']);
|
||||||
|
if (count($withRelations)) {
|
||||||
$columns = [];
|
$relations = array_intersect($relations, $withRelations);
|
||||||
|
|
||||||
foreach ($fields as $field) {
|
|
||||||
$columns[] = [$table, $field];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$columns = array_merge($columns, $this->getRelationColumns($relations));
|
$columns = array_merge($columns, $this->getRelationColumns($relations, $withRelations));
|
||||||
|
|
||||||
$this->leftJoinRelations($select, $table, $relations);
|
$this->leftJoinRelations($select, $table, $relations, $withRelations);
|
||||||
$select->columns($columns);
|
$select->columns($columns);
|
||||||
} else {
|
} else {
|
||||||
$select->columns($fields);
|
$select->columns($columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $select;
|
return $select;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getRelationColumns(array $relations): array
|
private function getRelationColumns(array $relations, array $withRelations): array
|
||||||
{
|
{
|
||||||
$columns = [];
|
$columns = [];
|
||||||
|
|
||||||
@@ -165,46 +192,28 @@ class PersistentDataManager
|
|||||||
foreach (call_user_func([$relationType, 'getFields']) as $relationField) {
|
foreach (call_user_func([$relationType, 'getFields']) as $relationField) {
|
||||||
$columns[] = [$relationTable, $relationField, $relation . '__' . $relationField];
|
$columns[] = [$relationTable, $relationField, $relation . '__' . $relationField];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$nextOrderRelations = call_user_func([$relationType, 'getRelations']);
|
||||||
|
if (count($withRelations)) {
|
||||||
|
$nextOrderRelations = array_intersect($nextOrderRelations, $withRelations);
|
||||||
|
}
|
||||||
|
$columns = array_merge($columns, $this->getRelationColumns($nextOrderRelations, $withRelations));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $columns;
|
return $columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function leftJoinRelations(Select $select, string $table, array $relations): void
|
private function leftJoinRelations(Select $select, string $table, array $relations, array $withRelations): void
|
||||||
{
|
{
|
||||||
foreach ($relations as $relation => $relationType) {
|
foreach ($relations as $relation => $relationType) {
|
||||||
$relationTable = call_user_func([$relationType, 'getTable']);
|
$relationTable = call_user_func([$relationType, 'getTable']);
|
||||||
$select->leftJoin($relationTable, [$relationTable, 'id'], '=', [$table, $relation . '_id']);
|
$select->leftJoin($relationTable, [$relationTable, 'id'], '=', [$table, $relation . '_id']);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function extractRelationData(string $key, $value, array &$relationData, array $relations): bool
|
$nextOrderRelations = call_user_func([$relationType, 'getRelations']);
|
||||||
{
|
if (count($withRelations)) {
|
||||||
$found = false;
|
$nextOrderRelations = array_intersect($nextOrderRelations, $withRelations);
|
||||||
|
|
||||||
foreach ($relations as $relation => $relationType) {
|
|
||||||
if (substr($key, 0, strlen($relation . '__')) === $relation . '__') {
|
|
||||||
$found = true;
|
|
||||||
$relationData[$relation][substr($key, strlen($relation . '__'))] = $value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $found;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setRelations(Model $model, array &$relations): void
|
|
||||||
{
|
|
||||||
foreach ($model::getRelations() as $relation => $relationType) {
|
|
||||||
if (isset($relations[$relation])) {
|
|
||||||
$object = new $relationType();
|
|
||||||
|
|
||||||
$this->fillWithData($relations[$relation], $object);
|
|
||||||
|
|
||||||
$method = 'set' . str_replace('_', '', ucwords($relation, '_'));
|
|
||||||
|
|
||||||
$model->$method($object);
|
|
||||||
}
|
}
|
||||||
|
$this->leftJoinRelations($select, $relationTable, $nextOrderRelations, $withRelations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<?php namespace MapGuesser\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
|
use MapGuesser\PersistentData\Model\Place;
|
||||||
|
use MapGuesser\PersistentData\Model\User;
|
||||||
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
|
||||||
|
class ChallengeRepository
|
||||||
|
{
|
||||||
|
private PersistentDataManager $pdm;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pdm = new PersistentDataManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getById(int $challengeId): ?Challenge
|
||||||
|
{
|
||||||
|
return $this->pdm->selectFromDbById($challengeId, Challenge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByToken(int $token): ?Challenge
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('token', '=', $token);
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select, Challenge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByTokenStr(string $token_str): ?Challenge
|
||||||
|
{
|
||||||
|
// validate token string
|
||||||
|
foreach (str_split($token_str) as $char) {
|
||||||
|
if (!(('0' <= $char && $char <= '9') || ('a' <= $char && $char <= 'f'))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// convert token to int
|
||||||
|
$token = hexdec($token_str);
|
||||||
|
|
||||||
|
return $this->getByToken($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByParticipant(User $user): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('user_in_challenge', ['challenge', 'id'], '=', ['user_in_challenge', 'challenge_id']);
|
||||||
|
$select->innerJoin('users', ['users', 'id'], '=', ['user_in_challenge', 'user_id']);
|
||||||
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Challenge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByOwner(User $user): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('user_in_challenge', ['challenge', 'id'], '=', ['user_in_challenge', 'challenge_id']);
|
||||||
|
$select->innerJoin('users', ['users', 'id'], '=', ['user_in_challenge', 'user_id']);
|
||||||
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
$select->where('is_owner', '=', true);
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Challenge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByPlace(Place $place): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('place_in_challenge', ['challenges', 'id'], '=', ['place_in_challenge', 'challenge_id']);
|
||||||
|
$select->where('place_id', '=', $place->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Challenge::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<?php namespace MapGuesser\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
|
use MapGuesser\PersistentData\Model\Guess;
|
||||||
|
use MapGuesser\PersistentData\Model\User;
|
||||||
|
use MapGuesser\PersistentData\Model\UserInChallenge;
|
||||||
|
use MapGuesser\PersistentData\Model\Place;
|
||||||
|
use MapGuesser\PersistentData\Model\PlaceInChallenge;
|
||||||
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
|
||||||
|
class GuessRepository
|
||||||
|
{
|
||||||
|
private PersistentDataManager $pdm;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pdm = new PersistentDataManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByUser(User $user): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Guess::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByUserAndChallenge(User $user, Challenge $challenge): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']);
|
||||||
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Guess::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByUserAndPlaceInChallenge(User $user, Challenge $challenge, Place $place): ?Guess
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']);
|
||||||
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
$select->where('place_id', '=', $place->getId());
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select, Guess::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllInChallengeByUser(int $userId, Challenge $challenge): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']);
|
||||||
|
$select->where('user_id', '=', $userId);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
$select->orderBy('round');
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Guess::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllInChallenge(Challenge $challenge, array $withRelations = []): Generator
|
||||||
|
{
|
||||||
|
if (count($withRelations)) {
|
||||||
|
$necessaryRelations = [PlaceInChallenge::class];
|
||||||
|
$withRelations = array_unique(array_merge($withRelations, $necessaryRelations));
|
||||||
|
}
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
$select->orderBy('round');
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Guess::class, true, $withRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllInChallengeByRound(int $round, Challenge $challenge, array $withRelations = []): Generator
|
||||||
|
{
|
||||||
|
if (count($withRelations)) {
|
||||||
|
$necessaryRelations = [PlaceInChallenge::class];
|
||||||
|
$withRelations = array_unique(array_merge($withRelations, $necessaryRelations));
|
||||||
|
}
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
$select->where('round', '=', $round);
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Guess::class, true, $withRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByPlace(Place $place): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']);
|
||||||
|
$select->where('place_id', '=', $place->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Guess::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
<?php namespace MapGuesser\Repository;
|
<?php namespace MapGuesser\Repository;
|
||||||
|
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
use MapGuesser\PersistentData\Model\Map;
|
use MapGuesser\PersistentData\Model\Map;
|
||||||
|
use MapGuesser\PersistentData\Model\Place;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
|
||||||
class MapRepository
|
class MapRepository
|
||||||
@@ -16,4 +19,20 @@ class MapRepository
|
|||||||
{
|
{
|
||||||
return $this->pdm->selectFromDbById($mapId, Map::class);
|
return $this->pdm->selectFromDbById($mapId, Map::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getByPlace(Place $place): ?Map
|
||||||
|
{
|
||||||
|
return $this->getById($place->getMapId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByChallenge(Challenge $challenge): ?Map
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('places', ['maps', 'id'], '=', ['places', 'map_id']);
|
||||||
|
$select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
$select->limit(1);
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select, Map::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php namespace MapGuesser\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
|
use MapGuesser\PersistentData\Model\Map;
|
||||||
|
use MapGuesser\PersistentData\Model\Place;
|
||||||
|
use MapGuesser\PersistentData\Model\PlaceInChallenge;
|
||||||
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
|
||||||
|
class PlaceInChallengeRepository
|
||||||
|
{
|
||||||
|
private PersistentDataManager $pdm;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pdm = new PersistentDataManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByPlace(Place $place, array $withRelations = []) : Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('place_id', '=', $place->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, PlaceInChallenge::class, true, $withRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByChallenge(Challenge $challenge) : Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, PlaceInChallenge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByPlaceAndChallenge(Place $place, Challenge $challenge) : ?PlaceInChallenge
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('place_id', '=', $place->getId());
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select, PlaceInChallenge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByRoundInChallenge(int $round, Challenge $challenge, array $withRelations = []): ?PlaceInChallenge
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
$select->orderBy('round');
|
||||||
|
$select->limit(1, $round);
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select, PlaceInChallenge::class, true, $withRelations);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use Generator;
|
use Generator;
|
||||||
use MapGuesser\Database\Query\Select;
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
use MapGuesser\PersistentData\Model\Map;
|
use MapGuesser\PersistentData\Model\Map;
|
||||||
use MapGuesser\PersistentData\Model\Place;
|
use MapGuesser\PersistentData\Model\Place;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
@@ -176,5 +177,26 @@ class PlaceRepository
|
|||||||
|
|
||||||
return $places;
|
return $places;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getByRoundInChallenge(Challenge $challenge, int $round): ?Place
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
$select->orderBy('round');
|
||||||
|
$select->limit(1, $round);
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select, Place::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllInChallenge(Challenge $challenge): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
$select->orderBy('round');
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, Place::class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<?php namespace MapGuesser\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
|
use MapGuesser\PersistentData\Model\User;
|
||||||
|
use MapGuesser\PersistentData\Model\UserInChallenge;
|
||||||
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
|
||||||
|
class UserInChallengeRepository
|
||||||
|
{
|
||||||
|
private PersistentDataManager $pdm;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pdm = new PersistentDataManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByUser(User $user) : Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, UserInChallenge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByChallenge(Challenge $challenge) : Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, UserInChallenge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByChallengeWithUsers(Challenge $challenge) : Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, UserInChallenge::class, true, [User::class]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByUserIdAndChallenge(int $userId, Challenge $challenge): ?UserInChallenge
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('user_id', '=', $userId);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select, UserInChallenge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByUserIdAndToken(int $userId, string $token_str, array $withRelations = []): ?UserInChallenge
|
||||||
|
{
|
||||||
|
if (count($withRelations)) {
|
||||||
|
$necessaryRelations = [Challenge::class];
|
||||||
|
$withRelations = array_unique(array_merge($withRelations, $necessaryRelations));
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate token string
|
||||||
|
if (!ctype_xdigit($token_str)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// convert token to int
|
||||||
|
$token = hexdec($token_str);
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('user_id', '=', $userId);
|
||||||
|
$select->where('token', '=', $token);
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select, UserInChallenge::class, true, $withRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isUserParticipatingInChallenge(int $userId, Challenge $challenge): bool
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection, 'user_in_challenge');
|
||||||
|
$select->where('user_id', '=', $userId);
|
||||||
|
$select->where('challenge_id', '=', $challenge->getId());
|
||||||
|
|
||||||
|
return $select->count() != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ class UserPlayedPlaceRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('user_id', '=', $user->getId());
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
|
||||||
yield from $this->pdm->selectMultipleFromDb($select, UserPlayedPlace::class, true);
|
yield from $this->pdm->selectMultipleFromDb($select, UserPlayedPlace::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByUserIdAndPlaceId(int $userId, int $placeId) : ?UserPlayedPlace
|
public function getByUserIdAndPlaceId(int $userId, int $placeId) : ?UserPlayedPlace
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use DateTime;
|
use DateTime;
|
||||||
use Generator;
|
use Generator;
|
||||||
use MapGuesser\Database\Query\Select;
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Guess;
|
||||||
use MapGuesser\PersistentData\Model\User;
|
use MapGuesser\PersistentData\Model\User;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
|
||||||
@@ -44,4 +45,9 @@ class UserRepository
|
|||||||
|
|
||||||
yield from $this->pdm->selectMultipleFromDb($select, User::class);
|
yield from $this->pdm->selectMultipleFromDb($select, User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getByGuess(Guess $guess): ?User
|
||||||
|
{
|
||||||
|
return $this->getById($guess->getUserId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php namespace MapGuesser\Util;
|
||||||
|
|
||||||
|
use MapGuesser\Http\Request;
|
||||||
|
|
||||||
|
class CaptchaValidator
|
||||||
|
{
|
||||||
|
public function validate(string $response)
|
||||||
|
{
|
||||||
|
$request = new Request('https://www.google.com/recaptcha/api/siteverify', Request::HTTP_GET);
|
||||||
|
$request->setQuery([
|
||||||
|
'secret' => $_ENV['RECAPTCHA_SECRET'],
|
||||||
|
'response' => $response
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $request->send();
|
||||||
|
|
||||||
|
return json_decode($response->getBody(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,24 +9,24 @@
|
|||||||
<?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
|
<?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
|
||||||
<p class="justify small">Please confirm your identity with your password or with Google to modify your account.</p>
|
<p class="justify small">Please confirm your identity with your password or with Google to modify your account.</p>
|
||||||
<div class="inputWithButton">
|
<div class="inputWithButton">
|
||||||
<input type="password" name="password" placeholder="Current password" required minlength="6" autofocus><!--
|
<input type="password" class="text name="password" placeholder="Current password" required minlength="6" autofocus><!--
|
||||||
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
|
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
|
||||||
</div>
|
</div>
|
||||||
<?php elseif ($user['password'] !== null): ?>
|
<?php elseif ($user['password'] !== null): ?>
|
||||||
<p class="justify small">Please confirm your identity with your password to modify your account.</p>
|
<p class="justify small">Please confirm your identity with your password to modify your account.</p>
|
||||||
<input class="big fullWidth" type="password" name="password" placeholder="Current password" required minlength="6" autofocus>
|
<input type="password" class="text big fullWidth" name="password" placeholder="Current password" required minlength="6" autofocus>
|
||||||
<?php elseif ($user['google_sub'] !== null): ?>
|
<?php elseif ($user['google_sub'] !== null): ?>
|
||||||
<p class="justify small">Please confirm your identity with Google to modify your account.</p>
|
<p class="justify small">Please confirm your identity with Google to modify your account.</p>
|
||||||
<div class="inputWithButton">
|
<div class="inputWithButton">
|
||||||
<input type="text" name="password" placeholder="Authenticate with Google..." disabled><!--
|
<input type="text" class="text" name="password" placeholder="Authenticate with Google..." disabled><!--
|
||||||
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
|
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<hr>
|
<hr>
|
||||||
<?php /* TODO: disabled for the time being, email modification should be implemented */ ?>
|
<?php /* TODO: disabled for the time being, email modification should be implemented */ ?>
|
||||||
<input class="big fullWidth" type="email" name="email" placeholder="Email address" value="<?= $user['email'] ?>" disabled>
|
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= $user['email'] ?>" disabled>
|
||||||
<input class="big fullWidth marginTop" type="password" name="password_new" placeholder="New password" minlength="6">
|
<input type="password" class="text big fullWidth marginTop" name="password_new" placeholder="New password" minlength="6">
|
||||||
<input class="big fullWidth marginTop" type="password" name="password_new_confirm" placeholder="New password confirmation" minlength="6">
|
<input type="password" class="text big fullWidth marginTop" name="password_new_confirm" placeholder="New password confirmation" minlength="6">
|
||||||
<p id="accountFormError" class="formError justify marginTop"></p>
|
<p id="accountFormError" class="formError justify marginTop"></p>
|
||||||
<div class="right marginTop">
|
<div class="right marginTop">
|
||||||
<button type="submit" name="submit" disabled>Save</button>
|
<button type="submit" name="submit" disabled>Save</button>
|
||||||
|
|||||||
@@ -10,16 +10,16 @@
|
|||||||
<?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
|
<?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
|
||||||
<p class="justify small">Please confirm your identity with your password or with Google to delete your account.</p>
|
<p class="justify small">Please confirm your identity with your password or with Google to delete your account.</p>
|
||||||
<div class="inputWithButton">
|
<div class="inputWithButton">
|
||||||
<input type="password" name="password" placeholder="Current password" required minlength="6" autofocus><!--
|
<input type="password" class="text" name="password" placeholder="Current password" required minlength="6" autofocus><!--
|
||||||
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
|
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
|
||||||
</div>
|
</div>
|
||||||
<?php elseif ($user['password'] !== null): ?>
|
<?php elseif ($user['password'] !== null): ?>
|
||||||
<p class="justify small">Please confirm your identity with your password to delete your account.</p>
|
<p class="justify small">Please confirm your identity with your password to delete your account.</p>
|
||||||
<input class="big fullWidth" type="password" name="password" placeholder="Current password" required minlength="6" autofocus>
|
<input type="password" class="text big fullWidth" name="password" placeholder="Current password" required minlength="6" autofocus>
|
||||||
<?php elseif ($user['google_sub'] !== null): ?>
|
<?php elseif ($user['google_sub'] !== null): ?>
|
||||||
<p class="justify small">Please confirm your identity with Google to delete your account.</p>
|
<p class="justify small">Please confirm your identity with Google to delete your account.</p>
|
||||||
<div class="inputWithButton">
|
<div class="inputWithButton">
|
||||||
<input type="text" name="password" placeholder="Authenticate with Google..." disabled><!--
|
<input type="text" class="text" name="password" placeholder="Authenticate with Google..." disabled><!--
|
||||||
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
|
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</span><!--
|
</span><!--
|
||||||
--><span class="inline hideOnMobile">
|
--><span class="inline hideOnMobile">
|
||||||
<input type="text" id="jumpCoordinates" placeholder="Insert coordinates here" />
|
<input type="text" class="text" id="jumpCoordinates" placeholder="Insert coordinates here" />
|
||||||
<button id="jumpButton" disabled >Jump</button>
|
<button id="jumpButton" disabled >Jump</button>
|
||||||
</span><!--
|
</span><!--
|
||||||
--><span><!--
|
--><span><!--
|
||||||
@@ -52,8 +52,11 @@
|
|||||||
<div id="metadata" class="modal">
|
<div id="metadata" class="modal">
|
||||||
<h2>Edit map data</h2>
|
<h2>Edit map data</h2>
|
||||||
<form id="metadataForm" class="marginTop" data-no-submit="true">
|
<form id="metadataForm" class="marginTop" data-no-submit="true">
|
||||||
<input class="fullWidth" type="text" name="name" value="<?= $mapName ?>" placeholder="Name of the map">
|
<input type="text" class="text fullWidth" name="name" value="<?= $mapName ?>" placeholder="Name of the map">
|
||||||
<textarea class="fullWidth marginTop" name="description" rows="4" placeholder="Description of the map"><?= $mapDescription ?></textarea>
|
<textarea class="fullWidth marginTop" name="description" rows="4" placeholder="Description of the map"><?= $mapDescription ?></textarea>
|
||||||
|
<div class="marginTop">
|
||||||
|
<input type="checkbox" id="unlisted" name="unlisted" <?= $mapUnlisted ? 'checked' : '' ?>><label for="unlisted">Unlisted</label>
|
||||||
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button class="marginTop marginRight" type="submit">Apply</button><!--
|
<button class="marginTop marginRight" type="submit">Apply</button><!--
|
||||||
--><button id="closeMetadataButton" class="gray marginTop" type="button">Close</button>
|
--><button id="closeMetadataButton" class="gray marginTop" type="button">Close</button>
|
||||||
|
|||||||
+19
-2
@@ -12,11 +12,25 @@
|
|||||||
<div id="players" class="marginTop"></div>
|
<div id="players" class="marginTop"></div>
|
||||||
<button id="startMultiGameButton" class="button fullWidth marginTop green">Start game</button>
|
<button id="startMultiGameButton" class="button fullWidth marginTop green">Start game</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="highscores" class="modal">
|
||||||
|
<h2>Highscores</h2>
|
||||||
|
<div>
|
||||||
|
<table id="highscoresTable">
|
||||||
|
<tr>
|
||||||
|
<th>Player</th>
|
||||||
|
<th>Score</th>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<button id="closeHighscoresButton" class="gray marginTop" type="button">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section(subheader)
|
@section(subheader)
|
||||||
<span id="mapName" class="bold">Loading map...</span><!--
|
<span id="mapName" class="bold">Loading map...</span><!--
|
||||||
--><span>Round <span id="currentRound" class="bold"></span></span><!--
|
--><span id="roundContainer">Round <span id="currentRound" class="bold"></span></span><!--
|
||||||
--><span>Score <span id="currentScoreSum" class="bold"></span></span>
|
--><span>Score <span id="currentScoreSum" class="bold"></span></span>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@@ -25,6 +39,7 @@
|
|||||||
<p id="countdownTime" class="mono bold"></p>
|
<p id="countdownTime" class="mono bold"></p>
|
||||||
</div>
|
</div>
|
||||||
<div id="panoCover"></div>
|
<div id="panoCover"></div>
|
||||||
|
<div id="panningBlockerCover"></div>
|
||||||
<div id="panorama"></div>
|
<div id="panorama"></div>
|
||||||
<div id="showGuessButtonContainer">
|
<div id="showGuessButtonContainer">
|
||||||
<button id="showGuessButton" class="fullWidth">Show guess map</button>
|
<button id="showGuessButton" class="fullWidth">Show guess map</button>
|
||||||
@@ -41,7 +56,7 @@
|
|||||||
<div id="distanceInfo">
|
<div id="distanceInfo">
|
||||||
<p>You were <span id="distance" class="bold"></span> close.</p>
|
<p>You were <span id="distance" class="bold"></span> close.</p>
|
||||||
<p>You didn't guess in this round.</p>
|
<p>You didn't guess in this round.</p>
|
||||||
<p class="bold">Game finished.</p>
|
<p id="summaryInfo" class="bold">Game finished.</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="scoreInfo">
|
<div id="scoreInfo">
|
||||||
<p>You earned <span id="score" class="bold"></span> points.</p>
|
<p>You earned <span id="score" class="bold"></span> points.</p>
|
||||||
@@ -57,6 +72,7 @@
|
|||||||
<button id="continueButton" class="fullWidth">Continue</button>
|
<button id="continueButton" class="fullWidth">Continue</button>
|
||||||
<button id="showSummaryButton" class="fullWidth">Show summary</button>
|
<button id="showSummaryButton" class="fullWidth">Show summary</button>
|
||||||
<button id="startNewGameButton" class="fullWidth">Play this map again</button>
|
<button id="startNewGameButton" class="fullWidth">Play this map again</button>
|
||||||
|
<a href="/" id="goToStart"><button class="fullWidth">Go back to the menu</button></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="navigation" class="circleControl">
|
<div id="navigation" class="circleControl">
|
||||||
@@ -84,5 +100,6 @@
|
|||||||
var multiUrl = '<?= $_ENV['MULTI_WS_URL'] ?>';
|
var multiUrl = '<?= $_ENV['MULTI_WS_URL'] ?>';
|
||||||
var roomId = <?= isset($roomId) ? '\'' . $roomId . '\'' : 'null' ?>;
|
var roomId = <?= isset($roomId) ? '\'' . $roomId . '\'' : 'null' ?>;
|
||||||
var mapId = <?= isset($mapId) ? '\'' . $mapId . '\'' : 'null' ?>;
|
var mapId = <?= isset($mapId) ? '\'' . $mapId . '\'' : 'null' ?>;
|
||||||
|
var challengeToken = <?= isset($challengeToken) ? '\'' . $challengeToken . '\'' : 'null' ?>;
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<p class="justify">Please confirm your sign up request. Your account will be linked to your Google account.</p>
|
<p class="justify">Please confirm your sign up request. Your account will be linked to your Google account.</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<input class="big fullWidth marginTop" type="email" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
|
<input type="email" class="text big fullWidth marginTop" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button class="marginTop marginRight" type="submit">
|
<button class="marginTop marginRight" type="submit">
|
||||||
<?php if ($found): ?>
|
<?php if ($found): ?>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
<h2>Login</h2>
|
<h2>Login</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="loginForm" action="/login" method="post" data-redirect-on-success="/">
|
<form id="loginForm" action="/login" method="post" data-redirect-on-success="/">
|
||||||
<input class="big fullWidth" type="email" name="email" placeholder="Email address" required autofocus>
|
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" required autofocus>
|
||||||
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password" required minlength="6">
|
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6">
|
||||||
<p id="loginFormError" class="formError justify marginTop"></p>
|
<p id="loginFormError" class="formError justify marginTop"></p>
|
||||||
<div class="right marginTop">
|
<div class="right marginTop">
|
||||||
<button type="submit">Login</button>
|
<button type="submit">Login</button>
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
|
@js(https://www.google.com/recaptcha/api.js)
|
||||||
|
|
||||||
@extends(templates/layout_normal)
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<h2>Request password reset</h2>
|
<h2>Request password reset</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="passwordResetForm" action="/password/requestReset" method="post" data-redirect-on-success="/password/requestReset/success">
|
<form id="passwordResetForm" action="/password/requestReset" method="post" data-redirect-on-success="/password/requestReset/success">
|
||||||
<input class="big fullWidth" type="email" name="email" placeholder="Email address" value="<?= isset($email) ? $email : '' ?>" required autofocus>
|
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= isset($email) ? $email : '' ?>" required autofocus>
|
||||||
|
<?php if (!empty($_ENV['RECAPTCHA_SITEKEY'])): ?>
|
||||||
|
<div class="marginTop">
|
||||||
|
<div class="g-recaptcha" data-sitekey="<?= $_ENV['RECAPTCHA_SITEKEY'] ?>"></div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<p id="passwordResetFormError" class="formError justify marginTop"></p>
|
<p id="passwordResetFormError" class="formError justify marginTop"></p>
|
||||||
<div class="right marginTop">
|
<div class="right marginTop">
|
||||||
<button type="submit">Continue</button>
|
<button type="submit">Continue</button>
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<?php if ($success) : ?>
|
<?php if ($success) : ?>
|
||||||
<form id="resetPasswordForm" action="/password/reset/<?= $token ?>" method="post" data-redirect-on-success="/">
|
<form id="resetPasswordForm" action="/password/reset/<?= $token ?>" method="post" data-redirect-on-success="/">
|
||||||
<input class="big fullWidth" type="email" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
|
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
|
||||||
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password" required minlength="6" autofocus>
|
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6" autofocus>
|
||||||
<input class="big fullWidth marginTop" type="password" name="password_confirm" placeholder="Password confirmation" required minlength="6">
|
<input type="password" class="text big fullWidth marginTop" name="password_confirm" placeholder="Password confirmation" required minlength="6">
|
||||||
<p id="resetPasswordFormError" class="formError justify marginTop"></p>
|
<p id="resetPasswordFormError" class="formError justify marginTop"></p>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button class="marginTop" type="submit">Reset password</button>
|
<button class="marginTop" type="submit">Reset password</button>
|
||||||
|
|||||||
+11
-5
@@ -1,3 +1,4 @@
|
|||||||
|
@js(https://www.google.com/recaptcha/api.js)
|
||||||
@js(js/login/signup.js)
|
@js(js/login/signup.js)
|
||||||
|
|
||||||
@extends(templates/layout_normal)
|
@extends(templates/layout_normal)
|
||||||
@@ -8,12 +9,17 @@
|
|||||||
<form id="signupForm" action="/signup" method="post" data-redirect-on-success="/signup/success">
|
<form id="signupForm" action="/signup" method="post" data-redirect-on-success="/signup/success">
|
||||||
<?php if (isset($email)): ?>
|
<?php if (isset($email)): ?>
|
||||||
<p class="justify">No user found with the given email address. Sign up with one click!</p>
|
<p class="justify">No user found with the given email address. Sign up with one click!</p>
|
||||||
<input class="big fullWidth marginTop" type="email" name="email" placeholder="Email address" value="<?= $email ?>" required>
|
<input type="email" class="text big fullWidth marginTop" name="email" placeholder="Email address" value="<?= $email ?>" required>
|
||||||
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password confirmation" required minlength="6" autofocus>
|
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password confirmation" required minlength="6" autofocus>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<input class="big fullWidth" type="email" name="email" placeholder="Email address" required autofocus>
|
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" required autofocus>
|
||||||
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password" required minlength="6">
|
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6">
|
||||||
<input class="big fullWidth marginTop" type="password" name="password_confirm" placeholder="Password confirmation" minlength="6">
|
<input type="password" class="text big fullWidth marginTop" name="password_confirm" placeholder="Password confirmation" minlength="6">
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($_ENV['RECAPTCHA_SITEKEY'])): ?>
|
||||||
|
<div class="marginTop">
|
||||||
|
<div class="g-recaptcha" data-sitekey="<?= $_ENV['RECAPTCHA_SITEKEY'] ?>"></div>
|
||||||
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<p id="signupFormError" class="formError justify marginTop"></p>
|
<p id="signupFormError" class="formError justify marginTop"></p>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
|
|||||||
+45
-7
@@ -11,6 +11,10 @@ TODO: condition!
|
|||||||
<a id="singleButton" class="button fullWidth marginTop" href="" title="Single player">Single player</a>
|
<a id="singleButton" class="button fullWidth marginTop" href="" title="Single player">Single player</a>
|
||||||
<p class="bold center marginTop marginBottom">OR</p>
|
<p class="bold center marginTop marginBottom">OR</p>
|
||||||
<button id="multiButton" class="fullWidth green" data-map-id="">Multiplayer (beta)</button>
|
<button id="multiButton" class="fullWidth green" data-map-id="">Multiplayer (beta)</button>
|
||||||
|
<?php if ($isLoggedIn): ?>
|
||||||
|
<p class="bold center marginTop marginBottom">OR</p>
|
||||||
|
<button id="challengeButton" class="fullWidth yellow" data-map-id="" data-timer="">Challenge (gamma)</button>
|
||||||
|
<?php endif; ?>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button id="closePlayModeButton" class="gray marginTop" type="button">Close</button>
|
<button id="closePlayModeButton" class="gray marginTop" type="button">Close</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -21,7 +25,7 @@ TODO: condition!
|
|||||||
<a id="createNewRoomButton" class="button fullWidth green" href="" title="Create new room">Create new room</a>
|
<a id="createNewRoomButton" class="button fullWidth green" href="" title="Create new room">Create new room</a>
|
||||||
<p class="bold center marginTop marginBottom">OR</p>
|
<p class="bold center marginTop marginBottom">OR</p>
|
||||||
<div class="inputWithButton">
|
<div class="inputWithButton">
|
||||||
<input type="text" name="roomId" placeholder="Room to connect" required minlength="6" maxlength="6"><!--
|
<input type="text" class="text" name="roomId" placeholder="Room to connect" required minlength="6" maxlength="6"><!--
|
||||||
--><button id="authenticateWithGoogleButton" type="submit">Connect</button>
|
--><button id="authenticateWithGoogleButton" type="submit">Connect</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
@@ -29,12 +33,42 @@ TODO: condition!
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="challenge" class="modal">
|
||||||
|
<h2>Challenge (gamma)</h2>
|
||||||
|
<form id="challengeForm" class="marginTop" method="get">
|
||||||
|
<div id="restrictions" class="marginTop marginBottom">
|
||||||
|
<h3>Optional restrictions</h3>
|
||||||
|
<div class="marginTop">
|
||||||
|
<input type="checkbox" id="timerEnabled" name="timerEnabled" value="timerEnabled" /><label id="timeLimitLabel" for="timerEnabled">Time limit measured in seconds</label>
|
||||||
|
</div>
|
||||||
|
<div id="timeLimitType">
|
||||||
|
<input class="fullWidth" type="range" id="timeLimit" name="timeLimit" min="10" max="1800" step="10" value="300" />
|
||||||
|
<input type="radio" id="timeLimitTypeGame" name="timeLimitType" value="game" checked /><label for="timeLimitTypeGame">for the whole game</label>
|
||||||
|
<input class="marginLeft" type="radio" id="timeLimitTypeRound" name="timeLimitType" value="round" /><label for="timeLimitTypeRound">per round</label>
|
||||||
|
</div>
|
||||||
|
<div class="marginTop">
|
||||||
|
<input type="checkbox" id="noMove" name="noMove" value="noMove" /><label for="noMove">No movement allowed</label>
|
||||||
|
</div>
|
||||||
|
<div class="marginTop">
|
||||||
|
<input type="checkbox" id="noZoom" name="noZoom" value="noZoom" /><label for="noZoom">No zoom allowed</label>
|
||||||
|
</div>
|
||||||
|
<div class="marginTop">
|
||||||
|
<input type="checkbox" id="noPan" name="noPan" value="noPan" /><label for="noPan">No camera change allowed</label>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="mapId" id="challengeMapId" />
|
||||||
|
</div>
|
||||||
|
<button id="createNewChallengeButton" type="submit" class="button fullWidth green" href="" title="Create new challenge">Create new challenge</button>
|
||||||
|
<div class="right">
|
||||||
|
<button id="closeChallengeButton" class="gray marginTop" type="button">Close</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<div id="mapContainer">
|
<div id="mapContainer">
|
||||||
<?php foreach ($maps as $map): ?>
|
<?php foreach ($maps as $map): ?>
|
||||||
<div class="mapItem">
|
<div class="mapItem <?= $map['unlisted'] ? 'unlisted' : '' ?>">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<p class="title"><?= $map['name'] ?></p>
|
<p class="title"><?= $map['name'] ?></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,12 +96,16 @@ TODO: condition!
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttonContainer">
|
<div class="buttonContainer">
|
||||||
<?php if ($isAdmin): ?>
|
<?php if (!empty($_ENV['ENABLE_GAME_FOR_GUESTS']) || Container::$request->user()): ?>
|
||||||
<button class="button fullWidth noRightRadius playButton" data-map-id="<?= $map['id'] ?>" data-map-name="<?= htmlspecialchars($map['name']) ?>" title="Play map '<?= $map['name'] ?>'">Play this map</button>
|
<?php if ($isAdmin): ?>
|
||||||
<a class="button yellow fullWidth noLeftRadius noRightRadius" href="/admin/mapEditor/<?= $map['id']; ?>" title="Edit map '<?= $map['name'] ?>'">Edit</a>
|
<button class="button fullWidth noRightRadius playButton" data-map-id="<?= $map['id'] ?>" data-map-name="<?= htmlspecialchars($map['name']) ?>" title="Play map '<?= $map['name'] ?>'">Play this map</button>
|
||||||
<button class="button red fullWidth noLeftRadius deleteButton" data-map-id="<?= $map['id'] ?>" data-map-name="<?= htmlspecialchars($map['name']) ?>" title="Delete map '<?= $map['name'] ?>'">Delete</button>
|
<a class="button yellow fullWidth noLeftRadius noRightRadius" href="/admin/mapEditor/<?= $map['id']; ?>" title="Edit map '<?= $map['name'] ?>'">Edit</a>
|
||||||
|
<button class="button red fullWidth noLeftRadius deleteButton" data-map-id="<?= $map['id'] ?>" data-map-name="<?= htmlspecialchars($map['name']) ?>" title="Delete map '<?= $map['name'] ?>'">Delete</button>
|
||||||
|
<?php else: ?>
|
||||||
|
<button class="button fullWidth playButton" data-map-id="<?= $map['id'] ?>" data-map-name="<?= htmlspecialchars($map['name']) ?>" title="Play map '<?= $map['name'] ?>'">Play this map</button>
|
||||||
|
<?php endif; ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<button class="button fullWidth playButton" data-map-id="<?= $map['id'] ?>" data-map-name="<?= htmlspecialchars($map['name']) ?>" title="Play map '<?= $map['name'] ?>'">Play this map</button>
|
<a href="/login" class="button fullWidth" title="Play map '<?= $map['name'] ?>'">Play this map</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ Container::$routeCollection->group('multiGame', function (MapGuesser\Routing\Rou
|
|||||||
$routeCollection->post('multiGame.nextRound-json', '{roomId}/nextRound.json', [MapGuesser\Controller\GameFlowController::class, 'multiNextRound']);
|
$routeCollection->post('multiGame.nextRound-json', '{roomId}/nextRound.json', [MapGuesser\Controller\GameFlowController::class, 'multiNextRound']);
|
||||||
$routeCollection->post('multiGame.guess-json', '{roomId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'multiGuess']);
|
$routeCollection->post('multiGame.guess-json', '{roomId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'multiGuess']);
|
||||||
});
|
});
|
||||||
|
Container::$routeCollection->group('challenge', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
||||||
|
$routeCollection->post('challenge.create', 'create.json', [\MapGuesser\Controller\GameController::class, 'createNewChallenge']);
|
||||||
|
$routeCollection->get('challenge', '{challengeToken}', [MapGuesser\Controller\GameController::class, 'getChallenge']);
|
||||||
|
$routeCollection->post('challenge.prepare-json', '{challengeToken}/prepare.json', [MapGuesser\Controller\GameController::class, 'prepareChallenge']);
|
||||||
|
$routeCollection->post('challenge.initialData-json', '{challengeToken}/initialData.json', [MapGuesser\Controller\GameFlowController::class, 'challengeInitialData']);
|
||||||
|
$routeCollection->post('challenge.guess-json', '{challengeToken}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'challengeGuess']);
|
||||||
|
});
|
||||||
Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
||||||
$routeCollection->get('admin.mapEditor', 'mapEditor/{mapId?}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']);
|
$routeCollection->get('admin.mapEditor', 'mapEditor/{mapId?}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']);
|
||||||
$routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']);
|
$routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']);
|
||||||
|
|||||||
Reference in new issue
Block a user