Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature - extend building queue requirements #421

Merged
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
Loading