diff options
-rw-r--r-- | public/assets/style.css | 1 | ||||
-rw-r--r-- | src/Model/Event/SendResourcesCarriers.php | 3 | ||||
-rw-r--r-- | src/View.php | 4 | ||||
-rw-r--r-- | src/http/Controller/Event.php | 36 | ||||
-rw-r--r-- | src/http/Controller/User.php | 57 | ||||
-rw-r--r-- | src/http/Controller/Village.php | 44 | ||||
-rw-r--r-- | src/http/Router.php | 3 | ||||
-rw-r--r-- | src/http/Support/RouteLoader.php | 13 | ||||
-rw-r--r-- | views/http/account.twig | 22 | ||||
-rw-r--r-- | views/http/base.twig | 7 | ||||
-rw-r--r-- | views/http/components/timer.twig | 6 | ||||
-rw-r--r-- | views/http/village.twig | 72 |
12 files changed, 253 insertions, 15 deletions
diff --git a/public/assets/style.css b/public/assets/style.css index 300d282..a6b425b 100644 --- a/public/assets/style.css +++ b/public/assets/style.css @@ -19,6 +19,7 @@ button, input[type="submit"] { color: #fff; border: none; padding: 0.25rem 0.75rem; + cursor: pointer; } button:hover:not(:disabled), input[type="submit"]:hover:not(:disabled) { diff --git a/src/Model/Event/SendResourcesCarriers.php b/src/Model/Event/SendResourcesCarriers.php index b6c3772..d129732 100644 --- a/src/Model/Event/SendResourcesCarriers.php +++ b/src/Model/Event/SendResourcesCarriers.php @@ -34,6 +34,9 @@ class SendResourcesCarriers extends BaseEvent } } + public function cancel(): void + {} + public function dbInsert(): void { DB::query( diff --git a/src/View.php b/src/View.php index 10ce9db..761ef62 100644 --- a/src/View.php +++ b/src/View.php @@ -25,6 +25,10 @@ class View // self::$twig->addExtension(new IntlExtension()); self::$twig->addFilter(new TwigFilter('buildTime', function ($buildTime) { + if ($buildTime > 3600*24) { + return @sprintf('%02d:%02d:%02d:%02d', $buildTime / (3600*24), $buildTime / 3600, ($buildTime / 60) % 60, $buildTime % 60); + } + return @sprintf('%02d:%02d:%02d', $buildTime / 3600, ($buildTime / 60) % 60, $buildTime % 60); })); } diff --git a/src/http/Controller/Event.php b/src/http/Controller/Event.php index 8b9b92a..264fa26 100644 --- a/src/http/Controller/Event.php +++ b/src/http/Controller/Event.php @@ -8,7 +8,10 @@ use App\Model\Event\SendResources; use App\Model\Event\SendUnits; use App\Model\Event\TrainUnits; use App\Model\Event\UpgradeBuilding; +use App\Model\Unit; +use App\Model\Unit\MailCarrier; use App\Model\Village; +use App\View; use App\http\Router; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -20,6 +23,39 @@ class Event #[Route(path: '/village/{x}/{y}/send-resources', methods: ['POST'])] public function sendResources(Request $request): Response { + $village = Village::getByCoordinates($request->get('x'), $request->get('y')); + + $totalAmount = $request->get('wood') + $request->get('clay') + $request->get('iron') + $request->get('food'); + + $resourceCapabilities = MailCarrier::getResourceCapabilities($village); + $necessaryMailCarriers = ceil($totalAmount / $resourceCapabilities); + $mailCarriers = DB::fetch(MailCarrier::class, 'select sum(amount) as amount from village_units where type=:type and residence_village_id=:villageId and is_traveling=false', ['villageId' => $village->id, 'type' => 'MailCarrier'])[0]->amount ?? 0; + if ($mailCarriers === 0 || $totalAmount > $resourceCapabilities) { + return new Response(View::render('error.twig', ['message' => 'Insufficient Mail Carriers']), 403); + } + + $destination = Village::get($request->get('village')); + + // event + $event = new Model(); + $event->time = (new \DateTime())->add( + \DateInterval::createFromDateString( + Unit::getTravelTime(new MailCarrier(), Village::getDistance($village->x, $village->y, $destination->x, $destination->y)) + . ' seconds' + ) + ); + $event->villageId = $village->id; + $sendResourcesEvent = new SendResources(); + $sendResourcesEvent->event = $event; + $sendResourcesEvent->wood = $request->get('wood'); + $sendResourcesEvent->clay = $request->get('clay'); + $sendResourcesEvent->iron = $request->get('iron'); + $sendResourcesEvent->food = $request->get('food'); + $sendResourcesEvent->source = $village->id; + $sendResourcesEvent->destination = $destination->id; + $sendResourcesEvent->dbInsert(); + + return new RedirectResponse( Router::generate( 'village.show', diff --git a/src/http/Controller/User.php b/src/http/Controller/User.php new file mode 100644 index 0000000..c47e32e --- /dev/null +++ b/src/http/Controller/User.php @@ -0,0 +1,57 @@ +<?php + +namespace App\http\Controller; + +use App\DB; +use App\View; +use App\http\Router; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; + +class User +{ + #[Route(path: '/account', methods: ['GET'])] + public function account(Request $request): Response + { + $user = DB::query('select username,email from users where id=:id', ['id' => $_SESSION['user']['id']])->fetch(); + + return new Response(View::render('account.twig', [ + 'user' => $user, + ])); + } + #[Route(path: '/account', methods: ['POST'])] + public function accountSave(Request $request): Response + { + $username = $request->get('username'); + $email = $request->get('email'); + + if ($request->get('password')) { + $password = password_hash($request->get('password'), PASSWORD_DEFAULT); + DB::query( + 'update users set username=:username, email=:email, password=:password where id=:id', + [ + 'username' => $username, + 'email' => $email, + 'password' => $password, + 'id' => $_SESSION['user']['id'], + ] + ); + } else { + DB::query( + 'update users set username=:username, email=:email where id=:id', + [ + 'username' => $username, + 'email' => $email, + 'id' => $_SESSION['user']['id'], + ] + ); + } + + $_SESSION['user']['username'] = $request->get('username'); + + + return new RedirectResponse(Router::generate('user.account')); + } +} diff --git a/src/http/Controller/Village.php b/src/http/Controller/Village.php index 7b1f227..2d6aa39 100644 --- a/src/http/Controller/Village.php +++ b/src/http/Controller/Village.php @@ -4,6 +4,8 @@ namespace App\http\Controller; use App\DB; use App\Guard; +use App\Model\Event\SendResources; +use App\Model\Event\SendResourcesCarriers; use App\Model\Event\SendUnits; use App\Model\Event\TrainUnits; use App\Model\Event\UpgradeBuilding; @@ -27,7 +29,7 @@ class Village join user_villages on villages.id = user_villages.village_id where user_villages.user_id=:id SQL, - ['id' => $_SESSION['user']['id']] + ['id' => $_SESSION['user']['id'] ?? -1] ); return new Response(View::render('villages.twig', [ @@ -90,6 +92,46 @@ class Village $events['SendUnits'][] = DB::convertToModel(SendUnits::class, $row);; } + $eventsResourcesSendOwn = DB::query( + <<<SQL + select * from events_send_resources as event + left join events on event.event_id = events.id + where village_id=:id + SQL, ['id' => $village->id] + )->fetchAll(); + + $eventsResourcesSendOther = DB::query( + <<<SQL + select * from events_send_resources as event + left join events on event.event_id = events.id + where (destination=:id or source=:id) and village_id!=:id and is_canceled=false + SQL, ['id' => $village->id] + )->fetchAll(); + + foreach ([...$eventsResourcesSendOwn, ...$eventsResourcesSendOther] as $row) { + $events['SendResources'][] = DB::convertToModel(SendResources::class, $row);; + } + + $eventsResourcesCarriersSendOwn = DB::query( + <<<SQL + select * from events_send_resources_carriers as event + left join events on event.event_id = events.id + where village_id=:id + SQL, ['id' => $village->id] + )->fetchAll(); + + $eventsResourcesCarriersSendOther = DB::query( + <<<SQL + select * from events_send_resources_carriers as event + left join events on event.event_id = events.id + where (destination=:id or source=:id) and village_id!=:id + SQL, ['id' => $village->id] + )->fetchAll(); + + foreach ([...$eventsResourcesCarriersSendOwn, ...$eventsResourcesCarriersSendOther] as $row) { + $events['SendResourcesCarriers'][] = DB::convertToModel(SendResourcesCarriers::class, $row);; + } + $buildings = []; foreach (Model::getBuildings($village->id, true) as $building) { $buildings[$building->type] = $building; diff --git a/src/http/Router.php b/src/http/Router.php index db75f81..62f0514 100644 --- a/src/http/Router.php +++ b/src/http/Router.php @@ -10,6 +10,7 @@ use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Loader\AnnotationFileLoader; +use Symfony\Component\Routing\Loader\AttributeFileLoader; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; @@ -28,7 +29,7 @@ class Router self::$context->fromRequest($request); self::$routes = new RouteCollection(); - $loader = new AnnotationFileLoader(new FileLocator(), new RouteLoader()); + $loader = new AttributeFileLoader(new FileLocator(), new RouteLoader()); $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__ . '/Controller')); foreach ($iterator as $file) { /**@var \SplFileInfo $file*/ diff --git a/src/http/Support/RouteLoader.php b/src/http/Support/RouteLoader.php index b0e74cb..d11011c 100644 --- a/src/http/Support/RouteLoader.php +++ b/src/http/Support/RouteLoader.php @@ -2,10 +2,10 @@ namespace App\http\Support; -use Symfony\Component\Routing\Loader\AnnotationClassLoader; +use Symfony\Component\Routing\Loader\AttributeClassLoader; use Symfony\Component\Routing\Route; -class RouteLoader extends AnnotationClassLoader +class RouteLoader extends AttributeClassLoader { protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annotation) { $route->setDefault('_', compact('class', 'method', 'annotation')); @@ -15,10 +15,9 @@ class RouteLoader extends AnnotationClassLoader { $name = parent::getDefaultRouteName($class, $method); - return str_replace( - '_', - '.', - str_replace('app_controller_', '', $name) - ); + $name = preg_replace('/app_\w+_controller_/', '', $name); + $name = str_replace('_', '.', $name); + + return $name; } } diff --git a/views/http/account.twig b/views/http/account.twig new file mode 100644 index 0000000..8b32feb --- /dev/null +++ b/views/http/account.twig @@ -0,0 +1,22 @@ +{% extends 'base.twig' %} + +{% block main %} +<h1>Account</h1> + +<form action="/account" method="post"> + <label> + Username + <input type="text" name="username" value="{{ user.username }}"> + </label> + <label> + E-Mail + <input type="email" name="email" value="{{ user.email }}"> + </label> + <label> + Password + <input type="password" name="password"> + </label> + + <input type="submit" value="Save"> +</form> +{% endblock %} diff --git a/views/http/base.twig b/views/http/base.twig index 15ddafd..bd59207 100644 --- a/views/http/base.twig +++ b/views/http/base.twig @@ -5,7 +5,12 @@ <header> <nav> <a href="/villages">Overview</a> - <a href="/logout">Logout</a> + {% if session.user %} + <a href="/account">Account</a> + <a href="/logout">Logout</a> + {% else %} + <a href="/login">Login</a> + {% endif %} </nav> <span>Logged in as {{ session.user.username }}</span> </header> diff --git a/views/http/components/timer.twig b/views/http/components/timer.twig index ccb31a7..4898a2e 100644 --- a/views/http/components/timer.twig +++ b/views/http/components/timer.twig @@ -14,12 +14,18 @@ document.addEventListener('DOMContentLoaded', function (ev) { window.location.reload(); } + const dd = Math.floor(diff/1000/60/60/24); + diff -= dd*1000*60*60*24; const hh = Math.floor(diff/1000/60/60); diff -= hh*1000*60*60; const mm = Math.floor(diff/1000/60); diff -= mm*1000*60; const ss = Math.floor(diff/1000); + timer.innerHTML = `${('00' + hh).slice(-2)}:${('00' + mm).slice(-2)}:${('00' + ss).slice(-2)}`; + if (dd > 0) { + timer.innerHTML = `${('00' + dd).slice(-2)}:${timer.innerHTML}`; + } } setTime(); }); diff --git a/views/http/village.twig b/views/http/village.twig index c157a04..a498ea6 100644 --- a/views/http/village.twig +++ b/views/http/village.twig @@ -132,7 +132,7 @@ {% endif %} {% if events['SendUnits'] %} - <h4>Send Resources / Units</h4> + <h4>Send Units</h4> <table> <thead> <tr> @@ -172,6 +172,68 @@ </tbody> </table> {% endif %} + + {% if events['SendResources'] %} + <h4>Send Resources</h4> + <table> + <thead> + <tr> + <th>Source</th> + <th>Destination</th> + <th>Resources</th> + <th>Time</th> + <th></th> + </tr> + </thead> + <tbody> + {% for event in events['SendResources'] %} + <tr> + <td>{{ village.get(event.source).name }}</td> + <td>{{ village.get(event.destination).name }}</td> + <td>Resources</td> + <td class="timer"> + {% include 'components/timer.twig' with { 'time': event.event.time|date('c') } %} + </td> + <td> + {% if event.isCanceled %} + Canceled + {% else %} + {% if event.event.villageId == village.id %} + <form action="/event/{{ event.event.id }}/cancel" method="post"> + <input type="submit" value="Cancel"> + </form> + {% endif %} + {% endif %} + </td> + </tr> + {% endfor %} + </tbody> + </table> + {% endif %} + + {% if events['SendResourcesCarriers'] %} + <h4>Send Resources Carriers</h4> + <table> + <thead> + <tr> + <th>Source</th> + <th>Destination</th> + <th>Time</th> + </tr> + </thead> + <tbody> + {% for event in events['SendResourcesCarriers'] %} + <tr> + <td>{{ village.get(event.source).name }}</td> + <td>{{ village.get(event.destination).name }}</td> + <td class="timer"> + {% include 'components/timer.twig' with { 'time': event.event.time|date('c') } %} + </td> + </tr> + {% endfor %} + </tbody> + </table> + {% endif %} </div> <div class="village__main"> @@ -344,10 +406,10 @@ {% if village.getBuilding(village.id, 'PostOffice') %} <h3>Send Resources</h3> <form action="/village/{{ village.x }}/{{ village.y }}/send-resources" method="post"> - <input type="number" min="1" name="wood" placeholder="Amount Wood" required> - <input type="number" min="1" name="clay" placeholder="Amount Clay" required> - <input type="number" min="1" name="iron" placeholder="Amount Iron" required> - <input type="number" min="1" name="food" placeholder="Amount Food" required> + <input type="number" min="0" name="wood" placeholder="Amount Wood" required> + <input type="number" min="0" name="clay" placeholder="Amount Clay" required> + <input type="number" min="0" name="iron" placeholder="Amount Iron" required> + <input type="number" min="0" name="food" placeholder="Amount Food" required> <select name="village"> {% for v in villages %} <option value="{{ v.id }}">{{ v.name }}</option> |