jaroslavtyc/drd-plus-tables

View on GitHub
src/Theurgist/Spells/Modifier.php

Summary

Maintainability
D
1 day
Test Coverage
<?php declare(strict_types = 1);

namespace DrdPlus\Tables\Theurgist\Spells;

use DrdPlus\Codes\Theurgist\ModifierCode;
use DrdPlus\Codes\Theurgist\ModifierMutableParameterCode;
use DrdPlus\Tables\Tables;
use DrdPlus\Tables\Theurgist\Exceptions\InvalidValueForMutableParameter;
use DrdPlus\Tables\Theurgist\Exceptions\UnknownParameter;
use DrdPlus\Tables\Theurgist\Partials\SanitizeMutableParameterChangesTrait;
use DrdPlus\Tables\Theurgist\Spells\Exceptions\InvalidValueForModifierParameter;
use DrdPlus\Tables\Theurgist\Spells\Exceptions\UnknownModifierParameter;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\SpellAttack;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\CastingRounds;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\Noise;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\NumberOfConditions;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\DifficultyChange;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\EpicenterShift;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\Grafts;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\Invisibility;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\NumberOfSituations;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\Partials\CastingParameter;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\NumberOfWaypoints;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\SpellPower;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\Quality;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\SpellRadius;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\Realm;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\RealmsAffection;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\Resistance;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\SpellSpeed;
use DrdPlus\Tables\Theurgist\Spells\SpellParameters\Threshold;
use Granam\Strict\Object\StrictObject;
use Granam\Tools\ValueDescriber;

class Modifier extends StrictObject
{
    use ToFlatArrayTrait;
    use SanitizeMutableParameterChangesTrait;

    private \DrdPlus\Codes\Theurgist\ModifierCode $modifierCode;
    private \DrdPlus\Tables\Tables $tables;
    private array $modifierSpellParameterChanges;
    /** @var array|SpellTrait[] */
    private array $modifierSpellTraits;

    /**
     * @param ModifierCode $modifierCode
     * @param Tables $tables
     * @param array|int[] $modifierSpellParameterValues spell parameters current values (delta will be calculated from them)
     * by @param array|SpellTrait[] $modifierSpellTraits
     * @throws \DrdPlus\Tables\Theurgist\Spells\Exceptions\UnknownModifierParameter
     * @throws \DrdPlus\Tables\Theurgist\Spells\Exceptions\InvalidValueForModifierParameter
     * @throws \DrdPlus\Tables\Theurgist\Spells\Exceptions\InvalidSpellTrait
     * @see ModifierMutableParameterCode value indexed its value change
     */
    public function __construct(
        ModifierCode $modifierCode,
        Tables $tables,
        array $modifierSpellParameterValues,
        array $modifierSpellTraits
    )
    {
        $this->modifierCode = $modifierCode;
        $this->tables = $tables;
        $this->modifierSpellParameterChanges = $this->sanitizeSpellParameterChanges($modifierSpellParameterValues);
        $this->modifierSpellTraits = $this->getCheckedSpellTraits($this->toFlatArray($modifierSpellTraits));
    }

    /**
     * @param array $spellParameterValues
     * @return array
     * @throws \DrdPlus\Tables\Theurgist\Spells\Exceptions\InvalidValueForModifierParameter
     * @throws \DrdPlus\Tables\Theurgist\Spells\Exceptions\UnknownModifierParameter
     */
    private function sanitizeSpellParameterChanges(array $spellParameterValues): array
    {
        try {
            return $this->sanitizeMutableParameterChanges(
                $spellParameterValues,
                ModifierMutableParameterCode::getPossibleValues(),
                $this->getModifierCode(),
                $this->tables->getModifiersTable()
            );
        } catch (InvalidValueForMutableParameter $invalidValueForMutableParameter) {
            throw new InvalidValueForModifierParameter(
                $invalidValueForMutableParameter->getMessage(),
                $invalidValueForMutableParameter->getCode(),
                $invalidValueForMutableParameter
            );
        } catch (UnknownParameter $unknownParameter) {
            throw new UnknownModifierParameter(
                $unknownParameter->getMessage(),
                $unknownParameter->getCode(),
                $unknownParameter
            );
        }
    }

    /**
     * @param array $spellTraits
     * @return array|SpellTrait[]
     * @throws \DrdPlus\Tables\Theurgist\Spells\Exceptions\InvalidSpellTrait
     */
    private function getCheckedSpellTraits(array $spellTraits): array
    {
        foreach ($spellTraits as $spellTrait) {
            if (!is_a($spellTrait, SpellTrait::class)) {
                throw new Exceptions\InvalidSpellTrait(
                    'Expected instance of ' . static::class . ', got ' . ValueDescriber::describe($spellTrait)
                );
            }
        }

        return $spellTraits;
    }

    /**
     * @return ModifierCode
     */
    public function getModifierCode(): ModifierCode
    {
        return $this->modifierCode;
    }

