We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.
1 | <?php declare(strict_types=1); |
||||
2 | |||||
3 | use Smr\Database; |
||||
4 | use Smr\DatabaseRecord; |
||||
5 | use Smr\Epoch; |
||||
6 | use Smr\Exceptions\UserError; |
||||
7 | use Smr\Pages\Player\AttackPlanetProcessor; |
||||
8 | use Smr\Pages\Player\ExaminePlanet; |
||||
9 | use Smr\Pages\Player\Planet\BondConfirm; |
||||
10 | use Smr\Pages\Player\Planet\ConstructionProcessor; |
||||
11 | use Smr\Pages\Player\Planet\FinancialProcessor; |
||||
12 | use Smr\Pages\Player\Planet\LandProcessor; |
||||
13 | use Smr\PlanetTypes\PlanetType; |
||||
14 | |||||
15 | class SmrPlanet { |
||||
16 | |||||
17 | /** @var array<int, array<int, self>> */ |
||||
18 | protected static array $CACHE_PLANETS = []; |
||||
19 | |||||
20 | public const DAMAGE_NEEDED_FOR_DOWNGRADE_CHANCE = 100; |
||||
21 | protected const CHANCE_TO_DOWNGRADE = 15; // percent |
||||
22 | protected const TIME_TO_CREDIT_BUST = 10800; // 3 hours |
||||
23 | protected const TIME_ATTACK_NEWS_COOLDOWN = 3600; // 1 hour |
||||
24 | public const MAX_STOCKPILE = 600; |
||||
25 | |||||
26 | protected Database $db; |
||||
27 | protected readonly string $SQL; |
||||
28 | |||||
29 | protected bool $exists; |
||||
30 | protected string $planetName; |
||||
31 | protected int $ownerID; |
||||
32 | protected string $password; |
||||
33 | protected int $shields; |
||||
34 | protected int $armour; |
||||
35 | protected int $drones; |
||||
36 | protected int $credits; |
||||
37 | protected int $bonds; |
||||
38 | protected int $maturity; |
||||
39 | /** @var array<int, int> */ |
||||
40 | protected array $stockpile; |
||||
41 | /** @var array<int, int> */ |
||||
42 | protected array $buildings; |
||||
43 | protected int $inhabitableTime; |
||||
44 | /** @var array<int, array<string, int>> */ |
||||
45 | protected array $currentlyBuilding; |
||||
46 | /** @var array<int, SmrWeapon> */ |
||||
47 | protected array $mountedWeapons; |
||||
48 | protected int $typeID; |
||||
49 | protected PlanetType $typeInfo; |
||||
50 | |||||
51 | protected bool $hasChanged = false; |
||||
52 | protected bool $hasChangedFinancial = false; // for credits, bond, maturity |
||||
53 | protected bool $hasChangedStockpile = false; |
||||
54 | /** @var array<int, bool> */ |
||||
55 | protected array $hasChangedWeapons = []; |
||||
56 | /** @var array<int, bool> */ |
||||
57 | protected array $hasChangedBuildings = []; |
||||
58 | /** @var array<int> */ |
||||
59 | protected array $hasStoppedBuilding = []; |
||||
60 | protected bool $isNew = false; |
||||
61 | |||||
62 | public function __sleep() { |
||||
63 | return ['sectorID', 'gameID', 'planetName', 'ownerID', 'typeID']; |
||||
64 | } |
||||
65 | |||||
66 | public static function clearCache(): void { |
||||
67 | self::$CACHE_PLANETS = []; |
||||
68 | } |
||||
69 | |||||
70 | public static function savePlanets(): void { |
||||
71 | foreach (self::$CACHE_PLANETS as $gamePlanets) { |
||||
72 | foreach ($gamePlanets as $planet) { |
||||
73 | $planet->update(); |
||||
74 | } |
||||
75 | } |
||||
76 | } |
||||
77 | |||||
78 | /** |
||||
79 | * @return array<int, self> |
||||
80 | */ |
||||
81 | public static function getGalaxyPlanets(int $gameID, int $galaxyID, bool $forceUpdate = false): array { |
||||
82 | $db = Database::getInstance(); |
||||
83 | $dbResult = $db->read('SELECT planet.* FROM planet LEFT JOIN sector USING (game_id, sector_id) WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND galaxy_id = ' . $db->escapeNumber($galaxyID)); |
||||
84 | $galaxyPlanets = []; |
||||
85 | foreach ($dbResult->records() as $dbRecord) { |
||||
86 | $sectorID = $dbRecord->getInt('sector_id'); |
||||
87 | $galaxyPlanets[$sectorID] = self::getPlanet($gameID, $sectorID, $forceUpdate, $dbRecord); |
||||
88 | } |
||||
89 | return $galaxyPlanets; |
||||
90 | } |
||||
91 | |||||
92 | public static function getPlanet(int $gameID, int $sectorID, bool $forceUpdate = false, DatabaseRecord $dbRecord = null): self { |
||||
93 | if ($forceUpdate || !isset(self::$CACHE_PLANETS[$gameID][$sectorID])) { |
||||
94 | self::$CACHE_PLANETS[$gameID][$sectorID] = new self($gameID, $sectorID, $dbRecord); |
||||
95 | } |
||||
96 | return self::$CACHE_PLANETS[$gameID][$sectorID]; |
||||
97 | } |
||||
98 | |||||
99 | public static function createPlanet(int $gameID, int $sectorID, int $typeID = 1, int $inhabitableTime = null): self { |
||||
100 | if (self::getPlanet($gameID, $sectorID)->exists()) { |
||||
101 | throw new Exception('Planet already exists in sector ' . $sectorID . ' game ' . $gameID); |
||||
102 | } |
||||
103 | |||||
104 | if ($inhabitableTime === null) { |
||||
105 | $minTime = SmrGame::getGame($gameID)->getStartTime(); |
||||
106 | $inhabitableTime = $minTime + pow(rand(45, 85), 3); |
||||
107 | } |
||||
108 | |||||
109 | // insert planet into db |
||||
110 | $db = Database::getInstance(); |
||||
111 | $db->insert('planet', [ |
||||
112 | 'game_id' => $db->escapeNumber($gameID), |
||||
113 | 'sector_id' => $db->escapeNumber($sectorID), |
||||
114 | 'inhabitable_time' => $db->escapeNumber($inhabitableTime), |
||||
115 | 'planet_type_id' => $db->escapeNumber($typeID), |
||||
116 | ]); |
||||
117 | return self::getPlanet($gameID, $sectorID, true); |
||||
118 | } |
||||
119 | |||||
120 | public static function removePlanet(int $gameID, int $sectorID): void { |
||||
121 | $db = Database::getInstance(); |
||||
122 | $SQL = 'game_id = ' . $db->escapeNumber($gameID) . ' AND sector_id = ' . $db->escapeNumber($sectorID); |
||||
123 | $db->write('DELETE FROM planet WHERE ' . $SQL); |
||||
124 | $db->write('DELETE FROM planet_has_weapon WHERE ' . $SQL); |
||||
125 | $db->write('DELETE FROM planet_has_cargo WHERE ' . $SQL); |
||||
126 | $db->write('DELETE FROM planet_has_building WHERE ' . $SQL); |
||||
127 | $db->write('DELETE FROM planet_is_building WHERE ' . $SQL); |
||||
128 | //kick everyone from planet |
||||
129 | $db->write('UPDATE player SET land_on_planet = \'FALSE\' WHERE ' . $SQL); |
||||
130 | |||||
131 | self::$CACHE_PLANETS[$gameID][$sectorID] = null; |
||||
132 | unset(self::$CACHE_PLANETS[$gameID][$sectorID]); |
||||
133 | } |
||||
134 | |||||
135 | protected function __construct( |
||||
136 | protected readonly int $gameID, |
||||
137 | protected readonly int $sectorID, |
||||
138 | DatabaseRecord $dbRecord = null |
||||
139 | ) { |
||||
140 | $this->db = Database::getInstance(); |
||||
141 | $this->SQL = 'game_id = ' . $this->db->escapeNumber($gameID) . ' AND sector_id = ' . $this->db->escapeNumber($sectorID); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
142 | |||||
143 | if ($dbRecord === null) { |
||||
144 | $dbResult = $this->db->read('SELECT * FROM planet WHERE ' . $this->SQL); |
||||
145 | if ($dbResult->hasRecord()) { |
||||
146 | $dbRecord = $dbResult->record(); |
||||
147 | } |
||||
148 | } |
||||
149 | $this->exists = $dbRecord !== null; |
||||
150 | |||||
151 | if ($this->exists) { |
||||
152 | $this->planetName = $dbRecord->getString('planet_name'); |
||||
153 | $this->ownerID = $dbRecord->getInt('owner_id'); |
||||
154 | $this->password = $dbRecord->getString('password'); |
||||
155 | $this->shields = $dbRecord->getInt('shields'); |
||||
156 | $this->armour = $dbRecord->getInt('armour'); |
||||
157 | $this->drones = $dbRecord->getInt('drones'); |
||||
158 | $this->credits = $dbRecord->getInt('credits'); |
||||
159 | $this->bonds = $dbRecord->getInt('bonds'); |
||||
160 | $this->maturity = $dbRecord->getInt('maturity'); |
||||
161 | $this->inhabitableTime = $dbRecord->getInt('inhabitable_time'); |
||||
162 | $this->typeID = $dbRecord->getInt('planet_type_id'); |
||||
163 | |||||
164 | $this->typeInfo = PlanetType::getTypeInfo($this->getTypeID()); |
||||
165 | $this->checkBondMaturity(); |
||||
166 | } |
||||
167 | } |
||||
168 | |||||
169 | public function getInterestRate(): float { |
||||
170 | $level = $this->getLevel(); |
||||
171 | return match (true) { |
||||
172 | $level < 9 => .0404, |
||||
173 | $level < 19 => .0609, |
||||
174 | $level < 29 => .1236, |
||||
175 | $level < 39 => .050625, |
||||
176 | $level < 49 => .0404, |
||||
177 | $level < 59 => .030225, |
||||
178 | $level < 69 => .0201, |
||||
179 | default => .018081, |
||||
180 | }; |
||||
181 | } |
||||
182 | |||||
183 | public function checkBondMaturity(bool $partial = false): void { |
||||
184 | if ($this->getMaturity() > 0 && ($partial === true || $this->getMaturity() < Epoch::time())) { |
||||
185 | // calc the interest for the time |
||||
186 | $interest = $this->getBonds() * $this->getInterestRate(); |
||||
187 | |||||
188 | if ($partial === true && $this->getMaturity() > Epoch::time()) { |
||||
189 | // Adjust interest based upon how much of the bond duration has passed. |
||||
190 | $interest -= ($interest / $this->getBondTime()) * ($this->getMaturity() - Epoch::time()); |
||||
191 | } |
||||
192 | |||||
193 | // transfer money to free avail cash |
||||
194 | $this->increaseCredits($this->getBonds() + IFloor($interest)); |
||||
0 ignored issues
–
show
The function
IFloor 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
![]() |
|||||
195 | |||||
196 | // reset bonds |
||||
197 | $this->setBonds(0); |
||||
198 | |||||
199 | // reset maturity |
||||
200 | $this->setMaturity(0); |
||||
201 | } |
||||
202 | } |
||||
203 | |||||
204 | public function getBondTime(): int { |
||||
205 | return IRound(BOND_TIME / $this->getGame()->getGameSpeed()); |
||||
0 ignored issues
–
show
The function
IRound 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
![]() |
|||||
206 | } |
||||
207 | |||||
208 | public function bond(): void { |
||||
209 | $this->checkBondMaturity(true); |
||||
210 | |||||
211 | // add it to bond |
||||
212 | $this->increaseBonds($this->getCredits()); |
||||
213 | |||||
214 | // set free cash to 0 |
||||
215 | $this->setCredits(0); |
||||
216 | |||||
217 | // initialize time |
||||
218 | $this->setMaturity(Epoch::time() + $this->getBondTime()); |
||||
219 | } |
||||
220 | |||||
221 | public function getGameID(): int { |
||||
222 | return $this->gameID; |
||||
223 | } |
||||
224 | |||||
225 | public function getGame(): SmrGame { |
||||
226 | return SmrGame::getGame($this->gameID); |
||||
227 | } |
||||
228 | |||||
229 | public function getSectorID(): int { |
||||
230 | return $this->sectorID; |
||||
231 | } |
||||
232 | |||||
233 | public function getGalaxy(): SmrGalaxy { |
||||
234 | return SmrGalaxy::getGalaxyContaining($this->getGameID(), $this->getSectorID()); |
||||
235 | } |
||||
236 | |||||
237 | public function getOwnerID(): int { |
||||
238 | return $this->ownerID; |
||||
239 | } |
||||
240 | |||||
241 | public function hasOwner(): bool { |
||||
242 | return $this->ownerID != 0; |
||||
243 | } |
||||
244 | |||||
245 | public function removeOwner(): void { |
||||
246 | $this->setOwnerID(0); |
||||
247 | } |
||||
248 | |||||
249 | public function setOwnerID(int $claimerID): void { |
||||
250 | if ($this->ownerID === $claimerID) { |
||||
251 | return; |
||||
252 | } |
||||
253 | $this->ownerID = $claimerID; |
||||
254 | $this->hasChanged = true; |
||||
255 | } |
||||
256 | |||||
257 | public function getOwner(): AbstractSmrPlayer { |
||||
258 | return SmrPlayer::getPlayer($this->getOwnerID(), $this->getGameID()); |
||||
259 | } |
||||
260 | |||||
261 | public function getPassword(): string { |
||||
262 | return $this->password; |
||||
263 | } |
||||
264 | |||||
265 | public function setPassword(string $password): void { |
||||
266 | if ($this->password === $password) { |
||||
267 | return; |
||||
268 | } |
||||
269 | $this->password = $password; |
||||
270 | $this->hasChanged = true; |
||||
271 | } |
||||
272 | |||||
273 | public function removePassword(): void { |
||||
274 | $this->setPassword(''); |
||||
275 | } |
||||
276 | |||||
277 | public function getCredits(): int { |
||||
278 | return $this->credits; |
||||
279 | } |
||||
280 | |||||
281 | public function setCredits(int $num): void { |
||||
282 | if ($this->credits === $num) { |
||||
283 | return; |
||||
284 | } |
||||
285 | if ($num < 0) { |
||||
286 | throw new Exception('You cannot set negative credits.'); |
||||
287 | } |
||||
288 | if ($num > MAX_MONEY) { |
||||
289 | throw new Exception('You cannot set more than the max credits.'); |
||||
290 | } |
||||
291 | $this->credits = $num; |
||||
292 | $this->hasChangedFinancial = true; |
||||
293 | } |
||||
294 | |||||
295 | /** |
||||
296 | * Increases planet credits up to the maximum allowed credits. |
||||
297 | * Returns the amount that was actually added to handle overflow. |
||||
298 | */ |
||||
299 | public function increaseCredits(int $num): int { |
||||
300 | if ($num === 0) { |
||||
301 | return 0; |
||||
302 | } |
||||
303 | $newTotal = min($this->credits + $num, MAX_MONEY); |
||||
304 | $actualAdded = $newTotal - $this->credits; |
||||
305 | $this->setCredits($newTotal); |
||||
306 | return $actualAdded; |
||||
307 | } |
||||
308 | |||||
309 | public function decreaseCredits(int $num): void { |
||||
310 | if ($num === 0) { |
||||
311 | return; |
||||
312 | } |
||||
313 | $newTotal = $this->credits - $num; |
||||
314 | $this->setCredits($newTotal); |
||||
315 | } |
||||
316 | |||||
317 | public function getMaturity(): int { |
||||
318 | return $this->maturity; |
||||
319 | } |
||||
320 | |||||
321 | public function setMaturity(int $num): void { |
||||
322 | if ($this->maturity === $num) { |
||||
323 | return; |
||||
324 | } |
||||
325 | if ($num < 0) { |
||||
326 | throw new Exception('You cannot set negative maturity.'); |
||||
327 | } |
||||
328 | $this->maturity = $num; |
||||
329 | $this->hasChangedFinancial = true; |
||||
330 | } |
||||
331 | |||||
332 | public function getBonds(): int { |
||||
333 | return $this->bonds; |
||||
334 | } |
||||
335 | |||||
336 | public function setBonds(int $num): void { |
||||
337 | if ($this->bonds === $num) { |
||||
338 | return; |
||||
339 | } |
||||
340 | if ($num < 0) { |
||||
341 | throw new Exception('You cannot set negative bonds.'); |
||||
342 | } |
||||
343 | $this->bonds = $num; |
||||
344 | $this->hasChangedFinancial = true; |
||||
345 | } |
||||
346 | |||||
347 | public function increaseBonds(int $num): void { |
||||
348 | if ($num === 0) { |
||||
349 | return; |
||||
350 | } |
||||
351 | $this->setBonds($this->getBonds() + $num); |
||||
352 | } |
||||
353 | |||||
354 | public function decreaseBonds(int $num): void { |
||||
355 | if ($num === 0) { |
||||
356 | return; |
||||
357 | } |
||||
358 | $this->setBonds($this->getBonds() - $num); |
||||
359 | } |
||||
360 | |||||
361 | public function checkForExcessDefense(): void { |
||||
362 | if ($this->getShields() > $this->getMaxShields()) { |
||||
363 | $this->setShields($this->getMaxShields()); |
||||
364 | } |
||||
365 | if ($this->getCDs() > $this->getMaxCDs()) { |
||||
366 | $this->setCDs($this->getMaxCDs()); |
||||
367 | } |
||||
368 | if ($this->getArmour() > $this->getMaxArmour()) { |
||||
369 | $this->setArmour($this->getMaxArmour()); |
||||
370 | } |
||||
371 | // Remove a random (0-indexed) mounted weapon, if over max mount slots |
||||
372 | while ($this->getMountedWeapons() && max(array_keys($this->getMountedWeapons())) >= $this->getMaxMountedWeapons()) { |
||||
373 | $removeID = array_rand($this->getMountedWeapons()); |
||||
374 | $this->removeMountedWeapon($removeID); |
||||
375 | foreach ($this->getMountedWeapons() as $orderID => $weapon) { |
||||
376 | if ($orderID > $removeID) { |
||||
377 | $this->moveMountedWeaponUp($orderID); |
||||
378 | } |
||||
379 | } |
||||
380 | } |
||||
381 | } |
||||
382 | |||||
383 | public function getShields(): int { |
||||
384 | return $this->shields; |
||||
385 | } |
||||
386 | |||||
387 | public function hasShields(): bool { |
||||
388 | return $this->getShields() > 0; |
||||
389 | } |
||||
390 | |||||
391 | public function setShields(int $shields): void { |
||||
392 | $shields = max(0, min($shields, $this->getMaxShields())); |
||||
393 | if ($this->shields === $shields) { |
||||
394 | return; |
||||
395 | } |
||||
396 | $this->shields = $shields; |
||||
397 | $this->hasChanged = true; |
||||
398 | } |
||||
399 | |||||
400 | public function decreaseShields(int $number): void { |
||||
401 | if ($number === 0) { |
||||
402 | return; |
||||
403 | } |
||||
404 | $this->setShields($this->getShields() - $number); |
||||
405 | } |
||||
406 | |||||
407 | public function increaseShields(int $number): void { |
||||
408 | if ($number === 0) { |
||||
409 | return; |
||||
410 | } |
||||
411 | $this->setShields($this->getShields() + $number); |
||||
412 | } |
||||
413 | |||||
414 | public function getMaxShields(): int { |
||||
415 | return $this->getBuilding(PLANET_GENERATOR) * PLANET_GENERATOR_SHIELDS; |
||||
416 | } |
||||
417 | |||||
418 | public function getArmour(): int { |
||||
419 | return $this->armour; |
||||
420 | } |
||||
421 | |||||
422 | public function hasArmour(): bool { |
||||
423 | return $this->getArmour() > 0; |
||||
424 | } |
||||
425 | |||||
426 | public function setArmour(int $armour): void { |
||||
427 | $armour = max(0, min($armour, $this->getMaxArmour())); |
||||
428 | if ($this->armour === $armour) { |
||||
429 | return; |
||||
430 | } |
||||
431 | $this->armour = $armour; |
||||
432 | $this->hasChanged = true; |
||||
433 | } |
||||
434 | |||||
435 | public function decreaseArmour(int $number): void { |
||||
436 | if ($number === 0) { |
||||
437 | return; |
||||
438 | } |
||||
439 | $this->setArmour($this->getArmour() - $number); |
||||
440 | } |
||||
441 | |||||
442 | public function increaseArmour(int $number): void { |
||||
443 | if ($number === 0) { |
||||
444 | return; |
||||
445 | } |
||||
446 | $this->setArmour($this->getArmour() + $number); |
||||
447 | } |
||||
448 | |||||
449 | public function getMaxArmour(): int { |
||||
450 | return $this->getBuilding(PLANET_BUNKER) * PLANET_BUNKER_ARMOUR; |
||||
451 | } |
||||
452 | |||||
453 | public function getCDs(): int { |
||||
454 | return $this->drones; |
||||
455 | } |
||||
456 | |||||
457 | public function hasCDs(): bool { |
||||
458 | return $this->getCDs() > 0; |
||||
459 | } |
||||
460 | |||||
461 | public function setCDs(int $combatDrones): void { |
||||
462 | $combatDrones = max(0, min($combatDrones, $this->getMaxCDs())); |
||||
463 | if ($this->drones === $combatDrones) { |
||||
464 | return; |
||||
465 | } |
||||
466 | $this->drones = $combatDrones; |
||||
467 | $this->hasChanged = true; |
||||
468 | } |
||||
469 | |||||
470 | public function decreaseCDs(int $number): void { |
||||
471 | if ($number === 0) { |
||||
472 | return; |
||||
473 | } |
||||
474 | $this->setCDs($this->getCDs() - $number); |
||||
475 | } |
||||
476 | |||||
477 | public function increaseCDs(int $number): void { |
||||
478 | if ($number === 0) { |
||||
479 | return; |
||||
480 | } |
||||
481 | $this->setCDs($this->getCDs() + $number); |
||||
482 | } |
||||
483 | |||||
484 | public function getMaxCDs(): int { |
||||
485 | return $this->getBuilding(PLANET_HANGAR) * PLANET_HANGAR_DRONES; |
||||
486 | } |
||||
487 | |||||
488 | public function getMaxMountedWeapons(): int { |
||||
489 | return $this->getBuilding(PLANET_WEAPON_MOUNT); |
||||
490 | } |
||||
491 | |||||
492 | /** |
||||
493 | * @return array<int, SmrWeapon> |
||||
494 | */ |
||||
495 | public function getMountedWeapons(): array { |
||||
496 | if (!isset($this->mountedWeapons)) { |
||||
497 | $this->mountedWeapons = []; |
||||
498 | if ($this->hasBuilding(PLANET_WEAPON_MOUNT)) { |
||||
499 | $dbResult = $this->db->read('SELECT * FROM planet_has_weapon JOIN weapon_type USING (weapon_type_id) WHERE ' . $this->SQL); |
||||
500 | foreach ($dbResult->records() as $dbRecord) { |
||||
501 | $weaponTypeID = $dbRecord->getInt('weapon_type_id'); |
||||
502 | $orderID = $dbRecord->getInt('order_id'); |
||||
503 | $weapon = SmrWeapon::getWeapon($weaponTypeID, $dbRecord); |
||||
504 | $weapon->setBonusAccuracy($dbRecord->getBoolean('bonus_accuracy')); |
||||
505 | $weapon->setBonusDamage($dbRecord->getBoolean('bonus_damage')); |
||||
506 | $this->mountedWeapons[$orderID] = $weapon; |
||||
507 | } |
||||
508 | } |
||||
509 | } |
||||
510 | return $this->mountedWeapons; |
||||
511 | } |
||||
512 | |||||
513 | public function hasMountedWeapon(int $orderID): bool { |
||||
514 | $this->getMountedWeapons(); // Make sure array is initialized |
||||
515 | return isset($this->mountedWeapons[$orderID]); |
||||
516 | } |
||||
517 | |||||
518 | public function addMountedWeapon(SmrWeapon $weapon, int $orderID): void { |
||||
519 | $this->getMountedWeapons(); // Make sure array is initialized |
||||
520 | $this->mountedWeapons[$orderID] = $weapon; |
||||
521 | $this->hasChangedWeapons[$orderID] = true; |
||||
522 | } |
||||
523 | |||||
524 | public function removeMountedWeapon(int $orderID): void { |
||||
525 | $this->getMountedWeapons(); // Make sure array is initialized |
||||
526 | unset($this->mountedWeapons[$orderID]); |
||||
527 | $this->hasChangedWeapons[$orderID] = true; |
||||
528 | } |
||||
529 | |||||
530 | private function swapMountedWeapons(int $orderID1, int $orderID2): void { |
||||
531 | $this->getMountedWeapons(); // Make sure array is initialized |
||||
532 | if (isset($this->mountedWeapons[$orderID1])) { |
||||
533 | $saveWeapon = $this->mountedWeapons[$orderID1]; |
||||
534 | } |
||||
535 | if (isset($this->mountedWeapons[$orderID2])) { |
||||
536 | $this->mountedWeapons[$orderID1] = $this->mountedWeapons[$orderID2]; |
||||
537 | } else { |
||||
538 | unset($this->mountedWeapons[$orderID1]); |
||||
539 | } |
||||
540 | if (isset($saveWeapon)) { |
||||
541 | $this->mountedWeapons[$orderID2] = $saveWeapon; |
||||
542 | } else { |
||||
543 | unset($this->mountedWeapons[$orderID2]); |
||||
544 | } |
||||
545 | $this->hasChangedWeapons[$orderID1] = true; |
||||
546 | $this->hasChangedWeapons[$orderID2] = true; |
||||
547 | } |
||||
548 | |||||
549 | public function moveMountedWeaponUp(int $orderID): void { |
||||
550 | if ($orderID == 0) { |
||||
551 | throw new Exception('Cannot move this weapon up!'); |
||||
552 | } |
||||
553 | $this->swapMountedWeapons($orderID - 1, $orderID); |
||||
554 | } |
||||
555 | |||||
556 | public function moveMountedWeaponDown(int $orderID): void { |
||||
557 | if ($orderID == $this->getMaxMountedWeapons() - 1) { |
||||
558 | throw new Exception('Cannot move this weapon down!'); |
||||
559 | } |
||||
560 | $this->swapMountedWeapons($orderID + 1, $orderID); |
||||
561 | } |
||||
562 | |||||
563 | |||||
564 | public function isDestroyed(): bool { |
||||
565 | return !$this->hasCDs() && !$this->hasShields() && !$this->hasArmour(); |
||||
566 | } |
||||
567 | |||||
568 | public function exists(): bool { |
||||
569 | return $this->exists; |
||||
570 | } |
||||
571 | |||||
572 | /** |
||||
573 | * @return ($goodID is null ? array<int, int> : int) |
||||
0 ignored issues
–
show
|
|||||
574 | */ |
||||
575 | public function getStockpile(int $goodID = null): int|array { |
||||
576 | if (!isset($this->stockpile)) { |
||||
577 | // initialize cargo array |
||||
578 | $this->stockpile = []; |
||||
579 | // get supplies from db |
||||
580 | $dbResult = $this->db->read('SELECT good_id, amount FROM planet_has_cargo WHERE ' . $this->SQL); |
||||
581 | // adding cargo and amount to array |
||||
582 | foreach ($dbResult->records() as $dbRecord) { |
||||
583 | $this->stockpile[$dbRecord->getInt('good_id')] = $dbRecord->getInt('amount'); |
||||
584 | } |
||||
585 | } |
||||
586 | if ($goodID === null) { |
||||
587 | return $this->stockpile; |
||||
588 | } |
||||
589 | if (isset($this->stockpile[$goodID])) { |
||||
590 | return $this->stockpile[$goodID]; |
||||
591 | } |
||||
592 | return 0; |
||||
593 | } |
||||
594 | |||||
595 | public function hasStockpile(int $goodID = null): bool { |
||||
596 | if ($goodID === null) { |
||||
597 | $stockpile = $this->getStockpile(); |
||||
598 | return count($stockpile) > 0 && max($stockpile) > 0; |
||||
599 | } |
||||
600 | return $this->getStockpile($goodID) > 0; |
||||
601 | } |
||||
602 | |||||
603 | public function setStockpile(int $goodID, int $amount): void { |
||||
604 | if ($this->getStockpile($goodID) === $amount) { |
||||
605 | return; |
||||
606 | } |
||||
607 | if ($amount < 0) { |
||||
608 | throw new Exception('Trying to set negative stockpile.'); |
||||
609 | } |
||||
610 | $this->stockpile[$goodID] = $amount; |
||||
611 | $this->hasChangedStockpile = true; |
||||
612 | } |
||||
613 | |||||
614 | public function decreaseStockpile(int $goodID, int $amount): void { |
||||
615 | if ($amount < 0) { |
||||
616 | throw new Exception('Trying to decrease negative stockpile.'); |
||||
617 | } |
||||
618 | $this->setStockpile($goodID, $this->getStockpile($goodID) - $amount); |
||||
619 | } |
||||
620 | |||||
621 | public function increaseStockpile(int $goodID, int $amount): void { |
||||
622 | if ($amount < 0) { |
||||
623 | throw new Exception('Trying to increase negative stockpile.'); |
||||
624 | } |
||||
625 | $this->setStockpile($goodID, $this->getStockpile($goodID) + $amount); |
||||
626 | } |
||||
627 | |||||
628 | /** |
||||
629 | * @return array<int, int> |
||||
630 | */ |
||||
631 | public function getBuildings(): array { |
||||
632 | if (!isset($this->buildings)) { |
||||
633 | $this->buildings = []; |
||||
634 | |||||
635 | // get buildingss from db |
||||
636 | $dbResult = $this->db->read('SELECT construction_id, amount FROM planet_has_building WHERE ' . $this->SQL); |
||||
637 | // adding building and amount to array |
||||
638 | foreach ($dbResult->records() as $dbRecord) { |
||||
639 | $this->buildings[$dbRecord->getInt('construction_id')] = $dbRecord->getInt('amount'); |
||||
640 | } |
||||
641 | |||||
642 | // Update building counts if construction has finished |
||||
643 | $this->getCurrentlyBuilding(); |
||||
644 | } |
||||
645 | return $this->buildings; |
||||
646 | } |
||||
647 | |||||
648 | public function getBuilding(int $buildingTypeID): int { |
||||
649 | $buildings = $this->getBuildings(); |
||||
650 | if (isset($buildings[$buildingTypeID])) { |
||||
651 | return $buildings[$buildingTypeID]; |
||||
652 | } |
||||
653 | return 0; |
||||
654 | } |
||||
655 | |||||
656 | public function hasBuilding(int $buildingTypeID): bool { |
||||
657 | return $this->getBuilding($buildingTypeID) > 0; |
||||
658 | } |
||||
659 | |||||
660 | public function setBuilding(int $buildingTypeID, int $number): void { |
||||
661 | if ($this->getBuilding($buildingTypeID) === $number) { |
||||
662 | return; |
||||
663 | } |
||||
664 | if ($number < 0) { |
||||
665 | throw new Exception('Cannot set negative number of buildings.'); |
||||
666 | } |
||||
667 | |||||
668 | $this->buildings[$buildingTypeID] = $number; |
||||
669 | $this->hasChangedBuildings[$buildingTypeID] = true; |
||||
670 | } |
||||
671 | |||||
672 | public function increaseBuilding(int $buildingTypeID, int $number): void { |
||||
673 | $this->setBuilding($buildingTypeID, $this->getBuilding($buildingTypeID) + $number); |
||||
674 | } |
||||
675 | |||||
676 | public function destroyBuilding(int $buildingTypeID, int $number): void { |
||||
677 | $this->setBuilding($buildingTypeID, $this->getBuilding($buildingTypeID) - $number); |
||||
678 | } |
||||
679 | |||||
680 | /** |
||||
681 | * @return array<int, array<string, int>> |
||||
682 | */ |
||||
683 | public function getCurrentlyBuilding(): array { |
||||
684 | if (!isset($this->currentlyBuilding)) { |
||||
685 | $this->currentlyBuilding = []; |
||||
686 | |||||
687 | $dbResult = $this->db->read('SELECT * FROM planet_is_building WHERE ' . $this->SQL); |
||||
688 | foreach ($dbResult->records() as $dbRecord) { |
||||
689 | $this->currentlyBuilding[$dbRecord->getInt('building_slot_id')] = [ |
||||
690 | 'BuildingSlotID' => $dbRecord->getInt('building_slot_id'), |
||||
691 | 'ConstructionID' => $dbRecord->getInt('construction_id'), |
||||
692 | 'ConstructorID' => $dbRecord->getInt('constructor_id'), |
||||
693 | 'Finishes' => $dbRecord->getInt('time_complete'), |
||||
694 | 'TimeRemaining' => $dbRecord->getInt('time_complete') - Epoch::time(), |
||||
695 | ]; |
||||
696 | } |
||||
697 | |||||
698 | // Check if construction has completed |
||||
699 | foreach ($this->currentlyBuilding as $id => $building) { |
||||
700 | if ($building['TimeRemaining'] <= 0) { |
||||
701 | unset($this->currentlyBuilding[$id]); |
||||
702 | $expGain = $this->getConstructionExp($building['ConstructionID']); |
||||
703 | $player = SmrPlayer::getPlayer($building['ConstructorID'], $this->getGameID()); |
||||
704 | $player->increaseHOF(1, ['Planet', 'Buildings', 'Built'], HOF_ALLIANCE); |
||||
705 | $player->increaseExperience($expGain); |
||||
706 | $player->increaseHOF($expGain, ['Planet', 'Buildings', 'Experience'], HOF_ALLIANCE); |
||||
707 | $this->hasStoppedBuilding[] = $building['BuildingSlotID']; |
||||
708 | $this->increaseBuilding($building['ConstructionID'], 1); |
||||
709 | |||||
710 | // WARNING: The above modifications to the player/planet are dangerous because |
||||
711 | // they may not be part of the current sector lock. But since they might not be, |
||||
712 | // we may as well just update now to avoid either a) needing to remember to call |
||||
713 | // this explicitly in all appropriate engine files or b) inconsistent exp display |
||||
714 | // if this is called during the template display only and therefore unsaved. |
||||
715 | $player->update(); |
||||
716 | $this->update(); |
||||
717 | } |
||||
718 | } |
||||
719 | } |
||||
720 | return $this->currentlyBuilding; |
||||
721 | } |
||||
722 | |||||
723 | /** |
||||
724 | * @return ($buildingTypeID is null ? array<int, int> : int) |
||||
0 ignored issues
–
show
|
|||||
725 | */ |
||||
726 | public function getMaxBuildings(int $buildingTypeID = null): int|array { |
||||
727 | if ($buildingTypeID === null) { |
||||
728 | $maxBuildings = []; |
||||
729 | foreach ($this->getStructureTypes() as $ID => $type) { |
||||
730 | $maxBuildings[$ID] = $type->maxAmount(); |
||||
731 | } |
||||
732 | return $maxBuildings; |
||||
733 | } |
||||
734 | return $this->getStructureTypes($buildingTypeID)->maxAmount(); |
||||
735 | } |
||||
736 | |||||
737 | public function getTypeID(): int { |
||||
738 | return $this->typeID; |
||||
739 | } |
||||
740 | |||||
741 | public function setTypeID(int $num): void { |
||||
742 | if (isset($this->typeID) && $this->typeID === $num) { |
||||
743 | return; |
||||
744 | } |
||||
745 | $this->typeID = $num; |
||||
746 | $this->db->write('UPDATE planet SET planet_type_id = ' . $this->db->escapeNumber($num) . ' WHERE ' . $this->SQL); |
||||
747 | $this->typeInfo = PlanetType::getTypeInfo($this->getTypeID()); |
||||
748 | |||||
749 | //trim buildings first |
||||
750 | foreach ($this->getBuildings() as $id => $amt) { |
||||
751 | if ($this->getMaxBuildings($id) < $amt) { |
||||
752 | $this->destroyBuilding($id, $amt - $this->getMaxBuildings($id)); |
||||
753 | } |
||||
754 | } |
||||
755 | |||||
756 | //trim excess defenses |
||||
757 | $this->checkForExcessDefense(); |
||||
758 | |||||
759 | $this->hasChanged = true; |
||||
760 | $this->update(); |
||||
761 | } |
||||
762 | |||||
763 | public function getTypeImage(): string { |
||||
764 | return $this->typeInfo->imageLink(); |
||||
765 | } |
||||
766 | |||||
767 | public function getTypeName(): string { |
||||
768 | return $this->typeInfo->name(); |
||||
769 | } |
||||
770 | |||||
771 | public function getTypeDescription(): string { |
||||
772 | return $this->typeInfo->description(); |
||||
773 | } |
||||
774 | |||||
775 | public function getMaxAttackers(): int { |
||||
776 | return $this->typeInfo->maxAttackers(); |
||||
777 | } |
||||
778 | |||||
779 | public function getMaxLanded(): int { |
||||
780 | return $this->typeInfo->maxLanded(); |
||||
781 | } |
||||
782 | |||||
783 | /** |
||||
784 | * @return ($structureID is null ? array<int, SmrPlanetStructureType> : SmrPlanetStructureType) |
||||
0 ignored issues
–
show
|
|||||
785 | */ |
||||
786 | public function getStructureTypes(int $structureID = null): SmrPlanetStructureType|array { |
||||
787 | return $this->typeInfo->structureTypes($structureID); |
||||
788 | } |
||||
789 | |||||
790 | public function hasStructureType(int $structureID): bool { |
||||
791 | return isset($this->getStructureTypes()[$structureID]); |
||||
792 | } |
||||
793 | |||||
794 | /** |
||||
795 | * Specifies which menu options the planet has. |
||||
796 | */ |
||||
797 | public function hasMenuOption(string $option): bool { |
||||
798 | // We do not set options that are unavailable |
||||
799 | return in_array($option, $this->typeInfo->menuOptions()); |
||||
800 | } |
||||
801 | |||||
802 | public function update(): void { |
||||
803 | if (!$this->exists()) { |
||||
804 | return; |
||||
805 | } |
||||
806 | if ($this->hasChanged) { |
||||
807 | $this->db->write('UPDATE planet SET |
||||
808 | owner_id = ' . $this->db->escapeNumber($this->ownerID) . ', |
||||
809 | password = ' . $this->db->escapeString($this->password) . ', |
||||
810 | planet_name = ' . $this->db->escapeString($this->planetName) . ', |
||||
811 | shields = ' . $this->db->escapeNumber($this->shields) . ', |
||||
812 | armour = ' . $this->db->escapeNumber($this->armour) . ', |
||||
813 | drones = ' . $this->db->escapeNumber($this->drones) . ' |
||||
814 | WHERE ' . $this->SQL); |
||||
815 | $this->hasChanged = false; |
||||
816 | } |
||||
817 | |||||
818 | // Separate update for financial since these can be modified by looking |
||||
819 | // at the planet list (i.e. you might not have sector lock and could |
||||
820 | // cause a race condition with events happening in the planet sector). |
||||
821 | if ($this->hasChangedFinancial) { |
||||
822 | $this->db->write('UPDATE planet SET |
||||
823 | credits = ' . $this->db->escapeNumber($this->credits) . ', |
||||
824 | bonds = ' . $this->db->escapeNumber($this->bonds) . ', |
||||
825 | maturity = ' . $this->db->escapeNumber($this->maturity) . ' |
||||
826 | WHERE ' . $this->SQL); |
||||
827 | $this->hasChangedFinancial = false; |
||||
828 | } |
||||
829 | |||||
830 | if ($this->hasChangedStockpile) { |
||||
831 | // write stockpile info |
||||
832 | foreach ($this->getStockpile() as $id => $amount) { |
||||
833 | if ($amount != 0) { |
||||
834 | $this->db->replace('planet_has_cargo', [ |
||||
835 | 'game_id' => $this->db->escapeNumber($this->getGameID()), |
||||
836 | 'sector_id' => $this->db->escapeNumber($this->getSectorID()), |
||||
837 | 'good_id' => $this->db->escapeNumber($id), |
||||
838 | 'amount' => $this->db->escapeNumber($amount), |
||||
839 | ]); |
||||
840 | } else { |
||||
841 | $this->db->write('DELETE FROM planet_has_cargo WHERE ' . $this->SQL . ' |
||||
842 | AND good_id = ' . $this->db->escapeNumber($id)); |
||||
843 | } |
||||
844 | } |
||||
845 | } |
||||
846 | |||||
847 | if (count($this->hasChangedWeapons) > 0) { |
||||
848 | foreach (array_keys($this->hasChangedWeapons) as $orderID) { |
||||
849 | if (isset($this->mountedWeapons[$orderID])) { |
||||
850 | $this->db->replace('planet_has_weapon', [ |
||||
851 | 'game_id' => $this->db->escapeNumber($this->getGameID()), |
||||
852 | 'sector_id' => $this->db->escapeNumber($this->getSectorID()), |
||||
853 | 'order_id' => $this->db->escapeNumber($orderID), |
||||
854 | 'weapon_type_id' => $this->db->escapeNumber($this->mountedWeapons[$orderID]->getWeaponTypeID()), |
||||
855 | 'bonus_accuracy' => $this->db->escapeBoolean($this->mountedWeapons[$orderID]->hasBonusAccuracy()), |
||||
856 | 'bonus_damage' => $this->db->escapeBoolean($this->mountedWeapons[$orderID]->hasBonusDamage()), |
||||
857 | ]); |
||||
858 | } else { |
||||
859 | $this->db->write('DELETE FROM planet_has_weapon WHERE ' . $this->SQL . ' AND order_id=' . $this->db->escapeNumber($orderID)); |
||||
860 | } |
||||
861 | } |
||||
862 | $this->hasChangedWeapons = []; |
||||
863 | } |
||||
864 | |||||
865 | if (count($this->hasStoppedBuilding) > 0) { |
||||
866 | $this->db->write('DELETE FROM planet_is_building WHERE ' . $this->SQL . ' |
||||
867 | AND building_slot_id IN (' . $this->db->escapeArray($this->hasStoppedBuilding) . ') LIMIT ' . count($this->hasStoppedBuilding)); |
||||
868 | $this->hasStoppedBuilding = []; |
||||
869 | } |
||||
870 | // write building info |
||||
871 | foreach ($this->hasChangedBuildings as $id => $hasChanged) { |
||||
872 | if ($hasChanged === true) { |
||||
873 | if ($this->hasBuilding($id)) { |
||||
874 | $this->db->replace('planet_has_building', [ |
||||
875 | 'game_id' => $this->db->escapeNumber($this->gameID), |
||||
876 | 'sector_id' => $this->db->escapeNumber($this->sectorID), |
||||
877 | 'construction_id' => $this->db->escapeNumber($id), |
||||
878 | 'amount' => $this->db->escapeNumber($this->getBuilding($id)), |
||||
879 | ]); |
||||
880 | } else { |
||||
881 | $this->db->write('DELETE FROM planet_has_building WHERE ' . $this->SQL . ' |
||||
882 | AND construction_id = ' . $this->db->escapeNumber($id)); |
||||
883 | } |
||||
884 | $this->hasChangedBuildings[$id] = false; |
||||
885 | } |
||||
886 | } |
||||
887 | } |
||||
888 | |||||
889 | public function getLevel(): float { |
||||
890 | return array_sum($this->getBuildings()) / 3; |
||||
891 | } |
||||
892 | |||||
893 | public function getMaxLevel(): float { |
||||
894 | return array_sum($this->getMaxBuildings()) / 3; |
||||
895 | } |
||||
896 | |||||
897 | /** |
||||
898 | * Returns the modified accuracy of turrets on this planet. |
||||
899 | * Only used for display purposes. |
||||
900 | */ |
||||
901 | public function getTurretAccuracy(): float { |
||||
902 | return SmrWeapon::getWeapon(WEAPON_PLANET_TURRET)->getModifiedPlanetAccuracy($this); |
||||
903 | } |
||||
904 | |||||
905 | /** |
||||
906 | * Returns the accuracy bonus for mounted weaons (as a percent) |
||||
907 | */ |
||||
908 | public function getAccuracyBonus(): int { |
||||
909 | return 5 * $this->getBuilding(PLANET_RADAR); |
||||
910 | } |
||||
911 | |||||
912 | public function getRemainingStockpile(int $id): int { |
||||
913 | return self::MAX_STOCKPILE - $this->getStockpile($id); |
||||
914 | } |
||||
915 | |||||
916 | /** |
||||
917 | * Returns true if there is a building in progress |
||||
918 | */ |
||||
919 | public function hasCurrentlyBuilding(): bool { |
||||
920 | return count($this->getCurrentlyBuilding()) > 0; |
||||
921 | } |
||||
922 | |||||
923 | /** |
||||
924 | * Returns the reason a build cannot be performed, or false if there is |
||||
925 | * no restriction. |
||||
926 | */ |
||||
927 | public function getBuildRestriction(AbstractSmrPlayer $constructor, int $constructionID): string|false { |
||||
928 | if ($this->hasCurrentlyBuilding()) { |
||||
929 | return 'There is already a building in progress!'; |
||||
930 | } |
||||
931 | if ($this->getBuilding($constructionID) >= $this->getMaxBuildings($constructionID)) { |
||||
932 | return 'This planet has reached the maximum buildings of that type.'; |
||||
933 | } |
||||
934 | $cost = $this->getStructureTypes($constructionID)->creditCost(); |
||||
935 | if ($constructor->getCredits() < $cost) { |
||||
936 | return 'You do not have enough credits.'; |
||||
937 | } |
||||
938 | if ($constructor->getTurns() < TURNS_TO_BUILD) { |
||||
939 | return 'You do not have enough turns to build.'; |
||||
940 | } |
||||
941 | foreach ($this->getStructureTypes($constructionID)->hardwareCost() as $hardwareID) { |
||||
942 | if (!$constructor->getShip()->getHardware($hardwareID)) { |
||||
943 | return 'You do not have the hardware needed for this type of building!'; |
||||
944 | } |
||||
945 | } |
||||
946 | // take the goods that are needed |
||||
947 | foreach ($this->getStructureTypes($constructionID)->goods() as $goodID => $amount) { |
||||
948 | if ($this->getStockpile($goodID) < $amount) { |
||||
949 | return 'There are not enough goods available.'; |
||||
950 | } |
||||
951 | } |
||||
952 | return false; |
||||
953 | } |
||||
954 | |||||
955 | // Modifier for planet building based on the number of buildings. |
||||
956 | // The average value of this modifier should roughly be 1. |
||||
957 | private function getCompletionModifier(int $constructionID): float { |
||||
958 | $currentBuildings = $this->getBuilding($constructionID); |
||||
959 | $maxBuildings = $this->getMaxBuildings($constructionID); |
||||
960 | return 0.01 + 2.97 * pow($currentBuildings / $maxBuildings, 2); |
||||
961 | } |
||||
962 | |||||
963 | // Amount of exp gained to build the next building of this type |
||||
964 | private function getConstructionExp(int $constructionID): int { |
||||
965 | return $this->getStructureTypes($constructionID)->expGain(); |
||||
966 | } |
||||
967 | |||||
968 | // Amount of time (in seconds) to build the next building of this type |
||||
969 | public function getConstructionTime(int $constructionID): int { |
||||
970 | $baseTime = $this->getStructureTypes($constructionID)->baseTime(); |
||||
971 | return ICeil($baseTime * $this->getCompletionModifier($constructionID) / $this->getGame()->getGameSpeed()); |
||||
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
![]() |
|||||
972 | } |
||||
973 | |||||
974 | /** |
||||
975 | * @throws \Smr\Exceptions\UserError If the player cannot build the structure. |
||||
976 | */ |
||||
977 | public function startBuilding(AbstractSmrPlayer $constructor, int $constructionID): void { |
||||
978 | $restriction = $this->getBuildRestriction($constructor, $constructionID); |
||||
979 | if ($restriction !== false) { |
||||
980 | throw new UserError('Unable to start building: ' . $restriction); |
||||
981 | } |
||||
982 | |||||
983 | // gets the time for the buildings |
||||
984 | $timeComplete = Epoch::time() + $this->getConstructionTime($constructionID); |
||||
985 | $insertID = $this->db->insert('planet_is_building', [ |
||||
986 | 'game_id' => $this->db->escapeNumber($this->getGameID()), |
||||
987 | 'sector_id' => $this->db->escapeNumber($this->getSectorID()), |
||||
988 | 'construction_id' => $this->db->escapeNumber($constructionID), |
||||
989 | 'constructor_id' => $this->db->escapeNumber($constructor->getAccountID()), |
||||
990 | 'time_complete' => $this->db->escapeNumber($timeComplete), |
||||
991 | ]); |
||||
992 | |||||
993 | $this->currentlyBuilding[$insertID] = [ |
||||
994 | 'BuildingSlotID' => $insertID, |
||||
995 | 'ConstructionID' => $constructionID, |
||||
996 | 'ConstructorID' => $constructor->getAccountID(), |
||||
997 | 'Finishes' => $timeComplete, |
||||
998 | 'TimeRemaining' => $timeComplete - Epoch::time(), |
||||
999 | ]; |
||||
1000 | |||||
1001 | // Consume the required resources |
||||
1002 | $constructor->decreaseCredits($this->getStructureTypes($constructionID)->creditCost()); |
||||
1003 | $constructor->takeTurns(TURNS_TO_BUILD); |
||||
1004 | foreach ($this->getStructureTypes($constructionID)->goods() as $goodID => $amount) { |
||||
1005 | $this->decreaseStockpile($goodID, $amount); |
||||
1006 | } |
||||
1007 | foreach ($this->getStructureTypes($constructionID)->hardwareCost() as $hardwareID) { |
||||
1008 | $constructor->getShip()->setHardware($hardwareID, 0); |
||||
1009 | } |
||||
1010 | } |
||||
1011 | |||||
1012 | public function stopBuilding(int $constructionID): bool { |
||||
1013 | $matchingBuilding = false; |
||||
1014 | $latestFinish = 0; |
||||
1015 | foreach ($this->getCurrentlyBuilding() as $building) { |
||||
1016 | if ($building['ConstructionID'] == $constructionID && $building['Finishes'] > $latestFinish) { |
||||
1017 | $latestFinish = $building['Finishes']; |
||||
1018 | $matchingBuilding = $building; |
||||
1019 | } |
||||
1020 | } |
||||
1021 | if ($matchingBuilding) { |
||||
1022 | $this->hasStoppedBuilding[] = $matchingBuilding['BuildingSlotID']; |
||||
1023 | unset($this->currentlyBuilding[$matchingBuilding['BuildingSlotID']]); |
||||
1024 | return true; |
||||
1025 | } |
||||
1026 | return false; |
||||
1027 | } |
||||
1028 | |||||
1029 | public function setName(string $name): void { |
||||
1030 | if ($this->planetName === $name) { |
||||
1031 | return; |
||||
1032 | } |
||||
1033 | $this->planetName = $name; |
||||
1034 | $this->hasChanged = true; |
||||
1035 | } |
||||
1036 | |||||
1037 | /** |
||||
1038 | * Returns the name of the planet, suitably escaped for HTML display. |
||||
1039 | */ |
||||
1040 | public function getDisplayName(): string { |
||||
1041 | return htmlentities($this->planetName); |
||||
1042 | } |
||||
1043 | |||||
1044 | /** |
||||
1045 | * Returns the name of the planet, intended for combat messages. |
||||
1046 | */ |
||||
1047 | public function getCombatName(): string { |
||||
1048 | return '<span style="color:yellow;font-variant:small-caps">' . $this->getDisplayName() . ' (#' . $this->getSectorID() . ')</span>'; |
||||
1049 | } |
||||
1050 | |||||
1051 | public function isInhabitable(): bool { |
||||
1052 | return $this->inhabitableTime <= Epoch::time(); |
||||
1053 | } |
||||
1054 | |||||
1055 | public function getInhabitableTime(): int { |
||||
1056 | return $this->inhabitableTime; |
||||
1057 | } |
||||
1058 | |||||
1059 | public function getExamineHREF(): string { |
||||
1060 | return (new ExaminePlanet())->href(); |
||||
1061 | } |
||||
1062 | |||||
1063 | public function getLandHREF(): string { |
||||
1064 | return (new LandProcessor())->href(); |
||||
1065 | } |
||||
1066 | |||||
1067 | public function getAttackHREF(): string { |
||||
1068 | return (new AttackPlanetProcessor())->href(); |
||||
1069 | } |
||||
1070 | |||||
1071 | public function getBuildHREF(int $structureID): string { |
||||
1072 | $container = new ConstructionProcessor('Build', $structureID); |
||||
1073 | return $container->href(); |
||||
1074 | } |
||||
1075 | |||||
1076 | public function getCancelHREF(int $structureID): string { |
||||
1077 | $container = new ConstructionProcessor('Cancel', $structureID); |
||||
1078 | return $container->href(); |
||||
1079 | } |
||||
1080 | |||||
1081 | public function getFinancesHREF(): string { |
||||
1082 | return (new FinancialProcessor())->href(); |
||||
1083 | } |
||||
1084 | |||||
1085 | public function getBondConfirmationHREF(): string { |
||||
1086 | return (new BondConfirm())->href(); |
||||
1087 | } |
||||
1088 | |||||
1089 | /** |
||||
1090 | * @param array<AbstractSmrPlayer> $attackers |
||||
1091 | */ |
||||
1092 | public function attackedBy(AbstractSmrPlayer $trigger, array $attackers): void { |
||||
1093 | $trigger->increaseHOF(1, ['Combat', 'Planet', 'Number Of Triggers'], HOF_PUBLIC); |
||||
1094 | foreach ($attackers as $attacker) { |
||||
1095 | $attacker->increaseHOF(1, ['Combat', 'Planet', 'Number Of Attacks'], HOF_PUBLIC); |
||||
1096 | $this->db->replace('player_attacks_planet', [ |
||||
1097 | 'game_id' => $this->db->escapeNumber($this->getGameID()), |
||||
1098 | 'account_id' => $this->db->escapeNumber($attacker->getAccountID()), |
||||
1099 | 'sector_id' => $this->db->escapeNumber($this->getSectorID()), |
||||
1100 | 'time' => $this->db->escapeNumber(Epoch::time()), |
||||
1101 | 'level' => $this->db->escapeNumber($this->getLevel()), |
||||
1102 | ]); |
||||
1103 | } |
||||
1104 | |||||
1105 | // Add each unique attack to news unless it was already added recently. |
||||
1106 | // Note: Attack uniqueness determined by planet owner. |
||||
1107 | $owner = $this->getOwner(); |
||||
1108 | $dbResult = $this->db->read('SELECT 1 FROM news WHERE type = \'BREAKING\' AND game_id = ' . $this->db->escapeNumber($trigger->getGameID()) . ' AND dead_id=' . $this->db->escapeNumber($owner->getAccountID()) . ' AND time > ' . $this->db->escapeNumber(Epoch::time() - self::TIME_ATTACK_NEWS_COOLDOWN) . ' LIMIT 1'); |
||||
1109 | if (!$dbResult->hasRecord()) { |
||||
1110 | if (count($attackers) >= 5) { |
||||
1111 | $text = count($attackers) . ' members of ' . $trigger->getAllianceBBLink() . ' have been spotted attacking ' . |
||||
1112 | $this->getDisplayName() . ' in sector ' . Globals::getSectorBBLink($this->getSectorID()) . '. The planet is owned by ' . $owner->getBBLink(); |
||||
1113 | if ($owner->hasAlliance()) { |
||||
1114 | $text .= ', a member of ' . $owner->getAllianceBBLink(); |
||||
1115 | } |
||||
1116 | $text .= '.'; |
||||
1117 | $this->db->insert('news', [ |
||||
1118 | 'game_id' => $this->db->escapeNumber($this->getGameID()), |
||||
1119 | 'time' => $this->db->escapeNumber(Epoch::time()), |
||||
1120 | 'news_message' => $this->db->escapeString($text), |
||||
1121 | 'type' => $this->db->escapeString('breaking'), |
||||
1122 | 'killer_id' => $this->db->escapeNumber($trigger->getAccountID()), |
||||
1123 | 'killer_alliance' => $this->db->escapeNumber($trigger->getAllianceID()), |
||||
1124 | 'dead_id' => $this->db->escapeNumber($owner->getAccountID()), |
||||
1125 | 'dead_alliance' => $this->db->escapeNumber($owner->getAllianceID()), |
||||
1126 | ]); |
||||
1127 | } |
||||
1128 | } |
||||
1129 | } |
||||
1130 | |||||
1131 | |||||
1132 | /** |
||||
1133 | * @return array<int, SmrPlayer> |
||||
1134 | */ |
||||
1135 | public function getPlayers(): array { |
||||
1136 | return SmrPlayer::getPlanetPlayers($this->getGameID(), $this->getSectorID()); |
||||
1137 | } |
||||
1138 | |||||
1139 | public function countPlayers(): int { |
||||
1140 | return count($this->getPlayers()); |
||||
1141 | } |
||||
1142 | |||||
1143 | public function hasPlayers(): bool { |
||||
1144 | return $this->countPlayers() > 0; |
||||
1145 | } |
||||
1146 | |||||
1147 | /** |
||||
1148 | * @return array<int, SmrPlayer> |
||||
1149 | */ |
||||
1150 | public function getOtherTraders(AbstractSmrPlayer $player): array { |
||||
1151 | $players = SmrPlayer::getPlanetPlayers($this->getGameID(), $this->getSectorID()); //Do not use & because we unset something and only want that in what we return |
||||
1152 | unset($players[$player->getAccountID()]); |
||||
1153 | return $players; |
||||
1154 | } |
||||
1155 | |||||
1156 | public function hasOtherTraders(AbstractSmrPlayer $player): bool { |
||||
1157 | return count($this->getOtherTraders($player)) > 0; |
||||
1158 | } |
||||
1159 | |||||
1160 | public function hasEnemyTraders(AbstractSmrPlayer $player): bool { |
||||
1161 | if (!$this->hasOtherTraders($player)) { |
||||
1162 | return false; |
||||
1163 | } |
||||
1164 | $otherPlayers = $this->getOtherTraders($player); |
||||
1165 | foreach ($otherPlayers as $otherPlayer) { |
||||
1166 | if (!$player->traderNAPAlliance($otherPlayer)) { |
||||
1167 | return true; |
||||
1168 | } |
||||
1169 | } |
||||
1170 | return false; |
||||
1171 | } |
||||
1172 | |||||
1173 | public function hasFriendlyTraders(AbstractSmrPlayer $player): bool { |
||||
1174 | if (!$this->hasOtherTraders($player)) { |
||||
1175 | return false; |
||||
1176 | } |
||||
1177 | $otherPlayers = $this->getOtherTraders($player); |
||||
1178 | foreach ($otherPlayers as $otherPlayer) { |
||||
1179 | if ($player->traderNAPAlliance($otherPlayer)) { |
||||
1180 | return true; |
||||
1181 | } |
||||
1182 | } |
||||
1183 | return false; |
||||
1184 | } |
||||
1185 | |||||
1186 | /** |
||||
1187 | * @return array<SmrWeapon> |
||||
1188 | */ |
||||
1189 | public function getWeapons(): array { |
||||
1190 | $weapons = $this->getMountedWeapons(); |
||||
1191 | return array_pad( |
||||
1192 | $weapons, |
||||
1193 | count($weapons) + $this->getBuilding(PLANET_TURRET), |
||||
1194 | SmrWeapon::getWeapon(WEAPON_PLANET_TURRET) |
||||
1195 | ); |
||||
1196 | } |
||||
1197 | |||||
1198 | /** |
||||
1199 | * @param array<AbstractSmrPlayer> $targetPlayers |
||||
1200 | * @return array<string, mixed> |
||||
1201 | */ |
||||
1202 | public function shootPlayers(array $targetPlayers): array { |
||||
1203 | $results = ['Planet' => $this, 'TotalDamage' => 0, 'TotalDamagePerTargetPlayer' => []]; |
||||
1204 | foreach ($targetPlayers as $targetPlayer) { |
||||
1205 | $results['TotalDamagePerTargetPlayer'][$targetPlayer->getAccountID()] = 0; |
||||
1206 | } |
||||
1207 | if ($this->isDestroyed()) { |
||||
1208 | $results['DeadBeforeShot'] = true; |
||||
1209 | return $results; |
||||
1210 | } |
||||
1211 | $results['DeadBeforeShot'] = false; |
||||
1212 | $weapons = $this->getWeapons(); |
||||
1213 | foreach ($weapons as $orderID => $weapon) { |
||||
1214 | $results['Weapons'][$orderID] = $weapon->shootPlayerAsPlanet($this, array_rand_value($targetPlayers)); |
||||
0 ignored issues
–
show
The function
array_rand_value 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
![]() |
|||||
1215 | if ($results['Weapons'][$orderID]['Hit']) { |
||||
1216 | $results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage']; |
||||
1217 | $results['TotalDamagePerTargetPlayer'][$results['Weapons'][$orderID]['TargetPlayer']->getAccountID()] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage']; |
||||
1218 | } |
||||
1219 | } |
||||
1220 | if ($this->hasCDs()) { |
||||
1221 | $thisCDs = new SmrCombatDrones($this->getCDs(), true); |
||||
1222 | $results['Drones'] = $thisCDs->shootPlayerAsPlanet($this, array_rand_value($targetPlayers)); |
||||
1223 | $results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage']; |
||||
1224 | $results['TotalDamagePerTargetPlayer'][$results['Drones']['TargetPlayer']->getAccountID()] += $results['Drones']['ActualDamage']['TotalDamage']; |
||||
1225 | } |
||||
1226 | return $results; |
||||
1227 | } |
||||
1228 | |||||
1229 | /** |
||||
1230 | * Returns an array of structure losses due to damage taken. |
||||
1231 | * |
||||
1232 | * @return array<int, int> |
||||
1233 | */ |
||||
1234 | public function checkForDowngrade(int $damage): array { |
||||
1235 | $results = []; |
||||
1236 | // For every 70 damage there is a 15% chance of destroying a structure. |
||||
1237 | // Which structure is destroyed depends on the ratio of buildings and |
||||
1238 | // the time it takes to build them. |
||||
1239 | $numChances = floor($damage / self::DAMAGE_NEEDED_FOR_DOWNGRADE_CHANCE); |
||||
1240 | for ($i = 0; $i < $numChances; $i++) { |
||||
1241 | // Stop if the planet has no more buildlings |
||||
1242 | if ($this->getLevel() == 0) { |
||||
1243 | break; |
||||
1244 | } |
||||
1245 | //15% chance to destroy something |
||||
1246 | if (rand(1, 100) <= self::CHANCE_TO_DOWNGRADE) { |
||||
1247 | $chanceFactors = []; |
||||
1248 | foreach ($this->getStructureTypes() as $structureID => $structure) { |
||||
1249 | $chanceFactors[$structureID] = ($this->getBuilding($structureID) / $this->getMaxBuildings($structureID)) / $structure->baseTime(); |
||||
1250 | } |
||||
1251 | $destroyID = getWeightedRandom($chanceFactors); |
||||
1252 | $this->destroyBuilding($destroyID, 1); |
||||
0 ignored issues
–
show
It seems like
$destroyID can also be of type string ; however, parameter $buildingTypeID of SmrPlanet::destroyBuilding() does only seem to accept integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1253 | $this->checkForExcessDefense(); |
||||
1254 | if (isset($results[$destroyID])) { |
||||
1255 | $results[$destroyID] += 1; |
||||
1256 | } else { |
||||
1257 | $results[$destroyID] = 1; |
||||
1258 | } |
||||
1259 | } |
||||
1260 | } |
||||
1261 | return $results; |
||||
1262 | } |
||||
1263 | |||||
1264 | /** |
||||
1265 | * @param WeaponDamageData $damage |
||||
0 ignored issues
–
show
The type
WeaponDamageData was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||
1266 | * @return array<string, int|bool> |
||||
1267 | */ |
||||
1268 | public function takeDamage(array $damage): array { |
||||
1269 | $alreadyDead = $this->isDestroyed(); |
||||
1270 | $shieldDamage = 0; |
||||
1271 | $cdDamage = 0; |
||||
1272 | $armourDamage = 0; |
||||
1273 | if (!$alreadyDead) { |
||||
1274 | $shieldDamage = $this->takeDamageToShields($damage['Shield']); |
||||
1275 | if ($shieldDamage == 0 || $damage['Rollover']) { |
||||
1276 | $cdMaxDamage = $damage['Armour'] - $shieldDamage; |
||||
1277 | if ($shieldDamage == 0 && $this->hasShields()) { |
||||
1278 | $cdMaxDamage = IFloor($cdMaxDamage * DRONES_BEHIND_SHIELDS_DAMAGE_PERCENT); |
||||
0 ignored issues
–
show
The function
IFloor 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
![]() |
|||||
1279 | } |
||||
1280 | $cdDamage = $this->takeDamageToCDs($cdMaxDamage); |
||||
1281 | if (!$this->hasShields() && ($cdDamage == 0 || $damage['Rollover'])) { |
||||
1282 | $armourMaxDamage = $damage['Armour'] - $shieldDamage - $cdDamage; |
||||
1283 | $armourDamage = $this->takeDamageToArmour($armourMaxDamage); |
||||
1284 | } |
||||
1285 | } |
||||
1286 | } |
||||
1287 | |||||
1288 | return [ |
||||
1289 | 'KillingShot' => !$alreadyDead && $this->isDestroyed(), |
||||
1290 | 'TargetAlreadyDead' => $alreadyDead, |
||||
1291 | 'Shield' => $shieldDamage, |
||||
1292 | 'CDs' => $cdDamage, |
||||
1293 | 'NumCDs' => $cdDamage / CD_ARMOUR, |
||||
1294 | 'HasCDs' => $this->hasCDs(), |
||||
1295 | 'Armour' => $armourDamage, |
||||
1296 | 'TotalDamage' => $shieldDamage + $cdDamage + $armourDamage, |
||||
1297 | ]; |
||||
1298 | } |
||||
1299 | |||||
1300 | protected function takeDamageToShields(int $damage): int { |
||||
1301 | $actualDamage = min($this->getShields(), $damage); |
||||
1302 | $this->decreaseShields($actualDamage); |
||||
1303 | return $actualDamage; |
||||
1304 | } |
||||
1305 | |||||
1306 | protected function takeDamageToCDs(int $damage): int { |
||||
1307 | $actualDamage = min($this->getCDs(), IFloor($damage / CD_ARMOUR)); |
||||
0 ignored issues
–
show
The function
IFloor 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
![]() |
|||||
1308 | $this->decreaseCDs($actualDamage); |
||||
1309 | return $actualDamage * CD_ARMOUR; |
||||
1310 | } |
||||
1311 | |||||
1312 | protected function takeDamageToArmour(int $damage): int { |
||||
1313 | $actualDamage = min($this->getArmour(), $damage); |
||||
1314 | $this->decreaseArmour($actualDamage); |
||||
1315 | return $actualDamage; |
||||
1316 | } |
||||
1317 | |||||
1318 | public function creditCurrentAttackersForKill(): void { |
||||
1319 | //get all players involved for HoF |
||||
1320 | $dbResult = $this->db->read('SELECT account_id,level FROM player_attacks_planet WHERE ' . $this->SQL . ' AND time > ' . $this->db->escapeNumber(Epoch::time() - self::TIME_TO_CREDIT_BUST)); |
||||
1321 | foreach ($dbResult->records() as $dbRecord) { |
||||
1322 | $currPlayer = SmrPlayer::getPlayer($dbRecord->getInt('account_id'), $this->getGameID()); |
||||
1323 | $currPlayer->increaseHOF($dbRecord->getFloat('level'), ['Combat', 'Planet', 'Levels'], HOF_PUBLIC); |
||||
1324 | $currPlayer->increaseHOF(1, ['Combat', 'Planet', 'Completed'], HOF_PUBLIC); |
||||
1325 | } |
||||
1326 | $this->db->write('DELETE FROM player_attacks_planet WHERE ' . $this->SQL); |
||||
1327 | } |
||||
1328 | |||||
1329 | /** |
||||
1330 | * @return array<string, mixed> |
||||
1331 | */ |
||||
1332 | public function killPlanetByPlayer(AbstractSmrPlayer $killer): array { |
||||
0 ignored issues
–
show
The parameter
$killer 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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
1333 | $this->creditCurrentAttackersForKill(); |
||||
1334 | |||||
1335 | //kick everyone from planet |
||||
1336 | $this->db->write('UPDATE player SET land_on_planet = \'FALSE\' WHERE ' . $this->SQL); |
||||
1337 | $this->removeOwner(); |
||||
1338 | $this->removePassword(); |
||||
1339 | return []; |
||||
1340 | } |
||||
1341 | |||||
1342 | } |
||||
1343 |