summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Weipert <code@drogueronin.de>2023-11-29 09:35:27 +0100
committerDaniel Weipert <code@drogueronin.de>2023-11-29 09:35:27 +0100
commit3afcaef927391db23fe23c6c8c26b8960e8dae32 (patch)
tree143b9f6df9e8c795c8c6ed901bffdc7119f40df1
parentc4ce3e884a6aa527bcc138771617215cf03265a4 (diff)
intermediate commit
-rw-r--r--.env.example1
-rw-r--r--.gitignore1
-rw-r--r--bin/db.php (renamed from bin/villages.php)41
-rw-r--r--public/assets/img/map.jpgbin0 -> 1149770 bytes
-rw-r--r--public/assets/style.css82
-rw-r--r--src/Controller/Building.php8
-rw-r--r--src/Controller/Event.php65
-rw-r--r--src/Controller/Unit.php2
-rw-r--r--src/Controller/Village.php27
-rw-r--r--src/DB.php2
-rw-r--r--src/EventRunner.php9
-rw-r--r--src/Model.php2
-rw-r--r--src/Model/Building.php10
-rw-r--r--src/Model/Building/ClayPit.php2
-rw-r--r--src/Model/Building/Embassy.php17
-rw-r--r--src/Model/Building/Farm.php2
-rw-r--r--src/Model/Building/IronMine.php2
-rw-r--r--src/Model/Building/Marketplace.php17
-rw-r--r--src/Model/Building/Storage.php4
-rw-r--r--src/Model/Building/WoodCutter.php4
-rw-r--r--src/Model/Event.php3
-rw-r--r--src/Model/Event/BaseEvent.php14
-rw-r--r--src/Model/Event/SendUnits.php87
-rw-r--r--src/Model/Event/UpgradeBuilding.php32
-rw-r--r--src/Model/Unit.php30
-rw-r--r--src/Model/Unit/Diplomat.php18
-rw-r--r--src/Model/Unit/Merchant.php18
-rw-r--r--src/Model/Village.php46
-rw-r--r--views/components/timer.twig3
-rw-r--r--views/map.twig28
-rw-r--r--views/village.twig52
-rw-r--r--views/villages.twig4
32 files changed, 542 insertions, 91 deletions
diff --git a/.env.example b/.env.example
index 072c25f..6cd359f 100644
--- a/.env.example
+++ b/.env.example
@@ -10,3 +10,4 @@ BASE_UNIT_BUILD_TIME_FACTOR=256
BASE_UNIT_TRAVEL_TIME_FACTOR=60
BASE_UNIT_RESOURCE_REQUIREMENT_FACTOR=64
BASE_RESOURCE_GENERATION_FACTOR=128
+BASE_STORAGE_CAPACITY_FACTOR=2048
diff --git a/.gitignore b/.gitignore
index 02166b7..c87432b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
.env
+config.yml
/vendor/
diff --git a/bin/villages.php b/bin/db.php
index 092a754..951c0b3 100644
--- a/bin/villages.php
+++ b/bin/db.php
@@ -25,8 +25,8 @@ DB::query(<<<SQL
"food" bigint not null,
"satisfaction" bigint not null,
- "created_at" timestamp(0) not null,
- "updated_at" timestamp(0) not null,
+ "created_at" timestamp(0) not null default current_timestamp,
+ "updated_at" timestamp(0) not null default current_timestamp,
unique ("x", "y")
);
@@ -43,8 +43,8 @@ DB::query(<<<SQL
constraint "relation_village"
foreign key ("village_id") references villages("id") on delete cascade,
- "created_at" timestamp(0) not null,
- "updated_at" timestamp(0) not null,
+ "created_at" timestamp(0) not null default current_timestamp,
+ "updated_at" timestamp(0) not null default current_timestamp,
unique ("type", "village_id")
);
@@ -67,8 +67,8 @@ DB::query(<<<SQL
constraint "relation_village_residence"
foreign key ("residence_village_id") references villages("id") on delete cascade,
- "created_at" timestamp(0) not null,
- "updated_at" timestamp(0) not null,
+ "created_at" timestamp(0) not null default current_timestamp,
+ "updated_at" timestamp(0) not null default current_timestamp,
unique ("type", "home_village_id", "residence_village_id")
);
@@ -87,8 +87,8 @@ DB::query(<<<SQL
constraint "relation_village"
foreign key ("village_id") references villages("id") on delete cascade,
- "created_at" timestamp(0) not null,
- "updated_at" timestamp(0) not null
+ "created_at" timestamp(0) not null default current_timestamp,
+ "updated_at" timestamp(0) not null default current_timestamp
);
SQL);
@@ -96,9 +96,7 @@ DB::query(<<<SQL
create table if not exists "events" (
"id" bigserial primary key,
- "type" character varying(255) not null,
"time" timestamp(0) not null,
- "payload" jsonb not null,
"village_id" bigint not null,
constraint "relation_village"
@@ -110,6 +108,21 @@ DB::query(<<<SQL
SQL);
DB::query(<<<SQL
+ create table if not exists "events_upgrade_building" (
+ "id" bigserial primary key,
+
+ "event_id" bigint not null,
+ constraint "relation_event"
+ foreign key ("event_id") references events("id") on delete cascade,
+
+ "type" character varying(255) not null,
+
+ "created_at" timestamp(0) not null default current_timestamp,
+ "updated_at" timestamp(0) not null default current_timestamp
+ );
+SQL);
+
+DB::query(<<<SQL
create table if not exists "users" (
"id" bigserial primary key,
@@ -117,8 +130,8 @@ DB::query(<<<SQL
"password" character varying(255) not null,
"email" character varying(255) not null,
- "created_at" timestamp(0) not null,
- "updated_at" timestamp(0) not null,
+ "created_at" timestamp(0) not null default current_timestamp,
+ "updated_at" timestamp(0) not null default current_timestamp,
unique ("username")
);
@@ -134,8 +147,8 @@ DB::query(<<<SQL
constraint "relation_user"
foreign key ("user_id") references users("id") on delete cascade,
- "created_at" timestamp(0) not null,
- "updated_at" timestamp(0) not null
+ "created_at" timestamp(0) not null default current_timestamp,
+ "updated_at" timestamp(0) not null default current_timestamp
);
SQL);
diff --git a/public/assets/img/map.jpg b/public/assets/img/map.jpg
new file mode 100644
index 0000000..0ad74a7
--- /dev/null
+++ b/public/assets/img/map.jpg
Binary files differ
diff --git a/public/assets/style.css b/public/assets/style.css
index 8a40e43..e7472ed 100644
--- a/public/assets/style.css
+++ b/public/assets/style.css
@@ -3,6 +3,10 @@ body {
color: #000;
}
+* {
+ box-sizing: border-box;
+}
+
.wrap {
max-width: 1200px;
@@ -63,6 +67,22 @@ button:disabled, input[type="submit"]:disabled {
background-image: url("/assets/img/icons/1F4E5.svg");
}
+.icon-arrow-up {
+ background-image: url("/assets/img/icons/1F53A.svg");
+}
+.icon-arrow-down {
+ background-image: url("/assets/img/icons/1F53A.svg");
+ transform: rotate(180deg);
+}
+.icon-arrow-left {
+ background-image: url("/assets/img/icons/1F53A.svg");
+ transform: rotate(270deg);
+}
+.icon-arrow-right {
+ background-image: url("/assets/img/icons/1F53A.svg");
+ transform: rotate(90deg);
+}
+
/* Village */
@@ -91,14 +111,76 @@ button:disabled, input[type="submit"]:disabled {
/* Map */
+.map {
+ position: relative;
+ padding: 2rem;
+}
+
+.map__up, .map__down {
+ position: absolute;
+ width: 100%;
+ text-align: center;
+}
+
+.map__up {
+ top: 0;
+ left: 0;
+}
+
+.map__down {
+ bottom: 0;
+ left: 0;
+}
+
+.map__left, .map__right {
+ position: absolute;
+ height: 100%;
+
+ display: flex;
+ align-items: center;
+}
+
+.map__left {
+ left: 0;
+ top: 0;
+}
+
+.map__right {
+ right: 0;
+ top: 0;
+}
+
+.map .icon {
+ font-size: 1.5rem;
+}
+
+
.map__villages {
--map-range: 1;
display: grid;
+
+ background-image: url('/assets/img/map.jpg');
+ background-position: center;
+ background-size: cover;
+
+ min-height: 75vh;
}
+
.map__village {
border: 1px solid #000;
min-width: 1rem;
min-height: 1rem;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.map__village a {
+ text-decoration: none;
+ border: 2px solid #fff;
+ padding: 0 0.25rem;
+ background: rgba(255, 255, 255, 0.8);
}
diff --git a/src/Controller/Building.php b/src/Controller/Building.php
index d8fe656..6199286 100644
--- a/src/Controller/Building.php
+++ b/src/Controller/Building.php
@@ -5,6 +5,7 @@ namespace App\Controller;
use App\DB;
use App\Model\Building as Model;
use App\Model\Event;
+use App\Model\Event\UpgradeBuilding;
use App\Model\Village;
use App\Router;
use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -18,7 +19,7 @@ class Building
public function levelUp(Request $request): Response
{
$village = Village::getByCoordinates($request->get('x'), $request->get('y'));
- $building = Model::getByVillage($village->id, $request->get('type'));
+ $building = Model::getByVillage($village->id, $request->get('type')) ?? Model::getEmpty($village->id, $request->get('type'));
// resources
foreach ($building->getResourceRequirements() as $resourceType => $resourceValue) {
@@ -27,11 +28,12 @@ class Building
$village->updateResources();
// event
- $event = new Event();
+ $event = new UpgradeBuilding();
$event->type = 'UpgradeBuilding';
$event->time = (new \DateTime())->add(\DateInterval::createFromDateString($building->getBuildTime() . ' seconds'));
$event->payload = json_encode([
- 'id' => $building->id,
+ 'type' => $building->type,
+ 'village_id' => $building->villageId,
]);
DB::query(
diff --git a/src/Controller/Event.php b/src/Controller/Event.php
new file mode 100644
index 0000000..a6538fe
--- /dev/null
+++ b/src/Controller/Event.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace App\Controller;
+
+use App\DB;
+use App\Model\Event as Model;
+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 Event
+{
+ #[Route(path: '/village/{x}/{y}/send-resources', methods: ['POST'])]
+ public function sendResources(Request $request): Response
+ {
+ return new RedirectResponse(
+ Router::generate(
+ 'village.show',
+ ['x' => $request->get('x'), 'y' => $request->get('y')]
+ )
+ );
+ }
+
+ #[Route(path: '/event/{id}/cancel', methods: ['POST'])]
+ public function cancel(Request $request): Response
+ {
+ $event = DB::fetch(Model::class, 'select * from events where id=:id', ['id' => $request->get('id')])[0] ?? null;
+ $village = Village::get($event->villageId);
+
+ if ($event->type === 'SendUnits') {
+ $payload = json_decode($event->payload, true);
+
+ if ($payload['type'] === 'SendBack') {
+ $cancelTimeDiff = $event->createdAt->diff(new \DateTime());
+ $cancelTime = (new \DateTime())->add($cancelTimeDiff);
+
+ $cancelPayload = array_replace_recursive($payload, [
+ 'cancel' => [
+ 'home' => $payload['destination'],
+ 'residence' => $payload['source'],
+ ],
+ ]);
+
+ DB::query(
+ 'update events set time=:time, payload=:payload where id=:id',
+ ['time' => $cancelTime->format('c'), 'payload' => json_encode($cancelPayload), 'id' => $request->get('id')]
+ );
+ }
+ }
+
+ else {
+ DB::query('delete from event where id=:id', ['id' => $request->get('id')]);
+ }
+
+ return new RedirectResponse(
+ Router::generate(
+ 'village.show',
+ ['x' => $village->x, 'y' => $village->y]
+ )
+ );
+ }
+}
diff --git a/src/Controller/Unit.php b/src/Controller/Unit.php
index 602c6e8..c57ff01 100644
--- a/src/Controller/Unit.php
+++ b/src/Controller/Unit.php
@@ -226,7 +226,7 @@ class Unit
)
);
$event->payload = json_encode([
- 'type' => 'Borrow',
+ 'type' => $request->get('type'),
'unit' => $request->get('unit'),
'amount' => $amount,
'source' => $village->id,
diff --git a/src/Controller/Village.php b/src/Controller/Village.php
index dfca298..59e6d4b 100644
--- a/src/Controller/Village.php
+++ b/src/Controller/Village.php
@@ -34,9 +34,7 @@ class Village
$eventsBuilding = DB::query(
<<<SQL
- select events.*, village_buildings.type as building from events
- join village_buildings
- on village_buildings.id=(events.payload->'id')::bigint
+ select * from events
where events.village_id=:id and events.type=:type
SQL, ['id' => $village->id, 'type' => 'UpgradeBuilding']
)->fetchAll();
@@ -44,9 +42,7 @@ class Village
foreach ($eventsBuilding as $row) {
$events[$row['type']][] = [
'event' => DB::convertToModel(UpgradeBuilding::class, $row),
- 'data' => [
- 'building' => $row['building'],
- ],
+ 'data' => json_decode($row['payload']),
];
}
@@ -64,23 +60,36 @@ class Village
];
}
- $eventsUnitsSend = DB::query(
+ $eventsUnitsSendOwn = DB::query(
<<<SQL
select * from events
- where type=:type and (village_id=:id or (payload->>'destination')::bigint=:id)
+ where type=:type and village_id=:id
SQL, ['type' => 'SendUnits', 'id' => $village->id]
)->fetchAll();
- foreach ($eventsUnitsSend as $row) {
+ $eventsUnitsSendOther = DB::query(
+ <<<SQL
+ select * from events
+ where type=:type and (payload->>'destination')::bigint=:id and (payload->>'cancel') is null
+ SQL, ['type' => 'SendUnits', 'id' => $village->id]
+ )->fetchAll();
+
+ foreach ([...$eventsUnitsSendOwn, ...$eventsUnitsSendOther] as $row) {
$events[$row['type']][] = [
'event' => DB::convertToModel(SendUnits::class, $row),
'data' => json_decode($row['payload'], true),
];
}
+ $buildings = [];
+ foreach (Model::getBuildings($village->id, true) as $building) {
+ $buildings[$building->type] = $building;
+ }
+
return new Response(View::render('village.twig', [
'village' => $village,
'events' => $events,
+ 'buildings' => $buildings,
'villages' => DB::fetch(Model::class, "select * from villages where id!=:id", ['id' => $village->id]),
]));
}
diff --git a/src/DB.php b/src/DB.php
index 4e34d32..82a81c4 100644
--- a/src/DB.php
+++ b/src/DB.php
@@ -3,7 +3,7 @@
namespace App;
class DB {
- private static \PDO $connection;
+ public static \PDO $connection;
public static function init(): void
{
diff --git a/src/EventRunner.php b/src/EventRunner.php
index 4f3bce9..a071d32 100644
--- a/src/EventRunner.php
+++ b/src/EventRunner.php
@@ -31,6 +31,15 @@ class EventRunner
);
}
+ # Upgrade Building
+
+ $results = DB::query(<<<SQL
+ select * from events
+ inner join event_upgrade_building as event
+ on events.id = event.event_id
+ where events.time < now()
+ SQL)->fetchAll();
+
// Resources
diff --git a/src/Model.php b/src/Model.php
index 5f45ed4..2720486 100644
--- a/src/Model.php
+++ b/src/Model.php
@@ -9,7 +9,7 @@ class Model
$object = new $cast();
foreach (get_class_vars(get_class($original)) as $property => $_) {
- if (! empty($original->$property) && empty($object->$property)) {
+ if (isset($original->$property) && ! isset($object->$property)) {
$object->$property = $original->$property;
}
}
diff --git a/src/Model/Building.php b/src/Model/Building.php
index 78d1aa5..7cae067 100644
--- a/src/Model/Building.php
+++ b/src/Model/Building.php
@@ -54,6 +54,16 @@ class Building
return isset($results[0]) ? $results[0]->cast() : null;
}
+ public static function getEmpty(int $villageId, string $buildingType): Building
+ {
+ $building = new Building();
+ $building->type = $buildingType;
+ $building->level = 0;
+ $building->villageId = $villageId;
+
+ return $building->cast();
+ }
+
public function getBuildTime(): int
{
diff --git a/src/Model/Building/ClayPit.php b/src/Model/Building/ClayPit.php
index 8127818..b4905eb 100644
--- a/src/Model/Building/ClayPit.php
+++ b/src/Model/Building/ClayPit.php
@@ -10,6 +10,8 @@ class ClayPit extends ResourceGenerator
public array $resourceRequirements = [
'wood' => 1.0,
+ 'clay' => 2.0,
+ 'iron' => 1.0,
];
public string $resourceType = 'clay';
diff --git a/src/Model/Building/Embassy.php b/src/Model/Building/Embassy.php
new file mode 100644
index 0000000..3be1f7f
--- /dev/null
+++ b/src/Model/Building/Embassy.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Model\Building;
+
+use App\Model\Building;
+
+class Embassy extends Building
+{
+ public int $buildTimeFactor = 1;
+ public int $maxLevel = 25;
+
+ public array $resourceRequirements = [
+ 'wood' => 25.0,
+ 'clay' => 25.0,
+ 'iron' => 30.0,
+ ];
+}
diff --git a/src/Model/Building/Farm.php b/src/Model/Building/Farm.php
index aaa58b5..222d8cd 100644
--- a/src/Model/Building/Farm.php
+++ b/src/Model/Building/Farm.php
@@ -13,6 +13,8 @@ class Farm extends ResourceGenerator
public array $resourceRequirements = [
'wood' => 1.0,
+ 'clay' => 1.0,
+ 'iron' => 1.0,
];
public string $resourceType = 'food';
diff --git a/src/Model/Building/IronMine.php b/src/Model/Building/IronMine.php
index 4bf5cc6..d240e3a 100644
--- a/src/Model/Building/IronMine.php
+++ b/src/Model/Building/IronMine.php
@@ -10,6 +10,8 @@ class IronMine extends ResourceGenerator
public array $resourceRequirements = [
'wood' => 1.0,
+ 'clay' => 1.0,
+ 'iron' => 2.0,
];
public string $resourceType = 'iron';
diff --git a/src/Model/Building/Marketplace.php b/src/Model/Building/Marketplace.php
new file mode 100644
index 0000000..714de0d
--- /dev/null
+++ b/src/Model/Building/Marketplace.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Model\Building;
+
+use App\Model\Building;
+
+class Marketplace extends Building
+{
+ public int $buildTimeFactor = 1;
+ public int $maxLevel = 25;
+
+ public array $resourceRequirements = [
+ 'wood' => 10.0,
+ 'clay' => 10.0,
+ 'iron' => 8.0,
+ ];
+}
diff --git a/src/Model/Building/Storage.php b/src/Model/Building/Storage.php
index fde4c4e..de2df92 100644
--- a/src/Model/Building/Storage.php
+++ b/src/Model/Building/Storage.php
@@ -12,11 +12,13 @@ class Storage extends Building
public array $resourceRequirements = [
'wood' => 1.0,
+ 'clay' => 1.0,
+ 'iron' => 1.0,
];
public function getCapacity(): int
{
- return $this->level * 2560;
+ return $this->level * $_ENV['BASE_STORAGE_CAPACITY_FACTOR'];
}
public function getResourceCapacity(string $resourceType): int
diff --git a/src/Model/Building/WoodCutter.php b/src/Model/Building/WoodCutter.php
index 86bde9b..726cdbc 100644
--- a/src/Model/Building/WoodCutter.php
+++ b/src/Model/Building/WoodCutter.php
@@ -9,7 +9,9 @@ class WoodCutter extends ResourceGenerator
public int $maxLevel = 25;
public array $resourceRequirements = [
- 'wood' => 1.0,
+ 'wood' => 2.0,
+ 'clay' => 1.0,
+ 'iron' => 1.0,
];
public string $resourceType = 'wood';
diff --git a/src/Model/Event.php b/src/Model/Event.php
index aa235f9..b4c131e 100644
--- a/src/Model/Event.php
+++ b/src/Model/Event.php
@@ -14,6 +14,9 @@ class Event
public int $villageId;
+ public \DateTime $createdAt;
+ public \DateTime $updatedAt;
+
/* OOP */
diff --git a/src/Model/Event/BaseEvent.php b/src/Model/Event/BaseEvent.php
new file mode 100644
index 0000000..d3cc3fb
--- /dev/null
+++ b/src/Model/Event/BaseEvent.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace App\Model\Event;
+
+abstract class BaseEvent
+{
+ public int $id;
+ public int $eventId;
+
+ public \DateTime $createdAt;
+ public \DateTime $updatedAt;
+
+ abstract function sqlInsert(): void;
+}
diff --git a/src/Model/Event/SendUnits.php b/src/Model/Event/SendUnits.php
index f104a08..bf81031 100644
--- a/src/Model/Event/SendUnits.php
+++ b/src/Model/Event/SendUnits.php
@@ -14,28 +14,75 @@ class SendUnits extends Event
{
$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']]
- );
+ if (isset($payload['cancel'])) {
+ if ($payload['type'] === 'SendBack') {
+ $payload['source'] = $payload['cancel']['home'];
+ $payload['destination'] = $payload['cancel']['residence'];
+
+ $this->borrow($payload);
+ }
}
- 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']]
- );
+ else {
+ if ($payload['type'] === 'Recall' || $payload['type'] === 'SendBack') {
+ $this->return($payload);
+ }
+
+ else if ($payload['type'] === 'Borrow') {
+ $this->borrow($payload);
+ }
+
+ else if ($payload['type'] === 'Gift') {
+ $this->gift($payload);
+ }
}
}
+
+ /**
+ * @param array $payload
+ */
+ private function return(array $payload): void
+ {
+ 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']]
+ );
+ }
+
+ /**
+ * @param array $payload
+ */
+ private function borrow(array $payload): void
+ {
+ DB::query(
+ <<<SQL
+ insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id)
+ values (:amount, :type, false, :home, :residence)
+ 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' => $payload['source'], 'residence' => $payload['destination']]
+ );
+ }
+
+ /**
+ * @param array $payload
+ */
+ private function gift(array $payload): void
+ {
+ DB::query(
+ <<<SQL
+ insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id)
+ values (:amount, :type, false, :home, :residence)
+ 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' => $payload['destination'], 'residence' => $payload['destination']]
+ );
+ }
}
diff --git a/src/Model/Event/UpgradeBuilding.php b/src/Model/Event/UpgradeBuilding.php
index c014cfe..f4f427d 100644
--- a/src/Model/Event/UpgradeBuilding.php
+++ b/src/Model/Event/UpgradeBuilding.php
@@ -5,23 +5,43 @@ namespace App\Model\Event;
use App\DB;
use App\Model\Event;
-class UpgradeBuilding extends Event
+class UpgradeBuilding extends BaseEvent
{
+ public Event $event;
+ public string $type;
+
+ public function __construct(Event $event, string $type)
+ {
+ $this->event = $event;
+ $this->type = $type;
+ }
+
/**
* @return void
*/
public function __invoke(): void
{
- $payload = json_decode($this->payload, true);
+ DB::query(
+ <<<SQL
+ insert into village_buildings (level, type, village_id)
+ values (1, :type, :village_id)
+ on conflict (type, village_id)
+ do update set level = village_buildings.level+1
+ SQL,
+ ['type' => $this->type, 'village_id' => $this->event->villageId]
+ );
+ }
+ public function sqlInsert(): void
+ {
DB::query(
- 'update village_buildings set level=level+1 where id=:id',
- ['id' => $payload['id']]
+ 'insert into events (time, village_id) values (:time, :village_id)',
+ ['time' => $this->event->time->format('c'), 'village_id' => $this->event->villageId]
);
DB::query(
- 'delete from events where id=:id',
- ['id' => $this->id]
+ 'insert into events_upgrade_building (event_id, type) values (:event_id, :type)',
+ ['event_id' => DB::$connection->lastInsertId(), $this->type]
);
}
}
diff --git a/src/Model/Unit.php b/src/Model/Unit.php
index 621651c..d563682 100644
--- a/src/Model/Unit.php
+++ b/src/Model/Unit.php
@@ -25,9 +25,26 @@ class Unit
public array $resourceRequirements = [];
+ public static function getEmpty(int $villageId, string $unitType): Unit
+ {
+ $unit = new Unit();
+ $unit->type = $unitType;
+ $unit->homeVillageId = $villageId;
+ $unit->amount = 0;
+
+ return $unit->cast();
+ }
+
+
public function getBuildTime(int $amount): int
{
- return intval(($_ENV['BASE_UNIT_BUILD_TIME_FACTOR'] / ($this->getBuilding()->level ?: 1)) * $amount);
+ $building = $this->getBuilding();
+
+ if (! $building) {
+ return -1;
+ }
+
+ return intval(($_ENV['BASE_UNIT_BUILD_TIME_FACTOR'] / ($building->level ?: 1)) * $amount);
}
public static function getTravelTime(Unit $unit, int $distance): int
@@ -63,7 +80,16 @@ class Unit
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));
+ $r += ceil(
+ (
+ pow(
+ $_ENV['BASE_UNIT_RESOURCE_REQUIREMENT_BASE'],
+ $currentAmount + 1
+ )
+ * $resourceRequirement * $_ENV['BASE_UNIT_RESOURCE_REQUIREMENT_FACTOR']
+ )
+ / ($building->level ?? 1)
+ );
}
return $r;
diff --git a/src/Model/Unit/Diplomat.php b/src/Model/Unit/Diplomat.php
new file mode 100644
index 0000000..e263cd3
--- /dev/null
+++ b/src/Model/Unit/Diplomat.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Model\Unit;
+
+use App\Model\Unit;
+
+class Diplomat extends Unit
+{
+ public string $buildingType = 'Embassy';
+ public int $travelTime = 1;
+ public int $populationDemandFactor = 1;
+ public array $resourceRequirements = [
+ 'wood' => 5.0,
+ 'clay' => 5.0,
+ 'iron' => 5.0,
+ 'food' => 5.0,
+ ];
+}
diff --git a/src/Model/Unit/Merchant.php b/src/Model/Unit/Merchant.php
new file mode 100644
index 0000000..61e2090
--- /dev/null
+++ b/src/Model/Unit/Merchant.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Model\Unit;
+
+use App\Model\Unit;
+
+class Merchant extends Unit
+{
+ public string $buildingType = 'Marketplace';
+ public int $travelTime = 1;
+ public int $populationDemandFactor = 1;
+ public array $resourceRequirements = [
+ 'wood' => 2.0,
+ 'clay' => 2.0,
+ 'iron' => 2.0,
+ 'food' => 2.0,
+ ];
+}
diff --git a/src/Model/Village.php b/src/Model/Village.php
index b1ab19e..6c80ed0 100644
--- a/src/Model/Village.php
+++ b/src/Model/Village.php
@@ -80,10 +80,21 @@ class Village
/* DB - Relations */
- public static function getBuildings(int $villageId): array
+ public static function getBuildings(int $villageId, bool $withEmpty = false): array
{
$buildings = DB::fetch(Building::class, 'select * from village_buildings where village_id=:id', ['id' => $villageId]);
+ if ($withEmpty) {
+ $nonBuiltBuildings = array_diff(
+ ['TownHall', 'Embassy', 'Marketplace', 'Storage', 'WoodCutter', 'ClayPit', 'IronMine', 'Farm'],
+ array_column($buildings, 'type'),
+ );
+
+ foreach ($nonBuiltBuildings as $type) {
+ $buildings[] = Building::getEmpty($villageId, $type);
+ }
+ }
+
return array_map(function (Building $building) {
return $building->cast();
}, $buildings);
@@ -121,30 +132,51 @@ class Village
public const FETCH_UNIT_SUPPORT_AT_HOME = 3;
public const FETCH_UNIT_RESIDENCE = 4;
+ public const RETURN_UNIT_EXISTING = 1;
+ public const RETURN_UNIT_ALL = 2;
+ public const RETURN_UNIT_TRAINABLE = 3;
+
public static function getUnit(string $unitType, int $flag): ?Unit
{
}
/**
- * @param int $flag
+ * @param int $fetchFlag
*
* @return array<int, Unit>
*/
- public static function getUnits(int $villageId, $flag = Village::FETCH_UNIT_ALL): array
+ public static function getUnits(int $villageId, int $fetchFlag = Village::FETCH_UNIT_ALL, int $returnFlag = Village::RETURN_UNIT_EXISTING): array
{
- if ($flag == Village::FETCH_UNIT_HOME_AT_HOME) {
+ if ($fetchFlag == Village::FETCH_UNIT_HOME_AT_HOME) {
$units = DB::fetch(Unit::class, 'select * from village_units where home_village_id=:id and residence_village_id=:id', ['id' => $villageId]);
}
- else if ($flag == Village::FETCH_UNIT_HOME_AT_SUPPORT) {
+ else if ($fetchFlag == Village::FETCH_UNIT_HOME_AT_SUPPORT) {
$units = DB::fetch(Unit::class, 'select * from village_units where home_village_id=:id and residence_village_id!=:id', ['id' => $villageId]);
}
- else if ($flag == Village::FETCH_UNIT_SUPPORT_AT_HOME) {
+ else if ($fetchFlag == Village::FETCH_UNIT_SUPPORT_AT_HOME) {
$units = DB::fetch(Unit::class, 'select * from village_units where home_village_id!=:id and residence_village_id=:id', ['id' => $villageId]);
}
- else if ($flag == Village::FETCH_UNIT_RESIDENCE) {
+ else if ($fetchFlag == Village::FETCH_UNIT_RESIDENCE) {
$units = DB::fetch(Unit::class, 'select * from village_units where residence_village_id=:id', ['id' => $villageId]);
}
+ if ($returnFlag == Village::RETURN_UNIT_ALL || $returnFlag == Village::RETURN_UNIT_TRAINABLE) {
+ $nonExistingUnits = array_diff(
+ ['WoodCutter', 'PitWorker', 'Miner', 'Farmer', 'Merchant', 'Diplomat'],
+ array_column($units, 'type'),
+ );
+
+ foreach ($nonExistingUnits as $type) {
+ $units[] = Unit::getEmpty($villageId, $type);
+ }
+
+ if ($returnFlag == Village::RETURN_UNIT_TRAINABLE) {
+ $units = array_filter($units, function (Unit $unit) {
+ return !! $unit->cast()->getBuilding();
+ });
+ }
+ }
+
return array_map(function (Unit $unit) {
return $unit->cast();
}, $units);
diff --git a/views/components/timer.twig b/views/components/timer.twig
index 97977da..ccb31a7 100644
--- a/views/components/timer.twig
+++ b/views/components/timer.twig
@@ -9,8 +9,9 @@ document.addEventListener('DOMContentLoaded', function (ev) {
const interval = setInterval(setTime, 1000);
function setTime() {
let diff = time - new Date();
- if (diff <= -1) {
+ if (diff <= 0) {
clearInterval(interval);
+ window.location.reload();
}
const hh = Math.floor(diff/1000/60/60);
diff --git a/views/map.twig b/views/map.twig
index 43f1a5b..29f0294 100644
--- a/views/map.twig
+++ b/views/map.twig
@@ -3,30 +3,40 @@
{% block main %}
<div class="map">
<div class="map__up">
- <a href="/map/{{ x }}/{{ y - 1 }}">Up</a>
+ <a href="/map/{{ x }}/{{ y - 1 }}">
+ <i class="icon icon-arrow-up"></i>
+ </a>
</div>
<div>
<div class="map__left">
- <a href="/map/{{ x - 1 }}/{{ y }}">Left</a>
+ <a href="/map/{{ x - 1 }}/{{ y }}">
+ <i class="icon icon-arrow-left"></i>
+ </a>
</div>
<div class="map__villages" style="grid-template-columns: repeat({{ range*2+1 }}, 1fr); grid-template-rows: repeat({{ range*2+1 }}, 1fr);">
{% for row in range(-range, range) %}
{% for column in range(-range, range) %}
{% set village = map[x + column][y + row] %}
- <div class="map__village">
- <a href="/village/{{ village.x }}/{{ village.y }}">
- {{ map[x + column][y + row].name }}
- </a>
- </div>
+ <div class="map__village">
+ {% if village %}
+ <a href="/village/{{ village.x }}/{{ village.y }}">
+ {{ map[x + column][y + row].name }}
+ </a>
+ {% endif %}
+ </div>
{% endfor %}
{% endfor %}
</div>
<div class="map__right">
- <a href="/map/{{ x + 1 }}/{{ y }}">Right</a>
+ <a href="/map/{{ x + 1 }}/{{ y }}">
+ <i class="icon icon-arrow-right"></i>
+ </a>
</div>
</div>
<div class="map__down">
- <a href="/map/{{ x }}/{{ y + 1 }}">Down</a>
+ <a href="/map/{{ x }}/{{ y + 1 }}">
+ <i class="icon icon-arrow-down"></i>
+ </a>
</div>
</div>
{% endblock %}
diff --git a/views/village.twig b/views/village.twig
index ea6635c..b843ef4 100644
--- a/views/village.twig
+++ b/views/village.twig
@@ -49,7 +49,7 @@
<h3 align="center">Storage Config</h3>
<form method="post" action="/village/{{ village.x }}/{{ village.y }}/storage/config">
<label>
- Wood:
+ <i class="icon icon-wood"></i>
<input type="text" name="wood" value="{{ village.getStorageConfig(village.id).wood }}">
</label>
<label>
@@ -86,12 +86,12 @@
<tbody>
{% for event in events['UpgradeBuilding'] %}
<tr>
- <td>{{ event.data.building }}</td>
+ <td>{{ event.data.type }}</td>
<td class="timer">
{% include 'components/timer.twig' with { 'time': event.event.time|date('c') } %}
</td>
<td>
- <a class="btn" href="/village/{{ village.x }}/{{ village.y }}/building/{{ event.data.building }}/build/cancel">
+ <a class="btn" href="/village/{{ village.x }}/{{ village.y }}/building/{{ event.data.type }}/build/cancel">
Cancel
</a>
</td>
@@ -119,7 +119,7 @@
{% include 'components/timer.twig' with { 'time': event.event.time|date('c') } %}
</td>
<td>
- <a class="btn" href="/village/{{ village.x }}/{{ village.y }}/unit/{{ event.data.type }}/train/cancel">
+ <a class="btn" href="/village/{{ village.x }}/{{ village.y }}/unit/train/cancel">
Cancel
</a>
</td>
@@ -155,9 +155,15 @@
{% include 'components/timer.twig' with { 'time': event.event.time|date('c') } %}
</td>
<td>
- <a class="btn" href="/village/{{ village.x }}/{{ village.y }}/unit/{{ event.data.type }}/train/cancel">
- Cancel
- </a>
+ {% if event.data.cancel %}
+ 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 %}
@@ -180,10 +186,10 @@
</tr>
</thead>
<tbody>
- {% for building in village.getBuildings(village.id) %}
+ {% for building in buildings %}
<tr class="village__buildings__row">
- <td>{{ building.type }}</td>
- <td>{{ building.level }}</td>
+ <td>{{ building.type | default(type) }}</td>
+ <td>{{ building.level | default(0) }}</td>
<td>{{ building.getBuildTime() | buildTime }}</td>
<td class="resources">
<span>
@@ -225,7 +231,7 @@
</tr>
</thead>
<tbody>
- {% for unit in village.getUnits(village.id, 1) %}
+ {% for unit in village.getUnits(village.id, 1, 3) %}
<tr>
<td>{{ unit.type }}</td>
<td>{{ unit.amount }}</td>
@@ -256,7 +262,7 @@
<td>
<form action="/village/{{ village.x }}/{{ village.y }}/unit/{{ unit.type }}/create" method="post" class="inline">
<input type="number" min="0" name="amount" placeholder="Amount">
- <input type="submit" value="Train">
+ <input type="submit" value="Train" {{ village.canTrain(village, unit, 1) ? '' : 'disabled' }}>
</form>
</td>
</tr>
@@ -311,15 +317,35 @@
<option>{{ unit.type }}</option>
{% endfor %}
</select>
+ <input type="number" min="1" name="amount" placeholder="Amount" required>
<select name="village">
{% for v in villages %}
<option value="{{ v.id }}">{{ v.name }}</option>
{% endfor %}
</select>
- <input type="number" min="1" name="amount" placeholder="Amount" required>
+ <select name="type">
+ <option>Borrow</option>
+ <option>Gift</option>
+ </select>
<button>Send</button>
</form>
</div>
+
+ {% if village.getBuilding(village.id, 'Marketplace') %}
+ <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>
+ <select name="village">
+ {% for v in villages %}
+ <option value="{{ v.id }}">{{ v.name }}</option>
+ {% endfor %}
+ </select>
+ <button>Send</button>
+ </form>
+ {% endif %}
</div>
</div>
{% endblock %}
diff --git a/views/villages.twig b/views/villages.twig
index 21d8cec..bdb87be 100644
--- a/views/villages.twig
+++ b/views/villages.twig
@@ -11,7 +11,7 @@
<td>Iron</td>
<td>Food</td>
<td>Storage</td>
- <td>Reputation</td>
+ <td>Satisfaction</td>
</tr>
</thead>
<tbody>
@@ -30,7 +30,7 @@
<td>{{ village.iron }}</td>
<td>{{ village.food }}</td>
<td>{{ village.getStorage(village.id).getCapacity() * (25 / 100) }}</td>
- <td>{{ village.reputation }}</td>
+ <td>{{ village.satisfaction }}</td>
</tr>
{% endfor %}
</tbody>