Passed
Push — dev ( 02e99e...928e2e )
by Nico
12:03
created

EnergyWeaponPhase::setWeaponShieldModificator()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 10
c 0
b 0
f 0
nc 4
nop 3
dl 0
loc 22
ccs 0
cts 11
cp 0
crap 20
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Module\Ship\Lib\Battle\Weapon;
6
7
use Stu\Component\Ship\ShipModuleTypeEnum;
8
use Stu\Lib\DamageWrapper;
9
use Stu\Module\Ship\Lib\Battle\Message\FightMessage;
10
use Stu\Module\Ship\Lib\Battle\Provider\EnergyAttackerInterface;
11
use Stu\Orm\Entity\PlanetFieldInterface;
12
use Stu\Orm\Entity\ShipInterface;
13
use Stu\Orm\Entity\WeaponInterface;
14
use Stu\Orm\Entity\WeaponShieldInterface;
15
16
//TODO unit tests
17
final class EnergyWeaponPhase extends AbstractWeaponPhase implements EnergyWeaponPhaseInterface
18
{
19
    public const FIRINGMODE_RANDOM = 1;
20
    public const FIRINGMODE_FOCUS = 2;
21
22
    public function fire(
23
        EnergyAttackerInterface $attacker,
24
        array $targetPool,
25
        bool $isAlertRed = false
26
    ): array {
27
        $fightMessages = [];
28
29
        $targetWrapper = $targetPool[array_rand($targetPool)];
30
31
        for ($i = 1; $i <= $attacker->getPhaserVolleys(); $i++) {
32
            if (count($targetPool) === 0) {
33
                break;
34
            }
35
            if (!$attacker->getPhaserState() || !$attacker->hasSufficientEnergy($this->getEnergyWeaponEnergyCosts())) {
36
                break;
37
            }
38
39
            $weapon = $attacker->getWeapon();
40
41
            $attacker->reduceEps($this->getEnergyWeaponEnergyCosts());
42
            if ($attacker->getFiringMode() === self::FIRINGMODE_RANDOM) {
43
                $targetWrapper = $targetPool[array_rand($targetPool)];
44
            }
45
46
            $target = $targetWrapper->get();
47
48
            $fightMessage = new FightMessage($attacker->getUser()->getId(), $target->getUser()->getId());
49
            $fightMessages[] = $fightMessage;
50
51
            $fightMessage->add(sprintf(
52
                "Die %s feuert mit einem %s auf die %s",
53
                $attacker->getName(),
54
                $weapon->getName(),
55
                $target->getName()
56
            ));
57
58
            if (
59
                $attacker->getHitChance() * (100 - $target->getEvadeChance()) < rand(1, 10000)
60
            ) {
61
                $fightMessage->add("Die " . $target->getName() . " wurde verfehlt");
62
                continue;
63
            }
64
            $isCritical = $this->isCritical($weapon, $target->getCloakState());
65
            $damage_wrapper = new DamageWrapper(
66
                $attacker->getWeaponDamage($isCritical)
67
            );
68
            $damage_wrapper->setCrit($isCritical);
69
            $damage_wrapper->setShieldDamageFactor($attacker->getPhaserShieldDamageFactor());
70
            $damage_wrapper->setHullDamageFactor($attacker->getPhaserHullDamageFactor());
71
            $damage_wrapper->setIsPhaserDamage(true);
72
            $this->setWeaponShieldModificator($target, $weapon, $damage_wrapper);
73
74
            $fightMessage->addMessageMerge($this->applyDamage->damage($damage_wrapper, $targetWrapper));
75
76
            if ($target->isDestroyed()) {
77
                if ($isAlertRed) {
78
                    $this->entryCreator->addShipEntry(
79
                        '[b][color=red]Alarm-Rot:[/color][/b] Die ' . $target->getName() . ' (' . $target->getRump()->getName() . ') wurde in Sektor ' . $target->getSectorString() . ' von der ' . $attacker->getName() . ' zerstört',
80
                        $attacker->getUser()->getId()
81
                    );
82
                } else {
83
                    $entryMsg = sprintf(
84
                        'Die %s (%s) wurde in Sektor %s von der %s zerstört',
85
                        $target->getName(),
86
                        $target->getRump()->getName(),
87
                        $target->getSectorString(),
88
                        $attacker->getName()
89
                    );
90
                    if ($target->isBase()) {
91
                        $this->entryCreator->addStationEntry(
92
                            $entryMsg,
93
                            $attacker->getUser()->getId()
94
                        );
95
                    } else {
96
                        $this->entryCreator->addShipEntry(
97
                            $entryMsg,
98
                            $attacker->getUser()->getId()
99
                        );
100
                    }
101
                }
102
103
                $this->checkForPrestige($attacker->getUser(), $target);
104
105
                $targetId = $target->getId();
106
                $fightMessage->add($this->shipRemover->destroy($targetWrapper));
107
108
                unset($targetPool[$targetId]);
109
110
                if ($weapon->getFiringMode() === self::FIRINGMODE_FOCUS) {
111
                    break;
112
                }
113
            }
114
        }
115
116
        return $fightMessages;
117
    }
118
119
    public function fireAtBuilding(
120
        EnergyAttackerInterface $attacker,
121
        PlanetFieldInterface $target,
122
        bool $isOrbitField
123
    ): array {
124
        $msg = [];
125
126
        $building = $target->getBuilding();
127
        if ($building === null) {
128
            $msg[] = _("Kein Gebäude vorhanden");
129
130
            return $msg;
131
        }
132
133
        for ($i = 1; $i <= $attacker->getPhaserVolleys(); $i++) {
134
            if (!$attacker->getPhaserState() || !$attacker->hasSufficientEnergy($this->getEnergyWeaponEnergyCosts())) {
135
                break;
136
            }
137
            $attacker->reduceEps($this->getEnergyWeaponEnergyCosts());
138
139
            $weapon = $attacker->getWeapon();
140
            $msg[] = sprintf(_("Die %s feuert mit einem %s auf das Gebäude %s auf Feld %d"), $attacker->getName(), $weapon->getName(), $building->getName(), $target->getFieldId());
141
142
            if (
143
                $attacker->getHitChance() < rand(1, 100)
144
            ) {
145
                $msg[] = _("Das Gebäude wurde verfehlt");
146
                continue;
147
            }
148
149
            $isCritical = rand(1, 100) <= $weapon->getCriticalChance();
150
151
            $damage_wrapper = new DamageWrapper(
152
                $attacker->getWeaponDamage($isCritical)
153
            );
154
            $damage_wrapper->setCrit($isCritical);
155
            $damage_wrapper->setShieldDamageFactor($attacker->getPhaserShieldDamageFactor());
156
            $damage_wrapper->setHullDamageFactor($attacker->getPhaserHullDamageFactor());
157
            $damage_wrapper->setIsPhaserDamage(true);
158
159
160
            $msg = array_merge($msg, $this->applyDamage->damageBuilding($damage_wrapper, $target, $isOrbitField));
161
162
            if ($target->getIntegrity() === 0) {
163
                $this->entryCreator->addColonyEntry(
164
                    sprintf(
165
                        _('Das Gebäude %s auf Kolonie %s wurde von der %s zerstört'),
166
                        $building->getName(),
167
                        $target->getColony()->getName(),
168
                        $attacker->getName()
169
                    )
170
                );
171
172
                $this->buildingManager->remove($target);
173
                break;
174
            }
175
            //deactivate if high damage
176
            elseif ($target->hasHighDamage()) {
177
                $this->buildingManager->deactivate($target);
178
            }
179
        }
180
181
        return $msg;
182
    }
183
184
    private function isCritical(WeaponInterface $weapon, bool $isTargetCloaked): bool
185
    {
186
        $critChance = $isTargetCloaked ? $weapon->getCriticalChance() * 2 : $weapon->getCriticalChance();
187
        if (rand(1, 100) <= $critChance) {
188
            return true;
189
        }
190
        return false;
191
    }
192
193
    private function setWeaponShieldModificator(
194
        ShipInterface $target,
195
        WeaponInterface $weapon,
196
        DamageWrapper $damageWrapper
197
    ): void {
198
        $targetBuildplan = $target->getBuildplan();
199
        if ($targetBuildplan === null) {
200
            return;
201
        }
202
203
        $targetShieldBuildplanModule = current($targetBuildplan->getModulesByType(ShipModuleTypeEnum::MODULE_TYPE_SHIELDS));
204
        if (!$targetShieldBuildplanModule) {
205
            return;
206
        }
207
208
        $targetShieldModule = $targetShieldBuildplanModule->getModule();
209
210
        /** @var WeaponShieldInterface|null */
211
        $weaponShield = $targetShieldModule->getWeaponShield()->get($weapon->getId());
212
213
        if ($weaponShield !== null) {
214
            $damageWrapper->setModificator($weaponShield->getModificator());
215
        }
216
    }
217
218
    private function getEnergyWeaponEnergyCosts(): int
219
    {
220
        // @todo
221
        return 1;
222
    }
223
}
224