summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Justfile3
-rw-r--r--bin/db.php51
-rw-r--r--docker-compose.yml2
-rw-r--r--docker/adminer.css113
-rw-r--r--src/Model/Building/Marketplace.php10
-rw-r--r--src/Model/Event/SendResources.php150
-rw-r--r--src/Model/Event/SendResourcesMerchants.php154
-rw-r--r--src/Model/Unit/Merchant.php8
-rw-r--r--src/Model/Village.php30
-rw-r--r--src/gemini/Controller/Village.php88
-rw-r--r--src/gemini/Gemini.php19
-rw-r--r--views/gemini/send-resources/01-resource-types.twig4
-rw-r--r--views/gemini/send-resources/02-villages.twig6
-rw-r--r--views/gemini/send-units/02-villages.twig2
-rw-r--r--views/gemini/village.twig33
15 files changed, 662 insertions, 11 deletions
diff --git a/Justfile b/Justfile
index 2207542..5610b26 100644
--- a/Justfile
+++ b/Justfile
@@ -1,2 +1,5 @@
+db:
+ docker compose exec app php bin/db.php
+
gemini:
find $(pwd) -type f -name '*.php' -o -name '*.twig' | GEMINI=true DB_HOST=localhost entr -r php public/index.php
diff --git a/bin/db.php b/bin/db.php
index 8047b42..7938f87 100644
--- a/bin/db.php
+++ b/bin/db.php
@@ -88,7 +88,9 @@ DB::query(<<<SQL
foreign key ("village_id") references villages("id") on delete cascade,
"created_at" timestamp(0) not null default current_timestamp,
- "updated_at" timestamp(0) not null default current_timestamp
+ "updated_at" timestamp(0) not null default current_timestamp,
+
+ unique ("village_id")
);
SQL);
@@ -181,6 +183,53 @@ DB::query(<<<SQL
SQL);
DB::query(<<<SQL
+ create table if not exists "events_send_resources" (
+ "id" bigserial primary key,
+
+ "event_id" bigint not null,
+ constraint "relation_event"
+ foreign key ("event_id") references events("id") on delete cascade,
+
+ "wood" bigint not null,
+ "clay" bigint not null,
+ "iron" bigint not null,
+ "food" bigint not null,
+
+ "is_canceled" boolean not null default false,
+
+ "source" bigint not null,
+ constraint "relation_village_source"
+ foreign key ("source") references villages("id"),
+
+ "destination" bigint not null,
+ constraint "relation_village_destination"
+ foreign key ("destination") references villages("id"),
+
+ "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 "events_send_resources_merchants" (
+ "id" bigserial primary key,
+
+ "event_id" bigint not null,
+ constraint "relation_event"
+ foreign key ("event_id") references events_send_resources("id") on delete cascade,
+
+ "unit_id" bigint not null,
+ constraint "relation_unit"
+ foreign key ("unit") references village_units("id"),
+
+ "amount" bigint 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,
diff --git a/docker-compose.yml b/docker-compose.yml
index 154d35f..c2a6b6a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -44,6 +44,8 @@ services:
- "8081:8080"
environment:
- "ADMINER_DEFAULT_SERVER=db"
+ volumes:
+ - "./docker/adminer.css:/var/www/html/adminer.css"
volumes:
db:
diff --git a/docker/adminer.css b/docker/adminer.css
new file mode 100644
index 0000000..9f30efb
--- /dev/null
+++ b/docker/adminer.css
@@ -0,0 +1,113 @@
+@media (prefers-color-scheme: dark) {
+ body {
+ background: #222;
+ color: #ebebeb;
+ }
+
+ a {
+ &, &:visited {
+ color: #ebebeb;
+ background: transparent !important;
+
+ &:hover {
+ color: #fff;
+ }
+ }
+ }
+
+ h1, h2 {
+ background: transparent;
+ color: #ebebeb;
+ }
+ h2 {
+ border-color: #999;
+ }
+
+ #breadcrumb {
+ background: transparent;
+ margin: 0;
+ }
+
+ #menu {
+ width: 21em;
+ }
+
+ #content {
+ margin-left: 22em;
+ }
+
+ .footer {
+ border-top-color: transparent;
+ border-image: none;
+
+ & > div {
+ background: #222;
+ }
+ }
+
+ td,
+ tbody th {
+ background: transparent !important;
+ }
+ th,
+ thead td,
+ tr.checked td,
+ tr:hover td {
+ background: #333 !important;
+ }
+ td, th {
+ padding: 0.1rem 0.4rem;
+ }
+
+ .column {
+ background: #222 !important;
+ }
+
+ code {
+ background: transparent;
+ padding: 0.1rem 0.2rem;
+ }
+ .jush, .jush a {
+ color: #ebebeb;
+ }
+ .jush-sqlite_quo {
+ color: inherit;
+ }
+ .jush-num {
+ color: inherit;
+ }
+ .jush-quo {
+ color: inherit;
+ }
+
+ .error, .message {
+ background: #333;
+ color: #ebebeb;
+
+ b {
+ background: #222;
+ padding: 0 0.2rem;
+ }
+ }
+
+ #help {
+ background: #333;
+ }
+
+ input, select {
+ background: #333;
+ color: #ebebeb;
+ border: 1px solid #777;
+ }
+
+ input[type="submit"] {
+ cursor: pointer;
+ padding: 0.1rem 0.4rem;
+ line-height: 1rem;
+
+ &:hover {
+ background: #ebebeb;
+ color: #222;
+ }
+ }
+}
diff --git a/src/Model/Building/Marketplace.php b/src/Model/Building/Marketplace.php
index 2d87379..8978003 100644
--- a/src/Model/Building/Marketplace.php
+++ b/src/Model/Building/Marketplace.php
@@ -3,9 +3,12 @@
namespace App\Model\Building;
use App\Model\Building;
+use App\Model\Unit\Merchant;
+use App\Model\Village;
class Marketplace extends Building
{
+ public string $unitType = 'Merchant';
public int $buildTimeFactor = 1;
public int $maxLevel = 25;
@@ -19,4 +22,11 @@ class Marketplace extends Building
'clay' => 10.0,
'iron' => 8.0,
];
+
+ public static function getResourceCapabilities(Village $village): int
+ {
+ $merchants = Village::getUnit($village, 'Merchant', Village::FETCH_UNIT_RESIDENCE, Village::RETURN_UNIT_EXISTING);
+
+ return Merchant::getResourceCapabilities($village) * $merchants->amount;
+ }
}
diff --git a/src/Model/Event/SendResources.php b/src/Model/Event/SendResources.php
new file mode 100644
index 0000000..5c751fd
--- /dev/null
+++ b/src/Model/Event/SendResources.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace App\Model\Event;
+
+use App\DB;
+use App\Model\Event;
+use App\Model\Unit;
+use App\Model\Unit\Merchant;
+use App\Model\Village;
+
+class SendResources extends BaseEvent
+{
+ public int $wood = 0;
+ public int $clay = 0;
+ public int $iron = 0;
+ public int $food = 0;
+
+ public int $source;
+ public int $destination;
+
+ public bool $isCanceled = false;
+
+ /**
+ * @return void
+ */
+ public function __invoke(): void
+ {
+ if ($this->isCanceled) {
+ // TODO: switch destination and source
+ // TODO: add resources back to "destination"
+ // TODO: add merchants back to "destination"
+ }
+
+ else {
+ // TODO: account for storage capacity
+ DB::query(
+ 'update villages set wood=wood+:wood, clay=clay+:clay, iron=iron+:iron, food=food+:food where id=:id',
+ [
+ 'wood' => $this->wood,
+ 'clay' => $this->clay,
+ 'iron' => $this->iron,
+ 'food' => $this->food,
+ 'id' => $this->destination,
+ ]
+ );
+
+ $source = Village::get($this->source);
+ $destination = Village::get($this->destination);
+
+ $event = new Event();
+ $event->time = (new \DateTime())->add(
+ \DateInterval::createFromDateString(
+ Unit::getTravelTime(new Merchant(), Village::getDistance($source->x, $source->y, $destination->x, $destination->y))
+ . ' seconds'
+ )
+ );
+ $event->villageId = $this->source;
+
+ $sendResourcesMerchants = new SendResourcesMerchants();
+ $sendResourcesMerchants->dbInsert($this->id);
+
+
+ // TODO: add resources to destination
+ // (TODO: add foreign merchants to destination)?
+ // TODO: create SendUnits event with merchants back to source
+ }
+ }
+
+ public function dbInsert(): void
+ {
+ DB::query(
+ 'insert into events (time, village_id) VALUES (:time, :village_id)',
+ ['time' => $this->event->time->format('c'), 'village_id' => $this->event->villageId]
+ );
+
+ DB::query(
+ <<<SQL
+ insert into events_send_resources (event_id, wood, clay, iron, food, source, destination, is_canceled)
+ VALUES (:event_id, :wood, :clay, :iron, :food, :source, :destination, :is_canceled)
+ SQL,
+ [
+ 'event_id' => DB::$connection->lastInsertId(),
+ 'wood' => $this->wood,
+ 'clay' => $this->clay,
+ 'iron' => $this->iron,
+ 'food' => $this->food,
+ 'source' => $this->source, 'destination' => $this->destination,
+ 'is_canceled' => $this->isCanceled ?: 0, // @see https://www.php.net/manual/de/pdostatement.execute.php#126013
+ ]
+ );
+ $sendResourcesEventId = DB::$connection->lastInsertId();
+
+ $resourceCapabilities = Merchant::getResourceCapabilities($this->event->villageId);
+ $resourcesTotal = $this->wood + $this->clay + $this->iron + $this->food;
+ $necessaryMerchants = ceil($resourcesTotal / $resourceCapabilities);
+
+ $merchantsAccountedFor = 0;
+ while ($merchantsAccountedFor < $necessaryMerchants) {
+ $merchants = DB::fetch(
+ Merchant::class,
+ 'select * from village_units where type=:type and residence_village_id=:villageId and is_traveling=false',
+ ['type' => 'Merchant', 'villageId' => $this->source]
+ );
+ foreach ($merchants as $merchant) {
+ /**@type Merchant $merchant*/
+
+ $currentlyNecessaryMerchants = $necessaryMerchants - $merchantsAccountedFor;
+ $currentlyUseableMerchants = $currentlyNecessaryMerchants - $merchant->amount < 0 ? $currentlyNecessaryMerchants : $merchant->amount;
+ $merchantsAccountedFor += $currentlyUseableMerchants;
+
+ DB::query(
+ <<<SQL
+ insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id)
+ VALUES (:amount, :type, true, :home, :residence)
+ on conflict (type, home_village_id, residence_village_id, is_traveling)
+ do update set is_traveling=true
+ SQL,
+ [
+ 'amount' => $currentlyUseableMerchants,
+ 'type' => 'Merchant',
+ 'home' => $merchant->homeVillageId,
+ 'residence' => $merchant->residenceVillageId,
+ ]
+ );
+ DB::query('update village_units set amount=amount-:amount where id=:unitId', ['amount' => $necessaryMerchants, 'unitId' => $merchant->id]);
+
+ DB::query(
+ <<<SQL
+ insert into events_send_resources_merchants (event_id, unit_id, amount)
+ VALUES (:event_id, :unit_id, :amount)
+ SQL,
+ [
+ 'event_id' => $sendResourcesEventId,
+ 'unit_id' => $merchant->id,
+ 'amount' => $necessaryMerchants,
+ ]
+ );
+ }
+ }
+
+ // TODO: remove resources from source
+ }
+
+ public function dbDelete(): void
+ {
+ DB::query('delete from events where id=:id', ['id' => $this->eventId]);
+ DB::query('delete from events_send_resources where id=:id', ['id' => $this->id]);
+ #DB::query('delete from events_send_resources_merchants where event_id=:id', ['id' => $this->id]);
+ }
+}
diff --git a/src/Model/Event/SendResourcesMerchants.php b/src/Model/Event/SendResourcesMerchants.php
new file mode 100644
index 0000000..a50b2bc
--- /dev/null
+++ b/src/Model/Event/SendResourcesMerchants.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace App\Model\Event;
+
+use App\DB;
+use App\Model\Event;
+use App\Model\Unit;
+use App\Model\Unit\Merchant;
+use App\Model\Village;
+
+class SendResourcesMerchants extends BaseEvent
+{
+ public int $wood = 0;
+ public int $clay = 0;
+ public int $iron = 0;
+ public int $food = 0;
+
+ public int $source;
+ public int $destination;
+
+ public bool $isCanceled = false;
+
+ /**
+ * @return void
+ */
+ public function __invoke(): void
+ {
+ if ($this->isCanceled) {
+ // TODO: switch destination and source
+ // TODO: add resources back to "destination"
+ // TODO: add merchants back to "destination"
+ }
+
+ else {
+ // TODO: account for storage capacity
+ DB::query(
+ 'update villages set wood=wood+:wood, clay=clay+:clay, iron=iron+:iron, food=food+:food where id=:id',
+ [
+ 'wood' => $this->wood,
+ 'clay' => $this->clay,
+ 'iron' => $this->iron,
+ 'food' => $this->food,
+ 'id' => $this->destination,
+ ]
+ );
+
+ // TODO: foreach send_resources_merchants
+ $source = Village::get($this->source);
+ $destination = Village::get($this->destination);
+ $event = new Event();
+ $event->time = (new \DateTime())->add(
+ \DateInterval::createFromDateString(
+ Unit::getTravelTime(new Merchant(), Village::getDistance($source->x, $source->y, $destination->x, $destination->y))
+ . ' seconds'
+ )
+ );
+ $event->villageId = $this->source;
+ $sendUnitsEvent = new SendUnits();
+ $sendUnitsEvent->event = $event;
+ $sendUnitsEvent->type = 'SendBack';
+ $sendUnitsEvent->unit = 'Merchant';
+ $sendUnitsEvent->amount = $amount;
+ $sendUnitsEvent->source = $village->id;
+ $sendUnitsEvent->destination = $destination->id;
+ $sendUnitsEvent->dbInsert();
+
+ // TODO: add resources to destination
+ // (TODO: add foreign merchants to destination)?
+ // TODO: create SendUnits event with merchants back to source
+ }
+ }
+
+ public function dbInsert(int $previouId): void
+ {
+ DB::query(
+ 'update events_send_resources_merchants set event_id=:new_id where event_id=:old_id',
+ ['old_id' => $previouId, 'new_id' => $this->id]
+ );
+
+ /*
+ DB::query(
+ 'insert into events (time, village_id) VALUES (:time, :village_id)',
+ ['time' => $this->event->time->format('c'), 'village_id' => $this->event->villageId]
+ );
+
+ DB::query(
+ <<<SQL
+ insert into events_send_resources (event_id, wood, clay, iron, food, source, destination, is_canceled)
+ VALUES (:event_id, :wood, :clay, :iron, :food, :source, :destination, :is_canceled)
+ SQL,
+ [
+ 'event_id' => DB::$connection->lastInsertId(),
+ 'wood' => $this->wood,
+ 'clay' => $this->clay,
+ 'iron' => $this->iron,
+ 'food' => $this->food,
+ 'source' => $this->source, 'destination' => $this->destination,
+ 'is_canceled' => $this->isCanceled ?: 0, // @see https://www.php.net/manual/de/pdostatement.execute.php#126013
+ ]
+ );
+ $sendResourcesEventId = DB::$connection->lastInsertId();
+
+ $resourceCapabilities = Merchant::getResourceCapabilities($this->event->villageId);
+ $resourcesTotal = $this->wood + $this->clay + $this->iron + $this->food;
+ $necessaryMerchants = ceil($resourcesTotal / $resourceCapabilities);
+
+ $merchantsAccountedFor = 0;
+ while ($merchantsAccountedFor < $necessaryMerchants) {
+ $merchants = DB::fetch(
+ Merchant::class,
+ 'select * from village_units where type=:type and residence_village_id=:villageId and is_traveling=false',
+ ['type' => 'Merchant', 'villageId' => $this->source]
+ );
+ foreach ($merchants as $merchant) {
+ $currentlyNecessaryMerchants = $necessaryMerchants - $merchantsAccountedFor;
+ $currentlyUseableMerchants = $currentlyNecessaryMerchants - $merchant->amount < 0 ? $currentlyNecessaryMerchants : $merchant->amount;
+ $merchantsAccountedFor += $currentlyUseableMerchants;
+
+ DB::query(
+ <<<SQL
+ insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id)
+ VALUES (:amount, :type, true, :home, :residence)
+ on conflict (type, home_village_id, residence_village_id, is_traveling)
+ do update set is_traveling=true
+ SQL,
+ [
+ 'amount' => $currentlyUseableMerchants,
+ 'type' => 'Merchant',
+ 'home' => $merchant->homeVillageId,
+ 'residence' => $merchant->residenceVillageId,
+ ]
+ );
+ DB::query('update village_units set amount=amount-:amount where id=:unitId', ['amount' => $necessaryMerchants, 'unitId' => $merchant->id]);
+
+ DB::query(
+ <<<SQL
+ insert into events_send_resources_merchants (event_id, unit_id, amount)
+ VALUES (:event_id, :unit_id, :amount)
+ SQL,
+ [
+ 'event_id' => $sendResourcesEventId,
+ 'unit_id' => $merchant->id,
+ 'amount' => $necessaryMerchants,
+ ]
+ );
+ }
+ }*/
+ }
+
+ public function dbDelete(): void
+ {
+ DB::query('delete from events_send_resources_merchants where event_id=:id', ['id' => $this->id]);
+ }
+}
diff --git a/src/Model/Unit/Merchant.php b/src/Model/Unit/Merchant.php
index 8958d27..be0b25e 100644
--- a/src/Model/Unit/Merchant.php
+++ b/src/Model/Unit/Merchant.php
@@ -3,6 +3,7 @@
namespace App\Model\Unit;
use App\Model\Unit;
+use App\Model\Village;
class Merchant extends Unit
{
@@ -22,4 +23,11 @@ class Merchant extends Unit
'iron' => 2.0,
'food' => 2.0,
];
+
+ public static function getResourceCapabilities(Village|int $village): int
+ {
+ $marketplace = Village::getBuilding($village->id ?? $village, 'Marketplace');
+
+ return $marketplace->level * 100;
+ }
}
diff --git a/src/Model/Village.php b/src/Model/Village.php
index c6709a6..019d9ba 100644
--- a/src/Model/Village.php
+++ b/src/Model/Village.php
@@ -3,6 +3,7 @@
namespace App\Model;
use App\DB;
+use App\Model\Building\Marketplace;
use App\Model\Building\Storage;
use App\Model\Village\StorageConfig;
@@ -53,6 +54,21 @@ class Village
return true;
}
+ public static function canSendResources(Village $village): bool
+ {
+ $marketplace = Village::getBuilding($village->id, 'Marketplace');
+ if (! $marketplace) {
+ return false;
+ }
+
+ $merchants = Village::getUnit($village, $marketplace->unitType, Village::FETCH_UNIT_RESIDENCE, Village::RETURN_UNIT_EXISTING);
+ if (! $merchants || $merchants->amount === 0) {
+ return false;
+ }
+
+ return true;
+ }
+
/* DB - Actions */
public static function get(int $id): ?Village
@@ -136,8 +152,20 @@ class Village
public const RETURN_UNIT_ALL = 2;
public const RETURN_UNIT_TRAINABLE = 3;
- public static function getUnit(string $unitType, int $flag): ?Unit
+ public static function getUnit(Village $village, string $unitType, int $fetchFlag = Village::FETCH_UNIT_RESIDENCE, int $returnFlag = Village::RETURN_UNIT_EXISTING): ?Unit
{
+ if ($fetchFlag == Village::FETCH_UNIT_RESIDENCE) {
+ $query = 'select * from village_units where residence_village_id=:id and type=:type and is_traveling=false';
+ }
+
+ $results = DB::fetch(Unit::class, $query, ['id' => $village->id, 'type' => $unitType]);
+ if (isset($results[0])) {
+ $unit = $results[0]->cast();
+ } else {
+ return null;
+ }
+
+ return $unit;
}
/**
diff --git a/src/gemini/Controller/Village.php b/src/gemini/Controller/Village.php
index 85b9205..70aa899 100644
--- a/src/gemini/Controller/Village.php
+++ b/src/gemini/Controller/Village.php
@@ -4,9 +4,13 @@ namespace App\gemini\Controller;
use App\DB;
use App\Guard;
+use App\Model\Event;
+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\Merchant;
use App\Model\Village as Model;
use App\View;
use GeminiFoundation\Request;
@@ -89,6 +93,26 @@ 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);;
+ }
+
$buildings = [];
foreach (Model::getBuildings($village->id, true) as $building) {
$buildings[$building->type] = $building;
@@ -100,6 +124,7 @@ class Village
'events' => $events,
'buildings' => $buildings,
'villages' => DB::fetch(Model::class, "select * from villages where id!=:id", ['id' => $village->id]),
+ 'marketplace' => $village->getBuilding($village->id, 'Marketplace'),
]));
}
@@ -164,4 +189,67 @@ class Village
meta: "/village/{$village->x}/{$village->y}/storage/config"
);
}
+
+ // #[Route(path: '/village/{x}/{y}/send-resources', methods: ['POST'])]
+ public function sendResources(Request $request): Response
+ {
+ $village = Model::getByCoordinates($request->get('x'), $request->get('y'));
+
+ $selectedResourceType = $request->get('selectedResourceType');
+ if (empty($selectedResourceType)) {
+ return new Response(body: View::render('send-resources/01-resource-types.twig', [
+ 'village' => $village,
+ 'marketplace' => $village->getBuilding($village->id, 'Marketplace'),
+ ]));
+ }
+
+ if (empty($request->get('selectedVillageX'))) {
+ return new Response(body: View::render('send-resources/02-villages.twig', [
+ 'village' => $village,
+ 'villages' => DB::fetch(Model::class, "select * from villages where id!=:id", ['id' => $village->id]),
+ 'selectedResourceType' => $selectedResourceType,
+ 'marketplace' => $village->getBuilding($village->id, 'Marketplace'),
+ ]));
+ }
+ $selectedVillage = Model::getByCoordinates($request->get('selectedVillageX'), $request->get('selectedVillageY'));
+
+ $amount = intval($request->get('input'));
+ if (empty($amount)) {
+ return new Response(statusCode: Status::INPUT, meta: 'Amount');
+ }
+
+ $resourceCapabilities = Merchant::getResourceCapabilities($village);
+ $necessaryMerchants = ceil($amount / $resourceCapabilities);
+ $merchants = DB::fetch(Merchant::class, 'select sum(amount) from village_units where type=:type and residence_village_id=:villageId and is_traveling=false', ['villageId' => $this->source, 'type' => 'Merchant'])[0]['amount'] ?? 0;
+ if ($merchants === 0) {
+ return new Response(
+ statusCode: Status::REDIRECT_TEMPORARY,
+ meta: "/village/{$village->x}/{$village->y}/type/$selectedResourceType/village/{$selectedVillage->x}/{$selectedVillage->y}"
+ );
+ }
+
+ $destination = $selectedVillage;
+
+ // event
+ $event = new Event();
+ $event->time = (new \DateTime())->add(
+ \DateInterval::createFromDateString(
+ Unit::getTravelTime(new Merchant(), Model::getDistance($village->x, $village->y, $destination->x, $destination->y))
+ . ' seconds'
+ )
+ );
+ $event->villageId = $village->id;
+ $sendResourcesEvent = new SendResources();
+ $sendResourcesEvent->event = $event;
+ $sendResourcesEvent->$selectedResourceType = $amount;
+ $sendResourcesEvent->source = $village->id;
+ $sendResourcesEvent->destination = $destination->id;
+ $sendResourcesEvent->dbInsert();
+
+
+ return new Response(
+ statusCode: Status::REDIRECT_TEMPORARY,
+ meta: "/village/{$village->x}/{$village->y}"
+ );
+ }
}
diff --git a/src/gemini/Gemini.php b/src/gemini/Gemini.php
index 507b962..4c420d4 100644
--- a/src/gemini/Gemini.php
+++ b/src/gemini/Gemini.php
@@ -129,6 +129,25 @@ class Gemini
$response = $unitController->sendUnits($request);
}
+ else if (preg_match('@village/(\d+)/(\d+)/send-resources@', $request->getPath(), $routeMatch)) {
+ $request
+ ->set('x', $routeMatch[1])
+ ->set('y', $routeMatch[2]);
+
+ if (preg_match('@village/(\d+)/(\d+)/send-resources/type/(\w+)$@', $request->getPath(), $routeMatch)) {
+ $request->set('selectedResourceType', $routeMatch[3]);
+ }
+ else if (preg_match('@village/(\d+)/(\d+)/send-resources/type/(\w+)/village/(\d+)/(\d+)$@', $request->getPath(), $routeMatch)) {
+ $request
+ ->set('selectedResourceType', $routeMatch[3])
+ ->set('selectedVillageX', $routeMatch[4])
+ ->set('selectedVillageY', $routeMatch[5]);
+ }
+
+ $villageController = new Village();
+ $response = $villageController->sendResources($request);
+ }
+
else if (preg_match('@village/(\d+)/(\d+)@', $request->getPath(), $routeMatch)) {
$request
->set('x', $routeMatch[1])
diff --git a/views/gemini/send-resources/01-resource-types.twig b/views/gemini/send-resources/01-resource-types.twig
new file mode 100644
index 0000000..586aa1e
--- /dev/null
+++ b/views/gemini/send-resources/01-resource-types.twig
@@ -0,0 +1,4 @@
+Sendable Resource Capability: {{ marketplace.getResourceCapabilities(village) }}
+{% for resourceType in ['wood', 'clay', 'iron', 'food'] %}
+=> /village/{{ village.x }}/{{ village.y }}/send-resources/type/{{ resourceType }} {{ resourceType }}
+{% endfor %}
diff --git a/views/gemini/send-resources/02-villages.twig b/views/gemini/send-resources/02-villages.twig
new file mode 100644
index 0000000..54ccc5c
--- /dev/null
+++ b/views/gemini/send-resources/02-villages.twig
@@ -0,0 +1,6 @@
+{{ selectedResourceType }}
+Sendable Resource Capability: {{ marketplace.getResourceCapabilities(village) }}
+
+{% for listVillage in villages %}
+=> /village/{{ village.x }}/{{ village.y }}/send-resources/type/{{ selectedResourceType }}/village/{{ listVillage.x }}/{{ listVillage.y }} {{ listVillage.name }} - {{ listVillage.x }} x {{ listVillage.y }}
+{% endfor %}
diff --git a/views/gemini/send-units/02-villages.twig b/views/gemini/send-units/02-villages.twig
index 80f5326..ea2741c 100644
--- a/views/gemini/send-units/02-villages.twig
+++ b/views/gemini/send-units/02-villages.twig
@@ -1,5 +1,5 @@
{{ selectedUnit }}
{% for listVillage in villages %}
-=> /village/{{ village.x }}/{{ village.y }}/send-units/type/{{ selectedUnit }}/village/{{ listVillage.x }}/{{ listVillage.y }} {{ listVillage.name }} - {{ listVillage.x }} - {{ listVillage.y }}
+=> /village/{{ village.x }}/{{ village.y }}/send-units/type/{{ selectedUnit }}/village/{{ listVillage.x }}/{{ listVillage.y }} {{ listVillage.name }} - {{ listVillage.x }} x {{ listVillage.y }}
{% endfor %}
diff --git a/views/gemini/village.twig b/views/gemini/village.twig
index d351eb6..d448020 100644
--- a/views/gemini/village.twig
+++ b/views/gemini/village.twig
@@ -57,6 +57,27 @@ Canceled
{% endfor %}
{% endif %}
+{% if events['SendResources'] %}
+## Send Resources
+{% for event in events['SendResources'] %}
+### {{ village.get(event.source).name }} -> {{ village.get(event.destination).name }}
+Wood: {{ event.wood }}
+Clay: {{ event.clay }}
+Iron: {{ event.iron }}
+Food: {{ event.food }}
+Source: {{ village.get(event.source).name }}
+Destination: {{ village.get(event.destination).name }}
+Finished: {{ event.event.time | date('c') }}
+{% if event.isCanceled %}
+Canceled
+{% else %}
+{% if event.event.villageId == village.id %}
+=> /event/{{ event.event.id }}/cancel Cancel
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+
# Buildings
@@ -120,15 +141,11 @@ Travel Time: {{ unit.getTravelTime(unit, village.getDistance(unit.getHomeVillage
{% include 'send-units/01-units.twig' %}
+{% if village.canSendResources(village) %}
# Send Resources
-TODO
-* list possible resource types
-=> /village/{{ village.x }}/{{ village.y }}/send-resources/type/wood Wood
-=> /village/{{ village.x }}/{{ village.y }}/send-resources/type/clay Clay
-=> /village/{{ village.x }}/{{ village.y }}/send-resources/type/iron Iron
-=> /village/{{ village.x }}/{{ village.y }}/send-resources/type/food Food
-* list possible villages
-* send INPUT request for amount
+{% include 'send-resources/01-resource-types.twig' %}
+{% endif %}
+
{% include 'footer.twig' %}