diff options
author | Daniel Weipert <code@drogueronin.de> | 2023-10-04 11:32:04 +0200 |
---|---|---|
committer | Daniel Weipert <code@drogueronin.de> | 2023-10-04 11:32:04 +0200 |
commit | 94a3dd52da3ae180af37c6fd0e8c24b3562da388 (patch) | |
tree | acced055660bfd65ca3e955ea26d412457ba4507 /src | |
parent | fa00b957378a393f8edbfc98ef111d35d18ecb09 (diff) |
initial commit 2
Diffstat (limited to 'src')
-rw-r--r-- | src/Controller/Map.php | 43 | ||||
-rw-r--r-- | src/Controller/Unit.php | 248 | ||||
-rw-r--r-- | src/Controller/Village.php | 79 | ||||
-rw-r--r-- | src/EventRunner.php | 6 | ||||
-rw-r--r-- | src/Model/Building.php | 6 | ||||
-rw-r--r-- | src/Model/Event/SendUnits.php | 41 | ||||
-rw-r--r-- | src/Model/Event/TrainUnits.php | 2 | ||||
-rw-r--r-- | src/Model/Unit.php | 52 | ||||
-rw-r--r-- | src/Model/Unit/Farmer.php | 3 | ||||
-rw-r--r-- | src/Model/Unit/Miner.php | 3 | ||||
-rw-r--r-- | src/Model/Unit/PitWorker.php | 3 | ||||
-rw-r--r-- | src/Model/Unit/WoodCutter.php | 5 | ||||
-rw-r--r-- | src/Model/Village.php | 17 |
13 files changed, 497 insertions, 11 deletions
diff --git a/src/Controller/Map.php b/src/Controller/Map.php new file mode 100644 index 0000000..59c1e4e --- /dev/null +++ b/src/Controller/Map.php @@ -0,0 +1,43 @@ +<?php + +namespace App\Controller; + +use App\DB; +use App\View; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; + +class Map +{ + #[Route(path: '/map/{x}/{y}/{range}', defaults: ['range' => 1], methods: ['GET'])] + public function train(Request $request): Response + { + $x = $request->get('x'); + $y = $request->get('y'); + $range = $request->get('range'); + + $statement = DB::query( + 'select * from villages where x>=:x1 and x<=:x2 and y>=:y1 and y<=:y2', + [ + 'x1' => $x - $range, + 'x2' => $x + $range, + 'y1' => $y - $range, + 'y2' => $y + $range, + ] + ); + $villages = $statement->fetchAll(); + + $map = []; + foreach ($villages as $village) { + $map[$village['x']][$village['y']] = $village; + } + + return new Response(View::render('map.twig', [ + 'x' => $x, + 'y' => $y, + 'range' => $range, + 'map' => $map, + ])); + } +} diff --git a/src/Controller/Unit.php b/src/Controller/Unit.php new file mode 100644 index 0000000..602c6e8 --- /dev/null +++ b/src/Controller/Unit.php @@ -0,0 +1,248 @@ +<?php + +namespace App\Controller; + +use App\DB; +use App\Model\Unit as Model; +use App\Model\Event; +use App\Model\Village; +use App\Router; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; + +class Unit +{ + #[Route(path: '/village/{x}/{y}/unit/{type}/create', methods: ['POST'])] + public function train(Request $request): Response + { + $village = Village::getByCoordinates($request->get('x'), $request->get('y')); + + /**@var Model $unit*/ + $unit = new (Model::resolveType($request->get('type')))(); + $unit->type = $request->get('type'); + $unit->homeVillageId = $village->id; + + $amount = intval($request->get('amount')); + + if (! Village::canTrain($village, $unit, $amount)) { + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } + + // resources + foreach (Model::getResourceRequirements($unit, $amount) as $resourceType => $resourceValue) { + $village->{$resourceType} -= $resourceValue; + } + $village->updateResources(); + + // event + $event = new Event(); + $event->type = 'TrainUnits'; + $event->time = (new \DateTime())->add(\DateInterval::createFromDateString($unit->getBuildTime($amount) . ' seconds')); + $event->payload = json_encode([ + 'type' => $request->get('type'), + 'amount' => $amount, + ]); + + DB::query( + 'insert into events (type, time, payload, village_id) VALUES (:type, :time, :payload, :id)', + ['type' => $event->type, 'time' => $event->time->format('c'), 'payload' => $event->payload, 'id' => $village->id] + ); + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } + + #[Route(path: '/village/{x}/{y}/unit/{type}/location/{lx}/{ly}/recall', methods: ['POST'])] + public function recall(Request $request): Response + { + $village = Village::getByCoordinates($request->get('x'), $request->get('y')); + $location = Village::getByCoordinates($request->get('lx'), $request->get('ly')); + + /**@var Model $unit*/ + $unit = new (Model::resolveType($request->get('type')))(); + + $amount = intval($request->get('amount')); + $amountUnits = DB::query( + 'select amount from village_units where home_village_id=:home and residence_village_id=:residence and type=:type', + ['home' => $village->id, 'residence' => $location->id, 'type' => $request->get('type')] + )->fetchColumn(); + + if ($amountUnits - $amount > 0) { + $statement = DB::query( + <<<SQL + update village_units set amount=:amount where home_village_id=:home and residence_village_id=:residence and type=:type + SQL, + ['amount' => $amountUnits - $amount, 'home' => $village->id, 'residence' => $location->id, 'type' => $request->get('type')] + ); + } else if ($amountUnits - $amount === 0) { + DB::query( + <<<SQL + delete from village_units where home_village_id=:home and residence_village_id=:residence and type=:type + SQL, + ['home' => $village->id, 'residence' => $location->id, 'type' => $request->get('type')] + ); + } + + // event + $event = new Event(); + $event->type = 'SendUnits'; + $event->time = (new \DateTime())->add( + \DateInterval::createFromDateString( + Model::getTravelTime($unit, Village::getDistance($village->x, $village->y, $location->x, $location->y)) + . ' seconds' + ) + ); + $event->payload = json_encode([ + 'type' => 'Recall', + 'unit' => $request->get('type'), + 'amount' => $amount, + 'source' => $location->id, + 'destination' => $village->id, + ]); + + DB::query( + 'insert into events (type, time, payload, village_id) VALUES (:type, :time, :payload, :id)', + ['type' => $event->type, 'time' => $event->time->format('c'), 'payload' => $event->payload, 'id' => $village->id] + ); + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } + + #[Route(path: '/village/{x}/{y}/unit/{type}/location/{lx}/{ly}/send-back', methods: ['POST'])] + public function sendBack(Request $request): Response + { + $village = Village::getByCoordinates($request->get('x'), $request->get('y')); + $location = Village::getByCoordinates($request->get('lx'), $request->get('ly')); + + /**@var Model $unit*/ + $unit = new (Model::resolveType($request->get('type')))(); + + $amount = intval($request->get('amount')); + $amountUnits = DB::query( + 'select amount from village_units where home_village_id=:home and residence_village_id=:residence and type=:type', + ['home' => $location->id, 'residence' => $village->id, 'type' => $request->get('type')] + )->fetchColumn(); + + if ($amountUnits - $amount > 0) { + $statement = DB::query( + <<<SQL + update village_units set amount=:amount where home_village_id=:home and residence_village_id=:residence and type=:type + SQL, + ['amount' => $amountUnits - $amount, 'home' => $location->id, 'residence' => $village->id, 'type' => $request->get('type')] + ); + } else if ($amountUnits - $amount === 0) { + DB::query( + <<<SQL + delete from village_units where home_village_id=:home and residence_village_id=:residence and type=:type + SQL, + ['home' => $location->id, 'residence' => $village->id, 'type' => $request->get('type')] + ); + } + + // event + $event = new Event(); + $event->type = 'SendUnits'; + $event->time = (new \DateTime())->add( + \DateInterval::createFromDateString( + Model::getTravelTime($unit, Village::getDistance($village->x, $village->y, $location->x, $location->y)) + . ' seconds' + ) + ); + $event->payload = json_encode([ + 'type' => 'SendBack', + 'unit' => $request->get('type'), + 'amount' => $amount, + 'source' => $village->id, + 'destination' => $location->id, + ]); + + DB::query( + 'insert into events (type, time, payload, village_id) VALUES (:type, :time, :payload, :id)', + ['type' => $event->type, 'time' => $event->time->format('c'), 'payload' => $event->payload, 'id' => $village->id] + ); + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } + + #[Route(path: '/village/{x}/{y}/send-units', methods: ['POST'])] + public function sendUnits(Request $request): Response + { + $village = Village::getByCoordinates($request->get('x'), $request->get('y')); + $destination = Village::get($request->get('village')); + + /**@var Model $unit*/ + $unit = new (Model::resolveType($request->get('unit')))(); + + $amount = intval($request->get('amount')); + $amountUnits = DB::query( + 'select amount from village_units where home_village_id=:home and residence_village_id=:home and type=:type', + ['home' => $village->id, 'type' => $request->get('unit')] + )->fetchColumn(); + + if ($amountUnits - $amount > 0) { + $statement = DB::query( + <<<SQL + update village_units set amount=:amount where home_village_id=:home and residence_village_id=:home and type=:type + SQL, + ['amount' => $amountUnits - $amount, 'home' => $village->id, 'type' => $request->get('unit')] + ); + } else if ($amountUnits - $amount === 0) { + DB::query( + <<<SQL + delete from village_units where home_village_id=:home and residence_village_id=:home and type=:type + SQL, + ['home' => $village->id, 'type' => $request->get('unit')] + ); + } + + // event + $event = new Event(); + $event->type = 'SendUnits'; + $event->time = (new \DateTime())->add( + \DateInterval::createFromDateString( + Model::getTravelTime($unit, Village::getDistance($village->x, $village->y, $destination->x, $destination->y)) + . ' seconds' + ) + ); + $event->payload = json_encode([ + 'type' => 'Borrow', + 'unit' => $request->get('unit'), + 'amount' => $amount, + 'source' => $village->id, + 'destination' => $destination->id, + ]); + + DB::query( + 'insert into events (type, time, payload, village_id) VALUES (:type, :time, :payload, :id)', + ['type' => $event->type, 'time' => $event->time->format('c'), 'payload' => $event->payload, 'id' => $village->id] + ); + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } +} diff --git a/src/Controller/Village.php b/src/Controller/Village.php index 3854d29..dfca298 100644 --- a/src/Controller/Village.php +++ b/src/Controller/Village.php @@ -3,9 +3,13 @@ namespace App\Controller; use App\DB; +use App\Model\Event\SendUnits; +use App\Model\Event\TrainUnits; use App\Model\Event\UpgradeBuilding; use App\Model\Village as Model; +use App\Router; use App\View; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -26,8 +30,9 @@ class Village public function show(Request $request): Response { $village = Model::getByCoordinates($request->get('x'), $request->get('y')); + $events = []; - $results = DB::query( + $eventsBuilding = DB::query( <<<SQL select events.*, village_buildings.type as building from events join village_buildings @@ -36,8 +41,7 @@ class Village SQL, ['id' => $village->id, 'type' => 'UpgradeBuilding'] )->fetchAll(); - $events = []; - foreach ($results as $row) { + foreach ($eventsBuilding as $row) { $events[$row['type']][] = [ 'event' => DB::convertToModel(UpgradeBuilding::class, $row), 'data' => [ @@ -46,9 +50,78 @@ class Village ]; } + $eventsUnits = DB::query( + <<<SQL + select * from events + where type=:type and village_id=:id + SQL, ['type' => 'TrainUnits', 'id' => $village->id] + )->fetchAll(); + + foreach ($eventsUnits as $row) { + $events[$row['type']][] = [ + 'event' => DB::convertToModel(TrainUnits::class, $row), + 'data' => json_decode($row['payload'], true), + ]; + } + + $eventsUnitsSend = DB::query( + <<<SQL + select * from events + where type=:type and (village_id=:id or (payload->>'destination')::bigint=:id) + SQL, ['type' => 'SendUnits', 'id' => $village->id] + )->fetchAll(); + + foreach ($eventsUnitsSend as $row) { + $events[$row['type']][] = [ + 'event' => DB::convertToModel(SendUnits::class, $row), + 'data' => json_decode($row['payload'], true), + ]; + } + return new Response(View::render('village.twig', [ 'village' => $village, 'events' => $events, + 'villages' => DB::fetch(Model::class, "select * from villages where id!=:id", ['id' => $village->id]), ])); } + + #[Route(path: '/village/{x}/{y}/storage/config', methods: ['POST'])] + public function storageConfig(Request $request): Response + { + $village = Model::getByCoordinates($request->get('x'), $request->get('y')); + + // calculate to max 100% + $wood = intval($request->get('wood')); + $clay = intval($request->get('clay')); + $iron = intval($request->get('iron')); + $food = intval($request->get('food')); + $total = $wood + $clay + $iron + $food; + $woodPercent = $wood / $total; + $clayPercent = $clay / $total; + $ironPercent = $iron / $total; + $foodPercent = $food / $total; + + $wood = round($woodPercent * 100); + $clay = round($clayPercent * 100); + $iron = round($ironPercent * 100); + $food = round($foodPercent * 100); + $newTotal = $wood+$clay+$iron+$food; + $food += (100 - $newTotal); + + DB::query( + <<<SQL + update village_storage_config + set wood=:wood, clay=:clay, iron=:iron, food=:food + where village_id=:id + SQL, + ['wood' => $wood, 'clay' => $clay, 'iron' => $iron, 'food' => $food, 'id' => $village->id] + ); + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } } diff --git a/src/EventRunner.php b/src/EventRunner.php index d2f1589..4f3bce9 100644 --- a/src/EventRunner.php +++ b/src/EventRunner.php @@ -16,6 +16,8 @@ class EventRunner { public function __construct() { + // Events + $results = DB::query('select * from events where time < now()')->fetchAll(); foreach ($results as $row) { @@ -79,8 +81,8 @@ class EventRunner } DB::query('delete from system where key=:key', ['key' => 'last_resource_tick']); - $value = (new \DateTime((new \DateTime())->format('Y-m-d H:i')))->format('c'); - DB::query('insert into system (key,value) VALUES (:key,:value)', ['key' => 'last_resource_tick', 'value' => json_encode($value)]); + $lastResourceTickMinute = (new \DateTime((new \DateTime())->format('Y-m-d H:i')))->format('c'); + DB::query('insert into system (key,value) VALUES (:key,:value)', ['key' => 'last_resource_tick', 'value' => json_encode($lastResourceTickMinute)]); } } } diff --git a/src/Model/Building.php b/src/Model/Building.php index eb166f9..78d1aa5 100644 --- a/src/Model/Building.php +++ b/src/Model/Building.php @@ -69,7 +69,7 @@ class Building */ public function getResourceRequirements(): array { - return $this->getResourceRequirementsForLevel($this->level); + return $this->getResourceRequirementsForLevel($this->level + 1); } /** @@ -77,10 +77,8 @@ class Building */ public function getResourceRequirementsForLevel(int $level): array { - $level += 1; - return array_map( - fn ($resourceRequirement) => ceil(log($level * 2) * $resourceRequirement * 64 * $level), + fn ($resourceRequirement) => ceil(log($level * 2) * $resourceRequirement * $_ENV['BASE_BUILDING_RESOURCE_REQUIREMENT_FACTOR'] * $level), $this->resourceRequirements ); } diff --git a/src/Model/Event/SendUnits.php b/src/Model/Event/SendUnits.php new file mode 100644 index 0000000..f104a08 --- /dev/null +++ b/src/Model/Event/SendUnits.php @@ -0,0 +1,41 @@ +<?php + +namespace App\Model\Event; + +use App\DB; +use App\Model\Event; + +class SendUnits extends Event +{ + /** + * @return void + */ + public function __invoke(): void + { + $payload = json_decode($this->payload, true); + + if ($payload['type'] === 'Recall' || $payload['type'] === 'SendBack') { + DB::query( + <<<SQL + insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id) + values (:amount, :type, false, :id, :id) + on conflict (type, home_village_id, residence_village_id) + do update set amount = village_units.amount+:amount + SQL, + ['amount' => $payload['amount'], 'type' => $payload['unit'], 'id' => $payload['destination']] + ); + } + + else if ($payload['type'] === 'Borrow') { + DB::query( + <<<SQL + insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id, created_at, updated_at) + values (:amount, :type, false, :home, :residence, now(), now()) + on conflict (type, home_village_id, residence_village_id) + do update set amount = village_units.amount+:amount + SQL, + ['amount' => $payload['amount'], 'type' => $payload['unit'], 'home' => $this->villageId, 'residence' => $payload['destination']] + ); + } + } +} diff --git a/src/Model/Event/TrainUnits.php b/src/Model/Event/TrainUnits.php index 0c7e0de..0090d0f 100644 --- a/src/Model/Event/TrainUnits.php +++ b/src/Model/Event/TrainUnits.php @@ -19,7 +19,7 @@ class TrainUnits extends Event insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id) values (:amount, :type, false, :id, :id) on conflict (type, home_village_id, residence_village_id) - do update set amount = excluded.amount+:amount + do update set amount = village_units.amount+:amount SQL, ['amount' => $payload['amount'], 'type' => $payload['type'], 'id' => $this->villageId] ); diff --git a/src/Model/Unit.php b/src/Model/Unit.php index a0d1a35..621651c 100644 --- a/src/Model/Unit.php +++ b/src/Model/Unit.php @@ -30,6 +30,48 @@ class Unit return intval(($_ENV['BASE_UNIT_BUILD_TIME_FACTOR'] / ($this->getBuilding()->level ?: 1)) * $amount); } + public static function getTravelTime(Unit $unit, int $distance): int + { + return self::getTravelTimePerCell($unit) * $distance; + } + + public static function getTravelTimePerCell(Unit $unit): int + { + return intval(ceil($unit->travelTime * $_ENV['BASE_UNIT_TRAVEL_TIME_FACTOR'])); + } + + + /** + * @return array<string, int> + */ + public static function getResourceRequirements(Unit $unit, int $amount): array + { + /**@var Building $building*/ + $building = DB::fetch( + Building::resolveType($unit->buildingType), + 'select level from village_buildings where type=:type and village_id=:id', + ['type' => $unit->buildingType, 'id' => $unit->homeVillageId] + )[0] ?? null; + + $currentAmount = DB::query( + 'select sum(amount) from village_units where type=:type and home_village_id=:id', + ['type' => $unit->type, 'id' => $unit->homeVillageId] + )->fetchColumn(); + + + return array_map( + function ($resourceRequirement) use ($amount, $currentAmount, $building) { + $r = 0; + for ($i = 0; $i <= $amount; $i++) { + $r += ceil((pow($_ENV['BASE_UNIT_RESOURCE_REQUIREMENT_BASE'], $currentAmount + 1) * $resourceRequirement * $_ENV['BASE_UNIT_RESOURCE_REQUIREMENT_FACTOR']) / ($building->level ?? 1)); + } + + return $r; + }, + $unit->resourceRequirements + ); + } + public function getPopulationDemand(): int { return $this->getPopulationDemandForAmount($this->amount); @@ -43,6 +85,16 @@ class Unit /* Relations */ + public function getHomeVillage(): Village + { + return DB::fetch(Village::class, 'select * from villages where id=:id', ['id' => $this->homeVillageId])[0]; + } + + public function getResidenceVillage(): Village + { + return DB::fetch(Village::class, 'select * from villages where id=:id', ['id' => $this->residenceVillageId])[0]; + } + public function getBuilding(): ?Building { return Village::getBuilding($this->homeVillageId, $this->buildingType); diff --git a/src/Model/Unit/Farmer.php b/src/Model/Unit/Farmer.php index de37802..8e3c1ea 100644 --- a/src/Model/Unit/Farmer.php +++ b/src/Model/Unit/Farmer.php @@ -11,5 +11,8 @@ class Farmer extends Unit public int $populationDemandFactor = 1; public array $resourceRequirements = [ 'wood' => 1.0, + 'clay' => 1.0, + 'iron' => 1.0, + 'food' => 0, ]; } diff --git a/src/Model/Unit/Miner.php b/src/Model/Unit/Miner.php index ae6c00a..2d246ef 100644 --- a/src/Model/Unit/Miner.php +++ b/src/Model/Unit/Miner.php @@ -11,5 +11,8 @@ class Miner extends Unit public int $populationDemandFactor = 1; public array $resourceRequirements = [ 'wood' => 1.0, + 'clay' => 1.0, + 'iron' => 2.0, + 'food' => 2.0, ]; } diff --git a/src/Model/Unit/PitWorker.php b/src/Model/Unit/PitWorker.php index 4f873b4..d2a52c3 100644 --- a/src/Model/Unit/PitWorker.php +++ b/src/Model/Unit/PitWorker.php @@ -11,5 +11,8 @@ class PitWorker extends Unit public int $populationDemandFactor = 1; public array $resourceRequirements = [ 'wood' => 1.0, + 'clay' => 2.0, + 'iron' => 1.0, + 'food' => 2.0, ]; } diff --git a/src/Model/Unit/WoodCutter.php b/src/Model/Unit/WoodCutter.php index 17923ca..d8c2c42 100644 --- a/src/Model/Unit/WoodCutter.php +++ b/src/Model/Unit/WoodCutter.php @@ -10,6 +10,9 @@ class WoodCutter extends Unit public int $travelTime = 1; public int $populationDemandFactor = 1; public array $resourceRequirements = [ - 'wood' => 1.0, + 'wood' => 2.0, + 'clay' => 1.0, + 'iron' => 1.0, + 'food' => 2.0, ]; } diff --git a/src/Model/Village.php b/src/Model/Village.php index cd1c749..b1ab19e 100644 --- a/src/Model/Village.php +++ b/src/Model/Village.php @@ -41,6 +41,18 @@ class Village return true; } + public static function canTrain(Village $village, Unit $unit, int $amount): bool + { + $resourceRequirements = Unit::getResourceRequirements($unit, $amount); + foreach ($resourceRequirements as $resourceType => $requirement) { + if ($village->$resourceType < $requirement) { + return false; + } + } + + return true; + } + /* DB - Actions */ public static function get(int $id): ?Village @@ -61,6 +73,11 @@ class Village ); } + public static function getDistance(int $x, int $y, int $dx, int $dy): int + { + return abs($x - $dx) + abs($y - $dy); + } + /* DB - Relations */ public static function getBuildings(int $villageId): array |