Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Issues (412)

src/lib/Default/SmrWeapon.php (4 issues)

1
<?php declare(strict_types=1);
2
3
use Smr\BuyerRestriction;
4
use Smr\DatabaseRecord;
5
use Smr\Pages\Player\ShopWeaponProcessor;
6
7
/**
8
 * Defines a concrete realization of a weapon type for ships/planets.
9
 */
10
class SmrWeapon extends AbstractSmrCombatWeapon {
11
12
	use Traits\RaceID;
13
14
	protected const BONUS_DAMAGE = 15; // additive bonus
15
	protected const BONUS_ACCURACY = 4; // additive bonus
16
17
	protected const HIGHEST_POWER_LEVEL = 5; // must track the highest power level in db
18
19
	protected readonly SmrWeaponType $weaponType;
20
	protected bool $bonusAccuracy = false; // default
21
	protected bool $bonusDamage = false; // default
22
	protected bool $damageRollover = false; // fixed for all SmrWeapons
23
24
	public static function getWeapon(int $weaponTypeID, DatabaseRecord $dbRecord = null): self {
25
		return new self($weaponTypeID, $dbRecord);
26
	}
27
28
	protected function __construct(
29
		protected readonly int $weaponTypeID,
30
		DatabaseRecord $dbRecord = null
31
	) {
32
		$this->weaponType = SmrWeaponType::getWeaponType($weaponTypeID, $dbRecord);
0 ignored issues
show
The property weaponType is declared read-only in SmrWeapon.
Loading history...
33
		$this->raceID = $this->weaponType->getRaceID();
34
	}
35
36
	public function hasBonusAccuracy(): bool {
37
		return $this->bonusAccuracy;
38
	}
39
40
	public function setBonusAccuracy(bool $bonusAccuracy): void {
41
		$this->bonusAccuracy = $bonusAccuracy;
42
	}
43
44
	public function hasBonusDamage(): bool {
45
		return $this->bonusDamage;
46
	}
47
48
	public function setBonusDamage(bool $bonusDamage): void {
49
		$this->bonusDamage = $bonusDamage;
50
	}
51
52
	private function hasEnhancements(): bool {
53
		return $this->getNumberOfEnhancements() > 0;
54
	}
55
56
	private function getNumberOfEnhancements(): int {
57
		return (int)$this->bonusAccuracy + (int)$this->bonusDamage;
58
	}
59
60
	/**
61
	 * Return weapon name suitable for HTML display.
62
	 * The name is displayed in green with pluses if enhancements are present.
63
	 */
64
	public function getName(): string {
65
		$name = $this->weaponType->getName();
66
		if ($this->hasEnhancements()) {
67
			$name = '<span class="green">' . $name . str_repeat('+', $this->getNumberOfEnhancements()) . '</span>';
68
		}
69
		return $name;
70
	}
71
72
	/**
73
	 * Return the weapon base accuracy.
74
	 */
75
	public function getBaseAccuracy(): int {
76
		$baseAccuracy = $this->weaponType->getAccuracy();
77
		if ($this->bonusAccuracy) {
78
			$baseAccuracy += self::BONUS_ACCURACY;
79
		}
80
		return $baseAccuracy;
81
	}
82
83
	/**
84
	 * Return the weapon shield damage.
85
	 */
86
	public function getShieldDamage(): int {
87
		$shieldDamage = $this->weaponType->getShieldDamage();
88
		if ($this->bonusDamage && $shieldDamage > 0) {
89
			$shieldDamage += self::BONUS_DAMAGE;
90
		}
91
		return $shieldDamage;
92
	}
93
94
	/**
95
	 * Return the weapon armour damage.
96
	 */
97
	public function getArmourDamage(): int {
98
		$armourDamage = $this->weaponType->getArmourDamage();
99
		if ($this->bonusDamage && $armourDamage > 0) {
100
			$armourDamage += self::BONUS_DAMAGE;
101
		}
102
		return $armourDamage;
103
	}
104
105
	public function getBuyHREF(SmrLocation $location): string {
106
		$container = new ShopWeaponProcessor($location->getTypeID(), $this);
107
		return $container->href();
108
	}
109
110
	public function getSellHREF(SmrLocation $location, int $orderID): string {
111
		$container = new ShopWeaponProcessor($location->getTypeID(), $this, $orderID);
112
		return $container->href();
113
	}
114
115
	public function getWeaponTypeID(): int {
116
		return $this->weaponTypeID;
117
	}
118
119
	/**
120
	 * Weapon cost is increased by 100% for each enhancement present
121
	 */
122
	public function getCost(): int {
123
		return $this->weaponType->getCost() * (1 + $this->getNumberOfEnhancements());
124
	}
125
126
	public function getPowerLevel(): int {
127
		return $this->weaponType->getPowerLevel();
128
	}
129
130
	public function getBuyerRestriction(): BuyerRestriction {
131
		return $this->weaponType->getBuyerRestriction();
132
	}
133
134
	/**
135
	 * Ships are only allowed to equip one of each type of Unique weapon
136
	 */
137
	public function isUniqueType(): bool {
138
		return $this->getPowerLevel() === self::HIGHEST_POWER_LEVEL;
139
	}
140
141
	protected function getWeightedRandomForPlayer(AbstractSmrPlayer $player): WeightedRandom {
142
		return WeightedRandom::getWeightedRandomForPlayer($player, 'Weapon', $this->getWeaponTypeID());
143
	}
144
145
	/**
146
	 * Given $weaponAccuracy as a percent, decide if the weapon hits.
147
	 */
148
	protected function checkHit(AbstractSmrPlayer $player, float $weaponAccuracy): bool {
149
		// Skip weighting factor for guaranteed hits/misses.
150
		return match (true) {
151
			$weaponAccuracy >= 100 => true,
152
			$weaponAccuracy <= 0 => false,
153
			default => $this->getWeightedRandomForPlayer($player)->flipWeightedCoin($weaponAccuracy),
154
		};
155
	}
156
157
	public static function getPlayerLevelAccuracyMod(AbstractSmrPlayer $player): float {
158
		return ($player->getLevelID() * $player->getLevelID() / 60 + $player->getLevelID() / 2 + 2) / 100;
159
	}
160
161
	public function getModifiedAccuracy(AbstractSmrPlayer $weaponPlayer): float {
162
		$modifiedAccuracy = $this->getBaseAccuracy();
163
		$modifiedAccuracy += $this->getBaseAccuracy() * self::getPlayerLevelAccuracyMod($weaponPlayer);
164
		return $modifiedAccuracy;
165
	}
166
167
	public function getModifiedAccuracyAgainstForces(AbstractSmrPlayer $weaponPlayer, SmrForce $forces): float {
0 ignored issues
show
The parameter $forces is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

167
	public function getModifiedAccuracyAgainstForces(AbstractSmrPlayer $weaponPlayer, /** @scrutinizer ignore-unused */ SmrForce $forces): float {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
168
		return $this->getModifiedAccuracy($weaponPlayer);
169
	}
170
171
	public function getModifiedAccuracyAgainstPort(AbstractSmrPlayer $weaponPlayer, SmrPort $port): float {
172
		$modifiedAccuracy = $this->getModifiedAccuracy($weaponPlayer);
173
		$modifiedAccuracy -= $this->getBaseAccuracy() * $port->getLevel() / 50;
174
		return $modifiedAccuracy;
175
	}
176
177
	public function getModifiedAccuracyAgainstPlanet(AbstractSmrPlayer $weaponPlayer, SmrPlanet $planet): float {
178
		$modifiedAccuracy = $this->getModifiedAccuracy($weaponPlayer);
179
		$modifiedAccuracy -= $this->getBaseAccuracy() * $planet->getLevel() / 350;
180
		return $modifiedAccuracy;
181
	}
182
183
	public function getModifiedAccuracyAgainstPlayer(AbstractSmrPlayer $weaponPlayer, AbstractSmrPlayer $targetPlayer): float {
184
		$modifiedAccuracy = $this->getModifiedAccuracy($weaponPlayer);
185
		$modifiedAccuracy -= $this->getBaseAccuracy() * self::getPlayerLevelAccuracyMod($targetPlayer) / 2;
186
187
		$weaponShip = $weaponPlayer->getShip();
188
		$targetShip = $targetPlayer->getShip();
189
		$mrDiff = $targetShip->getMR() - $weaponShip->getMR();
190
		if ($mrDiff > 0) {
191
			$modifiedAccuracy -= $this->getBaseAccuracy() * ($mrDiff / MR_FACTOR) / 100;
192
		}
193
194
		return $modifiedAccuracy;
195
	}
196
197
	public function getModifiedPortAccuracy(AbstractSmrPort $port): float {
0 ignored issues
show
The parameter $port is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

197
	public function getModifiedPortAccuracy(/** @scrutinizer ignore-unused */ AbstractSmrPort $port): float {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
198
		return $this->getBaseAccuracy();
199
	}
200
201
	public function getModifiedPortAccuracyAgainstPlayer(AbstractSmrPort $port, AbstractSmrPlayer $targetPlayer): float {
202
		$modifiedAccuracy = $this->getModifiedPortAccuracy($port);
203
		$modifiedAccuracy -= $this->getBaseAccuracy() * self::getPlayerLevelAccuracyMod($targetPlayer);
204
		return $modifiedAccuracy;
205
	}
206
207
	public function getModifiedPlanetAccuracy(SmrPlanet $planet): float {
208
		$modifiedAccuracy = $this->getBaseAccuracy();
209
		if ($this->getWeaponTypeID() == WEAPON_PLANET_TURRET) {
210
			$modifiedAccuracy += $planet->getLevel() / 2;
211
		} else {
212
			$modifiedAccuracy += $planet->getAccuracyBonus();
213
		}
214
		return $modifiedAccuracy;
215
	}
216
217
	public function getModifiedPlanetAccuracyAgainstPlayer(SmrPlanet $planet, AbstractSmrPlayer $targetPlayer): float {
218
		$modifiedAccuracy = $this->getModifiedPlanetAccuracy($planet);
219
		$modifiedAccuracy -= $this->getBaseAccuracy() * self::getPlayerLevelAccuracyMod($targetPlayer);
220
		return $modifiedAccuracy;
221
	}
222
223
	public function getModifiedDamageAgainstForces(AbstractSmrPlayer $weaponPlayer, SmrForce $forces): array {
224
		if (!$this->canShootForces()) {
225
			// If we can't shoot forces then just return a damageless array and don't waste resources calculated any damage mods.
226
			return ['Shield' => 0, 'Armour' => 0, 'Rollover' => $this->isDamageRollover()];
227
		}
228
		return $this->getDamage();
229
	}
230
231
	public function getModifiedDamageAgainstPort(AbstractSmrPlayer $weaponPlayer, SmrPort $port): array {
232
		if (!$this->canShootPorts()) {
233
			// If we can't shoot forces then just return a damageless array and don't waste resources calculated any damage mods.
234
			return ['Shield' => 0, 'Armour' => 0, 'Rollover' => $this->isDamageRollover()];
235
		}
236
		return $this->getDamage();
237
	}
238
239
	public function getModifiedDamageAgainstPlanet(AbstractSmrPlayer $weaponPlayer, SmrPlanet $planet): array {
240
		if (!$this->canShootPlanets()) {
241
			// If we can't shoot forces then just return a damageless array and don't waste resources calculated any damage mods.
242
			return ['Shield' => 0, 'Armour' => 0, 'Rollover' => $this->isDamageRollover()];
243
		}
244
		$damage = $this->getDamage();
245
246
		$planetMod = self::PLANET_DAMAGE_MOD;
247
		$damage['Shield'] = ICeil($damage['Shield'] * $planetMod);
0 ignored issues
show
The function ICeil was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

247
		$damage['Shield'] = /** @scrutinizer ignore-call */ ICeil($damage['Shield'] * $planetMod);
Loading history...
248
		$damage['Armour'] = ICeil($damage['Armour'] * $planetMod);
249
250
		return $damage;
251
	}
252
253
	public function getModifiedForceDamageAgainstPlayer(SmrForce $forces, AbstractSmrPlayer $targetPlayer): never {
254
		throw new Exception('This weapon should not be used in this context');
255
	}
256
257
	public function getModifiedDamageAgainstPlayer(AbstractSmrPlayer $weaponPlayer, AbstractSmrPlayer $targetPlayer): array {
258
		if (!$this->canShootTraders()) { // If we can't shoot traders then just return a damageless array and don't waste resources calculating any damage mods.
259
			return ['Shield' => 0, 'Armour' => 0, 'Rollover' => $this->isDamageRollover()];
260
		}
261
		return $this->getDamage();
262
	}
263
264
	public function getModifiedPortDamageAgainstPlayer(AbstractSmrPort $port, AbstractSmrPlayer $targetPlayer): array {
265
		if (!$this->canShootTraders()) { // If we can't shoot traders then just return a damageless array and don't waste resources calculating any damage mods.
266
			return ['Shield' => 0, 'Armour' => 0, 'Rollover' => $this->isDamageRollover()];
267
		}
268
		return $this->getDamage();
269
	}
270
271
	public function getModifiedPlanetDamageAgainstPlayer(SmrPlanet $planet, AbstractSmrPlayer $targetPlayer): array {
272
		if (!$this->canShootTraders()) { // If we can't shoot traders then just return a damageless array and don't waste resources calculating any damage mods.
273
			return ['Shield' => 0, 'Armour' => 0, 'Rollover' => $this->isDamageRollover()];
274
		}
275
		return $this->getDamage();
276
	}
277
278
	public function shootForces(AbstractSmrPlayer $weaponPlayer, SmrForce $forces): array {
279
		$return = ['Weapon' => $this, 'TargetForces' => $forces, 'Hit' => false];
280
		$modifiedAccuracy = $this->getModifiedAccuracyAgainstForces($weaponPlayer, $forces);
281
		if ($this->checkHit($weaponPlayer, $modifiedAccuracy)) {
282
			$return['Hit'] = true;
283
			return $this->doPlayerDamageToForce($return, $weaponPlayer, $forces);
284
		}
285
		return $return;
286
	}
287
288
	/**
289
	 * @return array<string, mixed>
290
	 */
291
	public function shootPort(AbstractSmrPlayer $weaponPlayer, SmrPort $port): array {
292
		$return = ['Weapon' => $this, 'TargetPort' => $port, 'Hit' => false];
293
		$modifiedAccuracy = $this->getModifiedAccuracyAgainstPort($weaponPlayer, $port);
294
		if ($this->checkHit($weaponPlayer, $modifiedAccuracy)) {
295
			$return['Hit'] = true;
296
			return $this->doPlayerDamageToPort($return, $weaponPlayer, $port);
297
		}
298
		return $return;
299
	}
300
301
	/**
302
	 * @return array<string, mixed>
303
	 */
304
	public function shootPlanet(AbstractSmrPlayer $weaponPlayer, SmrPlanet $planet): array {
305
		$return = ['Weapon' => $this, 'TargetPlanet' => $planet, 'Hit' => false];
306
		$modifiedAccuracy = $this->getModifiedAccuracyAgainstPlanet($weaponPlayer, $planet);
307
		if ($this->checkHit($weaponPlayer, $modifiedAccuracy)) {
308
			$return['Hit'] = true;
309
			return $this->doPlayerDamageToPlanet($return, $weaponPlayer, $planet);
310
		}
311
		return $return;
312
	}
313
314
	public function shootPlayer(AbstractSmrPlayer $weaponPlayer, AbstractSmrPlayer $targetPlayer): array {
315
		$return = ['Weapon' => $this, 'TargetPlayer' => $targetPlayer, 'Hit' => false];
316
		$modifiedAccuracy = $this->getModifiedAccuracyAgainstPlayer($weaponPlayer, $targetPlayer);
317
		if ($this->checkHit($weaponPlayer, $modifiedAccuracy)) {
318
			$return['Hit'] = true;
319
			return $this->doPlayerDamageToPlayer($return, $weaponPlayer, $targetPlayer);
320
		}
321
		return $return;
322
	}
323
324
	public function shootPlayerAsForce(SmrForce $forces, AbstractSmrPlayer $targetPlayer): never {
325
		throw new Exception('This weapon should not be used in this context');
326
	}
327
328
	/**
329
	 * @return array<string, mixed>
330
	 */
331
	public function shootPlayerAsPort(AbstractSmrPort $port, AbstractSmrPlayer $targetPlayer): array {
332
		$return = ['Weapon' => $this, 'TargetPlayer' => $targetPlayer, 'Hit' => false];
333
		$modifiedAccuracy = $this->getModifiedPortAccuracyAgainstPlayer($port, $targetPlayer);
334
		if ($this->checkHit($targetPlayer, $modifiedAccuracy)) {
335
			$return['Hit'] = true;
336
			return $this->doPortDamageToPlayer($return, $port, $targetPlayer);
337
		}
338
		return $return;
339
	}
340
341
	/**
342
	 * @return array<string, mixed>
343
	 */
344
	public function shootPlayerAsPlanet(SmrPlanet $planet, AbstractSmrPlayer $targetPlayer): array {
345
		$return = ['Weapon' => $this, 'TargetPlayer' => $targetPlayer, 'Hit' => false];
346
		$modifiedAccuracy = $this->getModifiedPlanetAccuracyAgainstPlayer($planet, $targetPlayer);
347
		if ($this->checkHit($targetPlayer, $modifiedAccuracy)) {
348
			$return['Hit'] = true;
349
			return $this->doPlanetDamageToPlayer($return, $planet, $targetPlayer);
350
		}
351
		return $return;
352
	}
353
354
}
355