parent
92c35a2087
commit
9ffedf6030
@ -9,6 +9,9 @@ if (($pos = strpos($url, '?')) !== false) {
|
||||
$url = substr($url, 0, $pos);
|
||||
}
|
||||
switch($url) {
|
||||
case '/maps':
|
||||
$controller = new MapGuesser\Controller\MapsController();
|
||||
break;
|
||||
case '/game':
|
||||
$mapId = isset($_GET['map']) ? (int) $_GET['map'] : 0;
|
||||
$controller = new MapGuesser\Controller\GameController($mapId);
|
||||
@ -22,7 +25,7 @@ switch($url) {
|
||||
$controller = new MapGuesser\Controller\PositionController($mapId);
|
||||
break;
|
||||
case '/':
|
||||
header('Location: ' . $host . '/game', true, 302);
|
||||
header('Location: ' . $host . '/maps', true, 302);
|
||||
die;
|
||||
default:
|
||||
echo 'Error 404';
|
||||
|
@ -12,13 +12,35 @@ html, body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p, button {
|
||||
p, h1, h2, button, a {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
p, h2 {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 300;
|
||||
font-size: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.mono {
|
||||
@ -29,7 +51,37 @@ p {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
button {
|
||||
.small {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.justify {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.marginTop {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.marginBottom {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
svg.inline {
|
||||
vertical-align: -0.15em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: #3b5998;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
button, a.button {
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
@ -39,11 +91,15 @@ button {
|
||||
height: 35px;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
button:enabled:hover, button:enabled:focus {
|
||||
button:enabled:hover, button:enabled:focus, a.button:hover, a.button:focus {
|
||||
background-color: #29457f;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
@ -52,19 +108,31 @@ button:disabled {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
button.fullWidth {
|
||||
button.fullWidth, a.button.fullWidth {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button.gray {
|
||||
button.gray, a.button.gray {
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
button.gray:hover, button.gray:focus {
|
||||
button.gray:hover, button.gray:focus, a.button.gray:hover, a.button.gray:focus {
|
||||
background-color: #555555;
|
||||
}
|
||||
|
||||
div.header {
|
||||
background-color: #333333;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
padding: 0 12px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.main {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
div.buttonContainer {
|
||||
height: 35px;
|
||||
}
|
||||
@ -73,12 +141,45 @@ div.buttonContainer > button {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
div.buttonContainer.top {
|
||||
margin-bottom: 10px;
|
||||
div.mapContainer {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
div.buttonContainer.bottom {
|
||||
margin-top: 10px;
|
||||
div.mapItem {
|
||||
width: 375px;
|
||||
background-color: #eeeeee;
|
||||
border-radius: 3px;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
div.mapItem>div.title {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
div.mapItem>div.title>p.title {
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
div.mapItem>div.inner {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
div.mapItem>div.inner>div.info {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
}
|
||||
|
||||
div.mapItem>div.inner>div.info>p:nth-child(1) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.mapItem>div.inner>div.info>p:nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#loading {
|
||||
@ -108,11 +209,12 @@ div.buttonContainer.bottom {
|
||||
|
||||
#roundInfo p {
|
||||
font-size: 16px;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
#panorama {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@ -127,8 +229,7 @@ div.buttonContainer.bottom {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#guess.result > #closeGuessButtonContainer,
|
||||
#guess.result > #guessButtonContainer {
|
||||
#guess.result>#closeGuessButtonContainer, #guess.result>#guessButtonContainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -147,8 +248,8 @@ div.buttonContainer.bottom {
|
||||
|
||||
#resultInfo {
|
||||
margin-top: 5px;
|
||||
height: 120px;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
padding: 5px 20px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
@ -162,8 +263,8 @@ div.buttonContainer.bottom {
|
||||
}
|
||||
|
||||
#resultInfo>div {
|
||||
height: 33.33%;
|
||||
width: 100%;
|
||||
height: 33.33%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -171,6 +272,7 @@ div.buttonContainer.bottom {
|
||||
|
||||
#resultInfo p {
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
#distanceInfo>p:nth-child(2), #scoreInfo>p:nth-child(2) {
|
||||
@ -185,8 +287,8 @@ div.buttonContainer.bottom {
|
||||
}
|
||||
|
||||
#scoreBar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
transition-property: width;
|
||||
transition-duration: 2.0s;
|
||||
@ -196,12 +298,35 @@ div.buttonContainer.bottom {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1600px) {
|
||||
div.mapContainer {
|
||||
grid-template-columns: auto auto auto auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1200px) and (max-width: 1599px) {
|
||||
div.mapContainer {
|
||||
grid-template-columns: auto auto auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) and (max-width: 1199px) {
|
||||
div.mapContainer {
|
||||
grid-template-columns: auto auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 799px) {
|
||||
div.mapContainer {
|
||||
grid-template-columns: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 599px) {
|
||||
button {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#showGuessButtonContainer {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
@ -209,18 +334,15 @@ div.buttonContainer.bottom {
|
||||
right: 20px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#guess {
|
||||
left: 20px;
|
||||
top: 40px;
|
||||
opacity: 0.95;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: calc(100% - 90px);
|
||||
}
|
||||
|
||||
#scoreBarBase {
|
||||
width: 100%;
|
||||
}
|
||||
@ -230,13 +352,11 @@ div.buttonContainer.bottom {
|
||||
#showGuessButtonContainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#guess {
|
||||
width: 500px;
|
||||
height: 375px;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
#guess.adapt {
|
||||
top: initial;
|
||||
width: 250px;
|
||||
@ -246,22 +366,18 @@ div.buttonContainer.bottom {
|
||||
transition-duration: 0.1s;
|
||||
transition-delay: 0.8s;
|
||||
}
|
||||
|
||||
#guess.adapt:hover {
|
||||
width: 500px;
|
||||
height: 375px;
|
||||
opacity: 0.95;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
#closeGuessButtonContainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: calc(100% - 45px);
|
||||
}
|
||||
|
||||
#guess.result {
|
||||
width: initial;
|
||||
height: initial;
|
||||
@ -270,22 +386,18 @@ div.buttonContainer.bottom {
|
||||
right: 50px;
|
||||
bottom: 50px;
|
||||
}
|
||||
|
||||
#scoreBarBase {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
@media screen and (max-height: 424px) {
|
||||
#guess {
|
||||
top: 40px;
|
||||
height: initial;
|
||||
}
|
||||
|
||||
#guess.adapt:hover {
|
||||
top: 40px;
|
||||
height: initial;
|
||||
}
|
||||
|
||||
#guess.result {
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
|
60
src/Controller/MapsController.php
Normal file
60
src/Controller/MapsController.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace MapGuesser\Controller;
|
||||
|
||||
use MapGuesser\Database\Query\Select;
|
||||
use MapGuesser\Database\RawExpression;
|
||||
use MapGuesser\Interfaces\Controller\IController;
|
||||
use MapGuesser\Interfaces\Database\IResultSet;
|
||||
use MapGuesser\Interfaces\View\IView;
|
||||
use MapGuesser\Util\Geo\Bounds;
|
||||
use MapGuesser\View\HtmlView;
|
||||
|
||||
class MapsController implements IController
|
||||
{
|
||||
public function run(): IView
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection, 'maps');
|
||||
$select->columns([
|
||||
['maps', 'id'],
|
||||
['maps', 'name'],
|
||||
['maps', 'description'],
|
||||
['maps', 'bound_south_lat'],
|
||||
['maps', 'bound_west_lng'],
|
||||
['maps', 'bound_north_lat'],
|
||||
['maps', 'bound_east_lng'],
|
||||
new RawExpression('COUNT(places.id) AS num_places')
|
||||
]);
|
||||
$select->leftJoin('places', ['places', 'map_id'], '=', ['maps', 'id']);
|
||||
$select->orderBy('name');
|
||||
|
||||
$result = $select->execute();
|
||||
|
||||
$maps = [];
|
||||
while ($map = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
||||
$bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']);
|
||||
|
||||
$map['area'] = $this->formatMapArea($bounds->calculateApproximateArea());
|
||||
|
||||
$maps[] = $map;
|
||||
}
|
||||
|
||||
$data = ['maps' => $maps];
|
||||
return new HtmlView('maps', $data);
|
||||
}
|
||||
|
||||
private function formatMapArea(float $area): string
|
||||
{
|
||||
//TODO: this should be formatted more properly
|
||||
|
||||
if ($area < 100000.0) {
|
||||
return round($area, 0) . ' m^2';
|
||||
} elseif ($area < 100000000.0) {
|
||||
return round($area / 1000000.0, 0) . ' km^2';
|
||||
} elseif ($area < 10000000000.0) {
|
||||
return round($area / 1000000.0, -2) . ' km^2';
|
||||
} else {
|
||||
return round($area / 1000000.0, -4) . ' km^2';
|
||||
}
|
||||
}
|
||||
}
|
@ -19,11 +19,11 @@
|
||||
<button id="showGuessButton" class="fullWidth">Show guess map</button>
|
||||
</div>
|
||||
<div id="guess">
|
||||
<div id="closeGuessButtonContainer" class="buttonContainer top">
|
||||
<div id="closeGuessButtonContainer" class="buttonContainer marginBottom">
|
||||
<button id="closeGuessButton" class="fullWidth gray">Close</button>
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
<div id="guessButtonContainer" class="buttonContainer bottom">
|
||||
<div id="guessButtonContainer" class="buttonContainer marginTop">
|
||||
<button id="guessButton" class="fullWidth" disabled>Guess</button>
|
||||
</div>
|
||||
<div id="resultInfo">
|
||||
@ -41,10 +41,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="continueButtonContainer" class="buttonContainer bottom">
|
||||
<div id="continueButtonContainer" class="buttonContainer marginTop">
|
||||
<button id="continueButton" class="fullWidth">Continue</button>
|
||||
<button id="showSummaryButton" class="fullWidth">Show summary</button>
|
||||
<button id="startNewGameButton" class="fullWidth">Start new game</button>
|
||||
<button id="startNewGameButton" class="fullWidth">Play this map again</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
57
views/maps.php
Normal file
57
views/maps.php
Normal file
@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>MapGuesser</title>
|
||||
<link href="static/css/mapguesser.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;500&family=Roboto+Mono:wght@300;500&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>
|
||||
<svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="#28a745" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M15.817.613A.5.5 0 0 1 16 1v13a.5.5 0 0 1-.402.49l-5 1a.502.502 0 0 1-.196 0L5.5 14.51l-4.902.98A.5.5 0 0 1 0 15V2a.5.5 0 0 1 .402-.49l5-1a.5.5 0 0 1 .196 0l4.902.98 4.902-.98a.5.5 0 0 1 .415.103zM10 2.41l-4-.8v11.98l4 .8V2.41zm1 11.98l4-.8V1.61l-4 .8v11.98zm-6-.8V1.61l-4 .8v11.98l4-.8z" />
|
||||
</svg>
|
||||
MapGuesser
|
||||
</h1>
|
||||
</div>
|
||||
<div class="main">
|
||||
<h2>Playable maps</h2>
|
||||
<div class="mapContainer">
|
||||
<?php foreach ($maps as $map) : ?>
|
||||
<div class="mapItem">
|
||||
<div class="title">
|
||||
<p class="title"><?= $map['name'] ?></p>
|
||||
</div>
|
||||
<img src="https://maps.googleapis.com/maps/api/staticmap?size=375x200&visible=<?= $map['bound_south_lat'] . ',' . $map['bound_west_lng'] . '|' . $map['bound_north_lat'] . ',' . $map['bound_east_lng'] ?>&key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>" width="375" height="200">
|
||||
<div class="inner">
|
||||
<div class="info">
|
||||
<p>
|
||||
<svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10zm0-7a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" />
|
||||
</svg>
|
||||
<?= $map['num_places'] ?> places
|
||||
</p>
|
||||
<p>
|
||||
<svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M12.5 2h-9V1h9v1zm-10 1.5v9h-1v-9h1zm11 9v-9h1v9h-1zM3.5 14h9v1h-9v-1z" />
|
||||
<path fill-rule="evenodd" d="M14 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 1a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 1a2 2 0 1 0 0-4 2 2 0 0 0 0 4zM2 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 1a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 1a2 2 0 1 0 0-4 2 2 0 0 0 0 4z" />
|
||||
</svg>
|
||||
~<?= $map['area'] ?>
|
||||
</p>
|
||||
</div>
|
||||
<p class="small justify marginTop"><?= $map['description'] ?></p>
|
||||
</div>
|
||||
<a class="button fullWidth" href="game?map=<?= $map['id']; ?>" title="Play map '<?= $map['name'] ?>'">Play this map</a>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php if (count($maps) < 4): ?>
|
||||
<?php for ($i = 0; $i < 4 - count($maps); ++$i): ?>
|
||||
<div class="mapItem"></div>
|
||||
<?php endfor; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user