Skip to content

Commit

Permalink
Merge pull request #421 from rautamik/feature/extend-building-queue-r…
Browse files Browse the repository at this point in the history
…equirements

Feature - extend building queue requirements
  • Loading branch information
lanedirt authored Nov 3, 2024
2 parents dc813c3 + 0064cfe commit 5605d90
Show file tree
Hide file tree
Showing 15 changed files with 683 additions and 111 deletions.
2 changes: 1 addition & 1 deletion app/Http/Controllers/Abstracts/AbstractUnitsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public function index(Request $request, PlayerService $player, ObjectService $ob
$amount = $planet->getObjectAmount($object->machine_name);

// Check requirements of this building
$requirements_met = $objects->objectRequirementsMet($object->machine_name, $planet, $player);
$requirements_met = $objects->objectRequirementsMet($object->machine_name, $planet, $player, 0, false);

// Check if the current planet has enough resources to build this building.
$enough_resources = $planet->hasResources($objects->getObjectPrice($object->machine_name, $planet));
Expand Down
112 changes: 82 additions & 30 deletions app/Services/BuildingQueueService.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ public function retrieveFinished(int $planet_id): Collection
->get();
}

/**
* Get building queue items
*
* @return Collection<int, BuildingQueue>
*/
public function retrieveQueueItems(PlanetService $planet): Collection
{
// Fetch queue items from model
return BuildingQueue::where([
['planet_id', $planet->getPlanetId()],
['processed', 0],
['canceled', 0],
])
->orderBy('time_start', 'asc')
->get();
}

/**
* Add a building to the building queue for the current planet.
*
Expand All @@ -67,15 +84,15 @@ public function add(PlanetService $planet, int $building_id): void
{
$build_queue = $this->retrieveQueue($planet);

$building = $this->objects->getObjectById($building_id);

// Max amount of buildings that can be in the queue in a given time.
// TODO: refactor throw exception into a more user-friendly message.
if ($build_queue->isQueueFull()) {
// Max amount of build queue items already exist, throw exception.
throw new Exception('Maximum number of items already in queue.');
}

$building = $this->objects->getObjectById($building_id);

// Check if user satisifes requirements to build this object.
// TODO: refactor throw exception into a more user-friendly message.
$requirements_met = $this->objects->objectRequirementsMet($building->machine_name, $planet, $planet->getPlayer());
Expand Down Expand Up @@ -113,14 +130,7 @@ public function add(PlanetService $planet, int $building_id): void
*/
public function retrieveQueue(PlanetService $planet): BuildingQueueListViewModel
{
// Fetch queue items from model
$queue_items = BuildingQueue::where([
['planet_id', $planet->getPlanetId()],
['processed', 0],
['canceled', 0],
])
->orderBy('time_start', 'asc')
->get();
$queue_items = $this->retrieveQueueItems($planet);

// Convert to ViewModel array
$list = array();
Expand Down Expand Up @@ -231,6 +241,14 @@ public function start(PlanetService $planet, int $time_start = 0): void
continue;
}

// Sanity check: check if the building requirements are still met. If not,
// then cancel build request.
if (!$this->objects->objectRequirementsMet($object->machine_name, $planet, $planet->getPlayer(), $queue_item->object_level_target, false)) {
$this->cancel($planet, $queue_item->id, $queue_item->object_id);

continue;
}

// All OK, deduct resources and start building process.
$planet->deductResources($price);

