FrankProjects/UltimateWarfare

View on GitHub
src/Service/OperationService.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php

declare(strict_types=1);

namespace FrankProjects\UltimateWarfare\Service;

use FrankProjects\UltimateWarfare\Entity\Operation;
use FrankProjects\UltimateWarfare\Entity\WorldRegion;
use FrankProjects\UltimateWarfare\Repository\ConstructionRepository;
use FrankProjects\UltimateWarfare\Repository\PlayerRepository;
use FrankProjects\UltimateWarfare\Repository\WorldRegionRepository;
use FrankProjects\UltimateWarfare\Repository\WorldRegionUnitRepository;
use FrankProjects\UltimateWarfare\Service\OperationEngine\OperationProcessor;
use FrankProjects\UltimateWarfare\Util\ReportCreator;
use RuntimeException;

final class OperationService
{
    private ReportCreator $reportCreator;
    private NetworthUpdaterService $networthUpdaterService;
    private IncomeUpdaterService $incomeUpdaterService;
    private PlayerRepository $playerRepository;
    private WorldRegionUnitRepository $worldRegionUnitRepository;
    private WorldRegionRepository $worldRegionRepository;
    private ConstructionRepository $constructionRepository;

    public function __construct(
        ReportCreator $reportCreator,
        NetworthUpdaterService $networthUpdaterService,
        IncomeUpdaterService $incomeUpdaterService,
        PlayerRepository $playerRepository,
        WorldRegionUnitRepository $worldRegionUnitRepository,
        WorldRegionRepository $worldRegionRepository,
        ConstructionRepository $constructionRepository
    ) {
        $this->reportCreator = $reportCreator;
        $this->networthUpdaterService = $networthUpdaterService;
        $this->incomeUpdaterService = $incomeUpdaterService;
        $this->playerRepository = $playerRepository;
        $this->worldRegionUnitRepository = $worldRegionUnitRepository;
        $this->worldRegionRepository = $worldRegionRepository;
        $this->constructionRepository = $constructionRepository;
    }

    /**
     * @return array<int, string>
     */
    public function executeOperation(
        WorldRegion $region,
        Operation $operation,
        WorldRegion $playerRegion,
        int $amount
    ): array {
        $this->ensureCanExecute($region, $operation, $playerRegion, $amount);
        $this->hasWorldRegionGameUnitAmount($playerRegion, $operation, $amount);

        $player = $playerRegion->getPlayer();
        if ($player === null) {
            throw new RuntimeException("Region has no owner");
        }

        $player->getResources()->addCash(-($operation->getCost() * $amount));
        $this->playerRepository->save($player);

        $operationProcessor = OperationProcessor::factory(
            $operation->getSubclass(),
            $region,
            $operation,
            $playerRegion,
            $amount,
            $this->reportCreator,
            $this->playerRepository,
            $this->worldRegionUnitRepository,
            $this->worldRegionRepository,
            $this->constructionRepository
        );
        $operationResults = $operationProcessor->execute();

        $regionPlayer = $region->getPlayer();
        if ($regionPlayer !== null) {
            $this->networthUpdaterService->updateNetworthForPlayer($regionPlayer);
            $this->incomeUpdaterService->updateIncomeForPlayer($regionPlayer);
        }

        $this->networthUpdaterService->updateNetworthForPlayer($player);
        $this->incomeUpdaterService->updateIncomeForPlayer($player);

        return $operationResults;
    }

    private function ensureCanExecute(
        WorldRegion $region,
        Operation $operation,
        WorldRegion $playerRegion,
        int $amount
    ): void {
        if (!$operation->isEnabled()) {
            throw new RuntimeException("Operation not enabled");
        }

        if ($region->getWorld()->getId() !== $playerRegion->getWorld()->getId()) {
            throw new RuntimeException("Regions not in same world");
        }

        if ($region->getPlayer() === null) {
            throw new RuntimeException("Target region has no owner");
        }

        if ($playerRegion->getPlayer() === null) {
            throw new RuntimeException("Region has no owner");
        }

        if ($region->getPlayer()->getId() === $playerRegion->getPlayer()->getId()) {
            throw new RuntimeException("You can not attack yourself");
        }

        if ($playerRegion->getPlayer()->getResources()->getCash() < $operation->getCost() * $amount) {
            throw new RuntimeException("You do not have enough cash");
        }

        foreach ($playerRegion->getPlayer()->getPlayerResearch() as $playerResearch) {
            if (
                $playerResearch->getResearch()->getId() === $operation->getResearch()->getId() &&
                $playerResearch->getResearch()->getActive() === true
            ) {
                return;
            }
        }
        throw new RuntimeException("You do not have all requirements to perform this operation");
    }

    private function hasWorldRegionGameUnitAmount(WorldRegion $region, Operation $operation, int $amount): void
    {
        if ($amount < 1) {
            throw new RuntimeException("Can not send negative game units");
        }

        foreach ($region->getWorldRegionUnits() as $regionUnit) {
            if ($regionUnit->getGameUnit()->getId() === $operation->getGameUnit()->getId()) {
                if ($regionUnit->getAmount() >= $amount) {
                    return;
                }
            }
        }
        throw new RuntimeException("Not enough game units");
    }
}