    public function getDifficultyChange(): DifficultyChange
    {
        $modifierParameters = [
            $this->getSpellAttackWithAddition(),
            $this->getNumberOfConditionsWithAddition(),
            $this->getEpicenterShiftWithAddition(),
            $this->getGraftsWithAddition(),
            $this->getInvisibilityWithAddition(),
            $this->getNumberOfSituationsWithAddition(),
            $this->getNumberOfWaypointsWithAddition(),
            $this->getSpellPowerWithAddition(),
            $this->getNoiseWithAddition(),
            $this->getQualityWithAddition(),
            $this->getSpellRadiusWithAddition(),
            $this->getResistanceWithAddition(),
            $this->getSpellSpeedWithAddition(),
            $this->getThresholdWithAddition(),
        ];
        $modifierParameters = array_filter(
            $modifierParameters,
            fn(CastingParameter $modifierParameter = null) => $modifierParameter !== null
        );
        $parametersDifficultyChangeSum = 0;
        /** @var CastingParameter $parameter */
        foreach ($modifierParameters as $parameter) {
            $parametersDifficultyChangeSum += $parameter->getAdditionByDifficulty()->getCurrentDifficultyIncrement();
        }
        $spellTraitsDifficultyChangeSum = 0;
        foreach ($this->modifierSpellTraits as $spellTrait) {
            $spellTraitsDifficultyChangeSum += $spellTrait->getDifficultyChange()->getValue();
        }
        $difficultyChange = $this->tables->getModifiersTable()->getDifficultyChange($this->getModifierCode());

        return $difficultyChange->add($parametersDifficultyChangeSum + $spellTraitsDifficultyChangeSum);
    }

    public function getCastingRounds(): CastingRounds
    {
        return $this->tables->getModifiersTable()->getCastingRounds($this->getModifierCode());
    }

    public function getRequiredRealm(): Realm
    {
        return $this->tables->getModifiersTable()->getRealm($this->getModifierCode());
    }

    public function getRealmsAffection(): ?RealmsAffection
    {
        return $this->tables->getModifiersTable()->getRealmsAffection($this->getModifierCode());
    }

    public function getBaseSpellRadius(): ?SpellRadius
    {
        return $this->tables->getModifiersTable()->getSpellRadius($this->modifierCode);
    }

    public function getSpellRadiusWithAddition(): ?SpellRadius
    {
        $baseRadius = $this->getBaseSpellRadius();
        if (!($baseRadius instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\SpellRadius)) {
            return null;
        }

        return $baseRadius->getWithAddition($this->getSpellRadiusAddition());
    }

