Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions app/GameMessages/BattleReport.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,15 @@ private function getBattleReportParams(): array
$attacker_planet_coords = $attackerPlanet->getPlanetCoordinates()->asString();
$attacker_planet_type = $attackerPlanet->getPlanetType() === PlanetType::Moon ? 'Moon' : 'Planet';
} else {
$attacker_planet_name = __('Unknown');
$attacker_planet_coords = '';
$attacker_planet_type = '';
$attacker_planet_name = $this->battleReportModel->attacker['planet_name'] ?? __('Unknown');
$attacker_planet_coords = $this->battleReportModel->attacker['planet_coords'] ?? '';
$attacker_planet_type = $this->battleReportModel->attacker['planet_type'] ?? '';
}
} catch (Throwable $e) {
// If attacker planet can't be loaded (e.g., planet deleted), use defaults
$attacker_planet_name = __('Unknown');
$attacker_planet_coords = '';
$attacker_planet_type = '';
// If attacker planet can't be loaded (e.g., planet deleted), use snapshotted data
$attacker_planet_name = $this->battleReportModel->attacker['planet_name'] ?? __('Unknown');
$attacker_planet_coords = $this->battleReportModel->attacker['planet_coords'] ?? '';
$attacker_planet_type = $this->battleReportModel->attacker['planet_type'] ?? '';
}
} else {
// No planet info available
Expand Down
39 changes: 24 additions & 15 deletions app/GameMissions/Abstracts/GameMission.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use OGame\Models\FleetUnion;
use OGame\Models\Planet\Coordinate;
use OGame\Models\Resources;
use OGame\Support\FleetMissionPlanetFormatter;
use OGame\Services\FleetMissionService;
use OGame\Services\FleetUnionService;
use OGame\Services\MessageService;
Expand Down Expand Up @@ -592,21 +593,8 @@ protected function sendFleetReturnMessage(FleetMission $mission, PlayerService $
{
$return_resources = $this->fleetMissionService->getResources($mission);

// Define from string based on whether the planet is available or not.
$from = "[coordinates]{$mission->galaxy_from}:{$mission->system_from}:{$mission->position_from}[/coordinates]";
switch ($mission->type_from) {
case PlanetType::Planet->value:
case PlanetType::Moon->value:
if ($mission->planet_id_from !== null) {
$from = __('planet') . " [planet]{$mission->planet_id_from}[/planet]";
}
break;
case PlanetType::DebrisField->value:
$from = "[debrisfield]{$mission->galaxy_from}:{$mission->system_from}:{$mission->position_from}[/debrisfield]";
break;
}

$to = __('planet') . " [planet]{$mission->planet_id_to}[/planet]";
$from = FleetMissionPlanetFormatter::returnEndpointLabel($mission, 'from');
$to = FleetMissionPlanetFormatter::returnEndpointLabel($mission, 'to');

if ($return_resources->any()) {
$params = [
Expand Down Expand Up @@ -735,4 +723,25 @@ abstract protected function processArrival(FleetMission $mission): void;
* @return void
*/
abstract protected function processReturn(FleetMission $mission): void;

/**
* @return array{planet_coords?: string, planet_name?: string, planet_type?: string}
*/
protected function buildAttackerPlanetSnapshot(int|null $planetId): array
{
if ($planetId === null) {
return [];
}

$planet = $this->planetServiceFactory->make($planetId, true);
if ($planet === null) {
return [];
}

return [
'planet_coords' => $planet->getPlanetCoordinates()->asString(),
'planet_name' => $planet->getPlanetName(),
'planet_type' => $planet->getPlanetType() === PlanetType::Moon ? 'Moon' : 'Planet',
];
}
}
7 changes: 4 additions & 3 deletions app/GameMissions/AttackMission.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use OGame\Models\BattleReport;
use OGame\Models\Enums\PlanetType;
use OGame\Models\FleetMission;
use OGame\Support\FleetMissionPlanetFormatter;
use OGame\Models\Planet\Coordinate;
use OGame\Models\Resources;
use OGame\Services\CharacterClassService;
Expand Down Expand Up @@ -466,7 +467,7 @@ protected function processArrival(FleetMission $mission): void
$reaperCargoCapacity = $reaperObject->properties->capacity->calculate($attackerPlayer)->totalValue * $reaperCount;

$this->messageService->sendSystemMessageToPlayer($attackerPlayer, DebrisFieldHarvest::class, [
'from' => '[planet]' . $mission->planet_id_from . '[/planet]',
'from' => FleetMissionPlanetFormatter::tag($mission, 'from'),
'to' => '[debrisfield]' . $defenderPlanet->getPlanetCoordinates()->asString(). '[/debrisfield]',
'coordinates' => '[coordinates]' . $defenderPlanet->getPlanetCoordinates()->asString() . '[/coordinates]',
'ship_name' => $reaperObject->title,
Expand Down Expand Up @@ -685,7 +686,7 @@ private function createBattleReport(PlayerService $attackPlayer, PlanetService $
$attackerCharacterClass = $characterClassService->getCharacterClass($attackPlayer->getUser());
$defenderCharacterClass = $characterClassService->getCharacterClass($defenderPlayer->getUser());

$report->attacker = [
$report->attacker = array_merge([
'player_id' => $attackPlayer->getId(),
'resource_loss' => $battleResult->attackerResourceLoss->sum(),
'units' => $battleResult->attackerUnitsStart->toArray(),
Expand All @@ -694,7 +695,7 @@ private function createBattleReport(PlayerService $attackPlayer, PlanetService $
'armor_technology' => $battleResult->attackerArmorLevel,
'planet_id' => $battleResult->attackerPlanetId,
'character_class' => $attackerCharacterClass?->getName(),
];
], $this->buildAttackerPlanetSnapshot($battleResult->attackerPlanetId));

// TODO: Enhance battle reports to show individual participating fleets/defenders
// Currently shows aggregated defender data (combined units, planet owner's tech, single player_id)
Expand Down
9 changes: 5 additions & 4 deletions app/GameMissions/DeploymentMission.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use OGame\GameObjects\Models\Units\UnitCollection;
use OGame\Models\Enums\PlanetType;
use OGame\Models\FleetMission;
use OGame\Support\FleetMissionPlanetFormatter;
use OGame\Models\Planet\Coordinate;
use OGame\Services\PlanetService;
use RuntimeException;
Expand Down Expand Up @@ -85,16 +86,16 @@ protected function processArrival(FleetMission $mission): void
// Send a message to the player that the mission has arrived
if ($resources->any()) {
$this->messageService->sendSystemMessageToPlayer($targetPlayer, FleetDeploymentWithResources::class, [
'from' => '[planet]' . $mission->planet_id_from . '[/planet]',
'to' => '[planet]' . $mission->planet_id_to . '[/planet]',
'from' => FleetMissionPlanetFormatter::tag($mission, 'from'),
'to' => FleetMissionPlanetFormatter::tag($mission, 'to'),
'metal' => (string)$mission->metal,
'crystal' => (string)$mission->crystal,
'deuterium' => (string)($mission->deuterium + ($mission->deuterium_consumption / 2)), //if mission deployment: Add half of the consumed deuterium
]);
} else {
$this->messageService->sendSystemMessageToPlayer($targetPlayer, FleetDeployment::class, [
'from' => '[planet]' . $mission->planet_id_from . '[/planet]',
'to' => '[planet]' . $mission->planet_id_to . '[/planet]',
'from' => FleetMissionPlanetFormatter::tag($mission, 'from'),
'to' => FleetMissionPlanetFormatter::tag($mission, 'to'),
]);
}

Expand Down
5 changes: 3 additions & 2 deletions app/GameMissions/EspionageMission.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use OGame\Models\Enums\PlanetType;
use OGame\Models\EspionageReport;
use OGame\Models\FleetMission;
use OGame\Support\FleetMissionPlanetFormatter;
use OGame\Models\Planet\Coordinate;
use OGame\Models\Resources;
use OGame\Services\CounterEspionageService;
Expand Down Expand Up @@ -182,8 +183,8 @@ protected function processArrival(FleetMission $mission): void

$params = [
// IMPORTANT: pass the raw mission planet id inside [planet]...[/planet]
'planet' => '[planet]' . $mission->planet_id_from . '[/planet]',
'defender' => '[planet]' . $mission->planet_id_to . '[/planet]', // defender planet
'planet' => FleetMissionPlanetFormatter::tag($mission, 'from'),
'defender' => FleetMissionPlanetFormatter::tag($mission, 'to'),
'attacker_name' => $attackerName,
'chance' => $counterEspionageChance,
];
Expand Down
4 changes: 2 additions & 2 deletions app/GameMissions/MoonDestructionMission.php
Original file line number Diff line number Diff line change
Expand Up @@ -445,15 +445,15 @@ private function createBattleReport(PlayerService $attackPlayer, PlanetService $
'moon_created' => false,
];

$report->attacker = [
$report->attacker = array_merge([
'player_id' => $attackPlayer->getId(),
'resource_loss' => $battleResult->attackerResourceLoss->sum(),
'units' => $battleResult->attackerUnitsStart->toArray(),
'weapon_technology' => $battleResult->attackerWeaponLevel,
'shielding_technology' => $battleResult->attackerShieldLevel,
'armor_technology' => $battleResult->attackerArmorLevel,
'planet_id' => $battleResult->attackerPlanetId,
];
], $this->buildAttackerPlanetSnapshot($battleResult->attackerPlanetId));

$report->defender = [
'player_id' => $defenderPlayer->getId(),
Expand Down
3 changes: 2 additions & 1 deletion app/GameMissions/RecycleMission.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use OGame\Models\FleetMission;
use OGame\Models\Planet\Coordinate;
use OGame\Models\Resources;
use OGame\Support\FleetMissionPlanetFormatter;
use OGame\Services\DebrisFieldService;
use OGame\Services\ObjectService;
use OGame\Services\PlanetService;
Expand Down Expand Up @@ -133,7 +134,7 @@ protected function processArrival(FleetMission $mission): void

// Send a message to the player that the mission has arrived and the resources (if any) have been collected.
$this->messageService->sendSystemMessageToPlayer($originPlayer, DebrisFieldHarvest::class, [
'from' => '[planet]' . $mission->planet_id_from . '[/planet]',
'from' => FleetMissionPlanetFormatter::tag($mission, 'from'),
'to' => '[debrisfield]' . $targetCoordinate->asString(). '[/debrisfield]',
'coordinates' => '[coordinates]' . $targetCoordinate->asString() . '[/coordinates]',
'ship_name' => $harvesterShip->title,
Expand Down
9 changes: 5 additions & 4 deletions app/GameMissions/TransportMission.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use OGame\GameObjects\Models\Units\UnitCollection;
use OGame\Models\Enums\PlanetType;
use OGame\Models\FleetMission;
use OGame\Support\FleetMissionPlanetFormatter;
use OGame\Models\Planet\Coordinate;
use OGame\Models\Resources;
use OGame\Services\PlanetService;
Expand Down Expand Up @@ -78,8 +79,8 @@ protected function processArrival(FleetMission $mission): void

// Send a message to the origin player that the mission has arrived
$this->messageService->sendSystemMessageToPlayer($originPlayer, TransportArrived::class, [
'from' => '[planet]' . $mission->planet_id_from . '[/planet]',
'to' => '[planet]' . $mission->planet_id_to . '[/planet]',
'from' => FleetMissionPlanetFormatter::tag($mission, 'from'),
'to' => FleetMissionPlanetFormatter::tag($mission, 'to'),
'metal' => (string)$mission->metal,
'crystal' => (string)$mission->crystal,
'deuterium' => (string)$mission->deuterium,
Expand All @@ -88,8 +89,8 @@ protected function processArrival(FleetMission $mission): void
if ($originPlayer->getId() !== $targetPlayer->getId()) {
// Send a message to the target player that the mission has arrived
$this->messageService->sendSystemMessageToPlayer($targetPlayer, TransportReceived::class, [
'from' => '[planet]' . $mission->planet_id_from . '[/planet]',
'to' => '[planet]' . $mission->planet_id_to . '[/planet]',
'from' => FleetMissionPlanetFormatter::tag($mission, 'from'),
'to' => FleetMissionPlanetFormatter::tag($mission, 'to'),
'metal' => (string)$mission->metal,
'crystal' => (string)$mission->crystal,
'deuterium' => (string)$mission->deuterium,
Expand Down
5 changes: 3 additions & 2 deletions app/Services/FleetMissionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use OGame\Models\FleetMission;
use OGame\Models\Planet\Coordinate;
use OGame\Models\Resources;
use OGame\Support\FleetMissionPlanetFormatter;

/**
* Class FleetMissionService.
Expand Down Expand Up @@ -650,12 +651,12 @@ private function sendAcsDefendArrivalMessages(FleetMission $mission): void

// Send message to sender (Fleet Command)
$this->messageService->sendSystemMessageToPlayer($origin_player, AcsDefendArrivalSender::class, [
'to' => '[planet]' . $mission->planet_id_to . '[/planet]',
'to' => FleetMissionPlanetFormatter::tag($mission, 'to'),
]);

// Send message to host/target (Space Monitoring)
$this->messageService->sendSystemMessageToPlayer($target_player, AcsDefendArrivalHost::class, [
'to' => '[planet]' . $mission->planet_id_to . '[/planet]',
'to' => FleetMissionPlanetFormatter::tag($mission, 'to'),
]);
}

Expand Down
17 changes: 13 additions & 4 deletions app/Services/PlanetService.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public function abandonPlanet(): void

// Moon-origin fleets must keep a valid home planet so they can still return after the moon is gone.
if ($this->isMoon()) {
$this->redirectActiveOutgoingMoonMissionsToParentPlanet();
$this->redirectActiveMoonMissionsToParentPlanet();
}

// Fleet missions
Expand Down Expand Up @@ -280,10 +280,9 @@ public function abandonPlanet(): void
}

/**
* Rebind active missions launched from a moon to its parent planet before the moon is deleted.
* This preserves the original flight while ensuring the fleet returns to the planet instead.
* Rebind active missions involving a moon to its parent planet before the moon is deleted.
*/
private function redirectActiveOutgoingMoonMissionsToParentPlanet(): void
private function redirectActiveMoonMissionsToParentPlanet(): void
{
$parentPlanet = $this->getParentPlanet();
if ($parentPlanet === null) {
Expand All @@ -301,6 +300,16 @@ private function redirectActiveOutgoingMoonMissionsToParentPlanet(): void
'position_from' => $parentCoordinates->position,
'type_from' => PlanetType::Planet->value,
]);

FleetMission::where('planet_id_to', $this->planet->id)
->where('processed', 0)
->update([
'planet_id_to' => $parentPlanet->getPlanetId(),
'galaxy_to' => $parentCoordinates->galaxy,
'system_to' => $parentCoordinates->system,
'position_to' => $parentCoordinates->position,
'type_to' => PlanetType::Planet->value,
]);
}

/**
Expand Down
44 changes: 44 additions & 0 deletions app/Support/FleetMissionPlanetFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace OGame\Support;

use OGame\Models\Enums\PlanetType;
use OGame\Models\FleetMission;

/**
* Builds planet/coordinate placeholders for fleet messages that survive deleted bodies.
*/
final class FleetMissionPlanetFormatter
{
public static function tag(FleetMission $mission, string $endpoint): string
{
$planetId = $endpoint === 'from' ? $mission->planet_id_from : $mission->planet_id_to;
$galaxy = $endpoint === 'from' ? $mission->galaxy_from : $mission->galaxy_to;
$system = $endpoint === 'from' ? $mission->system_from : $mission->system_to;
$position = $endpoint === 'from' ? $mission->position_from : $mission->position_to;
$type = $endpoint === 'from' ? $mission->type_from : $mission->type_to;

if ($type === PlanetType::DebrisField->value) {
return "[debrisfield]{$galaxy}:{$system}:{$position}[/debrisfield]";
}

if ($planetId !== null) {
return "[planet]{$planetId}[/planet]";
}

return "[coordinates]{$galaxy}:{$system}:{$position}[/coordinates]";
}

public static function returnEndpointLabel(FleetMission $mission, string $endpoint): string
{
$tag = self::tag($mission, $endpoint);
$type = $endpoint === 'from' ? $mission->type_from : $mission->type_to;

if (in_array($type, [PlanetType::Planet->value, PlanetType::Moon->value], true)
&& str_starts_with($tag, '[planet]')) {
return __('planet') . ' ' . $tag;
}

return $tag;
}
}
47 changes: 47 additions & 0 deletions tests/Unit/FleetMissionPlanetFormatterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Tests\Unit;

use OGame\Models\Enums\PlanetType;
use OGame\Models\FleetMission;
use OGame\Support\FleetMissionPlanetFormatter;
use Tests\UnitTestCase;

class FleetMissionPlanetFormatterTest extends UnitTestCase
{
public function testTagUsesPlanetIdWhenAvailable(): void
{
$mission = new FleetMission();
$mission->planet_id_from = 42;
$mission->galaxy_from = 1;
$mission->system_from = 2;
$mission->position_from = 3;
$mission->type_from = PlanetType::Planet->value;

$this->assertSame('[planet]42[/planet]', FleetMissionPlanetFormatter::tag($mission, 'from'));
}

public function testTagFallsBackToCoordinatesWhenPlanetIdIsNull(): void
{
$mission = new FleetMission();
$mission->planet_id_from = null;
$mission->galaxy_from = 1;
$mission->system_from = 2;
$mission->position_from = 3;
$mission->type_from = PlanetType::Moon->value;

$this->assertSame('[coordinates]1:2:3[/coordinates]', FleetMissionPlanetFormatter::tag($mission, 'from'));
}

public function testTagUsesDebrisFieldPlaceholder(): void
{
$mission = new FleetMission();
$mission->planet_id_to = null;
$mission->galaxy_to = 4;
$mission->system_to = 5;
$mission->position_to = 6;
$mission->type_to = PlanetType::DebrisField->value;

$this->assertSame('[debrisfield]4:5:6[/debrisfield]', FleetMissionPlanetFormatter::tag($mission, 'to'));
}
}
Loading