Expand Down Expand Up @@ -277,26 +295,6 @@ public function cancel(PlanetService $planet, int $building_queue_id, int $build

// If object is found: add canceled flag.
if ($queue_item) {
// Gets all building queue records of this target level and all that
// come after it. So e.g. if user cancels build order for metal mine
// level 5 then any other already queued build orders for lvl 6,7,8 etc.
// will also be canceled.
$queue_items_higher_level = BuildingQueue::where([
['planet_id', $planet->getPlanetId()],
['object_id', $building_id],
['object_level_target', '>', $queue_item->object_level_target],
['processed', 0],
['canceled', 0],
])->get();

// Add canceled flag to all entries with a higher level (if any).
foreach ($queue_items_higher_level as $queue_item_higher_level) {
$queue_item_higher_level->building = 0;
$queue_item_higher_level->canceled = 1;

$queue_item_higher_level->save();
}

// Give back resources if the current entry was already building.
if ($queue_item->building === 1) {
$planet->addResources(new Resources($queue_item->metal, $queue_item->crystal, $queue_item->deuterium, 0));
Expand All @@ -308,8 +306,62 @@ public function cancel(PlanetService $planet, int $building_queue_id, int $build

$queue_item->save();

// Check if requirements for all other items in the queue are still met.
// So e.g. if user cancels build order for metal mine
// level 5 then any other already queued build orders for lvl 6,7,8 etc.
// will also be canceled. Same applies to building requirements,
// if user cancels build order for robotics factory which is requirement
// for shipyard then shipyard will also be canceled.
// Requirements are checked only for building queue objects as
// unit queue objects cannot be canceled.
$this->cancelItemMissingRequirements($planet);

$research_queue = resolve('OGame\Services\ResearchQueueService');
$research_queue->cancelItemMissingRequirements($planet->getPlayer(), $planet);

// Set the next queue item to start (if applicable)
$this->start($planet);
}
}

/**
* Get is object in building queue
*
* @return bool
*/
public function objectInBuildingQueue(PlanetService $planet, string $machine_name, int $level): bool
{
$queue_items = $this->retrieveQueueItems($planet);

foreach ($queue_items as $item) {
$object = $this->objects->getObjectById($item->object_id);

if ($object->machine_name === $machine_name && $item->object_level_target === $level) {
return true;
}
}

return false;
}

/**
* Cancel first building queue item missing requirements.
* This function will be called recursively when it cancels the item.
*
* @return void
*/
public function cancelItemMissingRequirements(PlanetService $planet): void
{
$build_queue_items = $this->retrieveQueueItems($planet);

foreach ($build_queue_items as $build_queue_item) {
$object = $this->objects->getObjectById($build_queue_item->object_id);

if (!$this->objects->objectRequirementsMet($object->machine_name, $planet, $planet->getPlayer(), $build_queue_item->object_level_target)) {
$this->cancel($planet, $build_queue_item->id, $object->id);
break;
}

}
}
}
68 changes: 65 additions & 3 deletions app/Services/ObjectService.php
Original file line number Diff line number Diff line change
Expand Up @@ -329,21 +329,41 @@ public function getBuildingObjectsWithStorage(): array
* @param string $machine_name
* @param PlanetService $planet
* @param PlayerService $player
* @param int $level
* @param bool $queued
* @return bool
*/
public function objectRequirementsMet(string $machine_name, PlanetService $planet, PlayerService $player): bool
public function objectRequirementsMet(string $machine_name, PlanetService $planet, PlayerService $player, int $level = 0, bool $queued = true): bool
{
try {
$object = $this->getObjectByMachineName($machine_name);

// Check required prior levels
if ($level) {
if (!$this->objectLevelsMet($object, $planet, $player, $level, $queued)) {
return false;
}
}

foreach ($object->requirements as $requirement) {
// Load required object and check if requirements are met.
$object_required = $this->getObjectByMachineName($requirement->object_machine_name);
$check_queue = $queued;

// Skip queue check for research lab as it must be present for research objects
if ($object_required->machine_name === 'research_lab') {
$check_queue = false;
}

if ($object_required->type === GameObjectType::Research) {
if ($player->getResearchLevel($object_required->machine_name) < $requirement->level) {
// Check if requirements are met with existing technology or with research items in build queue.
if ($player->getResearchLevel($object_required->machine_name) < $requirement->level && (!$check_queue || !$player->isResearchingTech($requirement->object_machine_name, $requirement->level))) {
return false;
}
} else {
if ($planet->getObjectLevel($object_required->machine_name) < $requirement->level) {
// Check if requirements are met with existing buildings or with buildings in build queue.
// Building queue is checked only for building queue objects, not for unit queue objects.
if ($planet->getObjectLevel($object_required->machine_name) < $requirement->level && (!$check_queue || !$planet->isBuildingObject($requirement->object_machine_name, $requirement->level))) {
return false;
}
}
Expand Down Expand Up @@ -486,4 +506,46 @@ public function getObjectRawPrice(string $machine_name, int $level = 0): Resourc

return new Resources($metal, $crystal, $deuterium, $energy);
}

/**
* Check if object prior level requirements are met (for building it).
* Prior levels can be already built or in queues
*
* @param GameObject $object
* @param PlanetService $planet
* @param PlayerService $player
* @param int $level
* @param bool $queued
* @return bool
*/
private function objectLevelsMet(GameObject $object, PlanetService $planet, PlayerService $player, int $level, bool $queued): bool
{
$current_level = 0;

if ($object->type === GameObjectType::Research) {
$current_level = $planet->getPlayer()->getResearchLevel($object->machine_name);
} else {
$current_level = $planet->getObjectLevel($object->machine_name);
}

// Check if target level is next level
if ($current_level + 1 === $level) {
return true;
}

// Check if items in queues should be included or not
if (!$queued) {
// There are prior levels, but queue should not be included
return false;
}

// Check prior levels from queues
for ($i = $current_level + 1; $i < $level; $i++) {
if (!$planet->isBuildingObject($object->machine_name, $i) && !$player->isResearchingTech($object->machine_name, $i)) {
return false;
}
}

return true;
}
}
18 changes: 18 additions & 0 deletions app/Services/PlanetService.php
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,24 @@ public function isBuilding(): bool
return count($build_queue) > 0;
}

/**
* Get is the current planet building the object or not
*
* @return bool
*/
public function isBuildingObject(string $machine_name, int $level): bool
{
$object = $this->objects->getObjectByMachineName($machine_name);

// Check only building queue objects
if ($object->type !== GameObjectType::Building && $object->type !== GameObjectType::Station) {
return false;
}

$build_queue = resolve(BuildingQueueService::class);
return $build_queue->objectInBuildingQueue($this, $machine_name, $level);
}

/**
* Get building count from planet
*
Expand Down
11 changes: 11 additions & 0 deletions app/Services/PlayerService.php
Original file line number Diff line number Diff line change
Expand Up @@ -572,4 +572,15 @@ public function delete(): void
// Delete the actual user.
$this->user->delete();
}

/**
* Get is the player researching the tech or not
*
* @return bool
*/
public function isResearchingTech(string $machine_name, int $level): bool
{
$research_queue = resolve('OGame\Services\ResearchQueueService');
return $research_queue->objectInResearchQueue($this, $machine_name, $level);
}
}
Loading

0 comments on commit 5605d90

Please sign in to comment.