    public function getSpellRadiusAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::SPELL_RADIUS];
    }

    public function getBaseEpicenterShift(): ?EpicenterShift
    {
        return $this->tables->getModifiersTable()->getEpicenterShift($this->modifierCode);
    }

    public function getEpicenterShiftWithAddition(): ?EpicenterShift
    {
        $baseEpicenterShift = $this->getBaseEpicenterShift();
        if (!($baseEpicenterShift instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\EpicenterShift)) {
            return null;
        }

        return $baseEpicenterShift->getWithAddition($this->getEpicenterShiftAddition());
    }

    public function getEpicenterShiftAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::EPICENTER_SHIFT];
    }

    public function getBaseSpellPower(): ?SpellPower
    {
        return $this->tables->getModifiersTable()->getSpellPower($this->modifierCode);
    }

    public function getSpellPowerWithAddition(): ?SpellPower
    {
        $basePower = $this->getBaseSpellPower();
        if (!($basePower instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\SpellPower)) {
            return null;
        }

        return $basePower->getWithAddition($this->getSpellPowerAddition());
    }

    public function getSpellPowerAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::SPELL_POWER];
    }

    public function getBaseNoise(): ?Noise
    {
        return $this->tables->getModifiersTable()->getNoise($this->modifierCode);
    }

    public function getNoiseWithAddition(): ?Noise
    {
        $baseNoise = $this->getBaseNoise();
        if (!($baseNoise instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\Noise)) {
            return null;
        }

        return $baseNoise->getWithAddition($this->getNoiseAddition());
    }

    public function getNoiseAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::NOISE];
    }

    public function getBaseSpellAttack(): ?SpellAttack
    {
        return $this->tables->getModifiersTable()->getSpellAttack($this->modifierCode);
    }

    public function getSpellAttackWithAddition(): ?SpellAttack
    {
        $spellBaseAttack = $this->getBaseSpellAttack();
        if (!($spellBaseAttack instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\SpellAttack)) {
            return null;
        }

        return $spellBaseAttack->getWithAddition($this->getSpellAttackAddition());
    }

    public function getSpellAttackAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::SPELL_ATTACK];
    }

    public function getBaseGrafts(): ?Grafts
    {
        return $this->tables->getModifiersTable()->getGrafts($this->modifierCode);
    }

    public function getGraftsWithAddition(): ?Grafts
    {
        $baseGrafts = $this->getBaseGrafts();
        if (!($baseGrafts instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\Grafts)) {
            return null;
        }

        return $baseGrafts->getWithAddition($this->getGraftsAddition());
    }

    public function getGraftsAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::GRAFTS];
    }

    public function getBaseSpellSpeed(): ?SpellSpeed
    {
        return $this->tables->getModifiersTable()->getSpellSpeed($this->modifierCode);
    }

    public function getSpellSpeedWithAddition(): ?SpellSpeed
    {
        $baseSpellSpeed = $this->getBaseSpellSpeed();
        if (!($baseSpellSpeed instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\SpellSpeed)) {
            return null;
        }

        return $baseSpellSpeed->getWithAddition($this->getSpellSpeedAddition());
    }

    public function getSpellSpeedAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::SPELL_SPEED];
    }

    public function getBaseInvisibility(): ?Invisibility
    {
        return $this->tables->getModifiersTable()->getInvisibility($this->modifierCode);
    }

    public function getInvisibilityWithAddition(): ?Invisibility
    {
        $baseInvisibility = $this->getBaseInvisibility();
        if (!($baseInvisibility instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\Invisibility)) {
            return null;
        }

        return $baseInvisibility->getWithAddition($this->getInvisibilityAddition());
    }

    public function getInvisibilityAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::INVISIBILITY];
    }

    public function getBaseQuality(): ?Quality
    {
        return $this->tables->getModifiersTable()->getQuality($this->modifierCode);
    }

    public function getQualityWithAddition(): ?Quality
    {
        $baseQuality = $this->getBaseQuality();
        if (!($baseQuality instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\Quality)) {
            return null;
        }

        return $baseQuality->getWithAddition($this->getQualityAddition());
    }

    public function getQualityAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::QUALITY];
    }

    public function getBaseNumberOfConditions(): ?NumberOfConditions
    {
        return $this->tables->getModifiersTable()->getNumberOfConditions($this->modifierCode);
    }

    public function getNumberOfConditionsWithAddition(): ?NumberOfConditions
    {
        $baseConditions = $this->getBaseNumberOfConditions();
        if (!($baseConditions instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\NumberOfConditions)) {
            return null;
        }

        return $baseConditions->getWithAddition($this->getNumberOfConditionsAddition());
    }

    public function getNumberOfConditionsAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::NUMBER_OF_CONDITIONS];
    }

    public function getBaseResistance(): ?Resistance
    {
        return $this->tables->getModifiersTable()->getResistance($this->modifierCode);
    }

    public function getResistanceWithAddition(): ?Resistance
    {
        $baseResistance = $this->getBaseResistance();
        if (!($baseResistance instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\Resistance)) {
            return null;
        }

        return $baseResistance->getWithAddition($this->getResistanceAddition());
    }

    public function getResistanceAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::RESISTANCE];
    }

    public function getBaseNumberOfSituations(): ?NumberOfSituations
    {
        return $this->tables->getModifiersTable()->getNumberOfSituations($this->modifierCode);
    }

    public function getNumberOfSituationsWithAddition(): ?NumberOfSituations
    {
        $baseNumberOfSituations = $this->getBaseNumberOfSituations();
        if (!($baseNumberOfSituations instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\NumberOfSituations)) {
            return null;
        }

        return $baseNumberOfSituations->getWithAddition($this->getNumberOfSituationsAddition());
    }

    public function getNumberOfSituationsAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::NUMBER_OF_SITUATIONS];
    }

    public function getBaseThreshold(): ?Threshold
    {
        return $this->tables->getModifiersTable()->getThreshold($this->modifierCode);
    }

    public function getThresholdWithAddition(): ?Threshold
    {
        $baseThreshold = $this->getBaseThreshold();
        if (!($baseThreshold instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\Threshold)) {
            return null;
        }

        return $baseThreshold->getWithAddition($this->getThresholdAddition());
    }

    public function getThresholdAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::THRESHOLD];
    }

    public function getBaseNumberOfWaypoints(): ?NumberOfWaypoints
    {
        return $this->tables->getModifiersTable()->getNumberOfWaypoints($this->modifierCode);
    }

    public function getNumberOfWaypointsWithAddition(): ?NumberOfWaypoints
    {
        $baseNumberOfWaypoints = $this->getBaseNumberOfWaypoints();
        if (!($baseNumberOfWaypoints instanceof \DrdPlus\Tables\Theurgist\Spells\SpellParameters\NumberOfWaypoints)) {
            return null;
        }

        return $baseNumberOfWaypoints->getWithAddition($this->getNumberOfWaypointsAddition());
    }

    public function getNumberOfWaypointsAddition(): int
    {
        return $this->modifierSpellParameterChanges[ModifierMutableParameterCode::NUMBER_OF_WAYPOINTS];
    }

    public function __toString()
    {
        return $this->getModifierCode()->getValue();
    }
}